]> ruin.nu Git - moosique.git/blob - Moosique.java
Added MooViewCounter as the row header of MooView
[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         private static MetaMessage timeSigMsg;
23         private static MetaMessage[] tempoChgMsgs;
24
25         private static String filename, fileArg;
26         private static long editPosition;
27         private static boolean makeGUI = true, isEdited;
28         private static Thread player;
29         public static final int DEFAULT_RESOLUTION = 96, DEFAULT_TRACKS = 4;
30
31         /** 
32          * Starts the application.
33          */
34         public static void main (String[] args) {
35                 System.out.println("\nMoosique version 1.0\n");
36
37                 // Parses command-line arguments.
38                 for (int i = 0; i < args.length; i++) {
39                         if (args[i].equals("-n")) {makeGUI = false;}
40                         else if (fileArg == null) {fileArg = args[i];}
41                 }
42
43                 // Acquires MIDI devices and connects them.
44                 System.out.print("Initializing MIDI devices.");
45                 try {
46                         sequencer = MidiSystem.getSequencer();
47                         System.out.print(".");
48                         sequencer.open();
49                         synthesizer = MidiSystem.getSynthesizer();
50                         System.out.print(".");
51                         synthesizer.open();
52                         sequencer.getTransmitter().setReceiver(synthesizer.getReceiver());
53                         channels = synthesizer.getChannels();
54                         setActiveChannel(0);
55                 } catch (MidiUnavailableException e) {
56                         System.out.println("Failed, quitting.");
57 //                      System.exit(1);
58                 }
59                 System.out.println("Done");
60
61                 //If a filename is given as the command-line argument, attempts to load a sequence from the file.
62                 if (fileArg != null) {
63                         System.out.print("Loading MIDI sequence from " + fileArg + "...");
64                         if (!load(fileArg)) {
65                                 System.out.println("Failed");
66                                 clearSequence();
67                         } else {
68                                 System.out.println("Done");
69                         }
70                 } else {
71                         // Otherwise creates a new empty one.
72                         clearSequence();
73                 }
74
75                 // Builds GUI, unless n-flag is set.
76                 if (makeGUI) {
77                         System.out.print("Building GUI...");
78                         try {
79                                 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
80                         } catch (Exception e) {}
81                         gui = new MooGUI(seq);
82                         System.out.println("Done");
83                 } else {
84                         System.out.print("Playing...");
85                         play();
86                         while (sequencer.isRunning()) {}
87                         System.out.println("Done");
88                         quit();
89                 }
90         }
91
92         /** 
93          * Returns the GUI.
94          * @return the GUI
95          */
96         public static MooGUI getGUI() {
97                 return gui;
98         }
99
100         /** 
101          * Returns the current sequence.
102          * @return the current sequence
103          */
104         public static Sequence getSequence() {
105                 return seq;
106         }
107
108         /** 
109          * Returns the current sequencer.
110          * @return the current sequencer
111          */
112         public static Sequencer getSequencer() {
113                 return sequencer;
114         }
115
116         /** 
117          * Returns the MidiChannels of the selected synthesizer.
118          * @return the available MidiChannels
119          */
120         public static MidiChannel[] getChannels() {
121                 return channels;
122         }
123
124         /** 
125          * Returns the MidiChannels of the selected synthesizer.
126          * @return the available MidiChannels
127          */
128         public static MidiChannel getChannel(int i) {
129                 return channels[i];
130         }
131
132         /** 
133          * Returns the currently active MidiChannel.
134          * @return the active MidiChannel
135          */
136         public static MidiChannel getActiveChannel() {
137                 return activeChannel;
138         }
139
140         /** 
141          * Sets the currently active MidiChannel.
142          * @param channel       the number of the MidiChannel to activate
143          */
144         public static void setActiveChannel(int channel) {
145                 activeChannel = channels[channel];
146         }
147
148         /** 
149          * Replaces the current sequence with a new one, holding three empty tracks.
150          */
151         public static void clearSequence() {
152                 // Creates a new sequence and sends it to the sequencer.
153                 try {
154                         seq = new Sequence(Sequence.PPQ, DEFAULT_RESOLUTION, DEFAULT_TRACKS);
155                         sequencer.setSequence(seq);
156                 } catch (InvalidMidiDataException e) {}
157                 // Sends sequence to GUI.
158                 if (gui != null) gui.setSequence(seq);
159         }
160
161         /** 
162          * Starts playback of the current sequence.
163          */
164         public static void play() {
165                 sequencer.setTickPosition(editPosition);
166                 resume();
167         }
168
169         /** 
170          * Pauses playback of the current sequence.
171          */
172         public static void pause() {
173                 if (sequencer.isRunning()) {
174                         sequencer.stop();
175                 }
176                 if (player != null) player.interrupt();
177         }
178
179         /** 
180          * Resumes playback of the current sequence.
181          */
182         public static void resume() {
183                 gui.update(0);
184                 sequencer.start();
185
186                 // Disables input to volatile components
187                 // gui.disable();
188
189                 // Creates the visualization thread and starts it.
190                 player = new Thread () {
191                         public void run() {
192                                 while(sequencer.isRunning()) {
193                                         // Updates the GUI with the current tick position.
194                                         gui.update(sequencer.getTickPosition());
195
196                                         // Puts the thread to sleep for as long as it takes
197                                         // the sequencer to reach the next sixteenth.
198                                         try {
199                                                 sleep((long)((1000 * 60 * 1) / (getTempo() * 4)));
200                                         } catch (InterruptedException e) {
201                                                 Moosique.stop();
202                                         }
203                                 }
204                                 Moosique.stop();
205                         }
206                 };
207                 player.start();
208         }
209
210         /** 
211          * Stops playback of the current sequence.
212          */
213         public static void stop() {
214                 if (sequencer.isRunning()) {
215                         sequencer.stop();
216                 }
217                 sequencer.setTickPosition(editPosition);
218                 if (player != null) player.interrupt();
219                 gui.update((long)0);
220         }
221
222         /** 
223          * Returns the current editing position of the sequencer.
224          * @return the tick position
225          */
226         public static long getPosition() {
227                 return editPosition;
228         }
229
230         /** 
231          * Sets the current editing position of the sequencer.
232          * @param ticks         the tick position
233          */
234         public static void setPosition(long ticks) {
235                 editPosition = ticks;
236         }
237
238         /** 
239          * Returns the tempo of the current sequence.
240          * @return the tick position
241          */
242         public static int getTempo() {
243                 return 120;
244                 // if (tempoMsg == null) return 0;
245         }
246
247         /** 
248          * Sets the current editing position of the sequencer.
249          * @param ticks         the tick position
250          */
251         public static void setTempo(int bpm) {
252                 // tempoMsg
253         }
254
255         /** 
256          * Returns the tempo of the current sequence.
257          * @return the tick position
258          */
259         public static int[] getTimeSig() {
260                 int[] ts = {4, 4};
261                 return ts;
262                 // if (timeSigMsg == null) return 0;
263         }
264
265         /** 
266          * Sets the current editing position of the sequencer.
267          * @param ticks         the tick position
268          */
269         public static void setTimeSig(int bpm) {
270                 // timeSigMsg
271         }
272
273         /** 
274          * Returns true if the current sequence has been edited.
275          * @return the tick position
276          */
277         public static boolean isEdited() {
278                 return isEdited;
279         }
280
281         /** 
282          * Sets the current sequence as edited, which implies prompts when loading a new sequence.
283          */
284         public static void setEdited() {
285                 isEdited = true;
286         }
287
288         /** 
289          * Rewinds the current sequence the given number of measures.
290          * @param measures      the number of measures to rewind
291          */
292         public static void rewind(long ticks) {
293                 editPosition -= ticks;
294         }
295
296         /** 
297          * Fast forwards the current sequence the given number of measures.
298          * @param measures      the number of measures to fast forward
299          */
300         public static void forward(long ticks) {
301                 editPosition += ticks;
302         }
303
304         /** 
305          * Loads the MooSequence in the given file.
306          * @param filename      the filename to use
307          */
308         public static boolean load(String file) {
309                 // Loads sequence from file
310                 filename = file;
311                 try {
312                         seq = MidiSystem.getSequence(new File(filename));
313                 } catch (InvalidMidiDataException e) {
314                         return false;
315                 } catch (IOException e) {
316                         return false;
317                 }
318                 isEdited = false;
319
320                 Track[] tracks = seq.getTracks();
321
322                 // Stores tempo and time signature.
323                 MidiMessage msg;
324                 MetaMessage metaMsg;
325                 for (int i = 0; i < tracks[0].size(); i++) {
326                         msg = tracks[0].get(i).getMessage();
327                         if (msg.getStatus() == MetaMessage.META) {
328                                 metaMsg = (MetaMessage)msg;
329                                 if (metaMsg.getType() == 81) {
330 // FLERA!!!                     tempoMsg = metaMsg;
331                                 } else if (metaMsg.getType() == 88) {
332                                         timeSigMsg = (MetaMessage)msg;
333                                 }
334
335                         }
336                 }
337
338                 // Searches the sequence for NoteOn events
339                 MidiEvent noteOn, noteOff = null, nextEvent;
340                 MidiMessage nextMsg;
341                 ShortMessage shortMsg;
342                 ArrayList noteOns, noteOffs;
343                 for (int i = 0; i < tracks.length; i++) {
344                         for (int j = 0; j < tracks[i].size(); j++) {
345                                 noteOn = tracks[i].get(j);
346                                 if (noteOn.getMessage().getStatus() == ShortMessage.NOTE_ON) {
347                                         // Finds the corresponding NoteOff event
348                                         for (int k = j + 1; k < tracks[i].size(); k++) {
349                                                 nextEvent = tracks[i].get(k);
350                                                 nextMsg = nextEvent.getMessage();
351                                                 if (nextMsg instanceof ShortMessage) {
352                                                         shortMsg = (ShortMessage) nextMsg;
353                                                         if (shortMsg.getCommand() == ShortMessage.NOTE_OFF && shortMsg.getChannel() == ((ShortMessage)noteOn.getMessage()).getChannel() && shortMsg.getData1() == ((ShortMessage)noteOn.getMessage()).getData1()) {
354                                                                 noteOff = nextEvent;
355                                                                 break;
356                                                         }
357                                                 }
358                                         }
359                                         // Replaces the NoteOn event with a MooNote, if possible with the corresponding NoteOff event
360                                         tracks[i].remove(noteOn);
361                                         if (noteOff != null) {
362                                                 tracks[i].add(new MooNote(noteOn, noteOff));
363                                         } else {
364                                                 tracks[i].add(new MooNote(noteOn));
365                                         }
366                                 }
367                         }
368                 }
369
370                 // Sends sequence to GUI and sequencer, then returns
371                 if (gui != null) gui.setSequence(seq);
372                 try {
373                         sequencer.setSequence(seq);
374                 } catch (InvalidMidiDataException e) {}
375                 return true;
376         }
377
378         /** 
379          * Saves the current sequence to the given filename
380          * @param file  the filename to use
381          */
382         public static void saveAs(String file) {
383                 try {
384                         MidiSystem.write(seq, 1, new File(filename));
385                 } catch (IOException e) {}
386                 filename = file;
387                 gui.setStatus("Saved " + file);
388         }
389
390         /** 
391          * Saves the current sequence to the previously given filename.
392          */
393         public static void save() {
394                 saveAs(filename);
395         }
396
397         /** 
398          * Releases all reserved devices and exits the program.
399          */
400         public static void quit() {
401                 if (sequencer.isOpen()) sequencer.close();
402                 if (synthesizer.isOpen()) synthesizer.close();
403                 System.exit(0);
404         }
405 }