source: trunk/faces/apollo/src/org/apollo/meldex/RogTrack.java@ 754

Last change on this file since 754 was 315, checked in by bjn8, 16 years ago

Apollo spin-off added

  • Property svn:executable set to *
File size: 13.7 KB
Line 
1package org.apollo.meldex;
2
3
4import java.io.File;
5import java.io.FileInputStream;
6import java.io.FileOutputStream;
7import java.util.ArrayList;
8import javax.swing.JOptionPane;
9
10@SuppressWarnings("unchecked") // code in java 1.4
11public class RogTrack
12{
13 // The number of semitones from the key centre
14 final int nameOffset[] = { 0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6 };
15
16 // Data about the track (loaded from the file)
17 private String databaseName = "";
18 private String songName = "";
19 private int wholeNoteDivisions = 16;
20
21 // Data representing the track events
22 ArrayList trackData = new ArrayList();
23
24
25 // ---------------------------------------
26 // WavToRog specific functions
27 // ---------------------------------------
28 public RogTrack()
29 {
30 }
31
32
33 public void addTimeSignature(int numer, int denom)
34 {
35 trackData.add(new RogTrackEvent(RogTrackEvent.TIME_SIG_EVENT, numer, denom));
36 }
37
38
39 public void addNote(int noteLength, int notePitch, int key)
40 {
41 int noteName = getNoteName(key, notePitch);
42 trackData.add(new RogTrackEvent(noteLength, noteName, notePitch));
43 }
44
45
46 public void addRest(int restLength)
47 {
48 if (restLength > 0) {
49 trackData.add(new RogTrackEvent(restLength, 0, 0));
50 }
51 }
52
53
54 public void addKeySignature(int numSharpsFlats, int isMinor)
55 {
56 trackData.add(0, new RogTrackEvent(RogTrackEvent.KEY_SIG_EVENT, isMinor, numSharpsFlats));
57 }
58
59
60 public int calculateBestKey()
61 {
62 int accidental;
63 int minAccidental = trackData.size();
64 int minKey = 0;
65
66 for (int numSharpFlat = 0; numSharpFlat < 8; numSharpFlat++) {
67 accidental = checkAccidental(numSharpFlat);
68 if (accidental < minAccidental) {
69 minAccidental = accidental;
70 minKey = numSharpFlat;
71 }
72
73 accidental = checkAccidental(-numSharpFlat);
74 if (accidental < minAccidental) {
75 minAccidental = accidental;
76 minKey = -numSharpFlat;
77 }
78 }
79
80 return minKey;
81 }
82
83
84 public int checkAccidental(int key)
85 {
86 int sharpsFlats[] = { 0, 0, 0, 0, 0, 0, 0 };
87 int nameValue[] = { 0, 2, 4, 5, 7, 9, 11 };
88
89 // Calculate the sharps and flats for this key
90 if (key < 0) {
91 for (int flatNum = 1; flatNum <= (-key); flatNum++) {
92 sharpsFlats[((8 + flatNum) * 3) % 7]--;
93 }
94 }
95 else {
96 for (int sharpNum = 1; sharpNum <= key; sharpNum++) {
97 sharpsFlats[((sharpNum * 4) + 6) % 7]++;
98 }
99 }
100
101 // Iterate through the track
102 int sumAccidental = 0;
103 for (int i = 0; i < trackData.size(); i++) {
104 RogTrackEvent te = (RogTrackEvent) trackData.get(i);
105
106 // Have we found a note??
107 if (te.type <= RogTrackEvent.LAST_NOTE_EVENT) {
108 int semitone = te.param2;
109
110 // Calculate the accidental for this key
111 int noteName = getNoteName(key, semitone);
112 int numSharpsFlats = (semitone % 12) - nameValue[noteName];
113
114 if (numSharpsFlats < -6) {
115 numSharpsFlats += 12;
116 }
117 if (numSharpsFlats > 6) {
118 numSharpsFlats -= 12;
119 }
120
121 if (numSharpsFlats != sharpsFlats[noteName]) {
122 sumAccidental++;
123 }
124 }
125 }
126
127 return sumAccidental;
128 }
129
130
131 public void setNoteNames(int numSharpsFlats)
132 {
133 for (int i = 0; i < trackData.size(); i++) {
134 RogTrackEvent te = (RogTrackEvent) trackData.get(i);
135 if (te.type <= RogTrackEvent.LAST_NOTE_EVENT && te.param2 > 0) {
136 te.param1 = getNoteName(numSharpsFlats, te.param2);
137 }
138 }
139 }
140
141
142 public int getNoteName(int key, int notePitch)
143 {
144 // Calculate the name of the key
145 int keyName = (((7 - key) * 3) % 7);
146
147 // Calculate the semitone start position
148 int keySemitoneStart = (((12 - key) * 5) % 12);
149
150 // Calculate the note name
151 int noteName = (keyName + nameOffset[(notePitch + 12 - keySemitoneStart) % 12]) % 7;
152 return noteName;
153 }
154
155
156 public String toString()
157 {
158 String str = "song \"" + databaseName + "\" \"" + songName + "\" " + wholeNoteDivisions + "\n";
159
160 // Display each of the TrackEvents in turn
161 for (int i = 0; i < trackData.size(); i++) {
162 RogTrackEvent te = (RogTrackEvent) trackData.get(i);
163
164 // Is this a note??
165 if (te.type <= RogTrackEvent.LAST_NOTE_EVENT) {
166 char noteName;
167 if (te.param1 < 5) {
168 noteName = (char) (te.param1 + 'C');
169 }
170 else {
171 noteName = (char) (te.param1 - 5 + 'A');
172 }
173 str = str + te.type + " " + noteName + " " + te.param2 + "\n";
174 }
175
176 // Is this a time signature??
177 else if (te.type == RogTrackEvent.TIME_SIG_EVENT) {
178 str = str + "timesig " + te.param1 + " " + te.param2 + "\n";
179 }
180
181 // Is this a key signature??
182 else if (te.type == RogTrackEvent.KEY_SIG_EVENT) {
183 str = str + "keysig " + te.param1 + " " + te.param2 + "\n";
184 }
185
186 // Is this a fermata event??
187 else if (te.type == RogTrackEvent.FERMATA_EVENT) {
188 str = str + "fermata\n";
189 }
190
191 // Is this a tempo event??
192 else if (te.type == RogTrackEvent.TEMPO_EVENT) {
193 str = str + "tempo " + te.param1 + "\n";
194 }
195 }
196
197 // Return the finished string
198 return str;
199 }
200
201 public void fromString(String rog_text)
202 {
203 int rog_text_len = rog_text.length();
204 byte[] rog_bytes = rog_text.getBytes();
205
206 parseFile(rog_bytes, rog_text_len);
207 }
208
209
210 /**
211 *
212 * @return
213 * The converted melody. Null if was unable to convert
214 */
215 public Melody toMelody()
216 {
217 int track_len = trackData.size();
218
219 ArrayList filtered_td = new ArrayList();
220
221 for (int i = 0; i < track_len; i++) {
222 RogTrackEvent te = (RogTrackEvent) trackData.get(i);
223
224 // Is this a note??
225 if (te.type <= RogTrackEvent.LAST_NOTE_EVENT) {
226 filtered_td.add(te);
227 }
228 }
229
230 int filtered_track_len = filtered_td.size();
231
232 if (filtered_track_len<1) {
233 //System.err.println("Insufficient track data for form relative pitch melody");
234 return null;
235 }
236
237 ArrayList me_list = new ArrayList();
238
239 for (int i = 0; i < filtered_track_len-1; i++) {
240 RogTrackEvent te = (RogTrackEvent) filtered_td.get(i);
241 RogTrackEvent next_te = (RogTrackEvent) filtered_td.get(i+1);
242
243 int duration = te.type;
244
245 if (next_te.param2 == 0) {
246 // is a rest
247 MelodyEvent me = new MelodyEvent(MelodyEvent.REST_EVENT,duration);
248 me_list.add(me);
249
250 // skip ahead one further position
251 i++;
252 continue;
253 }
254
255 int rel_pitch = next_te.param2 - te.param2;
256
257 MelodyEvent me = new MelodyEvent(rel_pitch,duration);
258 me_list.add(me);
259 }
260
261 int me_list_len = me_list.size();
262
263 MelodyEvent[] me_array = new MelodyEvent[me_list_len];
264 for (int i=0; i<me_list_len; i++) {
265 me_array[i] = (MelodyEvent)me_list.get(i);
266 }
267
268 Melody me = new Melody(Melody.RELATIVE_PITCH,me_array);
269
270 return me;
271 }
272
273
274 // ---------------------------------------
275 // RogToGif specific functions
276 // ---------------------------------------
277
278 // ----------------------------------------------------------------------------------
279 // Method : loadFromFile
280 // Returns : false - if the track could not be successfully loaded
281 // true - if the track was successfully loaded
282 // ----------------------------------------------------------------------------------
283 public boolean loadFromFile(File rogFile)
284 {
285 try {
286 // Open the file with a FileInputStream, ready for parsing
287 FileInputStream fileIn = new FileInputStream(rogFile);
288
289 // Get the number of bytes in the file
290 int rogLength = fileIn.available();
291
292 // Create a buffer to store the file, and read the file into it
293 byte[] rogData = new byte[rogLength];
294 fileIn.read(rogData, 0, rogLength);
295
296 // Parse the .rog file
297 parseFile(rogData, rogLength);
298 }
299 catch (Exception ex) {
300 JOptionPane.showMessageDialog(null, "Exception occurred reading file.\n\n" + ex);
301 return false;
302 }
303
304 // File loaded successfully
305 return true;
306 }
307
308
309 private void parseFile(byte[] rogData, int rogLength)
310 {
311 // Break the file up into lines and parse each one seperately
312 int lineStart = 0;
313 for (int pos = 0; pos < rogLength; pos++) {
314 if (rogData[pos] == '\n') {
315 parseLine(new String(rogData, lineStart, (pos - lineStart)));
316 lineStart = pos + 1;
317 }
318 }
319 // If the last line is not newline-terminated then we have to parse it now
320 if (lineStart != rogLength) {
321 parseLine(new String(rogData, lineStart, (rogLength - lineStart)));
322 }
323 }
324
325
326 private void parseLine(String line)
327 {
328 String sent = null;
329 String word = nextWord(line, 0);
330 int parsed = (word.length() + 1);
331
332 if (word.equals("") || word.equals("metadata")) {
333 // do nothing => ignore it!
334 }
335 else if (word.equals("song")) {
336 // Parse the first parameter - databaseName
337 sent = nextSentence(line, parsed + 1);
338 parsed += (sent.length() + 3);
339 databaseName = sent;
340
341 // Parse the second parameter - songName
342 sent = nextSentence(line, parsed + 1);
343 parsed += (sent.length() + 3);
344 songName = sent;
345
346 // Parse the third parameter - wholeDivision
347 word = nextWord(line, parsed);
348 parsed += (word.length() + 1);
349 wholeNoteDivisions = (int) Double.parseDouble(word);
350 }
351
352 else if (word.equals("fermata")) {
353 // Add a fermata event to the track (no parameters)
354 trackData.add(new RogTrackEvent(RogTrackEvent.FERMATA_EVENT, 0, 0));
355 }
356
357 else if (word.equals("tempo")) {
358 // Parse the first parameter - tempoBPM
359 word = nextWord(line, parsed);
360 parsed += (word.length() + 1);
361 int tempoBPM = (int) Double.parseDouble(word);
362
363 // Add a tempo event to the track
364 trackData.add(new RogTrackEvent(RogTrackEvent.TEMPO_EVENT, tempoBPM, 0));
365 }
366
367 else if (word.equals("keysig")) {
368 // Parse the first parameter - isMinor
369 word = nextWord(line, parsed);
370 parsed += (word.length() + 1);
371 int isMinor = (int) Double.parseDouble(word);
372
373 // Parse the second parameter - numSharpsFlats
374 word = nextWord(line, parsed);
375 parsed += (word.length() + 1);
376 int numSharpsFlats = (int) Double.parseDouble(word);
377
378 // Add this key signature event to the track
379 trackData.add(0, new RogTrackEvent(RogTrackEvent.KEY_SIG_EVENT, isMinor, numSharpsFlats));
380 }
381
382 else if (word.equals("timesig")) {
383 // Parse the first parameter - numerator
384 word = nextWord(line, parsed);
385 parsed += (word.length() + 1);
386 int numerator = (int) Double.parseDouble(word);
387
388 // Parse the second parameter - denominator
389 word = nextWord(line, parsed);
390 parsed += (word.length() + 1);
391 int denominator = (int) Double.parseDouble(word);
392
393 // Add this time signature event to the track
394 trackData.add(new RogTrackEvent(RogTrackEvent.TIME_SIG_EVENT, numerator, denominator));
395 }
396
397 else {
398 // This must be a note line, so parse the duration
399 int duration = (int) Double.parseDouble(word);
400
401 // Parse the first parameter - noteName
402 word = nextWord(line, parsed);
403 parsed += (word.length() + 1);
404 char noteName = word.charAt(0);
405
406 // Check that the noteName is valid
407 if (noteName < 'A' || noteName > 'G') {
408 JOptionPane.showMessageDialog(null, "Parsing error - check that the Rog file is valid.");
409 return;
410 }
411
412 // Convert the noteName character into it's MIDI integer equivalent
413 int noteNum;
414 if (noteName >= 'C') {
415 noteNum = noteName - 'C';
416 }
417 else {
418 noteNum = noteName - 'A' + 5;
419 }
420
421 // Parse the second parameter - pitch
422 word = nextWord(line, parsed);
423 parsed += (word.length() + 1);
424 int pitch = (int) Double.parseDouble(word);
425
426 // Add this note event to the track
427 trackData.add(new RogTrackEvent(duration, noteNum, pitch));
428 }
429 }
430
431
432 private String nextSentence(String line, int sentStart)
433 {
434 for (int pos = sentStart; pos < line.length(); pos++) {
435 // We have found a quote mark, so return the sentence found
436 if (line.charAt(pos) == '"') {
437 return (new String(line.substring(sentStart, pos)));
438 }
439 }
440 // We have reached the end of the string, so return the remainder
441 return (new String(line.substring(sentStart, line.length())));
442 }
443
444
445 private String nextWord(String line, int wordStart)
446 {
447 for (int pos = wordStart; pos < line.length(); pos++) {
448 // We have found a space, so return the word found
449 if (line.charAt(pos) == ' ') {
450 return (new String(line.substring(wordStart, pos)));
451 }
452 }
453 // We have reached the end of the string, so return the remainder
454 return (new String(line.substring(wordStart, line.length())));
455 }
456
457
458 public String getDatabaseName()
459 {
460 return databaseName;
461 }
462
463
464 public String getSongName()
465 {
466 return songName;
467 }
468
469
470 public int getWholeNoteDivisions()
471 {
472 return wholeNoteDivisions;
473 }
474
475
476 // ---------------------------------------
477 // SymInput specific functions
478 // ---------------------------------------
479 public void setWholeNoteDivisions(int wnd)
480 {
481 wholeNoteDivisions = wnd;
482 }
483
484
485 // ----------------------------------------------------------------------------------
486 // Method : saveToFile
487 // Returns : false - if the track could not be successfully saved
488 // true - if the track was successfully saved
489 // ----------------------------------------------------------------------------------
490 public boolean saveToFile(File outFile)
491 {
492 // Convert the track to a string and save it to the specified file
493 try {
494 FileOutputStream fileOut = new FileOutputStream(outFile);
495 fileOut.write(toString().getBytes());
496 fileOut.close();
497 }
498 catch (Exception ex) {
499 JOptionPane.showMessageDialog(null, "Exception occurred writing to file.\n\n" + ex);
500 return false;
501 }
502
503 // File saved successfully
504 return true;
505 }
506}
Note: See TracBrowser for help on using the repository browser.