]> ruin.nu Git - moosique.git/blob - Moosique.java
Fixed the scrolling on play!!!
[moosique.git] / Moosique.java
1 import javax.sound.midi.*;
2 import java.io.*;
3 import javax.swing.*;
4 import java.util.*;
5
6 /**
7  * Moosique - The MIDI Tracker
8  * 
9  * Main class that handles initiation, IO and sound.
10  * 
11  * @author  Einar Pehrson
12  */
13  
14 public class Moosique {
15
16         private static MooGUI gui;
17         private static Sequence seq;
18         private static Sequencer sequencer;
19         private static Synthesizer synthesizer;
20         private static MidiChannel[] channels;
21         private static MidiChannel activeChannel;
22
23         private static String filename, fileArg;
24         private static long position;
25         private static boolean makeGUI = true, isEdited;
26         private static Thread player;
27         public static final int RESOLUTION = 96, DEFAULT_TRACKS = 4;
28
29         /** 
30          * Starts the application.
31          */
32         public static void main (String[] args) {
33                 System.out.println("\nMoosique version 1.0\n");
34
35                 // Parses command-line arguments.
36                 for (int i = 0; i < args.length; i++) {
37                         if (args[i].equals("-n")) {makeGUI = false;}
38                         else if (fileArg == null) {fileArg = args[i];}
39                 }
40
41                 // Acquires MIDI devices and connects them.
42                 System.out.print("Initializing MIDI devices.");
43                 try {
44                         sequencer = MidiSystem.getSequencer();
45                         System.out.print(".");
46                         sequencer.open();
47                         synthesizer = MidiSystem.getSynthesizer();
48                         System.out.print(".");
49                         synthesizer.open();
50                         sequencer.getTransmitter().setReceiver(synthesizer.getReceiver());
51                         channels = synthesizer.getChannels();
52                         setActiveChannel(0);
53                 } catch (MidiUnavailableException e) {
54                         System.out.println("Failed, quitting.");
55 //                      System.exit(1);
56                 }
57                 System.out.println("Done");
58
59                 //If a filename is given as the command-line argument, attempts to load a sequence from the file.
60                 if (fileArg != null) {
61                         System.out.print("Loading MIDI sequence from " + fileArg + "...");
62                         if (!load(fileArg)) {
63                                 System.out.println("Failed");
64                                 clearSequence();
65                         } else {
66                                 System.out.println("Done");
67                         }
68                 } else {
69                         // Otherwise creates a new empty one.
70                         clearSequence();
71                 }
72
73                 // Builds GUI, unless n-flag is set.
74                 if (makeGUI) {
75                         System.out.print("Building GUI...");
76                         try {
77                                 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
78                         } catch (Exception e) {}
79                         gui = new MooGUI(seq);
80                         System.out.println("Done");
81                 } else {
82                         System.out.print("Playing...");
83                         play();
84                         while (sequencer.isRunning()) {}
85                         System.out.println("Done");
86                         quit();
87                 }
88         }
89
90         /** 
91          * Returns the GUI.
92          * @return the GUI
93          */
94         public static MooGUI getGUI() {
95                 return gui;
96         }
97
98         /** 
99          * Returns the current sequence.
100          * @return the current sequence
101          */
102         public static Sequence getSequence() {
103                 return seq;
104         }
105
106         /** 
107          * Returns the current sequencer.
108          * @return the current sequencer
109          */
110         public static Sequencer getSequencer() {
111                 return sequencer;
112         }
113
114         /** 
115          * Returns the MidiChannels of the selected synthesizer.
116          * @return the available MidiChannels
117          */
118         public static MidiChannel[] getChannels() {
119                 return channels;
120         }
121
122         /** 
123          * Returns the currently active MidiChannel.
124          * @return the active MidiChannel
125          */
126         public static MidiChannel getActiveChannel() {
127                 return activeChannel;
128         }
129
130         /** 
131          * Sets the currently active MidiChannel.
132          * @param channel       the number of the MidiChannel to activate
133          */
134         public static void setActiveChannel(int channel) {
135                 activeChannel = channels[channel];
136         }
137
138         /** 
139          * Replaces the current sequence with a new one, holding three empty tracks.
140          */
141         public static void clearSequence() {
142                 // Creates a new sequence and sends it to the sequencer.
143                 try {
144                         seq = new Sequence(Sequence.PPQ, RESOLUTION, DEFAULT_TRACKS);
145                         sequencer.setSequence(seq);
146                 } catch (InvalidMidiDataException e) {}
147                 // Sends sequence to GUI.
148                 if (gui != null) gui.setSequence(seq);
149         }
150
151         /** 
152          * Starts playback of the current sequence.
153          */
154         public static void play() {
155                 sequencer.setTickPosition(position);
156                 resume();
157         }
158
159         /** 
160          * Pauses playback of the current sequence.
161          */
162         public static void pause() {
163                 sequencer.stop();
164                 player.interrupt();
165         }
166
167         /** 
168          * Resumes playback of the current sequence.
169          */
170         public static void resume() {
171                 sequencer.start();
172
173                 // Disables input to volatile components
174                 // gui.disable();
175
176                 // Creates the visualization thread and starts it.
177                 player = new Thread () {
178                         public void run() {
179                                 while(sequencer.isRunning()) gui.update(sequencer.getTickPosition());
180                                 Moosique.stop();
181                         }
182                 };
183                 player.start();
184         }
185
186         /** 
187          * Stops playback of the current sequence.
188          */
189         public static void stop() {
190                 sequencer.stop();
191                 sequencer.setTickPosition(position);
192                 player.interrupt();
193                 gui.update((long)0);
194         }
195
196         /** 
197          * Returns the current tick position of the sequencer.
198          * @return the tick position
199          */
200         public static long getPosition() {
201                 return position;
202         }
203
204         /** 
205          * Sets the current tick position of the sequencer.
206          * @param ticks         the tick position
207          */
208         public static void setPosition(long ticks) {
209                 position = ticks;
210         }
211
212         /** 
213          * Returns true if the current sequence has been edited.
214          * @return the tick position
215          */
216         public static boolean isEdited() {
217                 return isEdited;
218         }
219
220         /** 
221          * Sets the current sequence as edited, which implies prompts when loading a new sequence.
222          */
223         public static void setEdited() {
224                 isEdited = true;
225         }
226
227         /** 
228          * Rewinds the current sequence the given number of measures.
229          * @param measures      the number of measures to rewind
230          */
231         public static void rewind(long ticks) {
232                 setPosition(position - ticks);
233         }
234
235         /** 
236          * Fast forwards the current sequence the given number of measures.
237          * @param measures      the number of measures to fast forward
238          */
239         public static void forward(long ticks) {
240                 setPosition(position + ticks);
241         }
242
243         /** 
244          * Loads the MooSequence in the given file.
245          * @param filename      the filename to use
246          */
247         public static boolean load(String file) {
248                 // Loads sequence from file
249                 filename = file;
250                 try {
251                         seq = MidiSystem.getSequence(new File(filename));
252                 } catch (InvalidMidiDataException e) {
253                         return false;
254                 } catch (IOException e) {
255                         return false;
256                 }
257                 isEdited = false;
258                 
259                 // Searches the sequence for NoteOn events
260                 Track[] tracks = seq.getTracks();
261                 MidiEvent noteOn, noteOff = null, nextEvent;
262                 MidiMessage nextMsg;
263                 ShortMessage shortMsg;
264                 for (int i = 0; i < tracks.length; i++) {
265         /*
266                         Collections.sort(track[i].events, new Comparator() {
267                                 public int compare(Object o1, Object o2) {
268                                         return ((MidiEvent)o2).getTick() - ((MidiEvent)o1).getTick();
269                                 }
270                         });
271         */
272                         for (int j = 0; j < tracks[i].size(); j++) {
273                                 noteOn = tracks[i].get(j);
274                                 if (noteOn.getMessage() instanceof ShortMessage) {
275                                         if (((ShortMessage)noteOn.getMessage()).getCommand() == ShortMessage.NOTE_ON) {
276                                                 // Finds the corresponding NoteOff event
277                                                 for (int k = j + 1; k < tracks[i].size(); k++) {
278                                                         nextEvent = tracks[i].get(k);
279                                                         nextMsg = nextEvent.getMessage();
280                                                         if (nextMsg instanceof ShortMessage) {
281                                                                 shortMsg = (ShortMessage) nextMsg;
282                                                                 if (shortMsg.getCommand() == ShortMessage.NOTE_OFF && shortMsg.getChannel() == ((ShortMessage)noteOn.getMessage()).getChannel() && shortMsg.getData1() == ((ShortMessage)noteOn.getMessage()).getData1()) {
283                                                                         noteOff = nextEvent;
284                                                                         break;
285                                                                 }
286                                                         }
287                                                 }
288                                                 // Replaces the NoteOn event with a MooNote, if possible with the corresponding NoteOff event
289                                                 tracks[i].remove(noteOn);
290                                                 if (noteOff != null) {
291                                                         tracks[i].add(new MooNote(noteOn, noteOff));
292                                                 } else {
293                                                         tracks[i].add(new MooNote(noteOn));
294                                                 }
295                                         }
296                                 }
297                         }
298                 }
299                 // Sends sequence to GUI and sequencer, then returns
300                 if (gui != null) gui.setSequence(seq);
301                 try {
302                         sequencer.setSequence(seq);
303                 } catch (InvalidMidiDataException e) {}
304                 return true;
305         }
306
307         /** 
308          * Saves the current sequence to the given filename
309          * @param file  the filename to use
310          */
311         public static void saveAs(String file) {
312                 try {
313                         MidiSystem.write(seq, 1, new File(filename));
314                 } catch (IOException e) {}
315                 filename = file;
316         }
317
318         /** 
319          * Saves the current sequence to the previously given filename.
320          */
321         public static void save() {
322                 saveAs(filename);
323         }
324
325         /** 
326          * Releases all reserved devices and exits the program.
327          */
328         public static void quit() {
329                 if (sequencer.isOpen()) sequencer.close();
330                 if (synthesizer.isOpen()) synthesizer.close();
331                 System.exit(0);
332         }
333 }