All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jm.music.data.Phrase Maven / Gradle / Ivy

The newest version!
/*

 


 Copyright (C) 2000 Andrew Sorensen & Andrew Brown

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or any
 later version.

 This program is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 */

package jm.music.data;

import jm.JMC;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.Vector;

/**
 * The Phrase class is representative of a single musical phrase.
 * Phrases are held in Parts and can be played at any time
 * based on there start times. They may be played sequentially or in parallel.
 * Phrases can be added to an Part like this...
 * 
 *     Part inst = new Part("Flute");
 *     //Phrase for the right hand
 *     Phrase rightHand = new Phrase(0.0) //start this phrase on the first beat
 *     //Phrase for the left hand
 *     Phrase leftHane = new Phrase(4.0) //start this phrase on the fifth beat
 *     inst.addPhrase(rightHand);
 *     inst.addPhrase(leftHand);
 * 
* * @author Andrew Sorensen and Andrew Brown * @version 1.0, Sun Feb 25 18:43:32 2001 * @see Note * @see Part */ public class Phrase implements JMC, Cloneable, Serializable { //---------------------------------------------- // Limits //----------------------------------------------- /** * The smallest start time in beats */ public static final double MIN_START_TIME = 0.0; //---------------------------------------------- // Default constants //---------------------------------------------- public static final double DEFAULT_START_TIME = MIN_START_TIME; public static final String DEFAULT_TITLE = "Untitled Phrase"; public static final int DEFAULT_INSTRUMENT = NO_INSTRUMENT; public static final boolean DEFAULT_APPEND = false; public static final double DEFAULT_TEMPO = -1.0; public static final double DEFAULT_PAN = Note.DEFAULT_PAN; /** * The pan position for notes in this phrase. * This must be set delibertley to override a note's pan position. */ private double pan = DEFAULT_PAN; public static final int DEFAULT_NUMERATOR = 4; //---------------------------------------------- // Attributes //---------------------------------------------- public static final int DEFAULT_DENOMINATOR = 4; /** * An array containing mutiple voices */ private Vector noteList; // /** The phrases start time in beats */ // private double startTime; /** * The title/name given to this phrase */ private String title = "Unnamed Phrase"; private Position position; /** * instrumet / MIDI program change number for this phrase */ private int instrument; /** * speed in beats per minute for this phrase */ private double tempo; /** * Setting the phrase to append when added to a part * rather than use its start time. */ private boolean append = false; /** * A phrase to have a relative start time with if required. */ private Phrase linkedPhrase; /** * the top number of the time signature */ private int numerator; /** * the bottom number of the time signature */ private int denominator; /** * A reference to this phrases part */ private Part myPart = null; /** * Weather the phrase should play or not */ private boolean mute = false; //---------------------------------------------- // Constructors //---------------------------------------------- /** * Creates an empty Phrase. * The default start time is a flag which means the phrase will be * appended to the end of any part it is added to. */ public Phrase() { this(DEFAULT_START_TIME); this.append = true; } /** * Creates an empty Phrase starting at the specified beat. * * @param startTime The beat at which the phrase will be positioned in its part. */ public Phrase(double startTime) { this(startTime, DEFAULT_INSTRUMENT); } /** * Creates an empty Phrase * * @param startTime The beat at which the phrase will be positioned in its part. * @param instrument The sound or instrument number to be used for this phrase. */ public Phrase(double startTime, int instrument) { this(DEFAULT_TITLE, startTime, instrument); } /** * Creates an empty Phrase * * @param title The name for the phrase. */ public Phrase(String title) { this(title, DEFAULT_START_TIME); this.append = true; } /** * Creates an empty Phrase. * * @param title The name for the phrase. * @param startTime The beat at which the phrase will be positioned in its part. */ public Phrase(String title, double startTime) { this(title, startTime, DEFAULT_INSTRUMENT); } /** * Creates an empty Phrase. * * @param title The name for the phrase. * @param startTime The beat at which the phrase will be positioned in its part. * @param instrument The sound or instrument number to be used for this phrase. */ public Phrase(String title, double startTime, int instrument) { this(title, startTime, instrument, DEFAULT_APPEND); } /** * Creates an empty Phrase. * * @param title The name for the phrase. * @param startTime The beat at which the phrase will be positioned in its part. * @param instrument The sound or instrument number to be used for this phrase. * @param append A flag specifying wheather or not this phrase should be added to the * end of the part it is added too, or should use its start time value. */ public Phrase(String title, double startTime, int instrument, boolean append) { this.title = title; // this.startTime = startTime; this.position = new Position(startTime, this); this.append = append; if (instrument < NO_INSTRUMENT) { System.err.println(new Exception("jMusic EXCEPTION: instrument " + "value must be greater than 0")); (new Exception()).printStackTrace(); System.exit(1); //crash ungracefully } this.instrument = instrument; this.noteList = new Vector(); this.numerator = DEFAULT_NUMERATOR; this.denominator = DEFAULT_DENOMINATOR; this.tempo = DEFAULT_TEMPO; } /** * Constructs a new Phrase containing the specified note. * * @param note Note to be containing by the phrase. */ public Phrase(Note note) { this(); addNote(note); } /** * Constructs a new Phrase containing the specified notes. * * @param notes array of Note to be contained by the phrase. */ public Phrase(Note[] notes) { this(); addNoteList(notes); } /** * Constructs a new Phrase containing the specified note with * the specified title. * * @param note Note to be containing by the phrase. * @param title String describing the title of the Phrase. */ public Phrase(Note note, String title) { this(title); addNote(note); } /** * Constructs a new Phrase containing the specified notes with * the specified title. * * @param notes array of Note to be contained by the phrase. * @param title String describing the title of the Phrase. */ public Phrase(Note[] notes, String title) { this(title); this.addNoteList(notes); } /** * Constructs a new Phrase containing the specified note with * the specified title. * * @param note Note to be containing by the phrase. * @param title String describing the title of the Phrase. */ public Phrase(Note note, double startTime) { this(note); this.setStartTime(startTime); } //---------------------------------------------- // Data Methods //---------------------------------------------- /** * Return the program change assigned by this phrase * * @return int */ public int getInstrument() { return this.instrument; } /** * Sets the program change value * * @param int program change */ public void setInstrument(int value) { this.instrument = value; } /** * Add a note to this Phrase * * @param Note note - add a note to this phrase */ public void addNote(Note note) { note.setMyPhrase(this); noteList.addElement(note); } /** * Add a note to this Phrase * * @param pitch -the pitch of the note * @param rv - the rhythmValue of the note */ public void addNote(int pitch, double rv) { Note note = new Note(pitch, rv); this.addNote(note); } /** * Add a note to this Phrase * * @param Note note - add a note to this phrase */ public void add(Note note) { this.addNote(note); } /** * Add a rest to this Phrase * * @param Rest rest - The rest to be added to this phrase */ public void addRest(Rest newRest) { newRest.setMyPhrase(this); noteList.addElement(newRest); } /** * Appends the specified notes to the end of this Phrase. * * @param array of Notes to append. */ public void addNoteList(Note[] notes) { for (int i = 0; i < notes.length; i++) { this.addNote(notes[i]); } } /** * Adds a vector of notes to the phrase. * A boolean option when true appends the notes to the end of the list, * if false the notes in noteVector will replace the notes currently in the phrase. * * @param noteVector the vector of notes to add * @param append do we append or not? */ public void addNoteList(Vector noteVector, boolean append) { Enumeration enum1 = noteVector.elements(); if (!append) this.noteList.removeAllElements(); while (enum1.hasMoreElements()) { try { Note note = (Note) enum1.nextElement(); this.addNote(note); //note.setMyPhrase(this); } catch (RuntimeException re) { System.err.println("The vector passed to this method must " + "contain Notes only!"); } } } /** * Adds an array of notes to the phrase. * A boolean option when true appends the notes to the end of the list. * * @param noteArray the array of notes to add * @param append do we append or not? */ public void addNoteList(Note[] noteArray, boolean append) { if (!append) this.noteList.removeAllElements(); for (int i = 0; i < noteArray.length; i++) { this.addNote(noteArray[i]); } } /** * Adds Multiple notes to the phrase from a pitch array and rhythm value * * @param pitchArray array of pitch values * @param rhythmValue a rhythmic value */ public void addNoteList(int[] pitchArray, double rhythmValue) { double[] rvArray = new double[pitchArray.length]; for (int i = 0; i < rvArray.length; i++) { rvArray[i] = rhythmValue; } this.addNoteList(pitchArray, rvArray); } /** * Adds Multiple notes to the phrase from a pitch array, rhythm value, and dynmaic value * * @param pitchArray - An array of pitch values * @param rhythmValue - A rhythmic value * @param dynamic - A dynmaic value (1-127) */ public void addNoteList(int[] pitchArray, double rhythmValue, int dynamic) { double[] rvArray = new double[pitchArray.length]; int[] dynArray = new int[pitchArray.length]; for (int i = 0; i < rvArray.length; i++) { rvArray[i] = rhythmValue; dynArray[i] = dynamic; } this.addNoteList(pitchArray, rvArray, dynArray); } /** * Adds Multiple notes to the phrase from an array of frequency values * * @param freqArray array of freequency values * @param rhythmValue a rhythmic value */ public void addNoteList(double[] freqArray, double rhythmValue) { double[] rvArray = new double[freqArray.length]; for (int i = 0; i < rvArray.length; i++) { rvArray[i] = rhythmValue; } this.addNoteList(freqArray, rvArray); } /** * Adds Multiple notes to the phrase from several arrays * * @param pitchArray array of pitch values * @param rhythmArray array of rhythmic values */ public void addNoteList(int[] pitchArray, double[] rhythmArray) { int[] dynamic = new int[pitchArray.length]; for (int i = 0; i < pitchArray.length; i++) { dynamic[i] = Note.DEFAULT_DYNAMIC; } this.addNoteList(pitchArray, rhythmArray, dynamic); } /** * Adds Multiple notes to the phrase from several arrays * * @param freqArray array of frequency values * @param rhythmArray array of rhythmic values */ public void addNoteList(double[] freqArray, double[] rhythmArray) { int[] dynamic = new int[freqArray.length]; for (int i = 0; i < freqArray.length; i++) { dynamic[i] = Note.DEFAULT_DYNAMIC; } this.addNoteList(freqArray, rhythmArray, dynamic); } /** * Adds Multiple notes to the phrase from several arrays * * @param pitchArray array of pitch values * @param rhythmArray array of rhythmic values * @param dynmaic array of dynamic values */ public void addNoteList(int[] pitchArray, double[] rhythmArray, int[] dynamic) { this.addNoteList(pitchArray, rhythmArray, dynamic, true); } /** * Adds Multiple notes to the phrase from several arrays * * @param freqArray array of frequency values * @param rhythmArray array of rhythmic values * @param dynmaic array of dynamic values */ public void addNoteList(double[] freqArray, double[] rhythmArray, int[] dynamic) { this.addNoteList(freqArray, rhythmArray, dynamic, true); } /** * Adds Multiple notes to the phrase from several arrays * A boolean option when true appends the notes to the end of the list * if non true the current list is errased and replaced by the new notes * * @param pitchArray array of pitch values * @param rhythmArray array of rhythmic values * @param dynamic int * @param append do we append or not? */ public void addNoteList(int[] pitchArray, double[] rhythmArray, int[] dynamic, boolean append) { if (!append) this.noteList.removeAllElements(); for (int i = 0; i < pitchArray.length; i++) { try { Note knote = new Note(pitchArray[i], rhythmArray[i], dynamic[i]); this.addNote(knote); } catch (RuntimeException re) { System.err.println("You must enter arrays of even length"); } } } /** * Adds Multiple notes to the phrase from several arrays * A boolean option when true appends the notes to the end of the list * if non true the current list is errased and replaced by the new notes * * @param freqArray array of frequency values * @param rhythmArray array of rhythmic values * @param dynamic int * @param append do we append or not? */ public void addNoteList(double[] freqArray, double[] rhythmArray, int[] dynamic, boolean append) { if (!append) this.noteList.removeAllElements(); for (int i = 0; i < freqArray.length; i++) { try { Note knote = new Note(freqArray[i], rhythmArray[i], dynamic[i]); this.addNote(knote); } catch (RuntimeException re) { System.err.println("jMusic Phrase error: You must enter arrays of even length"); } } } /** * Adds Multiple notes to the phrase from one array of pitch, rhythm pairs * * @param pitchAndRhythmArray - an array of pitch and rhythm values */ public void addNoteList(double[] pitchAndRhythmArray) { for (int i = 0; i < pitchAndRhythmArray.length; i += 2) { try { Note knote = new Note((int) pitchAndRhythmArray[i], pitchAndRhythmArray[i + 1]); this.addNote(knote); } catch (RuntimeException re) { System.err.println("Error adding note list: Possibly the wrong number of values in the pitch and rhythm array."); } } } /** * Adds Multiple notes to the phrase from one pitch and an array of rhythm values * * @param pitch The pitch values for the notes * @param rhythms An array of rhythm values */ public void addNoteList(int pitch, double[] rhythms) { for (int i = 0; i < rhythms.length; i++) { this.addNote(new Note(pitch, rhythms[i])); } } /** * Adds Multiple notes to the phrase from one pitch and an array of rhythm values * * @param frequency The pitch values for the notes in hertz * @param rhythms An array of rhythm values */ public void addNoteList(double frequency, double[] rhythms) { for (int i = 0; i < rhythms.length; i++) { this.addNote(new Note(frequency, rhythms[i])); } } /** * Adds Multiple notes to the phrase all of which start at the same time * and share the same duration. * * @param pitches An array of pitch values * @param rv the rhythmValue */ public void addChord(int[] pitches, double rv) { for (int i = 0; i < pitches.length - 1; i++) { Note n = new Note(pitches[i], 0.0); n.setDuration(rv * Note.DEFAULT_DURATION_MULTIPLIER); this.addNote(n); } this.addNote(pitches[pitches.length - 1], rv); //System.out.println("In phrase" + this.toString()); } public int[] getPitchArray() { Note[] notes = this.getNoteArray(); int[] pitches = new int[notes.length]; for (int i = 0; i < notes.length; i++) { pitches[i] = notes[i].getPitch(); } return pitches; } public double[] getRhythmArray() { Note[] notes = this.getNoteArray(); double[] rhythms = new double[notes.length]; for (int i = 0; i < notes.length; i++) { rhythms[i] = notes[i].getRhythmValue(); } return rhythms; } public int[] getDynamicArray() { Note[] notes = this.getNoteArray(); int[] dynamics = new int[notes.length]; for (int i = 0; i < notes.length; i++) { dynamics[i] = notes[i].getPitch(); } return dynamics; } /** * Deletes the specified note in the phrase * * @param int noteNumb the index of the note to be deleted */ public void removeNote(int noteNumb) { Vector vct = (Vector) this.noteList; try { vct.removeElement(vct.elementAt(noteNumb)); } catch (RuntimeException re) { System.err.println("Note index to be deleted must be within the phrase."); } } /** * Deletes the first occurence of the specified note in the phrase * * @param note the note object to be deleted. */ public void removeNote(Note note) { this.noteList.removeElement(note); } /** * Deletes the last note in the phrase */ public void removeLastNote() { Vector vct = (Vector) this.noteList; vct.removeElementAt(vct.size() - 1); } /** * Returns the entire note list contained in a single voice * * @return Vector A vector containing all Note objects in this phrase */ public Vector getNoteList() { return this.noteList; } /** * Replaces the entire note list with a new note list vector * * @param Vector of notes */ public void setNoteList(Vector newNoteList) { this.noteList = newNoteList; } /** * Returns the all notes in the phrase as a array of notes * * @return Note[] An array containing all Note objects in this phrase */ public Note[] getNoteArray() { Vector vct = (Vector) this.noteList; Note[] noteArray = new Note[vct.size()]; for (int i = 0; i < noteArray.length; i++) { noteArray[i] = (Note) vct.elementAt(i); } return noteArray; } /** * Return the phrase's startTime * * @return double The phrases startTime in beats from the beginning of the part or score. */ public double getStartTime() { return position.getStartTime(); // return this.startTime; } /** * Sets the phrases startTime *

*

This positions the phrase absolutely. If this phrase is currently * positioned relative to another phrase that anchoring will be lost. *

*

To position this relative to another class use the * anchor method instead. * * @param double the time at which to start the phrase */ public void setStartTime(double startTime) { if (startTime >= MIN_START_TIME) { position.setStartTime(startTime); // this.startTime = startTime; this.setAppend(false); } else { System.err.println("Error setting phrase start time value: You must enter values greater than " + MIN_START_TIME); } } /** *

The positions tries the phrase relative to another using the * alignment specified. If the arrangement causes this class to start * before a start time of 0.0, the repositioning is considered invalid * and will fail. The original positioning will be restored and this * method will return false. *

* If successful, the previous positioning whether absolute or relative * will be lost. *

*

To position this absolutely use the setStartTime * method instead. * * @param anchor the phrase against which this should be positioned * @param alignment how this should be positioned relative to anchor * @returns false if anchoring failed due to being positioned * before the 0.0 start time barrier. True otherwise. */ public boolean attemptAnchoringTo(final Phrase anchor, final Alignment alignment, final double offset) { Position newPosition = new Position(anchor.position, alignment, offset, this); if (newPosition.getStartTime() < 0.0) { return false; } else { position = newPosition; return true; } } /** * Returns details of how this is aligned relative to another phrase. * Alternatively, if this phrase is aligned absolutely returns null. * * @returns null if aligned with setStartTime(), or details of alignment * if aligned with attemptAnchoringTo() */ public Anchoring getAnchoring() { return position.getAnchoring(); } /** * Return the phrases endTime * * @return double the phrases endTime */ public double getEndTime() { double tempStartTime = (getStartTime() < MIN_START_TIME) ? MIN_START_TIME : getStartTime(); double endTime = tempStartTime; Enumeration enum1 = this.noteList.elements(); while (enum1.hasMoreElements()) { Note nextNote = (Note) enum1.nextElement(); endTime += nextNote.getRhythmValue(); } return endTime; } /** * Returns the length of the whole phrase in beats. * * @return double duration in beats */ final double getTotalDuration() { double cumulativeLength = 0.0; Enumeration enum1 = this.noteList.elements(); while (enum1.hasMoreElements()) { Note nextNote = (Note) enum1.nextElement(); cumulativeLength += nextNote.getRhythmValue(); } return cumulativeLength; } /** * Return this phrases title * * @return String the phrases title */ public String getTitle() { return this.title; } /** * Gives the Phrase a new title * * @param phrases title */ public void setTitle(String title) { this.title = title; } /** * Return this phrases append status * * @return boolean the phrases append value */ public boolean getAppend() { return this.append; } /** * Gives the Phrase a new append status * * @param boolean the append status */ public void setAppend(boolean append) { this.append = append; } /** * Return this phrases this phrase is linked to * * @return Phrase the phrases linked to */ public Phrase getLinkedPhrase() { return this.linkedPhrase; } /** * Make a link from this phrase to another * * @param Phrase the phrase to link to */ public void setLinkedPhrase(Phrase link) { this.linkedPhrase = link; } /** * Return the pan position for this phrase * * @return double the phrases pan setting */ public double getPan() { return this.pan; } /** * Determine the pan position for all notes in this phrase. * * @param double the phrase's pan setting */ public void setPan(double pan) { this.pan = pan; Enumeration enum1 = noteList.elements(); while (enum1.hasMoreElements()) { Note note = (Note) enum1.nextElement(); note.setPan(pan); } } /** * Return the tempo in beats per minute for this phrase * * @return double the phrase's tempo setting */ public double getTempo() { return this.tempo; } /** * Determine the tempo in beats per minute for this phrase * * @param double the phrase's tempo */ public void setTempo(double newTempo) { this.tempo = newTempo; } /** * Get an individual note object by its number * * @param int number - the number of the Track to return * @return Note answer - the note object to return */ public Note getNote(int number) { Enumeration enum1 = noteList.elements(); int counter = 0; while (enum1.hasMoreElements()) { Note note = (Note) enum1.nextElement(); if (counter == number) { return note; } counter++; } return null; } /** * Get the number of notes in this phrase * * @return int The number of notes */ public int length() { return size(); } /** * Get the number of notes in this phrase * * @return int length - the number of notes */ public int size() { return (noteList.size()); } /** * Get the number of notes in this phrase * * @return int length - the number of notes */ public int getSize() { return (noteList.size()); } /** * Returns the numerator of the Phrase's time signature * * @return int time signature numerator */ public int getNumerator() { return this.numerator; } /** * Specifies the numerator of the Phrase's time signature * * @param int time signature numerator */ public void setNumerator(int num) { this.numerator = num; } /** * Returns the denominator of the Phrase's time signature * * @return int time signature denominator */ public int getDenominator() { return this.denominator; } /** * Specifies the denominator of the Phrase's time signature * * @param int time signature denominator */ public void setDenominator(int dem) { this.denominator = dem; } /** * returns a reference to the part that contains this phrase */ public Part getMyPart() { return myPart; } /** * Sets a reference to the part containing this phrase */ public void setMyPart(Part part) { this.myPart = part; } /** * Returns a copy of the entire Phrase * * @return Phrase a copy of the Phrase */ public Phrase copy() { Phrase phr = new Phrase(); copyAttributes(phr); Enumeration enum1 = this.noteList.elements(); while (enum1.hasMoreElements()) { phr.addNote(((Note) enum1.nextElement()).copy()); } return phr; } private void copyAttributes(Phrase phr) { // NB: start time now covered by position phr.position = this.position.copy(phr); phr.setTitle(this.title + " copy"); phr.setInstrument(this.instrument); phr.setAppend(this.append); phr.setPan(this.pan); phr.setLinkedPhrase(this.linkedPhrase); phr.setMyPart(this.getMyPart()); phr.setTempo(this.tempo); phr.setNumerator(this.numerator); phr.setDenominator(this.denominator); } /** * Returns a copy of a specified section of the Phrase, * pads beginning and end with shortedend notes and rests * if notes or phrase boundaries don't align with locations. * * @param double start location * @param double end location * @return Phrase a copy of the Phrase */ public Phrase copy(double startLoc, double endLoc) { Phrase phr = this.copy(startLoc, endLoc, true); return phr; } /** * Returns a copy of a specified section of the Phrase, * pads beginning and end with shortedend notes and rests * if notes or phrase boundaries don't align with locations. * * @param double start location * @param double end location * @param boolean requireNoteStart If true, only notes that start inside * the copy range are included in the copy. Notes starting prior but * overlapping are replaced by rests. Otherwise sections of notes inseide * the bounds are included. * @return Phrase a copy of the Phrase */ public Phrase copy(double startLoc, double endLoc, boolean requireNoteStart) { // are the arguments valid? if (startLoc >= endLoc || endLoc < this.getStartTime()) { return null; } Phrase tempPhr = new Phrase(0.0); copyAttributes(tempPhr); double beatCounter = this.getStartTime(); if (beatCounter < 0.0) beatCounter = 0.0; //is it before the phrase? if (startLoc < beatCounter) { Note r = new Note(REST, beatCounter - startLoc); tempPhr.addNote(r); endLoc += beatCounter - startLoc; } // are there notes before the startLoc to pass up? for (int i = 0; i < this.size(); i++) { if (beatCounter < startLoc) { // this note starts before the space if ((beatCounter + this.getNote(i).getRhythmValue() > startLoc) && (beatCounter + this.getNote(i).getRhythmValue() <= endLoc)) { // ends within the space if (requireNoteStart) { Note n = new Note(REST, beatCounter + this.getNote(i).getRhythmValue() - startLoc); tempPhr.addNote(n); } else { if (this.getNote(i).getPitchType() == Note.MIDI_PITCH) { Note n = new Note(this.getNote(i).getPitch(), beatCounter + this.getNote(i).getRhythmValue() - startLoc, this.getNote(i).getDynamic() ); tempPhr.addNote(n); } else { Note n = new Note(this.getNote(i).getFrequency(), beatCounter + this.getNote(i).getRhythmValue() - startLoc, this.getNote(i).getDynamic() ); tempPhr.addNote(n); } } } if (beatCounter + this.getNote(i).getRhythmValue() > endLoc) { // ends after the space if (requireNoteStart) { Note n = new Note(REST, beatCounter + this.getNote(i).getRhythmValue() - startLoc, this.getNote(i).getDynamic()); tempPhr.addNote(n); } else { if (this.getNote(i).getPitchType() == Note.MIDI_PITCH) { Note n = new Note(this.getNote(i).getPitch(), beatCounter + endLoc - startLoc, this.getNote(i).getDynamic()); tempPhr.addNote(n); } else { Note n = new Note(this.getNote(i).getPitch(), beatCounter + endLoc - startLoc, this.getNote(i).getDynamic()); tempPhr.addNote(n); } } } } if (beatCounter >= startLoc && beatCounter < endLoc) { // this note starts in the space if (beatCounter + this.getNote(i).getRhythmValue() <= endLoc) { // also ends in it tempPhr.addNote(this.getNote(i)); } else { //ends after the end. Make up last note. Note n = new Note(this.getNote(i).getPitch(), endLoc - beatCounter, this.getNote(i).getDynamic()); tempPhr.addNote(n); } } beatCounter += this.getNote(i).getRhythmValue(); } // is there more space past the end of the phrase? if (beatCounter < endLoc) { // make up a rest to fill the space Note r = new Note(REST, endLoc - beatCounter); tempPhr.addNote(r); } // done! return tempPhr; } /** * Returns a copy of a specified section of the Phrase, * pads beginning and end with shortedend notes and rests * if notes or phrase boundaries don't align with locations. * * @param boolean trimmed wether to truncte notes (as per the * other versions of copy) or not * @param boolean startTimeShifts wether to shift the start * time or to add a rest if if the start is afte startloc * @param double start location * @param double end location * @return Phrase a copy of the Phrase */ public Phrase copy(double startLoc, double endLoc, boolean trimmed, boolean truncated, boolean startTimeShifts) { // are the arguments valid? if (startLoc >= endLoc || endLoc < this.getStartTime()) { System.out.println("invalid arguments in Phrase.copy"); return null; } Phrase tempPhr = new Phrase("", startLoc, this.instrument); //this.title + " copy", startLoc, this.instrument); tempPhr.setAppend(this.append); tempPhr.setPan(this.pan); tempPhr.setLinkedPhrase(this.linkedPhrase); tempPhr.setMyPart(this.getMyPart()); double beatCounter = this.getStartTime(); if (beatCounter < 0.0) beatCounter = 0.0; //is it before the phrase? //make beatCounter add up to the right amount before going though the segment Enumeration noteEnum = this.getNoteList().elements(); while (startLoc > beatCounter && noteEnum.hasMoreElements()) { Note n = (Note) noteEnum.nextElement(); beatCounter += n.getRhythmValue(); } // now it is in the segment, should a rest be added in the begining because // a note overlaps? if (startLoc < beatCounter) { if (beatCounter < endLoc) { if (startTimeShifts) { tempPhr.setStartTime(beatCounter + this.getStartTime()); } else { Note r = new Note(REST, beatCounter - startLoc); tempPhr.addNote(r); } } else { Note r = new Note(REST, endLoc - startLoc); tempPhr.addNote(r); return tempPhr; } } double addedCounter = 0.0; // go through the rest of the notes in the segment, until it equals the // end or runs out of notes while (noteEnum.hasMoreElements() && beatCounter < endLoc) { Note n = ((Note) noteEnum.nextElement()).copy(); //if the note goes over the end if ((n.getRhythmValue() + beatCounter) > endLoc && trimmed) { //trimm it back n.setRhythmValue(endLoc - beatCounter, truncated); } tempPhr.addNote(n); addedCounter += n.getRhythmValue(); beatCounter += n.getRhythmValue(); } // is there more space past the end of the phrase? if (beatCounter < endLoc) { // make up a rest to fill the space Note r = new Note(REST, endLoc - beatCounter); tempPhr.addNote(r); } else if (addedCounter == 0.0) { // or if nothing was added at all Note r = new Note(REST, endLoc - startLoc); tempPhr.addNote(r); } // done! return tempPhr; } /** * Returns a copy of the entire Phrase only ontaining notes * between highest and lowset specified pitch. * * @return Phrase a partical copy of the Phrase * @ param highestPitch The top MIDI pitch to include in the copy * @ param lowestPitch The bottom MIDI pitch to include in the copy */ public Phrase copy(int highestPitch, int lowestPitch) { if (lowestPitch >= highestPitch) { System.err.println("jMusic Phrase copy error: " + "lowset pitch is not lower than highest pitch"); System.exit(0); } Phrase phr = new Phrase(this.title + " copy"); // phr.setStartTime(this.startTime); phr.position = this.position.copy(phr); phr.setInstrument(this.instrument); phr.setAppend(this.append); phr.setPan(this.pan); phr.setLinkedPhrase(this.linkedPhrase); phr.setMyPart(this.getMyPart()); Enumeration enum1 = this.noteList.elements(); while (enum1.hasMoreElements()) { Note n = ((Note) enum1.nextElement()).copy(); if (n.getPitch() > highestPitch && n.getPitch() < lowestPitch) n.setPitch(REST); phr.addNote(n); } return phr; } /** * Prints the tracks attributes to stdout */ public String toString() { String phraseData = new String("-------- jMusic PHRASE: '" + title + "' contains " + this.size() + " notes. Start time: " + getStartTime() + " --------" + '\n'); if (this.tempo > 0) phraseData += "Phrase Tempo = " + this.tempo + '\n'; Enumeration enum1 = getNoteList().elements(); int counter = 0; while (enum1.hasMoreElements()) { Note note = (Note) enum1.nextElement(); phraseData = phraseData + note.toString() + '\n'; } return phraseData; } /** * Empty removes all elements in the note list vector */ public void empty() { noteList.removeAllElements(); } /** * Returns a carbon copy of a specified Phrase * Changes to notes in the original or the alias will be echoed in the other. * Note: that for this to work other phrase classes must to change the * noteList attribute to point to another object, but instead * should always update the noteList itself. See shuffle() as an example. */ public Phrase alias() { Phrase phr = new Phrase(this.title + " alias", this.getStartTime(), this.instrument); phr.setTempo(this.tempo); phr.setAppend(this.append); phr.noteList = this.noteList; return phr; } /** * Return the pitch value of the highest note in the phrase. */ public int getHighestPitch() { int max = -1; Enumeration enum1 = getNoteList().elements(); while (enum1.hasMoreElements()) { Note note = (Note) enum1.nextElement(); if (note.getPitchType() == Note.MIDI_PITCH) if (note.getPitch() > max) max = note.getPitch(); } return max; } /** * Return the pitch value of the lowest note in the phrase. */ public int getLowestPitch() { int min = 128; Enumeration enum1 = getNoteList().elements(); while (enum1.hasMoreElements()) { Note note = (Note) enum1.nextElement(); if (note.getPitchType() == Note.MIDI_PITCH) if (note.getPitch() < min && note.getPitch() >= 0) min = note.getPitch(); ; } return min; } /** * Return the value of the longest rhythm value in the phrase. */ public double getLongestRhythmValue() { double max = 0.0; Enumeration enum1 = getNoteList().elements(); while (enum1.hasMoreElements()) { Note note = (Note) enum1.nextElement(); if (note.getRhythmValue() > max) max = note.getRhythmValue(); } return max; } /** * Return the value of the shortest rhythm value in the phrase. */ public double getShortestRhythmValue() { double min = 1000.0; Enumeration enum1 = getNoteList().elements(); while (enum1.hasMoreElements()) { Note note = (Note) enum1.nextElement(); if (note.getRhythmValue() < min) min = note.getRhythmValue(); } return min; } /** * Change the dynamic value of each note in the phrase. */ public void setDynamic(int dyn) { Enumeration enum1 = getNoteList().elements(); while (enum1.hasMoreElements()) { Note note = (Note) enum1.nextElement(); note.setDynamic(dyn); } } /** * Change the pitch value of each note in the phrase. */ public void setPitch(int val) { Enumeration enum1 = getNoteList().elements(); while (enum1.hasMoreElements()) { Note note = (Note) enum1.nextElement(); note.setPitch(val); } } /** * Change the rhythmValue value of each note in the phrase. */ public void setRhythmValue(double val) { Enumeration enum1 = getNoteList().elements(); while (enum1.hasMoreElements()) { Note note = (Note) enum1.nextElement(); note.setRhythmValue(val); } } /** * Change the Duration value of each note in the phrase. */ public void setDuration(double val) { Enumeration enum1 = getNoteList().elements(); while (enum1.hasMoreElements()) { Note note = (Note) enum1.nextElement(); note.setDuration(val); } } /** * Return the Duration of the phrase in beats. */ public double getBeatLength() { return getEndTime(); } /** * Generates and returns a new note with default values * and adds it to this phrase. */ public Note createNote() { Note n = new Note(); this.addNote(n); return n; } /* * Replace one note with another. * @param Note the new note * @param index the phrase position to replace */ public void setNote(Note n, int index) { if (index >= this.getSize()) { System.out.println("jMusic error: Phrase setNote index is too large."); return; } this.noteList.removeElementAt(index); this.noteList.insertElementAt(n, index); } /** * Retrieve the current mute status. * * @return boolean True or False, muted ot not. */ public boolean getMute() { return this.mute; } /** * Specify the mute status of this phrase. * * @param state True or False, muted or not. */ public void setMute(boolean state) { this.mute = state; } /** * Change both the rhythmValue and duration of each note in the phrase. * * @param newLength The new rhythmValue for the note (Duration is a proportion of this value) */ public void setLength(double newLength) { this.setRhythmValue(newLength); this.setDuration(newLength * Note.DEFAULT_DURATION_MULTIPLIER); } /** * Calculate the start time, in beats, of the note at the specified index. * * @param noteIndex The note's position in the phrase. * @return double The absolute (taking into account phrase start time) beat * position where the note starts. -1 is returned when an index out of range is encounterd. */ public double getNoteStartTime(int noteIndex) { if (noteIndex >= this.size()) return -1.0; double startLoc = this.getStartTime(); for (int i = 0; i < noteIndex; i++) { startLoc += this.getNote(i).getRhythmValue(); } return startLoc; } private final class Position implements Serializable { private final Phrase phrase; private double startTime = 0.0; private boolean isAbsolute = false; private Position anchor; private Alignment alignment = Alignment.AFTER; private double offset; private Position(final double startTime, final Phrase phrase) { this.isAbsolute = true; this.startTime = startTime; this.phrase = phrase; } private Position(final Position anchor, final Alignment alignment, final double offset, final Phrase phrase) { this.isAbsolute = false; this.anchor = anchor; this.alignment = alignment; this.offset = offset; this.phrase = phrase; } private final Anchoring getAnchoring() { if (isAbsolute) { return null; } return new Anchoring(anchor.phrase, alignment, offset); } private final double getStartTime() { if (isAbsolute) { return startTime; } else { return alignment.determineStartTime( phrase.getTotalDuration(), anchor.getStartTime(), anchor.getEndTime()) + offset; } } private final void setStartTime(final double startTime) { this.isAbsolute = true; this.startTime = startTime; } private final double getEndTime() { return phrase.getEndTime(); } private final Position copy(final Phrase newCopy) { return (isAbsolute) ? new Position(startTime, newCopy) : new Position(anchor, alignment, offset, newCopy); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy