]> ruin.nu Git - moosique.git/blob - Moosique.java
no message
[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 editPosition;
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 MidiChannels of the selected synthesizer.
124          * @return the available MidiChannels
125          */
126         public static MidiChannel getChannel(int i) {
127                 return channels[i];
128         }
129
130         /** 
131          * Returns the currently active MidiChannel.
132          * @return the active MidiChannel
133          */
134         public static MidiChannel getActiveChannel() {
135                 return activeChannel;
136         }
137
138         /** 
139          * Sets the currently active MidiChannel.
140          * @param channel       the number of the MidiChannel to activate
141          */
142         public static void setActiveChannel(int channel) {
143                 activeChannel = channels[channel];
144         }
145
146         /** 
147          * Replaces the current sequence with a new one, holding three empty tracks.
148          */
149         public static void clearSequence() {
150                 // Creates a new sequence and sends it to the sequencer.
151                 try {
152                         seq = new Sequence(Sequence.PPQ, RESOLUTION, DEFAULT_TRACKS);
153                         sequencer.setSequence(seq);
154                 } catch (InvalidMidiDataException e) {}
155                 // Sends sequence to GUI.
156                 if (gui != null) gui.setSequence(seq);
157         }
158
159         /** 
160          * Starts playback of the current sequence.
161          */
162         public static void play() {
163                 sequencer.setTickPosition(editPosition);
164                 resume();
165         }
166
167         /** 
168          * Pauses playback of the current sequence.
169          */
170         public static void pause() {
171                 sequencer.stop();
172                 player.interrupt();
173         }
174
175         /** 
176          * Resumes playback of the current sequence.
177          */
178         public static void resume() {
179                 gui.update(0);
180                 sequencer.start();
181
182                 // Disables input to volatile components
183                 // gui.disable();
184
185                 // Creates the visualization thread and starts it.
186                 player = new Thread () {
187                         public void run() {
188                                 while(sequencer.isRunning()) gui.update(sequencer.getTickPosition());
189                                 Moosique.stop();
190                         }
191                 };
192                 player.start();
193         }
194
195         /** 
196          * Stops playback of the current sequence.
197          */
198         public static void stop() {
199                 if (sequencer.isRunning()) {
200                         sequencer.stop();
201                         sequencer.setTickPosition(editPosition);
202                         player.interrupt();
203                         gui.update((long)0);
204                 }
205         }
206
207         /** 
208          * Returns the current editing position of the sequencer.
209          * @return the tick position
210          */
211         public static long getPosition() {
212                 return editPosition;
213         }
214
215         /** 
216          * Sets the current editing position of the sequencer.
217          * @param ticks         the tick position
218          */
219         public static void setPosition(long ticks) {
220                 editPosition = ticks;
221         }
222
223         /** 
224          * Returns true if the current sequence has been edited.
225          * @return the tick position
226          */
227         public static boolean isEdited() {
228                 return isEdited;
229         }
230
231         /** 
232          * Sets the current sequence as edited, which implies prompts when loading a new sequence.
233          */
234         public static void setEdited() {
235                 isEdited = true;
236         }
237
238         /** 
239          * Rewinds the current sequence the given number of measures.
240          * @param measures      the number of measures to rewind
241          */
242         public static void rewind(long ticks) {
243                 editPosition -= ticks;
244         }
245
246         /** 
247          * Fast forwards the current sequence the given number of measures.
248          * @param measures      the number of measures to fast forward
249          */
250         public static void forward(long ticks) {
251                 editPosition += ticks;
252         }
253
254         /** 
255          * Loads the MooSequence in the given file.
256          * @param filename      the filename to use
257          */
258         public static boolean load(String file) {
259                 // Loads sequence from file
260                 filename = file;
261                 try {
262                         seq = MidiSystem.getSequence(new File(filename));
263                 } catch (InvalidMidiDataException e) {
264                         return false;
265                 } catch (IOException e) {
266                         return false;
267                 }
268                 isEdited = false;
269                 
270                 // Searches the sequence for NoteOn events
271                 Track[] tracks = seq.getTracks();
272                 MidiEvent noteOn, noteOff = null, nextEvent;
273                 MidiMessage nextMsg;
274                 ShortMessage shortMsg;
275                 for (int i = 0; i < tracks.length; i++) {
276         /*
277                         Collections.sort(track[i].events, new Comparator() {
278                                 public int compare(Object o1, Object o2) {
279                                         return ((MidiEvent)o2).getTick() - ((MidiEvent)o1).getTick();
280                                 }
281                         });
282         */
283                         for (int j = 0; j < tracks[i].size(); j++) {
284                                 noteOn = tracks[i].get(j);
285                                 if (noteOn.getMessage() instanceof ShortMessage) {
286                                         if (((ShortMessage)noteOn.getMessage()).getCommand() == ShortMessage.NOTE_ON) {
287                                                 // Finds the corresponding NoteOff event
288                                                 for (int k = j + 1; k < tracks[i].size(); k++) {
289                                                         nextEvent = tracks[i].get(k);
290                                                         nextMsg = nextEvent.getMessage();
291                                                         if (nextMsg instanceof ShortMessage) {
292                                                                 shortMsg = (ShortMessage) nextMsg;
293                                                                 if (shortMsg.getCommand() == ShortMessage.NOTE_OFF && shortMsg.getChannel() == ((ShortMessage)noteOn.getMessage()).getChannel() && shortMsg.getData1() == ((ShortMessage)noteOn.getMessage()).getData1()) {
294                                                                         noteOff = nextEvent;
295                                                                         break;
296                                                                 }
297                                                         }
298                                                 }
299                                                 // Replaces the NoteOn event with a MooNote, if possible with the corresponding NoteOff event
300                                                 tracks[i].remove(noteOn);
301                                                 if (noteOff != null) {
302                                                         tracks[i].add(new MooNote(noteOn, noteOff));
303                                                 } else {
304                                                         tracks[i].add(new MooNote(noteOn));
305                                                 }
306                                         }
307                                 }
308                         }
309                 }
310                 // Sends sequence to GUI and sequencer, then returns
311                 if (gui != null) gui.setSequence(seq);
312                 try {
313                         sequencer.setSequence(seq);
314                 } catch (InvalidMidiDataException e) {}
315                 return true;
316         }
317
318         /** 
319          * Saves the current sequence to the given filename
320          * @param file  the filename to use
321          */
322         public static void saveAs(String file) {
323                 try {
324                         MidiSystem.write(seq, 1, new File(filename));
325                 } catch (IOException e) {}
326                 filename = file;
327         }
328
329         /** 
330          * Saves the current sequence to the previously given filename.
331          */
332         public static void save() {
333                 saveAs(filename);
334         }
335
336         /** 
337          * Releases all reserved devices and exits the program.
338          */
339         public static void quit() {
340                 if (sequencer.isOpen()) sequencer.close();
341                 if (synthesizer.isOpen()) synthesizer.close();
342                 System.exit(0);
343         }
344 }