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

jm.music.data.Part 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 Part class is representative of a single instrumental part.
 * An Part is made up of a number of Phrase objects, and
 * Parts in turn are contained by Score objects which form the
 * highest level in the jMusic data structure. Parts are added to
 * Score objects like this...
 * 
 *     Score score = new Score("Concerto for Solo Clarinet");
 *     Part inst = new Part("Clarinet");
 *     score.addPart(inst);
 * 
* * @author Andrew Sorensen * @version 1.0, Sun Feb 25 18:43:32 2001 * @see Score * @see Phrase */ public class Part implements Cloneable, Serializable, JMC { //---------------------------------------------- // Defaults //---------------------------------------------- public static final String DEFAULT_TITLE = "Untitled Part"; public static final int DEFAULT_INSTRUMENT = 0; public static final int DEFAULT_CHANNEL = 0; public static final double DEFAULT_TEMPO = Phrase.DEFAULT_TEMPO; public static final int DEFAULT_KEY_SIGNATURE = JMC.NO_KEY_SIGNATURE; public static final int DEFAULT_KEY_QUALITY = JMC.NO_KEY_QUALITY; public static final int DEFAULT_NUMERATOR = JMC.NO_NUMERATOR; public static final int DEFAULT_DENOMINATOR = JMC.NO_DENOMINATOR; public static final double DEFAULT_PAN = Note.DEFAULT_PAN; //---------------------------------------------- // Attributes //---------------------------------------------- /** * The stereo (quad etc.) postion of all notes in this part. */ private double pan = DEFAULT_PAN; /** * A container holding phrase objects */ private Vector phraseList; /** * The title of this Part */ private String title = "Unamed Part"; /** * The channel on which this part is to be played */ private int channel; /** * Optional instrument number/ MIDI program change to apply to this Part */ private int instrument; /** * the speed for this part */ private double tempo; /** * break points */ private double[] points = null; private long[] time = null; private int timeIndex = 0; // Possible Alternative: // Consider using the jm.music.data.KeySignature class and modifying // it to support the keyQuality field. /** * A reference to the Score containing this part */ private Score myScore = null; /** * the number of accidents this part * -1 is One Flat, 1 is One Sharp */ private int keySignature; /** * 0 = major, 1 = minor, others modes not specified */ private int keyQuality; /** * the top number of the time signature */ // Possible Alternative: // Consider making a TimeSignature class, containing the following // two fields. private int numerator; /** * the bottom number of the time signature */ private int denominator; //---------------------------------------------- // Constructors //---------------------------------------------- /** * Creates an empty Part */ public Part() { this(DEFAULT_TITLE); } /** * Creates an empty Part * * @param String title the title of the Part */ public Part(String title) { this(title, DEFAULT_INSTRUMENT); } /** * Creates an empty Part * * @param int instrument the instrument number for this Part */ public Part(int instrument) { this("", instrument); } /** * Creates an empty Part * * @param String title the title of the Part * @param int instrument */ public Part(String title, int instrument) { this(title, instrument, DEFAULT_CHANNEL); } /** * Creates an empty Part * * @param int instrument The instrument number for this Part * @param int channel The channel number for the part - only required for MIDI writing */ public Part(int instrument, int channel) { this("", instrument, channel); } /** * Creates an empty Part * * @param String title the title of the Part * @param int instrument * @param int channel */ public Part(String title, int instrument, int channel) { this.title = title; this.phraseList = new Vector(); if (this.channel > 16) { System.err.println(new Exception( "jMusic Warning: A MIDI Channel " + "cannot be greater than 16. " + "There can be any number of Audio channels." )); (new Exception()).printStackTrace(); //System.exit(1); } this.channel = channel; 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.tempo = DEFAULT_TEMPO; this.keySignature = DEFAULT_KEY_SIGNATURE; this.keyQuality = DEFAULT_KEY_QUALITY; this.numerator = DEFAULT_NUMERATOR; this.denominator = DEFAULT_DENOMINATOR; } /** * Constructs a Part containing the specified phrase. * * @param phrase Phrase to be contained in the Part */ public Part(Phrase phrase) { this(); phrase.setMyPart(this); addPhrase(phrase); this.setTempo(phrase.getTempo()); } /** * Constructs a Part containing the specified phrase. * * @param String Title of the Part * @param int The instrument number * @param phrase Phrase to be contained in the Part */ public Part(String title, int instrument, Phrase phrase) { this(title, instrument, DEFAULT_CHANNEL, phrase); this.setTempo(phrase.getTempo()); } /** * Constructs a Part containing the specified phrase. * * @param String Title of the Part * @param int The instrument number * @param int The channel number * @param phrase Phrase to be contained in the Part */ public Part(String title, int instrument, int channel, Phrase phrase) { this(title, instrument, channel); phrase.setMyPart(this); addPhrase(phrase); this.setTempo(phrase.getTempo()); } /** * Constructs a Part containing the specified phrases. * * @param phrases array of Phrases to be contained in the Score */ public Part(Phrase[] phrases) { this(); addPhraseList(phrases); } /** * Constructs a Part containing the specified cphrase. * * @param cphrase CPhrase to be contained in the Score */ public Part(CPhrase cphrase) { this(); addCPhrase(cphrase); } /** * Constructs a Part containing the specified phrase with the * specified title. * * @param phrase Phrase to be contained in the Part * @param title String describing the title of the Part */ public Part(Phrase phrase, String title) { this(title); addPhrase(phrase); this.setTempo(phrase.getTempo()); } /** * Constructs a Part containing the specified phrase with the * specified title. * * @param phrases array of Phrases to be contained in the Score * @param title String describing the title of the Part */ public Part(Phrase[] phrases, String title) { this(title); addPhraseList(phrases); } /** * Constructs a Part containing the specified phrase with the * specified title and with the timbre of the specified * instrument. * * @param phrase Phrase to be contained in the Part * @param title String describing the title of the Part * @param instrument integer describing the MIDI instrument number */ public Part(Phrase phrase, String title, int instrument) { this(title, instrument); addPhrase(phrase); this.setTempo(phrase.getTempo()); } /** * Constructs a Part containing the specified phrase with the * specified title and with the timbre of the specified * instrument. * * @param phrases array of Phrases to be contained in the Score * @param title String describing the title of the Part * @param instrument integer describing the MIDI instrument number */ public Part(Phrase[] phrases, String title, int instrument) { this(title, instrument); addPhraseList(phrases); } /** * Constructs a Part containing the specified phrase with the * specified title, with the timbre of the specified * instrument and using the specified MIDI channel. * * @param phrase Phrase to be contained in the Part * @param title String describing the title of the Part * @param instrument integer describing the MIDI instrument number * @param channel integer describing the MIDI channel */ public Part(Phrase phrase, String title, int instrument, int channel) { this(title, instrument, channel); addPhrase(phrase); this.setTempo(phrase.getTempo()); } /** * Constructs a Part containing the specified phrase with the * specified title, with the timbre of the specified * instrument and using the specified MIDI channel. * * @param phrases array of Phrases to be contained in the Score * @param title String describing the title of the Part * @param instrument integer describing the MIDI instrument number * @param channel integer describing the MIDI channel */ public Part(Phrase[] phrases, String title, int instrument, int channel) { this(title, instrument, channel); addPhraseList(phrases); } private static void swap(Phrase[] phrases, final int i, final int j) { Phrase temp = phrases[i]; phrases[i] = phrases[j]; phrases[j] = temp; } //---------------------------------------------- // Data Methods //---------------------------------------------- /** * Get an individual phrase object from its number * * @param int number - the number of the Track to return * @return phrase answer - the phrase to return */ public Phrase getPhrase(int number) { Enumeration enum1 = phraseList.elements(); int counter = 0; while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); if (counter == number) { return phrase; } counter++; } return null; } /** * Add a phrase to this Part * * @param Phrase phrase - add a phrase to this Part */ public void add(Phrase phrase) { this.addPhrase(phrase); } /** * Add a phrase to this Part * Phrases with a 'true' append flag are added to the end of the part. * * @param Phrase phrase - add a phrase to this Part */ public void addPhrase(Phrase phrase) { phrase.setMyPart(this); if (phrase.getAppend()) phrase.setStartTime(this.getEndTime()); this.phraseList.addElement(phrase); } /** * Add a copy of a phrase to the end of this Part * * @param Phrase the phrase to add */ public void appendPhrase(Phrase phrase) { Phrase newPhrase = phrase.copy(); newPhrase.setStartTime(this.getEndTime()); this.addPhrase(newPhrase); } /** * Adds multiple phrases to the part from an array of phrases * * @param phraseArray */ public void addPhraseList(Phrase[] phraseArray) { for (int i = 0; i < phraseArray.length; i++) { if (phraseArray[i].getAppend()) { Phrase newPhrase = phraseArray[i].copy(); newPhrase.setStartTime(this.getEndTime()); this.addPhrase(newPhrase); } else this.addPhrase(phraseArray[i]); } } /** * Deletes the specified phrase in the part * * @param int noteNumb the index of the note to be deleted */ public void removePhrase(int phraseNumb) { Vector vct = (Vector) this.phraseList; try { vct.removeElement(vct.elementAt(phraseNumb)); } catch (RuntimeException re) { System.err.println("The Phrase index to be deleted must be within the part."); } } /** * Deletes the first occurence of the specified phrase in the Part. * * @param phrase the Phrase object to be deleted. */ public void removePhrase(Phrase phrase) { this.phraseList.removeElement(phrase); } /** * Deletes the last phrase added to the part */ public void removeLastPhrase() { Vector vct = (Vector) this.phraseList; vct.removeElement(vct.lastElement()); } /** * Deletes all the phrases previously added to the part */ public void removeAllPhrases() { this.phraseList.removeAllElements(); } /** * Returns the entire phrase list * * @return Vector - A vector containing all this Parts phrase objects */ public Vector getPhraseList() { return this.phraseList; } /** * Updates the entire phrase list * * @param Vector containing phrase objects */ public void setPhraseList(Vector newPhraseList) { this.phraseList = newPhraseList; } /** * Returns the all phrases in this part as a array * * @return Phrase[] An array containing all Phrase objects in this part */ public Phrase[] getPhraseArray() { Vector vct = (Vector) this.phraseList; Phrase[] phraseArray = new Phrase[vct.size()]; for (int i = 0; i < phraseArray.length; i++) { phraseArray[i] = (Phrase) vct.elementAt(i); } return phraseArray; } /** * Add a chord phrase to the part * as part of this process we need to remove the * CPhrase's phrase list and add them to the Part's * normal phrase list. * CPhrases with a 'true' append flag are adeed to the end of the part. * * @param CPhrase */ public void addCPhrase(CPhrase cphrase) { if (cphrase.getAppend()) cphrase.setStartTime(this.getEndTime()); Enumeration enum1 = cphrase.getPhraseList().elements(); while (enum1.hasMoreElements()) { Phrase phr = (Phrase) enum1.nextElement(); //phr.setStartTime(phr.getStartTime() + cphrase.getStartTime()); this.addPhrase(phr); } } /** * Returns the Parts title * * @return String title */ public String getTitle() { return this.title; } /** * Sets the Parts title * * @param String title */ public void setTitle(String title) { this.title = title; } /** * Gets the channel for this channel * * @return short channel */ public int getChannel() { return this.channel; } /** * Sets the MidiChannel for this part * * @param short midiChannel */ public void setChannel(int channel) { this.channel = channel; } /** * Get Instrument number / MIDI Program Change * * @return program change */ public int getInstrument() { return this.instrument; } // for backwards compatability /** * Set instrument number / MIDI Program Change * * @param int program change */ public void setInstrument(int instrument) { this.instrument = instrument; } /** * Set instrument number / MIDI Program Change * This method is deprecated in favour of setInstrument!! * * @param int program change */ public void setProgChg(int program) { this.instrument = program; } /** * Returns the Part's tempo * * @return double tempo */ public double getTempo() { return this.tempo; } /** * Sets the Part's tempo * * @param double tempo */ public void setTempo(double tempo) { this.tempo = tempo; } /** * ' * Returns the Parts key signature * The number of sharps (+) or flats (-) * * @return int key signature */ public int getKeySignature() { return this.keySignature; } /** * Specifies the Part's key signature * The number of sharps (+) or flats (-) * * @param int key signature */ public void setKeySignature(int newSig) { this.keySignature = newSig; } /** * Returns the Parts key quality * 0 is Major, 1 is minor * * @return int key quality */ public int getKeyQuality() { return this.keyQuality; } /** * Specifies the Part's key quality * 0 is Major, 1 is minor * * @param int key quality (modality) */ public void setKeyQuality(int newQual) { this.keyQuality = newQual; } /** * Returns the Parts time signature numerator * * @return int time signature numerator */ public int getNumerator() { return this.numerator; } /** * Specifies the Part's time signature numerator * * @param int time signature numerator */ public void setNumerator(int num) { this.numerator = num; } /** * Returns the Parts time signature denominator * * @return int time signature denominator */ public int getDenominator() { return this.denominator; } /** * Returns a copy of the Part * @return Part a copy of the Part */ /** * Specifies the Part's time signature denominator * * @param int time signature denominator */ public void setDenominator(int dem) { this.denominator = dem; } /** * Return the pan position for this part * * @return double the part's pan setting */ public double getPan() { return this.pan; } /** * Determine the pan position for all notes in this part. * * @param double the part's pan setting */ public void setPan(double pan) { this.pan = pan; Enumeration enum1 = phraseList.elements(); while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); phrase.setPan(pan); } } /** * get a reference to the score that contains this part */ public Score getMyScore() { return myScore; } /** * set a reference to the score containing this part */ public void setMyScore(Score scr) { this.myScore = scr; } /** * Make a duplicate of the current part */ public Part copy() { Part i; i = new Part(); Enumeration enum1 = this.phraseList.elements(); while (enum1.hasMoreElements()) { Phrase oldPhrase = (Phrase) enum1.nextElement(); i.addPhrase((Phrase) oldPhrase.copy()); } copyAttributes(i); return (Part) i; } private void copyAttributes(Part i) { // copy the attrubutes i.setInstrument(this.getInstrument()); i.setChannel(this.getChannel()); i.setTitle(this.getTitle() + " copy"); i.setTempo(this.tempo); i.setPoints(this.points); i.setTime(this.time); i.setTimeIndex(this.timeIndex); i.setMyScore(this.getMyScore()); } /** * Returns a copy of the Part between specified loactions * * @param double start of copy section in beats * @param double end of copy section in beats * @return Part a copy of section of the Part */ public Part copy(double startLoc, double endLoc) { Part cp; Vector tempVect = new Vector(); cp = new Part(); copyAttributes(cp); Enumeration enum1 = this.phraseList.elements(); while (enum1.hasMoreElements()) { Phrase ph = (Phrase) enum1.nextElement(); if (ph.getStartTime() < endLoc && ph.getEndTime() > startLoc) { // if((ph.getStartTime() >= startLoc && ph.getStartTime() < endLoc) // || (ph.getEndTime() > startLoc && ph.getEndTime() <= endLoc)) { tempVect.addElement(ph.copy(startLoc, endLoc)); } } cp.setPhraseList(tempVect); return (Part) cp; } /** * Returns a copy of the Part between specified loactions * * @param boolean wether to trim the notes or not * @param boolean wether to truncated the notes duration * when trimming them or not * @param boolean wether to set the start time of the phrases * in relation to the start of the *
old part (true) or the new one (false) maybe should be * called "relative to old" * @param double start of copy section in beats * @param double end of copy section in beats * @return Part a copy of section of the Part */ public Part copy(double startLoc, double endLoc, boolean trimmed, boolean truncated, boolean relativeStartLoc) { Part cp = new Part(); copyAttributes(cp); Vector tempVect = new Vector(); // copy the core to which it belongs accross cp.setMyScore(this.getMyScore()); Enumeration enum1 = this.phraseList.elements(); while (enum1.hasMoreElements()) { Phrase ph = (Phrase) enum1.nextElement(); double startTime = ph.getStartTime(); if (startTime < endLoc && ph.getEndTime() > startLoc) { //trimmed, truncated, move start time if first // note is past startloc Phrase cpy = ph.copy(startLoc, endLoc, trimmed, truncated, false); // make the start time relative to the beginning // of the new Part double newStartTime = 0.0; if (newStartTime < 0.0) newStartTime = 0.0; if (relativeStartLoc) newStartTime += startLoc; cpy.setStartTime(newStartTime); // by this stage, the phrase should be as long as // is needed to fit the endLoc. // it isn't which means that something in //phrase.copy isn't working tempVect.addElement(cpy); } } cp.setPhraseList(tempVect); return (Part) cp; } /** * Return the Parts endTime * * @return double the Parts endTime */ public double getEndTime() { double endTime = 0.0; Enumeration enum1 = this.phraseList.elements(); while (enum1.hasMoreElements()) { Phrase nextPhr = (Phrase) enum1.nextElement(); double phraseEnd = nextPhr.getEndTime(); if (phraseEnd > endTime) endTime = phraseEnd; } return endTime; } /** * Collects the Parts attributes to a string */ public String toString() { String partData = new String("----- jMusic PART: '" + title + "' contains " + this.size() + " phrases. -----" + '\n'); partData += "Channel = " + channel + '\n'; partData += "Instrument = " + instrument + '\n'; if (this.tempo > 0) partData += "Part Tempo = " + this.tempo + '\n'; Enumeration enum1 = phraseList.elements(); while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); partData = partData + phrase.toString() + '\n'; } return partData; } /** * Empty removes all elements in the vector */ public void empty() { phraseList.removeAllElements(); } /** * Get the number of phrases in this part * * @return int The number of phrases */ public int length() { return size(); } /** * get the number of phrases in this part * * @return int length - the number of phrases */ public int size() { return (phraseList.size()); } /** * get the number of phrases in this part * * @return int length - the number of phrases */ public int getSize() { return (phraseList.size()); } /** * Remove phrases from the score. */ public void clean() { Enumeration enum1 = getPhraseList().elements(); while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); if (phrase.getInstrument() == this.instrument) phrase.setInstrument(Phrase.DEFAULT_INSTRUMENT); if (phrase.getNoteList().size() == 0) { this.removePhrase(phrase); } } } /** * Return the value of the highest note in the part. */ public int getHighestPitch() { int max = 0; Enumeration enum1 = getPhraseList().elements(); while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); if (phrase.getHighestPitch() > max) max = phrase.getHighestPitch(); } return max; } /** * Return the value of the lowest note in the part. */ public int getLowestPitch() { int min = 127; Enumeration enum1 = getPhraseList().elements(); while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); if (phrase.getLowestPitch() < min) min = phrase.getLowestPitch(); } return min; } /** * Return the value of the longest rhythm value in the part. */ public double getLongestRhythmValue() { double max = 0.0; Enumeration enum1 = getPhraseList().elements(); while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); if (phrase.getLongestRhythmValue() > max) max = phrase.getLongestRhythmValue(); } return max; } /** * Return the value of the shortest rhythm value in the part. */ public double getShortestRhythmValue() { double min = 1000.0; Enumeration enum1 = getPhraseList().elements(); while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); if (phrase.getShortestRhythmValue() < min) min = phrase.getShortestRhythmValue(); } return min; } /** * Change the dynamic value of each note in the Part. */ public void setDynamic(int dyn) { Enumeration enum1 = getPhraseList().elements(); while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); phrase.setDynamic(dyn); } } /** * Change the Pitch value of each note in the Part. */ public void setPitch(int val) { Enumeration enum1 = getPhraseList().elements(); while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); phrase.setPitch(val); } } /** * Change the rhythmValue value of each note in the Part. */ public void setRhythmValue(double val) { Enumeration enum1 = getPhraseList().elements(); while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); phrase.setRhythmValue(val); } } /** * Change the duration value of each note in the Part. */ public void setDuration(double val) { Enumeration enum1 = getPhraseList().elements(); while (enum1.hasMoreElements()) { Phrase phrase = (Phrase) enum1.nextElement(); phrase.setDuration(val); } } //this section is an incomplete implementation of part envelopes // needs updating or deleting - unstable!!! public void setPoints(double[] p) { points = p; } public double getPoint() { return this.points[timeIndex]; } public long getTime() { return this.time[timeIndex++]; } public void setTime(long[] t) { this.time = t; } public int getTimeIndex() { return this.timeIndex; } public void setTimeIndex(int index) { this.timeIndex = index; } /** * Add a note directly to a part, this method * automatically encapsulates the note within a phrase. * * @param Note the note to be added. * @param startTime the beat position where the note (phrase) will be placed */ public void addNote(Note n, double startTime) { Phrase phrase = new Phrase("Generated by Part.addNote()", startTime); phrase.addNote(n); this.addPhrase(phrase); } /** * Generates and returns a new empty phrase * and adds it to this part. */ public Phrase createPhrase() { Phrase p = new Phrase(); this.addPhrase(p); return p; } /** * Change both the rhythmValue and duration of each note in the part. * * @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); } /** * Orders the phrases in the phrase list by start time. */ public void sort() { Phrase[] arr = this.getPhraseArray(); quickSort(arr, 0, arr.length - 1); this.phraseList.removeAllElements(); this.phraseList.ensureCapacity(arr.length); for (int i = 0; i < arr.length; i++) { phraseList.add(arr[i]); } } private void quickSort(Phrase[] phrases, int left, int right) { int i, last; if (left >= right) return; // already sorted swap(phrases, left, (int) (Math.random() * (right - left)) + left); // choose new pivot point last = left; for (i = left + 1; i <= right; i++) { if (phrases[i].getStartTime() <= phrases[left].getStartTime()) swap(phrases, ++last, i); } swap(phrases, left, last); // restore pivot quickSort(phrases, left, last - 1); quickSort(phrases, last + 1, right); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy