private static long editPosition;
private static boolean makeGUI = true, initSound = true, edited = false, drawEmptyTracks = false;
public static final int DEFAULT_RESOLUTION = 96, DEFAULT_TRACKS = 4;
+ public static final int WHOLE_NOTE = 0, HALF_NOTE = 1, QUARTER_NOTE = 2, EIGHTH_NOTE = 3, SIXTEENTH_NOTE = 4;
/**
* Starts the application.
String fileArg = null;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-n")) makeGUI = false;
- else if (args[i].equals("-m")) initSound = false;
else if (fileArg == null) fileArg = args[i];
}
// Acquires MIDI devices and connects them.
System.out.print("Initializing MIDI devices.");
try {
+ // Configures sequencer
sequencer = MidiSystem.getSequencer();
System.out.print(".");
sequencer.open();
+ sequencer.addMetaEventListener(new SongEndListener());
+
+ // Configures synthesizer
synthesizer = MidiSystem.getSynthesizer();
System.out.print(".");
synthesizer.open();
+
+ // Configures receiver, transmitter and channels.
receiver = synthesizer.getReceiver();
sequencer.getTransmitter().setReceiver(receiver);
channels = synthesizer.getChannels();
}
System.out.println("Done");
+ // 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 + "...");
* Calculates the position (measures, beats, ticks) in the current sequence for the given tick position.
* @return an array of integers where index 0 is measures, 1 is beats and 2 is ticks.
*/
- public static int[] getPositionForTick(long ticks) {
+ public static int[] getPositionForTicks(long ticks) {
/*
int measures, beats, ticks;
for (int i = 0; i < timeSignatures.length; i++) {
}
}
-
// Disables input to volatile components
// gui.disable();
// Creates the visualisation thread and starts it.
- player = new Thread () {
- public void run() {
- while(sequencer.isRunning()) {
- // 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();
- }
- }
- Moosique.stop();
- }
- };
+ player = new PlayThread();
player.start();
}
/**
* Wraps each NoteOn event in the track with its NoteOff event in a MooNote.
+ * @param track the track to convert
+ * @param quantize whether to round locations and durations in the track to nearest 16th
+ * @return a list of the created MooNotes
*/
- public static void convertTrack(Track track) {
+ public static List convertTrack(Track track, boolean quantize) {
// Searches the track for NoteOn and NoteOff events
ArrayList noteOns = new ArrayList(track.size() / 2);
ArrayList noteOffs = new ArrayList(track.size() / 2);
+ ArrayList newMooNotes = new ArrayList();
MidiEvent event;
for (int j = 0; j < track.size(); j++) {
event = track.get(j);
ShortMessage onMsg, nextOffMsg;
while(iOn.hasNext()) {
on = (MidiEvent)iOn.next();
- onMsg = (ShortMessage)on.getMessage();
- iOff = noteOffs.iterator();
- while(iOff.hasNext()) {
- nextOff = (MidiEvent)iOff.next();
- nextOffMsg = (ShortMessage)nextOff.getMessage();
- if(onMsg.getChannel() == nextOffMsg.getChannel() &&
- onMsg.getData1() == nextOffMsg.getData1() &&
- c.compare(nextOff, on) > 0) {
- off = nextOff;
- iOff.remove();
- break;
+ if (!(on instanceof MooNote)) {
+ onMsg = (ShortMessage)on.getMessage();
+ iOff = noteOffs.iterator();
+ while(iOff.hasNext()) {
+ nextOff = (MidiEvent)iOff.next();
+ nextOffMsg = (ShortMessage)nextOff.getMessage();
+ if(onMsg.getChannel() == nextOffMsg.getChannel() &&
+ onMsg.getData1() == nextOffMsg.getData1() &&
+ c.compare(nextOff, on) > 0) {
+ off = nextOff;
+ iOff.remove();
+ break;
+ }
+
}
-
- }
- track.remove(on);
- if (off != null) {
- track.add(new MooNote(on, off));
- } else {
- track.add(new MooNote(on, new MidiEvent((ShortMessage)on.getMessage().clone(), on.getTick() + 48)));
+ track.remove(on);
+ MooNote mn;
+ if (off != null) {
+ mn = new MooNote(on, off);
+ } else {
+ mn = new MooNote(on, new MidiEvent((ShortMessage)on.getMessage().clone(), on.getTick() + 48));
+ }
+ track.add(mn);
+ newMooNotes.add(mn);
+ iOn.remove();
}
- iOn.remove();
}
+ if (quantize) quantize(newMooNotes, SIXTEENTH_NOTE, true, true);
+ return newMooNotes;
}
/**
public static boolean load(String file) {
// Loads sequence from file
filename = file;
- try {
- seq = MidiSystem.getSequence(new File(filename));
- } catch (InvalidMidiDataException e) {
- return false;
- } catch (IOException e) {
- return false;
- }
+ try {seq = MidiSystem.getSequence(new File(filename));}
+ catch (Exception e) {return false;}
edited = false;
Track[] tracks = seq.getTracks();
// Converts tracks.
for (int i = 0; i < tracks.length; i++) {
- convertTrack(tracks[i]);
+ convertTrack(tracks[i], false);
}
// Sends sequence to GUI and sequencer, then returns
return true;
}
+ /**
+ * Quantizes the given list of MIDI events
+ * @param notes a list of the notes to quantize
+ * @param resolution the note size to round each note to
+ * @param location whether the quantize should affect the location of the note
+ * @param duration whether the quantize should affect the duration of the note
+ */
+ public static void quantize(List notes, int resolution, boolean location, boolean duration) {
+ // Math.round(mn.getTick() / ticksPerSixteenth);
+ }
+
+ /**
+ * Loads the user preferences.
+ */
+ public static void loadPreferences() {
+
+ }
+
+ /**
+ * Saves the user preferences.
+ */
+ public static void savePreferences() {
+
+ }
+
/**
* Prompts the user .
*/
if (gui != null) {
if (promptOnUnsavedChanges()) return;
}
+ savePreferences();
if (sequencer.isOpen()) sequencer.close();
if (synthesizer.isOpen()) synthesizer.close();
System.exit(0);
}
/**
- * A Comparator for sorting lists of MidiEvents.
+ * A Ccmparator for sorting lists of MidiEvents.
*/
public static class MidiEventComparator implements Comparator {
public int compare(Object o1, Object o2) {
- return (int)(((MidiEvent)o1).getTick() - ((MidiEvent)o2).getTick());
+ int diff = (int)(((MidiEvent)o1).getTick() - ((MidiEvent)o2).getTick());
+ if (diff != 0 || !(((MidiEvent)o1).getMessage() instanceof ShortMessage) || !(((MidiEvent)o2).getMessage() instanceof ShortMessage)) return diff;
+ return (((ShortMessage)((MidiEvent)o1).getMessage()).getData1() - ((ShortMessage)((MidiEvent)o2).getMessage()).getData1());
+ }
+ }
+
+ /**
+ * The thread that updates the GUI during playback.
+ */
+ 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();
+ }
+ }
+ }
+
+ /**
+ * A listener for detecting the end of the sequence.
+ */
+ public static class SongEndListener implements MetaEventListener {
+ public void meta(MetaMessage event) {
+ if (event.getType() == 47)
+ // End of sequence
+ stop();
}
}
}