Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: jm/music/data/Phrase.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
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 jm.JMC;
27  import jm.util.*;
28  import jm.gui.cpn.Notate;
29  import java.io.ObjectInputStream;
30  import java.io.ObjectOutputStream;
31  import java.io.FileInputStream;
32  import java.io.FileOutputStream;
33  import java.io.IOException;
34  import java.io.Serializable;
35  import java.util.Vector;
36  import java.util.Enumeration;
37  
38  /**
39   * The Phrase class is representative of a single musical phrase.
40   * Phrases are held in Parts and can be played at any time
41   * based on there start times. They may be played sequentially or in parallel.
42   * Phrases can be added to an Part like this...
43   * <pre>
44   *     Part inst = new Part("Flute");
45   *     //Phrase for the right hand
46   *     Phrase rightHand = new Phrase(0.0) //start this phrase on the first beat
47   *     //Phrase for the left hand
48   *     Phrase leftHane = new Phrase(4.0) //start this phrase on the fifth beat
49   *     inst.addPhrase(rightHand);
50   *     inst.addPhrase(leftHand);
51   * </pre>
52   * @see Note
53   * @see Part
54   * @author Andrew Sorensen and Andrew Brown
55   * @version 1.0,Sun Feb 25 18:43:32  2001
56   */
57   
58  public class Phrase implements JMC, Cloneable, Serializable{
59      //----------------------------------------------
60      // Limits
61      //-----------------------------------------------
62      /** The smallest start time in beats */
63      public static final double MIN_START_TIME = 0.0;
64  
65      //----------------------------------------------
66      // Default constants
67      //----------------------------------------------
68  
69      public static final String DEFAULT_TITLE = "Untitled Phrase";
70  
71      public static final double DEFAULT_START_TIME = MIN_START_TIME;
72  
73      public static final int DEFAULT_INSTRUMENT = NO_INSTRUMENT;
74  
75      public static final boolean DEFAULT_APPEND = false;
76  
77      public static final double DEFAULT_TEMPO = -1.0;
78  
79      public static final double DEFAULT_PAN = Note.DEFAULT_PAN;
80  
81      public static final int DEFAULT_NUMERATOR = 4;
82  
83      public static final int DEFAULT_DENOMINATOR = 4;
84      
85      public static final int DEFAULT_VOLUME = 100;
86  
87    //----------------------------------------------
88    // Attributes
89    //----------------------------------------------
90    /** An array containing mutiple voices */
91    private Vector noteList;
92    /** The title/name given to this phrase */
93    private String title;
94          
95  //  /** The phrases start time in beats */
96  //  private double startTime;
97          
98          private Position position;
99    
100   /** instrumet / MIDI program change number for this phrase */
101   private int instrument;
102   /** speed in beats per minute for this phrase */
103   private double tempo;
104   /** Setting the phrase to append when added to a part
105   * rather than use its start time.
106   */
107   private boolean append = false;
108   /** A phrase to have a relative start time with if required. */
109   private Phrase linkedPhrase;
110   /** The pan position for notes in this phrase.
111   * This must be set delibertley to override a note's pan position. */
112   private double pan = DEFAULT_PAN;
113 
114   /** the top number of the time signature */
115   private int numerator;
116   /** the bottom number of the time signature */
117   private int denominator;
118   /** A reference to this phrases part */
119   private Part myPart = null;
120         /** The loudness for this phrase */
121         private int volume;
122         /** Weather the phrase should play or not */
123         private boolean mute = false;
124   //----------------------------------------------
125   // Constructors
126   //----------------------------------------------
127   /**
128    * Creates an empty Phrase.
129    * The default start time is a flag which means the phrase will be
130    * appended to the end of any part it is added to.
131    */
132   public Phrase(){
133     this(DEFAULT_START_TIME);
134     this.append = true;
135   }
136 
137   /**
138    * Creates an empty Phrase starting at the specified beat.
139    * @param startTime The beat at which the phrase will be positioned in its part.
140    */
141   public Phrase(double startTime){
142     this(startTime, DEFAULT_INSTRUMENT);
143   }
144 
145   /**
146    * Creates an empty Phrase
147    * @param startTime The beat at which the phrase will be positioned in its part.
148    * @param instrument The sound or instrument number to be used for this phrase.
149    */
150   public Phrase(double startTime, int instrument) {
151     this(DEFAULT_TITLE, startTime, instrument);
152   }
153   
154   /**
155    * Creates an empty Phrase
156    * @param title The name for the phrase.
157    */
158   public Phrase(String title){
159     this(title, DEFAULT_START_TIME);
160     this.append = true;
161   }
162   
163   /**
164    * Creates an empty Phrase.
165    * @param title The name for the phrase.
166    * @param startTime The beat at which the phrase will be positioned in its part.
167    */
168   public Phrase(String title, double startTime){
169     this(title, startTime, DEFAULT_INSTRUMENT);
170   }
171   
172   /**
173    * Creates an empty Phrase.
174    * @param title The name for the phrase.
175    * @param startTime The beat at which the phrase will be positioned in its part.
176    * @param instrument The sound or instrument number to be used for this phrase.
177    */
178   public Phrase(String title, double startTime, int instrument){
179     this(title, startTime, instrument, DEFAULT_APPEND);
180   }
181   
182   /**
183    * Creates an empty Phrase.
184    * @param title The name for the phrase.
185    * @param startTime The beat at which the phrase will be positioned in its part.
186    * @param instrument The sound or instrument number to be used for this phrase.
187    * @param append A flag specifying wheather or not this phrase should be added to the
188    * end of the part it is added too, or should use its start time value.
189    */
190   public Phrase(String title, double startTime, int instrument, boolean append){
191     this.title = title;
192 //    this.startTime = startTime;
193                 this.position = new Position(startTime, this);
194     this.append = append;
195     if(instrument < NO_INSTRUMENT){
196       System.err.println(new Exception("jMusic EXCEPTION: instrument " +
197                        "value must be greater than 0"));
198       (new Exception()).printStackTrace();
199       System.exit(1); //crash ungracefully 
200     }
201     this.instrument = instrument;
202     this.noteList = new Vector();
203     this.numerator = DEFAULT_NUMERATOR;
204     this.denominator = DEFAULT_DENOMINATOR;
205                 this.tempo = DEFAULT_TEMPO;
206                 this.volume = DEFAULT_VOLUME;
207   }
208 
209     /**
210      * Constructs a new Phrase containing the specified <CODE>note</CODE>.
211      *
212      * @param note  Note to be containing by the phrase.
213      */
214     public Phrase(Note note) {
215         this();
216         addNote(note);
217     }
218 
219     /**
220      * Constructs a new Phrase containing the specified <CODE>notes</CODE>.
221      *
222      * @param notes array of Note to be contained by the phrase.
223      */
224     public Phrase(Note[] notes) {
225         this();
226         addNoteList(notes);
227     }
228 
229     /**
230      * Constructs a new Phrase containing the specified <CODE>note</CODE> with
231      * the specified <CODE>title</CODE>.
232      *
233      * @param note  Note to be containing by the phrase.
234      * @param title String describing the title of the Phrase.
235      */
236     public Phrase(Note note, String title) {
237         this(title);
238         addNote(note);
239     }
240 
241     /**
242      * Constructs a new Phrase containing the specified <CODE>notes</CODE> with
243      * the specified <CODE>title</CODE>.
244      *
245      * @param notes array of Note to be contained by the phrase.
246      * @param title String describing the title of the Phrase.
247      */
248     public Phrase(Note[] notes, String title) {
249         this(title);
250         this.addNoteList(notes);
251     }
252 
253         /**
254             * Constructs a new Phrase containing the specified <CODE>note</CODE> with
255          * the specified <CODE>title</CODE>.
256          *
257          * @param note  Note to be containing by the phrase.
258          * @param title String describing the title of the Phrase.
259          */
260         public Phrase(Note note, double startTime) {
261             this(note);
262             this.setStartTime(startTime);
263         }
264 
265   //----------------------------------------------
266   // Data Methods
267   //----------------------------------------------
268 
269   /**
270    * Return the program change assigned by this phrase
271    * @return int
272    */
273   public int getInstrument(){
274     return this.instrument;
275   }
276 
277   /**
278    * Sets the program change value
279    * @param int program change
280    */
281   public void setInstrument(int value){
282     this.instrument = value;
283   }
284   
285   /**
286    * Add a note to this Phrase
287    * @param Note note - add a note to this phrase
288    */
289   public void addNote(Note note){
290     note.setMyPhrase(this);
291     noteList.addElement(note);
292   }
293 
294 
295         /**
296         * Add a note to this Phrase
297          * @param pitch -the pitch of the note
298          *@param rv - the rhythmValue of the note
299          */
300         public void addNote(int pitch, double rv){
301             Note note = new Note(pitch, rv);
302     this.addNote(note);
303         }
304         
305         /**
306    * Add a note to this Phrase
307    * @param Note note - add a note to this phrase
308    */
309   public void add(Note note){
310     this.addNote(note);
311   }
312         
313 
314     /**
315      * Appends the specified notes to the end of this Phrase.
316      *
317      * @param   array of Notes to append.
318      */
319     public void addNoteList(Note[] notes) {
320         for (int i = 0; i < notes.length; i++) {
321     this.addNote(notes[i]);
322         }
323     }
324 
325   /**
326    * Adds a vector of notes to the phrase
327    * A boolean option when true appends the notes to the end of the list
328    * @param noteVector the vector of notes to add
329    * @param append do we append or not?
330    */
331   public void addNoteList(Vector noteVector, boolean append){
332     Enumeration enum = noteVector.elements();
333     if(!append) this.noteList.removeAllElements();        
334     while(enum.hasMoreElements()){
335       try{
336         Note note = (Note) enum.nextElement();
337                                 this.addNote(note);
338         //note.setMyPhrase(this);
339       }catch(RuntimeException re){
340         System.err.println("The vector passed to this method must " + "contain Notes only!");
341       }
342     }
343   }
344 
345     /**
346      * Adds an array of notes to the phrase
347      * A boolean option when true appends the notes to the end of the list
348      * @param noteArray the array of notes to add
349      * @param append do we append or not?
350      */
351     public void addNoteList(Note[] noteArray, boolean append){
352         if(!append) this.noteList.removeAllElements();
353         for(int i=0;i<noteArray.length;i++){
354     this.addNote(noteArray[i]);
355         }
356     }
357     
358     /**
359    * Adds Multiple notes to the phrase from several arrays 
360    * @param pitchArray array of pitch values
361    * @param rhythmValue a rhythmic value
362    */
363   public void addNoteList(int[] pitchArray, double rhythmValue){
364     double[] rvArray = new double[pitchArray.length];
365     for(int i=0;i<rvArray.length;i++){
366       rvArray[i] = rhythmValue;
367     }
368     this.addNoteList(pitchArray, rvArray);
369   }
370 
371   /**
372   * Adds Multiple notes to the phrase from an array of frequency values
373    * @param freqArray array of freequency values
374    * @param rhythmValue a rhythmic value
375    */
376   public void addNoteList(double[] freqArray, double rhythmValue){
377     double[] rvArray = new double[freqArray.length];
378     for(int i=0;i<rvArray.length;i++){
379       rvArray[i] = rhythmValue;
380     }
381     this.addNoteList(freqArray, rvArray);
382   }
383   
384 
385 
386   /**
387    * Adds Multiple notes to the phrase from several arrays 
388    * @param pitchArray array of pitch values
389    * @param rhythmArray array of rhythmic values
390    */
391   public void addNoteList(int[] pitchArray, double[] rhythmArray){
392     int[] dynamic = new int[pitchArray.length];
393     for(int i=0;i<pitchArray.length;i++){
394       dynamic[i] = Note.DEFAULT_DYNAMIC;
395     }
396     this.addNoteList(pitchArray, rhythmArray, dynamic);
397   }
398 
399   /**
400   * Adds Multiple notes to the phrase from several arrays
401    * @param freqArray array of frequency values
402    * @param rhythmArray array of rhythmic values
403    */
404   public void addNoteList(double[] freqArray, double[] rhythmArray){
405     int[] dynamic = new int[freqArray.length];
406     for(int i=0;i<freqArray.length;i++){
407       dynamic[i] = Note.DEFAULT_DYNAMIC;
408     }
409     this.addNoteList(freqArray, rhythmArray, dynamic);
410   }
411 
412   /**
413    * Adds Multiple notes to the phrase from several arrays 
414    * @param pitchArray array of pitch values
415    * @param rhythmArray array of rhythmic values
416    * @param dynmaic array of dynamic values
417    */
418   public void addNoteList(int[] pitchArray, double[] rhythmArray, 
419               int[] dynamic){
420     this.addNoteList(pitchArray, rhythmArray, dynamic, true); 
421   }
422 
423   /**
424   * Adds Multiple notes to the phrase from several arrays
425    * @param freqArray array of frequency values
426    * @param rhythmArray array of rhythmic values
427    * @param dynmaic array of dynamic values
428    */
429   public void addNoteList(double[] freqArray, double[] rhythmArray,
430        int[] dynamic){
431     this.addNoteList(freqArray, rhythmArray, dynamic, true);
432   }
433 
434   /**
435    * Adds Multiple notes to the phrase from several arrays 
436    * A boolean option when true appends the notes to the end of the list
437    * if non true the current list is errased and replaced by the new notes
438    * @param pitchArray array of pitch values
439    * @param rhythmArray array of rhythmic values
440    * @param dynamic int
441    * @param append do we append or not?
442    */
443   public void addNoteList(int[] pitchArray, double[] rhythmArray, 
444               int[] dynamic, boolean append){
445     if(!append) this.noteList.removeAllElements();
446     for(int i=0;i<pitchArray.length;i++){
447       try{
448         Note knote = new Note(pitchArray[i],rhythmArray[i],dynamic[i]);
449         this.addNote(knote);
450       }catch(RuntimeException re){
451         System.err.println("You must enter arrays of even length");
452       }
453     }
454   }
455 
456   /**
457   * Adds Multiple notes to the phrase from several arrays
458    * A boolean option when true appends the notes to the end of the list
459    * if non true the current list is errased and replaced by the new notes
460    * @param freqArray array of frequency values
461    * @param rhythmArray array of rhythmic values
462    * @param dynamic int
463    * @param append do we append or not?
464    */
465   public void addNoteList(double[] freqArray, double[] rhythmArray,
466        int[] dynamic, boolean append){
467     if(!append) this.noteList.removeAllElements();
468     for(int i=0;i<freqArray.length;i++){
469       try{
470         Note knote = new Note(freqArray[i],rhythmArray[i],dynamic[i]);
471         this.addNote(knote);
472       }catch(RuntimeException re){
473         System.err.println("jMusic Phrase error: You must enter arrays of even length");
474       }
475     }
476   }
477   
478   /**
479    * Adds Multiple notes to the phrase from one array of pitch, rhythm pairs
480    * @param pitchAndRhythmArray  - an array of pitch and rhythm values
481    */
482   public void addNoteList(double[] pitchAndRhythmArray){
483       for(int i=0;i< pitchAndRhythmArray.length;i+=2){
484           try {
485       Note knote = new Note((int)pitchAndRhythmArray[i],pitchAndRhythmArray[i+1]);
486       this.addNote(knote);
487                 }catch(RuntimeException re) {
488                     System.err.println("Error adding note list: Possibly the wrong number of values in the pitch and rhythm array.");
489                 }
490             }
491         }
492 
493   /**
494   * Adds Multiple notes to the phrase from one pitch and an array of rhythm values
495    *@param pitch The pitch values for the notes
496    * @param rhythms An array of rhythm values
497    */
498   public void addNoteList(int pitch, double[] rhythms){
499     for(int i=0; i<rhythms.length; i++) {
500       this.addNote(new Note(pitch, rhythms[i]));
501     }
502   }
503 
504   /**
505   * Adds Multiple notes to the phrase from one pitch and an array of rhythm values
506    *@param frequency The pitch values for the notes in hertz
507    * @param rhythms An array of rhythm values
508    */
509   public void addNoteList(double frequency, double[] rhythms){
510     for(int i=0; i<rhythms.length; i++) {
511       this.addNote(new Note(frequency, rhythms[i]));
512     }
513   }
514   
515     
516         /**
517         * Adds Multiple notes to the phrase all of which start at the same time
518          * and share the same duration.
519          * @param pitches An array of pitch values
520          * @param rv the rhythmValue
521          */
522         public void addChord(int[] pitches, double rv) {
523             for(int i=0; i<pitches.length - 1; i++) {
524                 Note n = new Note(pitches[i], 0.0);
525                 n.setDuration(rv * Note.DEFAULT_DURATION_MULTIPLIER);
526                 this.addNote(n);
527             }
528             this.addNote(pitches[pitches.length - 1], rv);
529 
530             System.out.println("In phrase" + this.toString());
531         }
532     
533   /**
534    * Deletes the specified note in the phrase
535    * @param int noteNumb the index of the note to be deleted
536    */
537    public void removeNote(int noteNumb) {
538       Vector vct = (Vector)this.noteList;
539       try{
540           vct.removeElement(vct.elementAt(noteNumb));
541       } catch (RuntimeException re){
542       System.err.println("Note index to be deleted must be within the phrase.");
543     }
544   }
545     
546     /**
547    * Deletes the first occurence of the specified note in the phrase
548    * @param note  the note object to be deleted.
549    */
550    public void removeNote(Note note) {
551         this.noteList.removeElement(note);
552     }    
553         
554     /**
555    * Deletes the last note in the phrase
556    */
557    public void removeLastNote() {
558       Vector vct = (Vector)this.noteList;
559       vct.removeElementAt(vct.size()-1);
560   }
561   
562   /**
563    * Returns the entire note list contained in a single voice
564    * @return Vector A vector containing all Note objects in this phrase
565    */
566   public Vector getNoteList(){
567     return this.noteList;
568   }
569   
570   /**
571    * Replaces the entire note list with a new note list vector
572    * @param Vector of notes
573    */
574   public void setNoteList(Vector newNoteList){
575     this.noteList = newNoteList;
576   }
577   
578   /**
579    * Returns the all notes in the phrase as a array of notes
580    * @return Note[] An array containing all Note objects in this phrase
581    */
582   public Note[] getNoteArray(){
583     Vector vct = (Vector) this.noteList;
584     Note[] noteArray = new Note[vct.size()];
585     for(int i=0;i< noteArray.length;i++){
586         noteArray[i] = (Note) vct.elementAt(i);
587     }
588     return noteArray;
589   }
590     
591   /**
592    * Return the phrases startTime
593    * @return double the phrases startTime
594    */
595   public double getStartTime(){
596                 return position.getStartTime();
597 //    return this.startTime;
598   }
599     
600     
601   /**
602    * Sets the phrases startTime
603          *
604          * <p>This positions the phrase absolutely.  If this phrase is currently 
605          * positioned relative to another phrase that anchoring will be lost.
606          *
607          * <p>To position this relative to another class use the 
608          * <code>anchor</code> method instead.
609          *
610    * @param double the time at which to start the phrase
611    */
612   public void setStartTime(double startTime){
613         if(startTime >= MIN_START_TIME){
614                 position.setStartTime(startTime);
615 //            this.startTime = startTime;
616             this.setAppend(false);
617         }else{
618             System.err.println("Error setting phrase start time value: You must enter values greater than "+MIN_START_TIME);
619         }
620   }
621         
622         /** <p>The positions tries the phrase relative to another using the 
623          * alignment specified.  If the arrangement causes this class to start 
624          * before a start time of 0.0, the repositioning is considered invalid
625          * and will fail. The original positioning will be restored and this 
626          * method will return false.
627          * 
628          * If successful, the previous positioning whether absolute or relative
629          * will be lost.
630          *
631          * <p>To position this absolutely use the <code>setStartTime</code> 
632          * method instead.
633          *
634    * @param anchor    the phrase against which this should be positioned
635          * @param alignment how this should be positioned relative to anchor
636          * @returns         false if anchoring failed due to being positioned 
637          *                  before the 0.0 start time barrier.  True otherwise.
638    */
639   public boolean attemptAnchoringTo(final Phrase anchor, 
640                                           final Alignment alignment, 
641                                           final double offset) {
642                 Position newPosition = new Position(anchor.position, alignment, 
643                                                     offset, this);
644                 if (newPosition.getStartTime() < 0.0) {
645                         return false;
646                 } else {
647                         position = newPosition;
648                         return true;
649                 }
650                 
651         }
652         
653         /** Returns details of how this is aligned relative to another phrase.
654          * Alternatively, if this phrase is aligned absolutely returns null.
655          *
656          * @returns null if aligned with setStartTime(), or details of alignment
657          *          if aligned with attemptAnchoringTo()
658          */
659         public Anchoring getAnchoring() {
660                 return position.getAnchoring();
661         }
662   
663   /**
664    * Return the phrases endTime
665    * @return double the phrases endTime 
666    */
667   public double getEndTime(){  
668       double tempStartTime = (getStartTime() < MIN_START_TIME) ? MIN_START_TIME : getStartTime();
669       double endTime = tempStartTime;
670     Enumeration enum = this.noteList.elements();
671     while(enum.hasMoreElements()){
672       Note nextNote = (Note)enum.nextElement();
673       endTime += nextNote.getRhythmValue();
674     }    
675     return endTime;
676   }
677     
678   /**
679     * Returns the length of the whole phrase in beats.
680     * @return double duration in beats
681     */
682   final double getTotalDuration(){  
683       double cumulativeLength = 0.0;
684     Enumeration enum = this.noteList.elements();
685     while(enum.hasMoreElements()){
686       Note nextNote = (Note)enum.nextElement();
687       cumulativeLength += nextNote.getRhythmValue();
688     }    
689     return cumulativeLength;
690   }
691 
692         /**
693    * Return this phrases title
694    * @return String the phrases title
695    */
696   public String getTitle(){
697     return this.title;
698   }
699   
700   /**
701    * Gives the Phrase a new title 
702    * @param phrases title
703    */
704   public void setTitle(String title){
705     this.title = title;
706   }
707   
708   /**
709    * Return this phrases append status
710    * @return boolean the phrases append value
711    */
712   public boolean getAppend(){
713     return this.append;
714   }
715   
716   /**
717    * Gives the Phrase a new append status 
718    * @param boolean the append status
719    */
720   public void setAppend(boolean append){
721     this.append = append;
722   }
723   
724   /**
725    * Return this phrases this phrase is linked to
726    * @return Phrase the phrases linked to
727    */
728   public Phrase getLinkedPhrase(){
729     return this.linkedPhrase;
730   }
731   
732   /**
733    * Make a link from this phrase to another 
734    * @param Phrase the phrase to link to
735    */
736   public void setLinkedPhrase(Phrase link){
737     this.linkedPhrase = link;
738   }
739   
740   /**
741    * Return the pan position for this phrase
742    * @return double the phrases pan setting
743    */
744   public double getPan(){
745     return this.pan;
746   }
747   
748   /**
749    * Determine the pan position for all notes in this phrase. 
750    * @param double the phrase's pan setting
751    */
752   public void setPan(double pan){
753     this.pan = pan;
754     Enumeration enum = noteList.elements();
755     while(enum.hasMoreElements()){
756       Note note = (Note) enum.nextElement();
757       note.setPan(pan);
758       }
759   }
760         
761         /**
762    * Return the tempo in beats per minute for this phrase
763    * @return double the phrase's tempo setting
764    */
765   public double getTempo(){
766     return this.tempo;
767   }
768         
769          /**
770    * Determine the tempo in beats per minute for this phrase
771    * @param double the phrase's tempo
772    */
773   public void setTempo(double newTempo){
774      this.tempo = newTempo;
775   }
776 
777   /**
778    * Get an individual note object by its number 
779    * @param int number - the number of the Track to return
780    * @return Note answer - the note object to return
781    */
782   public Note getNote(int number){
783     Enumeration enum = noteList.elements();
784     int counter = 0;
785     while(enum.hasMoreElements()){
786       Note note = (Note) enum.nextElement();
787       if(counter == number){
788         return note;
789       }
790       counter++;
791     }
792     return null;
793   }
794   
795         
796         /**
797    * Get the number of notes in this phrase
798    * @return int  The number of notes
799    */
800   public int length(){
801             return size();
802         }
803         
804   /**
805    * Get the number of notes in this phrase
806    * @return int  length - the number of notes
807    */
808   public int size(){
809     return(noteList.size());
810   }
811     
812     /**
813    * Get the number of notes in this phrase
814    * @return int  length - the number of notes
815    */
816   public int getSize(){
817     return(noteList.size());
818   }
819 
820   
821   /**
822      * Returns the numerator of the Phrase's time signature 
823    * @return int time signature numerator
824    */
825   public int getNumerator(){
826     return this.numerator;
827   }
828   
829   /**
830      * Specifies the numerator of the Phrase's time signature
831    * @param int time signature numerator
832    */
833   public void setNumerator(int num){
834     this.numerator = num;
835   }
836   
837   /**
838      * Returns the denominator of the Phrase's time signature
839    * @return int time signature denominator
840    */
841   public int getDenominator(){
842     return this.denominator;
843   }
844   
845   /**
846      * Specifies the denominator of the Phrase's time signature
847    * @param int time signature denominator
848    */
849   public void setDenominator(int dem){
850     this.denominator= dem;
851   }
852 
853   /** Sets a reference to the part containing this phrase */
854   public void setMyPart(Part part){
855       this.myPart = part;
856   }
857 
858   /** returns a reference to the part that contains this phrase */
859   public Part getMyPart(){
860       return myPart;
861   }
862 
863   /**
864   * Returns a copy of the entire Phrase
865   * @return Phrase a copy of the Phrase
866   */
867   public Phrase copy(){
868     Phrase phr = new Phrase();
869                 copyAttributes(phr);
870     Enumeration enum = this.noteList.elements();
871     while(enum.hasMoreElements()){
872       phr.addNote( ((Note) enum.nextElement()).copy() );
873     }
874     return phr;
875   }
876         
877         private void copyAttributes(Phrase phr) {
878             // NB: start time now covered by position
879             phr.position = this.position.copy(phr);
880             phr.setTitle(this.title + " copy");
881             phr.setInstrument(this.instrument);
882             phr.setAppend(this.append);
883             phr.setPan(this.pan);
884             phr.setLinkedPhrase(this.linkedPhrase);
885             phr.setMyPart(this.getMyPart());
886             phr.setTempo(this.tempo);
887             phr.setNumerator(this.numerator);
888             phr.setDenominator(this.denominator);
889             phr.setVolume(this.volume);
890         }
891 
892    /**
893    * Returns a copy of a specified section of the Phrase,
894    * pads beginning and end with shortedend notes and rests
895    * if notes or phrase boundaries don't align with locations.
896    * @param double start location
897    * @param double end location
898    * @return Phrase a copy of the Phrase
899    */
900          public Phrase copy(double startLoc, double endLoc){
901                 Phrase phr = this.copy(startLoc, endLoc, true);
902                 return phr;
903         }
904         
905         /**
906    * Returns a copy of a specified section of the Phrase,
907    * pads beginning and end with shortedend notes and rests
908    * if notes or phrase boundaries don't align with locations.
909    * @param double start location
910    * @param double end location
911          * @param boolean requireNoteStart if true only notes that start inside
912          * the copy range are included in the copy. Notes starting prior but 
913          * overlapping are replaced by rests.
914    * @return Phrase a copy of the Phrase
915    */
916 
917   public Phrase copy(double startLoc, double endLoc, boolean requireNS){
918       // are the arguments valid?
919       if (startLoc >= endLoc || endLoc < this.getStartTime()) {
920                 return null;
921             }
922             boolean requireNoteStart = requireNS;
923       Phrase tempPhr = new Phrase(); 
924       copyAttributes(tempPhr);
925       double beatCounter = this.getStartTime();
926       if (beatCounter < 0.0) beatCounter = 0.0;
927       //is it before the phrase?
928       if(startLoc < beatCounter) {
929           Note r = new Note(REST, beatCounter - startLoc);
930           tempPhr.addNote(r);
931       }
932       
933       // re there notes before the startLoc to pass up?
934       for(int i=0; i< this.size(); i++) {
935       
936           if (beatCounter < startLoc) { // this note starts before the space
937               if((beatCounter + this.getNote(i).getRhythmValue() > startLoc) &&
938                   (beatCounter + this.getNote(i).getRhythmValue() <= endLoc)) { // ends within the space
939                         if (requireNoteStart) {
940                             Note n = new Note( REST, beatCounter + 
941                           this.getNote(i).getRhythmValue() - startLoc, this.getNote(i).getDynamic());
942                             tempPhr.addNote(n);
943                         } else {
944         if(this.getNote(i).getPitchType() == Note.MIDI_PITCH) {
945           Note n = new Note( this.getNote(i).getPitch(), beatCounter + 
946             this.getNote(i).getRhythmValue() - startLoc,
947             this.getNote(i).getDynamic());
948           tempPhr.addNote(n);
949         } else {
950           Note n = new Note( this.getNote(i).getFrequency(), beatCounter +
951             this.getNote(i).getRhythmValue() - startLoc,
952             this.getNote(i).getDynamic());
953           tempPhr.addNote(n);
954         }
955                         }
956                                 }
957               if(beatCounter + this.getNote(i).getRhythmValue() > endLoc) { // ends after the space
958                   if (requireNoteStart) {
959                             Note n = new Note( REST, beatCounter + 
960                           this.getNote(i).getRhythmValue() - startLoc, this.getNote(i).getDynamic());
961                             tempPhr.addNote(n);
962                         } else {
963         if(this.getNote(i).getPitchType() == Note.MIDI_PITCH) {
964           Note n = new Note( this.getNote(i).getPitch(), beatCounter + 
965             endLoc - startLoc, this.getNote(i).getDynamic());
966           tempPhr.addNote(n);
967         } else {
968           Note n = new Note( this.getNote(i).getPitch(), beatCounter +
969             endLoc - startLoc, this.getNote(i).getDynamic());
970           tempPhr.addNote(n);
971         }          
972                         }
973               }
974           }
975               
976           if ( beatCounter >= startLoc && beatCounter < endLoc) { // this note starts in the space
977               if (beatCounter + this.getNote(i).getRhythmValue() <= endLoc) { // also ends in it
978                   tempPhr.addNote(this.getNote(i));
979               } else { //ends after the end. Make up last note.
980                   Note n = new Note(this.getNote(i).getPitch(), endLoc - beatCounter, this.getNote(i).getDynamic());
981                   tempPhr.addNote(n);
982               }
983           }
984           beatCounter += this.getNote(i).getRhythmValue();
985       }
986       // is there more space past the end of the phrase?
987       if (beatCounter < endLoc) { // make up a rest to fill the space
988           Note r = new Note(REST, endLoc - beatCounter);
989           tempPhr.addNote(r);
990       }
991       // done!
992       return tempPhr;
993   }
994 
995 
996         /**
997         * Returns a copy of a specified section of the Phrase,
998          * pads beginning and end with shortedend notes and rests
999          * if notes or phrase boundaries don't align with locations.
1000         * @param boolean trimmed wether to truncte notes (as per the
1001         * other versions of copy) or not
1002         * @param boolean startTimeShifts wether to shift the start
1003         * time or to add a rest if if the start is afte startloc
1004         * @param double start location
1005         * @param double end location
1006         * @return Phrase a copy of the Phrase
1007         */
1008        public Phrase copy(double startLoc, double endLoc,
1009                           boolean trimmed, boolean truncated, boolean startTimeShifts) {
1010                // are the arguments valid?
1011                if (startLoc >= endLoc || endLoc < this.getStartTime()) {
1012                        System.out.println("invalid arguments in Phrase.copy");
1013                        return null;
1014                }
1015                Phrase tempPhr = new Phrase( "", startLoc, this.instrument);
1016                //this.title + " copy", startLoc, this.instrument);
1017                tempPhr.setAppend(this.append);
1018                tempPhr.setPan(this.pan);
1019                tempPhr.setLinkedPhrase(this.linkedPhrase);
1020                tempPhr.setMyPart(this.getMyPart());
1021                double beatCounter = this.getStartTime();
1022                if (beatCounter < 0.0) beatCounter = 0.0;
1023                //is it before the phrase?
1024
1025                //make beatCounter add up to the right amount before going though the segment
1026                Enumeration noteEnum = this.getNoteList().elements();
1027                while(startLoc > beatCounter && noteEnum.hasMoreElements()) {
1028                        Note n = (Note)noteEnum.nextElement();
1029                        beatCounter += n.getRhythmValue();
1030                }
1031
1032                // now it is in the segment, should a rest be added in the begining because
1033                // a note overlaps?
1034                if(startLoc < beatCounter) {
1035                        if(beatCounter < endLoc) {
1036                                if(startTimeShifts) {
1037                                        tempPhr.setStartTime(beatCounter+this.getStartTime());
1038                                } else {
1039                                        Note r = new Note(REST, beatCounter - startLoc);
1040                                        tempPhr.addNote(r);
1041                                }
1042                        } else {
1043                                Note r = new Note(REST, endLoc - startLoc);
1044                                tempPhr.addNote(r);
1045                                return tempPhr;
1046                        }
1047                }
1048                double addedCounter = 0.0;
1049
1050                // go through the rest of the notes in the segment, until it equals the
1051                // end or runs out of notes
1052                while(noteEnum.hasMoreElements() && beatCounter < endLoc) {
1053                        Note n = ((Note)noteEnum.nextElement()).copy();
1054                        //if the note goes over the end
1055                        if((n.getRhythmValue()+beatCounter) > endLoc && trimmed) {
1056                                //trimm it back
1057                                n.setRhythmValue(endLoc - beatCounter, truncated);
1058                        }
1059                        tempPhr.addNote(n);
1060                        addedCounter += n.getRhythmValue();
1061                        beatCounter += n.getRhythmValue();
1062                }
1063
1064                // is there more space past the end of the phrase?
1065                if (beatCounter < endLoc) { // make up a rest to fill the space
1066                        Note r = new Note(REST, endLoc - beatCounter);
1067                        tempPhr.addNote(r);
1068                } else if (addedCounter == 0.0) { // or if nothing was added at all
1069                        Note r = new Note(REST, endLoc - startLoc);
1070                        tempPhr.addNote(r);
1071                }
1072                // done!
1073                return tempPhr;
1074        }   
1075        
1076        /**
1077  * Returns a copy of the entire Phrase only ontaining notes
1078        * between highest and lowset specified pitch.
1079        * @ param highestPitch The top MIDI pitch to include in the copy
1080        * @ param lowestPitch The bottom MIDI pitch to include in the copy
1081  * @return Phrase a partical copy of the Phrase
1082  */
1083  public Phrase copy(int highestPitch, int lowestPitch){
1084                if (lowestPitch >= highestPitch) {
1085                    System.err.println("jMusic Phrase copy error: "+ 
1086                        "lowset pitch is not lower than highest pitch");
1087                    System.exit(0);
1088                }
1089    Phrase phr = new Phrase(this.title + " copy");
1090//    phr.setStartTime(this.startTime);
1091                phr.position = this.position.copy(phr);
1092    phr.setInstrument(this.instrument);
1093    phr.setAppend(this.append);
1094    phr.setPan(this.pan);
1095    phr.setLinkedPhrase(this.linkedPhrase);
1096    phr.setMyPart(this.getMyPart());
1097    Enumeration enum = this.noteList.elements();
1098    while(enum.hasMoreElements()){
1099                    Note n = ((Note)enum.nextElement()).copy();
1100                    if (n.getPitch() > highestPitch && n.getPitch() < lowestPitch) n.setPitch(REST);
1101                    phr.addNote(n);
1102    }
1103    return phr;
1104  }
1105
1106
1107  /**
1108   * Prints the tracks attributes to stdout
1109   */
1110  public String toString(){
1111    String phraseData = new String("-------- jMusic PHRASE: '" + 
1112      title + "' contains " + this.size() + " notes.  Start time: " + 
1113      getStartTime() +" --------" +'\n');
1114                if(this.tempo > 0) phraseData += "Phrase Tempo = "+ this.tempo + '\n';
1115    Enumeration enum = getNoteList().elements();
1116    int counter = 0; 
1117    while(enum.hasMoreElements()){
1118      Note note = (Note) enum.nextElement();
1119      phraseData = phraseData + note.toString() + '\n';
1120    }
1121    return phraseData;
1122  }
1123  
1124  /**
1125  * Empty removes all elements in the vector
1126  */
1127  public void empty(){
1128    noteList.removeAllElements();
1129  }
1130  
1131  /**
1132   * Returns a carbon copy of a specified Phrase
1133   * Changes to notes in the original or the alias will be echoed in the other.
1134   * Note: that for this to work other phrase classes must to change the 
1135   * noteList attribute to point to another object, but instead
1136   * should always update the noteList itself. See shuffle() as an example.
1137   */
1138  public Phrase alias() { 
1139      Phrase phr = new Phrase(this.title + " alias", this.getStartTime(), this.instrument);
1140            phr.setTempo(this.tempo); 
1141            phr.setAppend(this.append);
1142      phr.noteList = this.noteList;
1143      return phr;
1144  }
1145    
1146  /**
1147  * Return the pitch value of the highest note in the phrase.
1148  */
1149  public int getHighestPitch() {
1150    int max = -1;
1151    Enumeration enum = getNoteList().elements();
1152    while(enum.hasMoreElements()){
1153      Note note = (Note) enum.nextElement();
1154      if(note.getPitchType() == Note.MIDI_PITCH)
1155                            if (note.getPitch() > max) max = note.getPitch();
1156    }
1157    return max;
1158  }
1159    
1160  /**
1161  * Return the pitch value of the lowest note in the phrase.
1162  */
1163  public int getLowestPitch() {
1164    int min = 128;
1165    Enumeration enum = getNoteList().elements();
1166    while(enum.hasMoreElements()){
1167      Note note = (Note) enum.nextElement();
1168                        if(note.getPitchType() == Note.MIDI_PITCH)
1169                            if(note.getPitch() < min && note.getPitch() >= 0 ) min = note.getPitch();;
1170    }
1171    return min;
1172  }
1173    
1174  /**
1175  * Return the value of the longest rhythm value in the phrase.
1176  */
1177  public double getLongestRhythmValue() {
1178    double max = 0.0;
1179    Enumeration enum = getNoteList().elements();
1180    while(enum.hasMoreElements()){
1181      Note note = (Note) enum.nextElement();
1182      if(note.getRhythmValue() > max) max = note.getRhythmValue();
1183    }
1184    return max;
1185  }
1186    
1187    /**
1188   * Return the value of the shortest rhythm value in the phrase.
1189     */
1190    public double getShortestRhythmValue() {
1191        double min = 1000.0;
1192        Enumeration enum = getNoteList().elements();
1193    while(enum.hasMoreElements()){
1194      Note note = (Note) enum.nextElement();
1195            if(note.getRhythmValue() < min) min = note.getRhythmValue();
1196        }
1197        return min;
1198    }
1199    
1200    /**
1201   * Change the dynamic value of each note in the phrase.
1202     */
1203    public void setDynamic(int dyn) {
1204        Enumeration enum = getNoteList().elements();
1205    while(enum.hasMoreElements()){
1206      Note note = (Note) enum.nextElement();
1207            note.setDynamic(dyn);
1208        }
1209    }
1210    
1211    /**
1212   * Change the pitch value of each note in the phrase.
1213     */
1214    public void setPitch(int val) {
1215        Enumeration enum = getNoteList().elements();
1216    while(enum.hasMoreElements()){
1217      Note note = (Note) enum.nextElement();
1218            note.setPitch(val);
1219        }
1220    }
1221    
1222    /**
1223   * Change the rhythmValue value of each note in the phrase.
1224     */
1225    public void setRhythmValue(int val) {
1226        Enumeration enum = getNoteList().elements();
1227    while(enum.hasMoreElements()){
1228      Note note = (Note) enum.nextElement();
1229            note.setRhythmValue(val);
1230        }
1231    }
1232    
1233  /**
1234  * Change the Duration value of each note in the phrase.
1235  */
1236  public void setDuration(double val) {
1237    Enumeration enum = getNoteList().elements();
1238    while(enum.hasMoreElements()){
1239      Note note = (Note) enum.nextElement();
1240    note.setDuration(val);
1241    }
1242  }
1243
1244  /**
1245  * Return the Duration of the phrase in beats.
1246   */
1247  public double getBeatLength() {
1248    return getEndTime();
1249  }
1250    
1251        private final class Position implements Serializable{
1252                private double startTime = 0.0;
1253                
1254                private final Phrase phrase;
1255                
1256                private boolean isAbsolute = false;
1257                
1258                private Position anchor;
1259                
1260                private Alignment alignment = Alignment.AFTER;
1261                
1262                private double offset;
1263
1264                private Position(final double startTime, final Phrase phrase) {
1265                        this.isAbsolute = true;
1266                        this.startTime  = startTime;
1267                        this.phrase     = phrase;
1268                }
1269                
1270                private Position(final Position anchor, 
1271                                 final Alignment alignment,
1272                                 final double offset,
1273                                 final Phrase phrase) {
1274                         this.isAbsolute = false;
1275                         this.anchor     = anchor;
1276                         this.alignment  = alignment;
1277                         this.offset     = offset;
1278                         this.phrase     = phrase;
1279                }
1280                
1281                private final Anchoring getAnchoring() {
1282                    if (isAbsolute) {
1283                            return null;
1284                    }
1285                    return new Anchoring(anchor.phrase, alignment, offset);
1286                }
1287                
1288                private final void setStartTime(final double startTime) {
1289                        this.isAbsolute = true; 
1290                        this.startTime  = startTime;
1291                }
1292                
1293                private final double getStartTime() {
1294                        if (isAbsolute) {
1295                                return startTime;
1296                        } else {
1297                                return alignment.determineStartTime(
1298                                                phrase.getTotalDuration(),
1299                                                anchor.getStartTime(),
1300                                                anchor.getEndTime()) 
1301                                       + offset;
1302                        }
1303                }
1304                
1305                private final double getEndTime() {
1306                        return phrase.getEndTime();
1307                }
1308                
1309                private final Position copy(final Phrase newCopy) {
1310                        return (isAbsolute)
1311                               ? new Position(startTime, newCopy)
1312                               : new Position(anchor, alignment, offset,
1313                                              newCopy);
1314                }
1315        } 
1316        
1317        /**
1318        * Generates and returns a new note with default values 
1319        * and adds it to this phrase.
1320        */
1321        public Note createNote() {
1322            Note n = new Note();
1323            this.addNote(n);
1324            return n;
1325        }
1326        
1327        /**
1328        * Specify a new volume amount for this phrase.
1329        */
1330        public void setVolume(int val) {
1331            this.volume = val;
1332        }
1333        
1334        /**
1335        * Retreive the current volume setting for this phrase.
1336        */
1337        public int getVolume() {
1338            return this.volume;
1339        }
1340        
1341        /*
1342        * Replace a note with another.
1343        * @param Note the new note
1344        * @param index the phrase position to replace
1345        */
1346        public void setNote(Note n, int index) {
1347            if(index >= this.getSize()) {
1348                System.out.println("jMusic error: Phrase setNote index is too large.");
1349                return;
1350            }
1351            this.noteList.removeElementAt(index);
1352            this.noteList.insertElementAt(n, index);
1353        }
1354        
1355        /**
1356        * Specify the mute status of this phrase.
1357        * @param state True or False, muted or not.
1358        */
1359        public void setMute(boolean state) {
1360            this.mute = state;
1361        }
1362        
1363        /** 
1364        * Retrieve the current mute status.
1365        * @return boolean True or False, muted ot not.
1366        */
1367        public boolean getMute() {
1368            return this.mute;
1369        }
1370}
1371