X-Git-Url: https://ruin.nu/git/?p=moosique.git;a=blobdiff_plain;f=Moosique.java;fp=Moosique.java;h=5ad1afe9d93b8ae55a9201e6c1464eed52dfd0a5;hp=e2b7f3e490821832347b01fae8c9289f9eff228a;hb=d1996d82793d7049115f5280ba8651b818e52e3c;hpb=1d2d2bfd14ba0addab97692527d4414d3a87e13d diff --git a/Moosique.java b/Moosique.java index e2b7f3e..5ad1afe 100644 --- a/Moosique.java +++ b/Moosique.java @@ -22,6 +22,7 @@ public class Moosique { 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; @@ -39,7 +40,7 @@ public class Moosique { * 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; @@ -49,17 +50,17 @@ public class Moosique { } // 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. @@ -68,22 +69,22 @@ public class Moosique { 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. @@ -92,17 +93,17 @@ public class Moosique { // 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(); } } @@ -167,17 +168,36 @@ public class Moosique { /** * 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; } @@ -228,13 +248,28 @@ public class Moosique { * @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; } @@ -427,7 +462,7 @@ public class Moosique { * @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]); } /** @@ -439,7 +474,7 @@ public class Moosique { 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 }; @@ -465,6 +500,71 @@ public class Moosique { + /* *** + ** 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 ** *** */ @@ -524,7 +624,7 @@ public class Moosique { if (sequencer.isRunning()) { sequencer.stop(); } - if (player != null) player.interrupt(); + player = null; } /** @@ -535,7 +635,7 @@ public class Moosique { sequencer.stop(); } sequencer.setTickPosition(editPosition); - if (player != null) player.interrupt(); + player = null; gui.update((long)0); } @@ -566,15 +666,13 @@ public class Moosique { 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)); @@ -582,17 +680,14 @@ public class Moosique { 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); } /** @@ -609,6 +704,9 @@ public class Moosique { 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)); @@ -691,17 +789,11 @@ public class Moosique { 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) { @@ -712,6 +804,9 @@ public class Moosique { } } } + Comparator c = new MidiEventComparator(); + Collections.sort(tempoChanges, c); + Collections.sort(timeSignatures, c); // Converts tracks. for (int i = 0; i < tracks.length; i++) { @@ -719,7 +814,7 @@ public class Moosique { } // 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) {} @@ -737,6 +832,19 @@ public class Moosique { // 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. */ @@ -752,7 +860,7 @@ public class Moosique { } /** - * Prompts the user . + * Prompts the user to save any unsaved changes. */ public static boolean promptOnUnsavedChanges() { if (!edited) return false; @@ -796,8 +904,16 @@ public class Moosique { /** * 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); } /** @@ -829,28 +945,25 @@ public class Moosique { */ 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(); - } + } } }