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();
- }
+ }
}
}