private MooView view;
private JLabel statusBar;
private java.util.Timer timer;
+ private boolean updateView = true;
public static final int statusResetDelay = 3000;
public static final Font FONT = new Font("Helvetica", Font.PLAIN, 10);
public static final Color bgColor = new Color(192, 224, 255);
* Changes the sequence of the GUI.
* @param sequence the MIDI sequence to visualize
*/
- public void setSequence(Sequence sequence) {
+ public void setSequence(Sequence sequence, File file) {
seq = sequence;
+ if (file != null) setTitle("Moosique - " + file.getName());
+ else setTitle("Moosique");
view.setTracks(seq.getTracks(), true);
toolbar.resetProgInd();
}
* Calls on the main view to update the track views,
* and on the toolbar to update the progress indicator.
*/
- public void update(long tickPosition){
- view.update(tickPosition);
+ public synchronized void update(long tickPosition){
+ if (updateView) view.update(tickPosition);
toolbar.updateProgInd(tickPosition);
}
return octaveAction;
}
+ /**
+ * Advances the current progress counter by printing a "." to the System output.
+ */
private void advanceStatus() {
System.out.print(".");
}
public MooInstrumentList(int chan, int listType, ShortMessage programMsg) {
super(instruments[listType]);
programChangeMessage = programMsg;
- setSelectedIndex(programChangeMessage.getData1());
+ if (programChangeMessage != null) setSelectedIndex(programChangeMessage.getData1());
setChannel(chan);
setFont(Moosique.getGUI().FONT);
addActionListener(this);
public void setChannel(int chan) {
channel = chan;
try {programChangeMessage.setMessage(programChangeMessage.getCommand(), chan, programChangeMessage.getData1(), 0);}
- catch (InvalidMidiDataException e) {}
+ catch (Exception e) {}
}
/**
import javax.sound.midi.*;
import javax.swing.*;
import javax.swing.filechooser.*;
+import java.awt.*;
import java.awt.event.*;
import java.io.*;
-import java.awt.*;
+import java.util.*;
/**
* Moosiques GUI representing a menubar, menus and menuitems
*/
public class MooMenu extends JMenuBar implements ActionListener {
- private JMenu file, edit, keyboard, playback, music, help;
+ private JMenu file, reopen, edit, keyboard, playback, music, help;
private JFileChooser chooser;
private File directory;
- private String[] openedFiles;
+ private ArrayList recentFiles;
/**
* Creates the menu bar.
*/
public MooMenu() {
+ // Fetches last work directory, and recent files.
+ // directory = Moosique.getRecentDirectory();
+ ArrayList recentFiles = new ArrayList(); // ... = Moosique.getRecentFiles();
+
+ // Creates the sub-menus and their items.
file = createMenu("File", KeyEvent.VK_F);
add(file);
- addItem(file, "New", KeyEvent.VK_N, ActionEvent.CTRL_MASK);
- addItem(file, "Open...", KeyEvent.VK_O, ActionEvent.CTRL_MASK);
- addItem(file, "Save", KeyEvent.VK_S, ActionEvent.CTRL_MASK);
- addItem(file, "Save as...", KeyEvent.VK_A);
+ addItem(file, "New", KeyEvent.VK_N, true);
+ addItem(file, "Open...", KeyEvent.VK_O, true);
+ reopen = createMenu("Reopen", KeyEvent.VK_R);
+ Iterator it = recentFiles.iterator();
+ while (it.hasNext()) {
+ JMenuItem recentFile = new JMenuItem((String)it.next());
+ recentFile.addActionListener(this);
+ reopen.add(recentFile);
+ }
+ file.add(reopen);
+ addItem(file, "Save", KeyEvent.VK_S, true);
+ addItem(file, "Save as...", KeyEvent.VK_A, false);
file.addSeparator();
- addItem(file, "Exit", KeyEvent.VK_Q, ActionEvent.CTRL_MASK);
+ addItem(file, "Exit", KeyEvent.VK_Q, true);
edit = createMenu("Edit", KeyEvent.VK_E);
add(edit);
- addItem(edit, "Copy", KeyEvent.VK_C, ActionEvent.CTRL_MASK);
- addItem(edit, "Cut", KeyEvent.VK_X, ActionEvent.CTRL_MASK);
- addItem(edit, "Paste", KeyEvent.VK_V, ActionEvent.CTRL_MASK);
+ addItem(edit, "Copy", KeyEvent.VK_C, true);
+ addItem(edit, "Cut", KeyEvent.VK_X, true);
+ addItem(edit, "Paste", KeyEvent.VK_V, true);
edit.addSeparator();
- addItem(edit, "Select all", KeyEvent.VK_E, ActionEvent.CTRL_MASK);
- addItem(edit, "Invert selection", KeyEvent.VK_I, ActionEvent.CTRL_MASK);
+ addItem(edit, "Select all", KeyEvent.VK_E, true);
+ addItem(edit, "Invert selection", KeyEvent.VK_I, true);
edit.addSeparator();
- addItem(edit, "Preferences...", KeyEvent.VK_P, ActionEvent.CTRL_MASK);
+ addItem(edit, "Preferences...", KeyEvent.VK_P, true);
playback = createMenu("Playback", KeyEvent.VK_P);
add(playback);
addItem(playback, "Pause", "F7", KeyEvent.VK_A);
addItem(playback, "Stop", "F6", KeyEvent.VK_S);
playback.addSeparator();
- addItem(playback, "Set position...", KeyEvent.VK_E);
+ addItem(playback, "Set position...", KeyEvent.VK_E, false);
playback.addSeparator();
keyboard = createMenu("Set keyboard octave", KeyEvent.VK_K);
edit.add(keyboard);
- for (int i = 9; i >= 0; i--) addItem(keyboard, "Octave " + i, i + 48);
+ for (int i = 9; i >= 0; i--) addItem(keyboard, "Octave " + i, i + 48, false);
music = createMenu("Music", KeyEvent.VK_M);
add(music);
- addItem(music, "Add track...", KeyEvent.VK_A, ActionEvent.CTRL_MASK);
- addItem(music, "Delete track...", KeyEvent.VK_D, ActionEvent.CTRL_MASK);
- addItem(music, "Copy track...", KeyEvent.VK_Y, ActionEvent.CTRL_MASK);
- addItem(music, "Move track...", KeyEvent.VK_M, ActionEvent.CTRL_MASK);
+ addItem(music, "Add track...", KeyEvent.VK_A, true);
+ addItem(music, "Delete track...", KeyEvent.VK_D, true);
+ addItem(music, "Copy track...", KeyEvent.VK_Y, true);
+ addItem(music, "Move track...", KeyEvent.VK_M, true);
music.addSeparator();
- addItem(music, "Insert measure...", KeyEvent.VK_I);
- addItem(music, "Delete measure...", KeyEvent.VK_E);
+ addItem(music, "Insert measure...", KeyEvent.VK_I, false);
+ addItem(music, "Delete measure...", KeyEvent.VK_E, false);
music.addSeparator();
- addItem(music, "Set time signature...", KeyEvent.VK_S);
- addItem(music, "Set tempo...", KeyEvent.VK_M);
- addItem(music, "Scale velocity...", KeyEvent.VK_V);
- addItem(music, "Transpose...", KeyEvent.VK_T);
+ addItem(music, "Set time signature...", KeyEvent.VK_S, false);
+ addItem(music, "Set tempo...", KeyEvent.VK_M, false);
+ addItem(music, "Scale velocity...", KeyEvent.VK_V, false);
+ addItem(music, "Transpose...", KeyEvent.VK_T, false);
help = createMenu("Help", KeyEvent.VK_L);
add(help);
-
+
addItem(help, "User manual", "F1", KeyEvent.VK_M);
help.addSeparator();
- addItem(help, "About", KeyEvent.VK_A);
+ addItem(help, "About", KeyEvent.VK_A, false);
}
/**
* Creats a menu in the menubar.
* Creats a menuitem in the menu.
* @param menu The menu where to add the menuitem.
* @param name The name of the menuitem.
+ * @param mnemonic The keyboard mnemonic used to access this menuitem
+ * @param accelerate whether to create a keyboard accelerator for this item
* @return item The menuitem created.
*/
- private JMenuItem addItem(JMenu menu, String name, int mnemonic) {
+ private JMenuItem addItem(JMenu menu, String name, int mnemonic, boolean accelerate) {
JMenuItem item = new JMenuItem(name);
item.addActionListener(this);
+ item.setMnemonic(mnemonic);
+ if (accelerate) item.setAccelerator(KeyStroke.getKeyStroke(mnemonic, ActionEvent.CTRL_MASK));
menu.add(item);
return item;
}
* @param menu The menu to where to add the menuitem.
* @param name The name of the menuitem.
* @param key The keystroke to access this menuitem.
+ * @param mnemonic The keyboard mnemonic used to access this menuitem
* @return item The menuitem created.
*/
private JMenuItem addItem(JMenu menu, String name, String key, int mnemonic) {
JMenuItem item = new JMenuItem(name);
- item.setAccelerator(KeyStroke.getKeyStroke(key));
item.setMnemonic(mnemonic);
+ item.setAccelerator(KeyStroke.getKeyStroke(key));
item.addActionListener(this);
menu.add(item);
return item;
}
-
- /**
- * Creats a menuitem in the menu.
- * @param menu The menu to where to add the menuitem.
- * @param name The name of the menuitem.
- * @param key The keystroke to access this menuitem.
- * @param mask The keyboard mask.
- * @return item The menuitem created.
- */
- private JMenuItem addItem(JMenu menu, String name, int key, int mask) {
- JMenuItem item = new JMenuItem(name);
- item.setAccelerator(KeyStroke.getKeyStroke(key, mask));
- item.setMnemonic(key);
- item.addActionListener(this);
- menu.add(item);
- return item;
- }
-
+
private boolean isMidiFile(File f) {
if(f != null) {
String extension = f.getName().substring(f.getName().lastIndexOf('.') + 1).toLowerCase().trim();
File file = chooser.getSelectedFile();
if(returnVal == JFileChooser.APPROVE_OPTION && isMidiFile(file)) {
directory = file.getParentFile();
- if (!Moosique.promptOnUnsavedChanges())
- Moosique.load(file);
+ if (!Moosique.promptOnUnsavedChanges()) {
+ if (Moosique.load(file)) {
+ // Adds an item for this file to the reopen menu.
+ String filename = file.getAbsolutePath();
+ JMenuItem recentFile = new JMenuItem(filename);
+ for (int i = 0; i < reopen.getMenuComponentCount(); i++) {
+ if (filename.equals(((JMenuItem)reopen.getMenuComponent(i)).getText()))
+ reopen.remove(i);
+ }
+ reopen.insert(recentFile, 0);
+ if (reopen.getMenuComponentCount() > 5) reopen.remove(5);
+ }
+ }
}
} else if (command == "Save") {
if (!Moosique.save()) showSaveAsDialog();
"About Moosique",
JOptionPane.INFORMATION_MESSAGE,
new ImageIcon(Moosique.getGUI().logo));
+ } else if (reopen.isMenuComponent((JMenuItem)e.getSource())) {
+ String recentFile = ((JMenuItem)e.getSource()).getText();
+ if (!Moosique.promptOnUnsavedChanges()) Moosique.load(new File(recentFile));
}
}
return "MIDI files";
}
}
-
-
}
import javax.sound.midi.*;
/**
- * Functional representation of a MIDI note, which adds functionality to the existent MidiEvent class.
+ * Functional representation of a MIDI note, which adds functionality to the existing MidiEvent class.
* Also provides a reference to the corresponding NoteOff event.
*
* @author Einar Pehrson
private MooTrackView mtv;
private MooNote note;
- private JPopupMenu popup;
- private JMenu popupTranspUp, popupTranspDown;
- private JMenuItem popupRemove, popupProp;
- private JMenuItem[] popupTranspUpItems, popupTranspDownItems;
private Rectangle pitchRect, veloRect;
private String notePitch, noteVelocity;
private boolean selected = false, leftMouseButtonPressed = false, mouseIn = false;
// Defines coordinates.
pitchRect = new Rectangle(0, 0, 15, 10);
veloRect = new Rectangle(20, 0, 40, 10);
-
- // Creates pop-up menu.
- popup = new JPopupMenu();
- popupProp = addMenuItem(popup, "Preferences...");
- popupRemove = addMenuItem(popup, "Remove");
- popupTranspUpItems = new JMenuItem[12];
- popupTranspDownItems = new JMenuItem[12];
- popupTranspUp = createTransposeMenu(popup, popupTranspUpItems, "note up");
- popupTranspDown = createTransposeMenu(popup, popupTranspDownItems, "note down");
}
/**
}
/**
- * Compares the note of this element to that of another note.
+ * Compares the note of this element to that of another element.
* @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object
*/
public int compareTo(Object o) {
*/
public void select() {
selected = true;
- mtv.selectNote(this);
+ Moosique.selectNote(this);
setBackground(invBgColor);
textColor = Color.white;
repaint();
mtv.layoutElement(this,true);
}
- /**
- * Adds a menu item with the given command to the given popup menu.
- */
- private JMenuItem addMenuItem(JPopupMenu menu, String command) {
- JMenuItem item = new JMenuItem(command);
- item.addActionListener(new PopupListener());
- menu.add(item);
- return item;
- }
-
- /**
- * Adds a menu item with the given command to the given menu.
- */
- private JMenuItem addMenuItem(JMenu menu, String command) {
- JMenuItem item = new JMenuItem(command);
- item.addActionListener(new PopupListener());
- menu.add(item);
- return item;
- }
-
- /**
- * Creates a transpose sub menu with the given title in the given popup menu,
- * inserting the items into the given array.
- */
- private JMenu createTransposeMenu(JPopupMenu menu, JMenuItem[] items, String title) {
- JMenu trans = new JMenu("Transpose " + title);
- menu.add(trans);
- items[0] = addMenuItem(trans, "One octave");
- for (int i = 1; i < 12; i++) {
- items[i] = addMenuItem(trans, (i) + " halftones");
- }
- return trans;
- }
-
/**
* Listener that checks the mouse actions on this element.
*/
public void mouseReleased(MouseEvent e) {
if (!maybeShowPopup(e) && !mouseIn) {
int y = e.getY();
- if (y < 0) mtv.maybeMoveSelectedNotes((int)Math.floor((double)y / MooTrackView.NOTE_HEIGHT) * MooTrackView.NOTE_HEIGHT);
- if (y > getHeight()) mtv.maybeMoveSelectedNotes((int)Math.ceil(((double)y - getHeight()) / MooTrackView.NOTE_HEIGHT) * MooTrackView.NOTE_HEIGHT);
-
+ if (y < 0) mtv.moveSelectedNotes((Moosique.getSequence().getResolution() / 4)* (int)Math.floor((double)y / MooTrackView.NOTE_HEIGHT));
+ if (y > getHeight()) mtv.moveSelectedNotes((Moosique.getSequence().getResolution() / 4) * (int)Math.ceil(((double)y - getHeight()) / MooTrackView.NOTE_HEIGHT));
}
}
*/
private boolean maybeShowPopup(MouseEvent e) {
if (!e.isPopupTrigger()) return false;
- if (!e.isControlDown()) {
- if (!selected || mtv.isTheOnlySelected((MooNoteElement)e.getComponent())) popup.show(e.getComponent(), e.getX(), e.getY());
- else mtv.showSelectionPopup(e.getComponent(), e.getX(), e.getY());
- }
+ if (!e.isControlDown()) mtv.showSelectionPopup(e.getComponent(), e.getX(), e.getY());
return true;
}
}
-
- /**
- * Takes the appropriate action when a user selects an item on the popup menu.
- */
- class PopupListener implements ActionListener {
- public void actionPerformed(ActionEvent e) {
- Object source = e.getSource();
- if (source == popupProp) {
- System.out.println("Duration: " + note.getDuration());
- new MooDialog(note);
- System.out.println("Duration: " + note.getDuration());
- newLayout();
- update();
- } else if (source == popupRemove) {
- remove();
- } else if (source == popupTranspUpItems[0]) {
- transpose(12);
- } else if (source == popupTranspDownItems[0]) {
- transpose(-12);
- } else {
- for (int i = 1; i < 12; i++) {
- if (source == popupTranspUpItems[i]) transpose(i);
- else if (source == popupTranspDownItems[i]) transpose(-i);
- }
- }
- }
- }
}
+import javax.sound.midi.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
-import javax.sound.midi.*;
/**
* The application's toolbar, with the most frequently used commands.
* @param tickPosition the tick position to visualize
*/
public void updateProgInd(long tickPosition) {
- ticksPerBeat = Moosique.getSequence().getResolution();
+ System.out.print("Updating to " + tickPosition + " = ");
if (tickPosition == 0) {
resetProgInd();
+ System.out.println("1:1:1");
} else {
- // Otherwise, calculates the current song position in measures, beats and ticks.
- long measures = tickPosition / (beatsPerMeasure * ticksPerBeat);
- long beats = (tickPosition - measures * beatsPerMeasure * ticksPerBeat) / ticksPerBeat;
- long ticks = tickPosition - measures * beatsPerMeasure * ticksPerBeat - beats * ticksPerBeat;
- measuresValue.setText(Long.toString(1 + measures));
- beatsValue.setText(Long.toString(1 + beats));
- ticksValue.setText(Long.toString(1 + ticks));
+ int[] position = Moosique.getPositionForTicks(tickPosition);
+ System.out.println("" + position[0] + ":" + position[1] + ":" + position[2]);
+ measuresValue.setText(Integer.toString(position[0]));
+ beatsValue.setText(Long.toString(position[1]));
+ ticksValue.setText(Long.toString(position[2]));
}
}
}
/**
- * Checks if the focus is lost.
+ * When the title field loses focus, updates the corresponding MidiEvent.
*/
class TitleFocusListener extends FocusAdapter {
public void focusLost(FocusEvent e) {
- // Update the MidiEvent containing the title of this track
+ try {
+ trackNameMessage.setMessage(3, title.getText().getBytes(), title.getText().length());
+ } catch (InvalidMidiDataException ex) {}
}
}
MidiEvent me;
MooNote mn;
instruments.setChannel(channel);
- // Query the user before rechannelling???
+ // Prompt the user before rechannelling???
for (int j = 0; j < track.size(); j++) {
me = track.get(j);
if (me instanceof MooNote){
private MooKeyboard keyboard;
private JPopupMenu popup, selPopup;
- private JMenu selPopupTranspUp, selPopupTranspDown;
- private JMenuItem popupAdd, popupPaste;
+ private JMenu popupAdd, selPopupTranspUp, selPopupTranspDown;
+ private JMenuItem popupAddItemsCustom, popupAddItemsLast, popupPaste;
+ private JMenuItem popupAddItemsWhole, popupAddItemsHalf, popupAddItemsQuarter, popupAddItemsEighth, popupAddItemsSixteenth;
private JMenuItem selPopupCopy, selPopupCut, selPopupRemove;
private JMenuItem[] selPopupTranspUpItems, selPopupTranspDownItems;
private ArrayList coords;
- private TreeSet selection;
private Insets insets;
private Rectangle box;
- private int ticksPerSixteenth, popupY = 0;
+ private int ticksPerSixteenth, popupY = 0, lastNoteLength = 2;
private boolean leftMouseButtonPressed = false;
private static boolean snapToSixteenths = true;
- protected static int viewLength = 0;
- protected static int extraHeight = 0;
+ private static int viewLength = 0;
+ private static int extraHeight = 0;
public static final int NOTE_HEIGHT = 10, NOTE_WIDTH = 40, VIEW_WIDTH = 200;
/**
ticksPerSixteenth = Moosique.getSequence().getResolution() / 4;
insets = getInsets();
coords = new ArrayList(track.size() / 2);
- selection = new TreeSet();
// Configures panel
setBackground(Color.white);
// Creates panel pop-up menu.
popup = new JPopupMenu();
- popupAdd = addMenuItem(popup, "Add note...");
+ popupAdd = new JMenu("Add note");
+ popup.add(popupAdd);
+ popupAddItemsCustom = addMenuItem(popupAdd, "Custom...");
+ popupAddItemsLast = addMenuItem(popupAdd, "As last added");
+ popupAdd.addSeparator();
+ popupAddItemsWhole = addMenuItem(popupAdd, "Whole");
+ popupAddItemsHalf = addMenuItem(popupAdd, "Half");
+ popupAddItemsQuarter = addMenuItem(popupAdd, "Quarter");
+ popupAddItemsEighth = addMenuItem(popupAdd, "Eighth");
+ popupAddItemsSixteenth = addMenuItem(popupAdd, "Sixteenth");
popupPaste = addMenuItem(popup, "Paste");
// Creates selection pop-up menu.
/**
* Adds a standard note to this track.
*/
- private void addStandardNote() {
+ private void addNoteAtClickY(int length) {
+ lastNoteLength = length;
int row = (popupY - insets.top) / NOTE_HEIGHT;
long timestamp = (long)(ticksPerSixteenth * row);
- addNote(new MooNote(title.getChannel(), 60, 100, timestamp, Moosique.getSequence().getResolution() / 4));
+ addNote(new MooNote(title.getChannel(), 60, 100, timestamp, ticksPerSixteenth * length));
}
/**
repaint();
}
- /**
- * Selects the given note
- * @param the note to select
- */
- public void selectNote(MooNoteElement elem) {
- selection.add(elem);
- }
-
- /**
- * Deselects the given note
- * @param the note to deselect
- */
- public void deselectNote(MooNoteElement elem) {
- selection.remove(elem);
- }
-
- /**
- * Deselects all notes.
- */
- public void deselectAllNotes() {
- Iterator it = selection.iterator();
- while(it.hasNext()) {
- ((MooNoteElement)it.next()).deselect();
- }
- selection.clear();
- }
-
- /**
- * Determines if the given MooNoteElement is the only one in the track view that is selected.
- * @return if the given element is the only selected one
- */
- public boolean isTheOnlySelected(MooNoteElement elem) {
- Iterator it = selection.iterator();
- while(it.hasNext()) {
- if (!it.next().equals(elem)) return false;
- }
- return true;
- }
-
/**
* Copies the current selection.
*/
public void copySelectedNotes() {
+ TreeSet selection = Moosique.getSelection();
ArrayList copyBuffer = new ArrayList(selection.size());
Iterator it = selection.iterator();
while(it.hasNext()) {
mn.setChannel(title.getChannel());
addNote(mn);
}
+ Moosique.setEdited();
}
}
* Removes the current selection.
*/
public void removeSelectedNotes() {
+ TreeSet selection = Moosique.getSelection();
Iterator it = selection.iterator();
while(it.hasNext()) {
removeNote((MooNoteElement)it.next());
}
selection.clear();
+ Moosique.setEdited();
}
/**
* Transposes all selected notes the given number of halftones.
*/
private void transposeSelectedNotes(int halftones) {
+ TreeSet selection = Moosique.getSelection();
Iterator it = selection.iterator();
while(it.hasNext()) {
MooNoteElement elem = (MooNoteElement)it.next();
elem.transpose(halftones);
}
+ Moosique.setEdited();
}
/**
* @param ticks the number of ticks to move the selection.
*/
public void moveSelectedNotes(int ticks) {
+ TreeSet selection = Moosique.getSelection();
if (ticks < 0) {
// If the selection should be moved upwards, traverses the list in the natural order.
Iterator it = selection.iterator();
layoutElement(elem, true);
}
}
- }
-
- /**
- * Moves the current selection, depending on the given delta y.
- * @param y the number of pixels the selection is moved.
- */
- public void maybeMoveSelectedNotes(int y) {
- moveSelectedNotes(ticksPerSixteenth * (y / NOTE_HEIGHT));
+ Moosique.setEdited();
}
/**
*/
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
- deselectAllNotes();
+ Moosique.deselectAllNotes();
if (e.getClickCount() == 2) {
popupY = e.getY();
- addStandardNote();
+ addNoteAtClickY(lastNoteLength);
}
}
}
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
// Handling panel popup actions.
- if (source == popupAdd) {
- addStandardNote();
+ if (source == popupAddItemsLast) {
+ addNoteAtClickY(lastNoteLength);
+ } else if (source == popupAddItemsCustom) {
+ /* Show the user a dialog (identical to note preferences...
+ then call addNote(new MooNote(
+ title.getChannel(), pitch, velocity, timestamp, duration));
+ */
+ } else if (source == popupAddItemsWhole) {
+ addNoteAtClickY(16);
+ } else if (source == popupAddItemsHalf) {
+ addNoteAtClickY(8);
+ } else if (source == popupAddItemsQuarter) {
+ addNoteAtClickY(4);
+ } else if (source == popupAddItemsEighth) {
+ addNoteAtClickY(2);
+ } else if (source == popupAddItemsSixteenth) {
+ addNoteAtClickY(1);
} else if (source == popupPaste) {
pasteCopiedNotes();
// Handling selection popup actions.
private static MidiChannel activeChannel;
private static ArrayList copyBuffer, emptyTracks, timeSignatures, tempoChanges;
+ private static TreeSet selection;
private static Map trackMute = new HashMap();
private static Map trackSolo = new HashMap();
private static Thread player;
* loads a sequence and creates the GUI.
*/
public static void main (String[] args) {
- out("\nMoosique version 1.0\n");
+ out("\nMoosique version 1.0\n", true);
// Parses command-line arguments.
String fileArg = null;
}
// Acquires MIDI devices and connects them.
- System.out.print("Initializing MIDI devices.");
+ out("Initializing MIDI devices.", false);
try {
// Configures sequencer
sequencer = MidiSystem.getSequencer();
- System.out.print(".");
+ advanceStatus();
sequencer.open();
sequencer.addMetaEventListener(new SongEndListener());
// Configures synthesizer
synthesizer = MidiSystem.getSynthesizer();
- System.out.print(".");
+ advanceStatus();
synthesizer.open();
// Configures receiver, transmitter and channels.
channels = synthesizer.getChannels();
setActiveChannel(0);
} catch (MidiUnavailableException e) {
- out("Failed, quitting.");
+ out("Failed, quitting.", true);
System.exit(1);
}
- out("Done");
+ out("Done", true);
// Loads user preferences (work directory, last opened files etc).
loadPreferences();
//If a filename is given as the command-line argument, attempts to load a sequence from the file.
if (fileArg != null) {
- System.out.print("Loading MIDI sequence from " + fileArg + "...");
+ out("Loading MIDI sequence from " + fileArg + "...", false);
if (!load(new File(fileArg))) {
- out("Failed, creating new sequence");
+ out("Failed, creating new sequence", true);
clearSequence();
} else {
- out("Done");
+ out("Done", true);
}
} else {
// Otherwise creates a new empty one.
// Builds GUI, unless n-flag is set.
if (makeGUI) {
- System.out.print("Building GUI.");
+ out("Building GUI.", false);
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {}
gui = new MooGUI(seq, file);
- out("Done");
+ out("Done", true);
} else {
- System.out.print("Playing...");
+ out("Playing...", false);
play();
while (sequencer.isRunning()) {}
- out("Done");
+ out("Done", true);
quit();
}
}
/**
* Calculates the position (measures, beats, ticks) in the current sequence for the given tick position.
+ * @param tickPosition the tick position for which to calculate the position
* @return an array of integers where index 0 is measures, 1 is beats and 2 is ticks.
*/
- public static int[] getPositionForTicks(long ticks) {
- /*
- int measures, beats, ticks;
- for (int i = 0; i < timeSignatures.length; i++) {
- long tick = timeSignatures[i].getTick();
- // Split the ticks in the interval into measures, beats and ticks.
+ public static int[] getPositionForTicks(long tickPosition) {
+ int ticksPerBeat = seq.getResolution(), sigs = timeSignatures.size(), beatsPerMeasure = 4;
+ long measures = 0, beats = 0, ticks = 0;
+ if (sigs > 1) {
+ /*
+ Iterator it = timeSignatures.iterator();
+ MidiEvent lastTSEvent = (MidiEvent)it.next();
+ if (lastTSEvent.getTick() != 0) tickPos += (int)lastTSEvent.getTick();
+ while(it.hasNext()) {
+ MidiEvent nextTSEvent = (MidiEvent)it.next();
+ long tickDiff = nextTSEvent.getTick() - lastTSEvent.getTick();
+ ts = decodeTimeSig(((MetaMessage)lastTSEvent.getMessage()).getData());
+ beatsPerMeasure = ts[0] * (4 / ts[1]);
+ tickPos += ((beatsPerMeasure * measures + beats) * res + ticks);
+ }
+ */
+ } else {
+ if (sigs == 1) {
+ MidiEvent TSEvent = (MidiEvent)timeSignatures.get(0);
+ int[] ts = decodeTimeSig(((MetaMessage)TSEvent.getMessage()).getData());
+ beatsPerMeasure = ts[0] * (4 / ts[1]);
+ }
+ measures = tickPosition / (beatsPerMeasure * ticksPerBeat);
+ beats = (tickPosition - measures * beatsPerMeasure * ticksPerBeat) / ticksPerBeat;
+ ticks = tickPosition - measures * beatsPerMeasure * ticksPerBeat - beats * ticksPerBeat;
}
- */
- int[] pos = {1, 1, 1};
+ int[] pos = {(int)measures + 1, (int)beats + 1, (int)ticks + 1};
return pos;
}
* @return the tick position.
*/
public static long getTicksForPosition(int measures, int beats, int ticks) {
+ int res = seq.getResolution();
long tickPos = 0;
- /*
- for (int i = 0; i < timeSignatures.length; i++) {
- long tick = timeSignatures[i].getTick();
- // Add the measures, beats and ticks in the interval.
+ switch (timeSignatures.size()) {
+ case 0:
+ tickPos = (4 * measures + beats) * res + ticks;
+ case 1:
+ MidiEvent TSEvent = (MidiEvent)timeSignatures.get(0);
+ int[] ts = decodeTimeSig(((MetaMessage)TSEvent.getMessage()).getData());
+ int beatsPerMeasure = ts[0] * (4 / ts[1]);
+ tickPos = (beatsPerMeasure * measures + beats) * res + ticks;
+ default:
+ Iterator it = timeSignatures.iterator();
+ MidiEvent lastTSEvent = (MidiEvent)it.next();
+ if (lastTSEvent.getTick() != 0) tickPos += (int)lastTSEvent.getTick();
+ while(it.hasNext()) {
+ MidiEvent nextTSEvent = (MidiEvent)it.next();
+ long tickDiff = nextTSEvent.getTick() - lastTSEvent.getTick();
+ ts = decodeTimeSig(((MetaMessage)lastTSEvent.getMessage()).getData());
+ beatsPerMeasure = ts[0] * (4 / ts[1]);
+ tickPos += ((beatsPerMeasure * measures + beats) * res + ticks);
+ }
}
- */
return tickPos;
}
* @return the tempo in beats per minute
*/
public static int decodeTempo(byte[] bytes) {
- return 60000000 / (bytes[0] * 65536 + bytes[1] * 256 + bytes[2]); // bytes[0] & 0xFF ???
+ return 60000000 / (bytes[0] * 65536 + bytes[1] * 256 + bytes[2]);
}
/**
public static byte[] encodeTimeSig(int numerator, int denominator) {
byte[] b = {
(byte)numerator,
- (byte)(Math.log(denominator) / Math.log(2)),
+ (byte)(Math.log(denominator) / Math.log(2)), // logarithm of denominator in base 2
(byte)96,
(byte)8
};
+ /* ***
+ ** SELECTION METHODS **
+ *** */
+
+
+
+
+
+
+
+
+ /**
+ * Returns the current selection.
+ * @return the current selection
+ */
+ public static TreeSet getSelection() {
+ return selection;
+ }
+
+ /**
+ * Selects the given note
+ * @param the note to select
+ */
+ public static void selectNote(MooNoteElement elem) {
+ selection.add(elem);
+ }
+
+ /**
+ * Deselects the given note
+ * @param the note to deselect
+ */
+ public static void deselectNote(MooNoteElement elem) {
+ selection.remove(elem);
+ }
+
+ /**
+ * Deselects all notes.
+ */
+ public static void deselectAllNotes() {
+ Iterator it = selection.iterator();
+ while(it.hasNext()) {
+ ((MooNoteElement)it.next()).deselect();
+ }
+ selection.clear();
+ }
+
+ /**
+ * Determines if the given MooNoteElement is the only one in the track view that is selected.
+ * @return if the given element is the only selected one
+ */
+ public static boolean isTheOnlySelected(MooNoteElement elem) {
+ Iterator it = selection.iterator();
+ while(it.hasNext()) {
+ if (!it.next().equals(elem)) return false;
+ }
+ return true;
+ }
+
+
+
+
+
+
+
+
/* ***
** PLAYBACK METHODS **
*** */
if (sequencer.isRunning()) {
sequencer.stop();
}
- if (player != null) player.interrupt();
+ player = null;
}
/**
sequencer.stop();
}
sequencer.setTickPosition(editPosition);
- if (player != null) player.interrupt();
+ player = null;
gui.update((long)0);
}
seq = new Sequence(Sequence.PPQ, DEFAULT_RESOLUTION, DEFAULT_TRACKS);
Track[] tracks = seq.getTracks();
- // Creates messages for default tempo (120) and time signature (4/4).
+ // Creates messages for default tempo (120) and time signature (4/4), and adds them to track 0.
MetaMessage timeSigMsg = new MetaMessage();
MetaMessage tempoMsg = new MetaMessage();
try {
timeSigMsg.setMessage(88, encodeTimeSig(4, 4), 4);
tempoMsg.setMessage(81, encodeTempo(120), 3);
} catch (InvalidMidiDataException e) {}
-
- // Adds them to track 0.
tracks[0].add(new MidiEvent(timeSigMsg, (long)0));
tracks[0].add(new MidiEvent(tempoMsg, (long)0));
initializeTrack(tracks[1], 0, 24, "Guitar");
initializeTrack(tracks[2], 1, 33, "Bass");
initializeTrack(tracks[3], 9, 0, "Drums");
-
- file = null;
- emptyTracks = new ArrayList();
- timeSignatures = new ArrayList();
- tempoChanges = new ArrayList();
- trackSolo = new HashMap();
- trackMute = new HashMap();
- copyBuffer = new ArrayList();
} catch (InvalidMidiDataException e) {}
+
+ // Reinitializes sequence variables
+ file = null;
+ reinitializeVariables();
+
// Sends the sequence to the GUI.
- if (gui != null) gui.setSequence(seq);
+ if (gui != null) gui.setSequence(seq, null);
}
/**
titleMsg.setMessage(3, title.getBytes(), title.length());
} catch (InvalidMidiDataException e) {}
+ // Sends the program change to the channel
+ getChannel(channel).programChange(program);
+
// Adds them to the track.
track.add(new MidiEvent(programMsg, (long)0));
track.add(new MidiEvent(titleMsg, (long)0));
edited = false;
Track[] tracks = seq.getTracks();
- emptyTracks = new ArrayList();
- timeSignatures = new ArrayList();
- tempoChanges = new ArrayList();
- trackMute = new HashMap();
- trackSolo = new HashMap();
- copyBuffer = new ArrayList();
+ reinitializeVariables();
// Searches track 0 for changes in tempo and time signature.
MidiEvent event;
MetaMessage metaMsg;
- ArrayList ts = new ArrayList(), tc = new ArrayList();
for (int i = 0; i < tracks[0].size(); i++) {
event = tracks[0].get(i);
if (event.getMessage().getStatus() == MetaMessage.META) {
}
}
}
+ Comparator c = new MidiEventComparator();
+ Collections.sort(tempoChanges, c);
+ Collections.sort(timeSignatures, c);
// Converts tracks.
for (int i = 0; i < tracks.length; i++) {
}
// Sends sequence to GUI and sequencer, then returns
- if (gui != null) gui.setSequence(seq);
+ if (gui != null) gui.setSequence(seq, file);
try {
sequencer.setSequence(seq);
} catch (InvalidMidiDataException e) {}
// Math.round(mn.getTick() / ticksPerSixteenth);
}
+ /**
+ * Reinitializes sequence-specific variables.
+ */
+ private static void reinitializeVariables() {
+ emptyTracks = new ArrayList();
+ timeSignatures = new ArrayList();
+ tempoChanges = new ArrayList();
+ trackSolo = new HashMap();
+ trackMute = new HashMap();
+ copyBuffer = new ArrayList();
+ selection = new TreeSet();
+ }
+
/**
* Loads the user preferences.
*/
}
/**
- * Prompts the user .
+ * Prompts the user to save any unsaved changes.
*/
public static boolean promptOnUnsavedChanges() {
if (!edited) return false;
/**
* Prints the given string to the System output.
*/
- private static void out(String text) {
- System.out.println(text);
+ private static void out(String text, boolean newLine) {
+ if (newLine) System.out.println(text);
+ else System.out.print(text);
+ }
+
+ /**
+ * Advances the current progress counter by printing a "." to the System output.
+ */
+ private static void advanceStatus() {
+ out(".", false);
}
/**
*/
public static class PlayThread extends Thread {
public void run() {
- // Updates the GUI with the current tick position.
- gui.update(sequencer.getTickPosition());
-
- // Puts the thread to sleep for as long as it takes
- // the sequencer to reach the next sixteenth.
- try {
- //sleep((long)((15000 / getTempo()) * (tickDiff / ticksPerSixteenth)));
- sleep (10);
- } catch (InterruptedException e) {
- Moosique.stop();
+ Thread currentThread = Thread.currentThread();
+ while(currentThread == player) {
+ gui.update(sequencer.getTickPosition());
+ try {
+ sleep(10);
+ } catch (InterruptedException e) {
+ Moosique.stop();
+ }
}
}
}
/**
- * A listener for detecting the end of the sequence.
+ * A listener for detecting the Meta event signifying the end of the sequence.
*/
public static class SongEndListener implements MetaEventListener {
public void meta(MetaMessage event) {
if (event.getType() == 47)
- // End of sequence
stop();
- }
+ }
}
}
\f
BUGGAR
+x Exceptions vid inspelning till ny fil.
x Play-knappen ändras inte till Pause.
x Varför ritar den ut de tomma spåren i en ny fil? Rätt, men hur?
x Varför hänger sig Play om man ändrar duration på en not?
\f
PREFERENCES
-x MIDI Devices - Comboboxar över tillgängliga sequencers och synthesizers.
-x "Allow smaller note elements than 1/16" - Checkbox mot variabeln MooTrackView.snapToSixteenths
+x MIDI Devices Comboboxar över tillgängliga sequencers och synthesizers.
+x "Follow song on play" Checkbox mot booleanen MooGUI.updateView
+x "Allow smaller note elements than 1/16" Checkbox mot booleanen MooTrackView.snapToSixteenths
+
Vilka mer booleaner ska man kunna ändra?
\f
Mnemonicsarna är konstiga.
MooToolbar
+ Lägg till:
+ ! Lablar för tempo och taktart
+ ! Fler knappar - Preferences, Octave up/down,
+ ? Volymkontroll Använd Moosique.setVolume(long volume);
+ ? Progress Bar getTickLength() för max, getTickPosition() för position.
Fixa så att allt sitter fast! Med BoxLayout?
- Lägg till?
- Volymkontroll Använd Moosique.setVolume(long volume);
- Progress Bar getTickLength() för max, getTickPosition() för position.
- Lablar för tempo och taktart
- Fler knappar - Preferences, Octave up/down,
MooTrackView
Ändra längden på MooNoteElement (JPanel).