Source code: jm/music/data/Score.java
1 /*
2
3 <This Java Class is part of the jMusic API version 1.4, February 2003.>
4
5
6 Copyright (C) 2000 Andrew Sorensen, Andrew Brown, Adam Kirby
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or any
11 later version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 */
23
24 package jm.music.data;
25
26 import java.io.ObjectInputStream;
27 import java.io.ObjectOutputStream;
28 import java.io.FileInputStream;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.Serializable;
32 import java.util.Vector;
33 import java.util.Enumeration;
34
35 import jm.audio.Instrument;
36 import jm.audio.Audio;
37 import jm.JMC;
38 import jm.midi.*;
39 import jm.util.*;
40 import jm.gui.show.*;
41
42 /**
43 * The Score class is used to hold score data. Score data includes
44 * is primarily made up of a vector of Part objects. Commonly score
45 * data is algorithmically generated or read from a standard MIDI file, but can also be read and saved to
46 * file using Java's object serialization. In this way a Score's data can
47 * be saved in a more native context.
48 * To find out how to read from and write to standard MIDI files
49 * or to use object serializationcheck out
50 * the jm.util.Read and km.util.Write classes.
51 *
52 * @see Part
53 * @see jm.midi.SMF
54 * @author Andrew Sorensen, Andrew Brown, Adam Kirby
55 * @version 1.0,Sun Feb 25 18:43:33 2001
56 */
57 public class Score implements JMC, Cloneable, Serializable{
58
59 //----------------------------------------------
60 // Defaults
61 //----------------------------------------------
62
63 public static final String DEFAULT_TITLE = "Untitled Score";
64
65 public static final double DEFAULT_TEMPO = 60.0;
66
67 public static final int DEFAULT_VOLUME = 100;
68
69 public static final int DEFAULT_KEY_SIGNATURE = 0;
70
71 public static final int DEFAULT_KEY_QUALITY = 0;
72
73 public static final int DEFAULT_NUMERATOR = 4;
74
75 public static final int DEFAULT_DENOMINATOR = 4;
76
77 //----------------------------------------------
78 // Attributes
79 //----------------------------------------------
80
81 /** the name assigned to a Score */
82 private String title;
83 /**
84 * a Vector containing the Part objects associated with this score
85 */
86 private Vector partList;
87 /** the speed for this score */
88 private double tempo;
89 /** the loudness for this score */
90 private int volume;
91
92 // Possible Alternative:
93 // Consider using the jm.music.data.KeySignature class and modifying
94 // it to support the keyQuality field.
95
96 /** the number of accidentals this score
97 * negative numbers are Flats, positive numbers are Sharps
98 */
99 private int keySignature;
100 /** 0 = major, 1 = minor, others modes not specified */
101 private int keyQuality;
102
103 // Possible Alternative:
104 // Consider making a TimeSignature class, containing the following
105 // two fields.
106
107 /** the top number of the time signature */
108 private int numerator;
109 /** the bottom number of the time signature */
110 private int denominator;
111
112
113 /**
114 //----------------------------------------------
115 // Constructors
116 //----------------------------------------------
117 /**
118 * Constructs an empty score with a default name
119 */
120 public Score(){
121 this(DEFAULT_TITLE);
122 }
123
124 /**
125 * Constructs an empty score.
126 * @param String title - give the score a name
127 */
128 public Score(String title){
129 this(title, DEFAULT_TEMPO);
130 }
131
132 /**
133 * Constructs an empty score at the specified tempo
134 * @param tempo The speed for this score in beats per minute
135 */
136 public Score(double tempo){
137 this(DEFAULT_TITLE, tempo);
138 }
139
140 /**
141 * Constructs an empty score.
142 * @param String title - give the score a name
143 * @param int tempo - define speed of playback in bpm
144 */
145 public Score(String title, double tempo){
146 this.title = title;
147 this.tempo = tempo;
148 this.partList = new Vector();
149 this.volume = DEFAULT_VOLUME;
150 this.keySignature = DEFAULT_KEY_SIGNATURE;
151 this.keyQuality = DEFAULT_KEY_QUALITY;
152 this.numerator = DEFAULT_NUMERATOR;
153 this.denominator = DEFAULT_DENOMINATOR;
154 }
155
156 /**
157 * Constructs a Score containing the specified <CODE>part</CODE>.
158 *
159 * @param part Part to be contained in the Score
160 */
161 public Score(Part part) {
162 this();
163 this.addPart(part);
164 }
165
166 /**
167 * Constructs a Score containing the specified <CODE>part</CODE>.
168 * @param String title - give the score a name
169 * @param int tempo - define speed of playback in bpm
170 * @param part Part to be contained in the Score
171 */
172 public Score(String title, double tempo, Part part) {
173 this(title, tempo);
174 this.addPart(part);
175 }
176
177
178 /**
179 * Constructs a Score containing the specified <CODE>parts</CODE>.
180 *
181 * @param parts array of Parts to be contained in the Score
182 */
183 public Score(Part[] parts) {
184 this();
185 addPartList(parts);
186 }
187
188 /**
189 * Constructs a Score containing the specified <CODE>part</CODE> with
190 * the specified <CODE>title</CODE>.
191 *
192 * @param part Part to be contained in the Score
193 * @param title String describing the title of the Score
194 */
195 public Score(Part part, String title) {
196 this(title);
197 addPart(part);
198 }
199
200 /**
201 * Constructs a Score containing the specified <CODE>parts</CODE> with
202 * the specified <CODE>title</CODE>.
203 *
204 * @param parts array of Parts to be contained in the Score
205 * @param title String describing the title of the Score
206 */
207 public Score(Part[] parts, String title) {
208 this(title);
209 addPartList(parts);
210 }
211
212 /**
213 * Constructs a Score containing the specified <CODE>part</CODE> with
214 * the specified <CODE>title</CODE> and the specified <CODE>tempo</CODE>.
215 *
216 * @param part Part to be contained in the Score
217 * @param title String describing the title of the Score
218 * @param tempo double describing the tempo of the Score
219 */
220 public Score(Part part, String title, double tempo) {
221 this(title, tempo);
222 addPart(part);
223 }
224
225 /**
226 * Constructs a Score containing the specified <CODE>parts</CODE> with
227 * the specified <CODE>title</CODE> and the specified <CODE>tempo</CODE>.
228 *
229 * @param parts array of Parts to be contained in the Score
230 * @param title String describing the title of the Score
231 * @param tempo double describing the tempo of the Score
232 */
233 public Score(Part[] parts, String title, double tempo) {
234 this(title, tempo);
235 addPartList(parts);
236 }
237
238
239 //----------------------------------------------
240 // Data Methods
241 //----------------------------------------------
242 /**
243 * Add a Track object to this Score
244 */
245 public void add(Part part){
246 this.addPart(part);
247 }
248
249 /**
250 * Add a Track object to this Score
251 */
252 public void addPart(Part part){
253 part.setMyScore(this);
254 this.partList.addElement(part);
255 }
256
257 /**
258 * Inserts <CODE>part</CODE> at the specified position, shifting all parts
259 * with indices greater than or equal to <CODE>index</CODE> up one position.
260 *
261 * @param part Part to be added
262 * @param index where it is to be inserted
263 * @throws ArrayIndexOutOfBoundsException
264 * when <CODE>index</CODE> is beyond the range of current
265 * parts.
266 */
267 public void insertPart(Part part, int index)
268 throws ArrayIndexOutOfBoundsException {
269 this.partList.insertElementAt(part, index);
270 part.setMyScore(this);
271 }
272
273 /**
274 * Adds multiple parts to the score from an array of parts
275 * @param partArray
276 */
277 public void addPartList(Part[] partArray){
278 for(int i=0;i< partArray.length;i++){
279 this.addPart( partArray[i]);
280 }
281 }
282
283 /**
284 * Deletes the specified Part in the Score
285 * @param int partNumb the index of the part to be deleted
286 */
287 public void removePart(int partNumb) {
288 Vector vct = (Vector)this.partList;
289 try{
290 vct.removeElement(vct.elementAt(partNumb));
291 } catch (RuntimeException re){
292 System.err.println("The Part index to be deleted must be within the score.");
293 }
294 }
295
296 /**
297 * Deletes the first occurence of the specified part in the Score.
298 * @param part the Part object to be deleted.
299 */
300 public void removePart(Part part) {
301 this.partList.removeElement(part);
302 }
303
304 /**
305 * Deletes the last Part added to the Score
306 */
307 public void removeLastPart() {
308 Vector vct = (Vector)this.partList;
309 vct.removeElement(vct.lastElement());
310 }
311
312 /**
313 * Deletes all the parts previously added to the score
314 */
315 public void removeAllParts() {
316 this.partList.removeAllElements();
317 }
318
319 /**
320 * Returns the Scores List of Tracks
321 */
322 public Vector getPartList(){
323 return partList;
324 }
325
326 /**
327 * Returns the all Parts in this Score as a array
328 * @return Part[] An array containing all Part objects in this score
329 */
330 public Part[] getPartArray(){
331 Vector vct = (Vector) this.partList.clone();
332 Part[] partArray = new Part[vct.size()];
333 for(int i=0;i< partArray.length;i++){
334 partArray[i] = (Part) vct.elementAt(i);
335 }
336 return partArray;
337 }
338
339 /**
340 * Get an individual Track object from its title
341 * @param String title - the name of the Track to return
342 * @return Track answer - the Track to return
343 */
344 public Part getPart(String title){
345 Enumeration enum = partList.elements();
346 while(enum.hasMoreElements()){
347 Part part = (Part) enum.nextElement();
348 if(part.getTitle().equalsIgnoreCase(title)){
349 return part;
350 }
351 }
352 return null;
353 }
354
355 /**
356 * Get an individual Track object from its number
357 * @param int number - the number of the Track to return
358 * @return Track answer - the Track to return
359 */
360 public Part getPart(int number){
361 Enumeration enum = partList.elements();
362 int counter = 0;
363 while(enum.hasMoreElements()){
364 Part part = (Part) enum.nextElement();
365 if(counter == number){
366 return part;
367 }
368 counter++;
369 }
370 return null;
371 }
372
373
374
375 //----------------------------------------------
376 // Utility Methods
377 //----------------------------------------------
378 /**
379 * Return the title of this Score
380 * @return String title - the name of this Score
381 */
382 public String getTitle(){
383 return title;
384 }
385 /**
386 * Assign a title to this Score
387 * @param String title - the name of this Score
388 */
389 public void setTitle(String title){
390 this.title = title;
391 }
392
393 /**
394 * Returns the Score's tempo
395 * @return double tempo
396 */
397 public double getTempo(){
398 return this.tempo;
399 }
400
401 /**
402 * Sets the Score's tempo
403 * @param double tempo
404 */
405 public void setTempo(double tempo){
406 this.tempo = tempo;
407 }
408
409 /**
410 * Returns the Score's volume
411 * @return int volume
412 */
413 public int getVolume(){
414 return this.volume;
415 }
416
417 /**
418 * Sets the Score's volume
419 * @param int volume
420 */
421 public void setVolume(int volume){
422 this.volume = volume;
423 }
424
425 /**'
426 * Returns the Score's key signature
427 * The number of sharps (+) or flats (-)
428 * @return int key signature
429 */
430 public int getKeySignature(){
431 return this.keySignature;
432 }
433
434 /**
435 * Specifies the Score's key signature
436 * The number of sharps (+) or flats (-)
437 * @param int key signature
438 */
439 public void setKeySignature(int newSig){
440 this.keySignature = newSig;
441 }
442
443 /**
444 * Returns the Score's key quality
445 * 0 is Major, 1 is minor
446 * @return int key quality
447 */
448 public int getKeyQuality(){
449 return this.keyQuality;
450 }
451
452 /**
453 * Specifies the Score's key quality
454 * 0 is Major, 1 is minor
455 * @param int key quality (modality)
456 */
457 public void setKeyQuality(int newQual){
458 this.keyQuality = newQual;
459 }
460
461 /**
462 * Returns the Score's time signature numerator
463 * @return int time signature numerator
464 */
465 public int getNumerator(){
466 return this.numerator;
467 }
468
469 /**
470 * Specifies the Score's time signature numerator
471 * @param int time signature numerator
472 */
473 public void setNumerator(int num){
474 this.numerator = num;
475 }
476
477 /**
478 * Returns the Score's time signature denominator
479 * @return int time signature denominator
480 */
481 public int getDenominator(){
482 return this.denominator;
483 }
484
485 /**
486 * Specifies the Score's time signature denominator
487 * @param int time signature denominator
488 */
489 public void setDenominator(int dem){
490 this.denominator= dem;
491 }
492
493 /**
494 * Make a copy of this Score object
495 * @return Score - return a new Score Object
496 */
497 public Score copy() {
498 Score newScore = new Score(title + " copy");
499 newScore.setTempo(this.tempo);
500 newScore.setVolume(this.volume);
501 Enumeration enum = this.partList.elements();
502 while(enum.hasMoreElements()){
503 Part oldPart = (Part) enum.nextElement();
504 newScore.addPart((Part) oldPart.copy());
505 }
506 return (Score)newScore;
507 }
508
509 public Score copy(final double startTime, final double endTime) {
510 Score score = this.copy();
511 score.removeAllParts();
512 int scoresize = this.size();
513 for (int i = 0; i < scoresize; i++) {
514 score.addPart(this.getPart(i).copy(startTime, endTime));
515 }
516 return score;
517 }
518
519 /**
520 * Return the beat where score ends. Where it's last Part ends.
521 * @return double the Parts endTime
522 */
523 public double getEndTime(){
524 double endTime = 0.0;
525 Enumeration enum = this.partList.elements();
526 while(enum.hasMoreElements()){
527 Part nextPart = (Part)enum.nextElement();
528 double partEnd = nextPart.getEndTime();
529 if (partEnd > endTime) endTime = partEnd;
530 }
531 return endTime;
532 }
533
534
535 /**
536 * Print the titles of all tracks to stdout
537 */
538 public String toString(){
539 String scoreData = new String("***** jMusic SCORE: '" + title +
540 "' contains " + this.size() + " parts. ****" + '\n');
541 scoreData += "Score Tempo = " + this.tempo + " bpm" +'\n';
542 Enumeration enum = partList.elements();
543 while(enum.hasMoreElements()){
544 Part part = (Part) enum.nextElement();
545 scoreData = scoreData + part.toString() + '\n';
546 }
547 return scoreData;
548 }
549
550 /**
551 * Empty removes all elements in the vector
552 */
553 public void empty(){
554 this.empty(false);
555 }
556
557 /**
558 * Empty removes all elements in the vector.
559 * @param nullObjects If ture this sets all jMusic data objects to null
560 * priot to removing. This facilitates garbage collection.
561 */
562 public void empty(boolean nullObjects){
563 if(nullObjects) {
564 Enumeration enum = getPartList().elements();
565 while(enum.hasMoreElements()){
566 Part part = (Part) enum.nextElement();
567 Enumeration enum2 = part.getPhraseList().elements();
568 while(enum2.hasMoreElements()){
569 Phrase phrase = (Phrase) enum2.nextElement();
570 Enumeration enum3 = part.getPhraseList().elements();
571 while(enum3.hasMoreElements()){
572 Note note = (Note) enum3.nextElement();
573 note = null;
574 }
575 phrase = null;
576 }
577 part = null;
578 }
579 }
580 partList.removeAllElements();
581 }
582
583 /**
584 * Get the number of Parts in this score
585 * @return int The number of parts
586 */
587 public int length(){
588 return size();
589 }
590
591 /**
592 * Get the number of Parts in this score
593 * @return int length - the number of parts
594 */
595 public int size(){
596 return(partList.size());
597 }
598
599 /**
600 * Get the number of Parts in this score
601 * @return int length - the number of parts
602 */
603 public int getSize(){
604 return(partList.size());
605 }
606
607 /**
608 * Remove any empty Parts or phrases from the Score.
609 */
610 public void clean() {
611 Enumeration enum = getPartList().elements();
612 while(enum.hasMoreElements()){
613 Part part = (Part) enum.nextElement();
614 // pass on the part to have phases cleaned
615 part.clean();
616 // check if part is empty
617 if (part.getPhraseList().size() == 0) {
618 this.removePart(part);
619 }
620 }
621 }
622
623 /**
624 * Return the value of the highest note in the Score.
625 */
626 public int getHighestPitch() {
627 int max = 0;
628 Enumeration enum = getPartList().elements();
629 while(enum.hasMoreElements()){
630 Part part = (Part) enum.nextElement();
631 if(part.getHighestPitch() > max) max = part.getHighestPitch();
632 }
633 return max;
634 }
635
636 /**
637 * Return the value of the lowest note in the Score.
638 */
639 public int getLowestPitch() {
640 int min = 127;
641 Enumeration enum = getPartList().elements();
642 while(enum.hasMoreElements()){
643 Part part = (Part) enum.nextElement();
644 if(part.getLowestPitch() < min) min = part.getLowestPitch();
645 }
646 return min;
647 }
648
649 /**
650 * Return the value of the longest rhythm value in the Score.
651 */
652 public double getLongestRhythmValue() {
653 double max = 0.0;
654 Enumeration enum = getPartList().elements();
655 while(enum.hasMoreElements()){
656 Part part = (Part) enum.nextElement();
657 if(part.getLongestRhythmValue() > max) max = part.getLongestRhythmValue();
658 }
659 return max;
660 }
661
662 /**
663 * Return the value of the shortest rhythm value in the Score.
664 */
665 public double getShortestRhythmValue() {
666 double min = 1000.0;
667 Enumeration enum = getPartList().elements();
668 while(enum.hasMoreElements()){
669 Part part = (Part) enum.nextElement();
670 if(part.getShortestRhythmValue() < min) min = part.getShortestRhythmValue();
671 }
672 return min;
673 }
674
675 /**
676 * Determine the pan position for all notes in this Score.
677 * @param double the phrase's pan setting
678 */
679 public void setPan(double pan){
680 Enumeration enum = partList.elements();
681 while(enum.hasMoreElements()){
682 Part part = (Part) enum.nextElement();
683 part.setPan(pan);
684 }
685 }
686
687 /**
688 * Generates and returns a new empty part
689 * and adds it to the score.
690 */
691 public Part createPart() {
692 Part p = new Part();
693 this.addPart(p);
694 return p;
695 }
696 }