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