jm.music.tools.Mod Maven / Gradle / Ivy
Show all versions of jmusic Show documentation
/*
* Mod.java 18th February 2003
*
* Copyright (C) 2000-2003 Andrew Sorensen, Andrew Brown, Adam Kirby
*
* 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 (at your option) 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package jm.music.tools;
import jm.JMC;
import jm.music.data.*;
import java.util.Enumeration;
import java.util.Vector;
/**
* A utility class that handles the modification of the basic jMusic types.
*
* @author Andrew Sorensen
* @author Andrew Brown
* @author Adam Kirby
* @version $Revision: 1.6 $, $Date: 2013/02/05 13:12:18 $
*/
public class Mod implements JMC {
/**
* The Mod class provides static methods and is not meant to be
* instansiated.
*/
private Mod() {
}
//----------------------- NOTE MODIFICATIONS -----------------------------//
/**
* Appends the duration and rhythm value of the second note onto the first.
*
* The second note remains unchanged. If either note1
or
* note2
is null then this method does nothing.
*
* @param note1 Note that absorbs the rhythm features
* @param note2 Note that passes on its rhythm features
*/
public static void append(Note note1, final Note note2) {
try {
if (note1 == null || note2 == null)
new NullPointerException();
} catch (NullPointerException e) {
e.printStackTrace();
}
note1.setRhythmValue(note1.getRhythmValue()
+ note2.getRhythmValue());
note1.setDuration(note1.getDuration() + note2.getDuration());
}
/**
* Transpose the note up or down in semitones.
*
* If note
is null then this method does nothing. If the
* transposition shifts a pitch above {@link Note#MAX_PITCH MAX_PITCH} or
* below {@link Note#MIN_PITCH MIN_PITCH}, the pitch will probably cap at
* those values. See the description of {@link Note#setPitch} method for
* exact details of what occurs when trying to set the pitch beyond the
* allowed range.
*
* @param note The Note to be transposed
* @param transposition the amount to transpose in semitones
*/
public static void transpose(Note note, final int transposition) {
try {
if (note == null) new NullPointerException();
} catch (NullPointerException e) {
e.printStackTrace();
}
if (note.getPitchType() == Note.MIDI_PITCH && note.getPitch() != REST)
note.setPitch(note.getPitch() + transposition);
if (note.getPitchType() == Note.FREQUENCY)
System.err.println("jMusic Mod transpose: No action taken - notes with frequency values cannot yet be transposed.");
}
/**
* Transpose the phrase up or down in scale degrees.
*
* If note
is null then this method does nothing. If the
* transposition shifts a pitch above {@link Note#MAX_PITCH MAX_PITCH} or
* below {@link Note#MIN_PITCH MIN_PITCH}, the pitch will probably cap at
* those values. See the description of {@link Note#setPitch} method for
* exact details of what occurs when trying to set the pitch beyond the
* allowed range.
*
* Transposition is in diatonic steps. For example in C major the note C
* transposed 1 will become D, transposed 4 will become G, and transposed
* by 7 will beome C an octave above. This can be somewhat unintuitive
* so be careful.
*
* @param note Note to be transposed
* @param transposition the amount to transpose in semitones
* @param mode the scale to use for the transposition
* (the JMC has some scale constants)
* @param key the chromatic note to be used as the root
* of the mode. i.e., 0 = C, 1 = C# etc.
*/
public static void transpose(Note note, final int transposition, final int[] mode, int key) {
try {
if (note == null) new NullPointerException();
} catch (NullPointerException e) {
e.printStackTrace();
}
int pitch = note.getPitch();
if (pitch != Note.REST) {
// work out the original scale octave, degree and accidental
int octave = pitch / 12;
int accidental = 0;
Note n = note.copy();
while (!n.isScale(mode)) {
n.setPitch(n.getPitch() - 1);
accidental++;
}
int degree = 0;
for (int i = 0; i < mode.length; i++) {
if (pitch % 12 - accidental == mode[i]) {
degree = i;
i = mode.length; // break out of loop next time
}
}
// calculate the new pitch
int newDegree = degree + transposition;
while (newDegree >= mode.length) {
octave++;
newDegree -= mode.length;
}
while (newDegree < 0) {
octave--;
newDegree += mode.length;
}
note.setPitch(mode[newDegree] + octave * 12 + accidental);
}
}
/**
* Shift the pitch down until it corresponds to a pitch in the specified mode.
*
* @param note Note to be qantized
* @param mode the scale to use for the process
* (the JMC has some scale constants)
* @param key the chromatic note to be used as the root
* of the mode. i.e., 0 = C, 1 = C# etc.
*/
public static void quantizePitch(Note note, final int[] mode, int key) {
while (!note.isScale(mode)) {
note.setPitch(note.getPitch() - 1);
}
}
//---------------------- PHRASE MODIFICATIONS ----------------------------//
public static final void crescendo(final Phrase phrase,
final double startTime,
final double endTime,
final int startDynamic,
final int endDynamic) {
double dynDiff = endDynamic - startDynamic;
double timeDiff = endTime - startTime;
if (timeDiff == 0.0) {
// This prevents a divide-by-zero error.
// Since the change requested applies to no region at all in
// the phrase, it is ideal to do nothing and return.
return;
}
double time = 0.0;
Vector v = phrase.getNoteList();
for (int i = 0; i < v.size(); i++) {
Note n = (Note) v.elementAt(i);
if (time >= startTime) {
n.setDynamic((int) ((time - startTime) / timeDiff
* dynDiff + startDynamic));
}
time += n.getRhythmValue();
if (time > endTime) {
break;
}
}
}
public static final void diminuendo(final Phrase phrase,
final double startTime,
final double endTime,
final int startDynamic,
final int endDynamic) {
crescendo(phrase, startTime, endTime, startDynamic, endDynamic);
}
public static final void decrescendo(final Phrase phrase,
final double startTime,
final double endTime,
final int startDynamic,
final int endDynamic) {
crescendo(phrase, startTime, endTime, startDynamic, endDynamic);
}
/**
* Transpose the phrase up or down in semitones.
*
* If phrase
is null then this method does nothing. If the
* transposition shifts a pitch above {@link Note#MAX_PITCH MAX_PITCH} or
* below {@link Note#MIN_PITCH MIN_PITCH}, the pitch will probably cap at
* those values. See the description of {@link Note#setPitch} method for
* exact details of what occurs when trying to set the pitch beyond the
* allowed range.
*
* @param phrase Phrase to be transposed
* @param transposition the amount to transpose in semitones
*/
public static void transpose(Phrase phrase, final int transposition) {
try {
if (phrase == null) new NullPointerException();
} catch (NullPointerException e) {
e.printStackTrace();
}
Vector noteList = phrase.getNoteList();
Enumeration enum1 = noteList.elements();
while (enum1.hasMoreElements()) {
Note note = (Note) enum1.nextElement();
if (note.getPitch() != Note.REST) {
note.setPitch(note.getPitch() + transposition);
}
}
phrase.setNoteList(noteList);
}
/**
* Transpose the phrase up or down in scale degrees.
*
* If phrase
is null then this method does nothing. If the
* transposition shifts a pitch above {@link Note#MAX_PITCH MAX_PITCH} or
* below {@link Note#MIN_PITCH MIN_PITCH}, the pitch will probably cap at
* those values. See the description of {@link Note#setPitch} method for
* exact details of what occurs when trying to set the pitch beyond the
* allowed range.
*
* Transposition is in diatonic steps. For example in C major the note C
* transposed 1 will become D, transposed 4 will become G, and transposed
* by 7 will beome C an octave above. This can be somewhat unintuitive
* so be careful.
*
* @param phrase Phrase to be transposed
* @param transposition the amount to transpose in semitones
* @param mode the scale to use for the transposition
* @param key the chromatic note to be used as the
* rooth of the mode. i.e., 0 = C, 1 = C# etc.
*/
public static void transpose(Phrase phrase, final int transposition, final int[] mode, int key) {
try {
if (phrase == null) new NullPointerException();
} catch (NullPointerException e) {
e.printStackTrace();
}
// make sure the root is in the first octave
int rootNote = key % 12;
Vector noteList = phrase.getNoteList();
Enumeration enum1 = noteList.elements();
while (enum1.hasMoreElements()) {
Note note = (Note) enum1.nextElement();
Mod.transpose(note, transposition, mode, key);
}
phrase.setNoteList(noteList);
}
/**
* Appends a copy of the phrase
to the end of itself.
*
* If phrase
is null then this method does nothing.
*
* @param phrase Phrase to be repeated
*/
public static void repeat(Phrase phrase) {
repeat(phrase, 2);
}
/**
* Makes the phrase
n
times as long by repeating.
*
* If phrase
is null or n
is less than on
* then this method does nothing.
*
* @param phrase Phrase to be repeated
* @param n integer representing the number of repeats, 1 will leave
* the phrase unchanged, 2 a single added repetitions, and
* so forth
*/
public static void repeat(Phrase phrase, final int n) {
try {
if (phrase == null) new NullPointerException();
} catch (NullPointerException e) {
e.printStackTrace();
}
int initialLength = phrase.size();
for (int t = 0; t < (n - 1); t++) {
for (int i = 0; i < initialLength; i++) {
phrase.addNote(phrase.getNote(i).copy());
}
}
}
/**
* Places a duplicate of a section of a phrase immediately after the end
* of the section.
*
* If phrase
is null; or startLoc
is greater
* than or equal to endLoc
then this method does nothing.
*
* @param phrase Phrase with section to be repeated
* @param startLoc double describing the time of the beginning of the
* repeated section, in crotchets.
* @param endLoc double describing the time of the end of the repeated
* section, in crotchets.
*/
public static void repeat(Phrase phrase, final double startLoc,
final double endLoc) {
repeat(phrase, 2, startLoc, endLoc);
}
/**
* Loops a section of phrase
n
times.
*
* If phrase
is null; startLoc
is greater than
* or equal to endLoc
; or n
is less than one then
* this method does nothing.
*
* @param phrase Phrase with section to be repeated
* @param times integer representing the number of repeats
* @param startLoc double describing the time of the beginning of the
* repeated section, in crotchets.
* @param endLoc double describing the time of the end of the repeated
* section, in crotchets.
*/
public static void repeat(Phrase phrase, int times, double startLoc,
double endLoc) {
// are the arguments valid?
if (phrase == null) {
System.err.println("phrase is null");
return;
} else if (startLoc >= endLoc) {
System.err.println("startlocation is bigger or equal to end " +
"location");
return;
} else if (times < 2) {
System.err.println("times is smaller than 2");
return;
} else if (startLoc < 0) {
System.err.println("startLoc is smaller than 0");
return;
}
// copy the section to repeat
Phrase repeatedBit = phrase.copy(startLoc, endLoc);
// setup
Phrase tempPhr = new Phrase();
boolean overlappingFirst = false;
int overlappingNoteAt = 0;
boolean overlappingLast = false;
double beatCounter = (phrase.getStartTime() < 0.0) ? 0.0 : phrase.getStartTime();
// Add notes for the first time through
int beforeCount;
for (beforeCount = 0; beforeCount < phrase.size() && beatCounter +
phrase.getNote(beforeCount).getRhythmValue() <= endLoc; beforeCount++) {
//yes add the whole note
tempPhr.addNote(phrase.getNote(beforeCount));
// overlapping first note?
if (beatCounter < startLoc && beatCounter + phrase.getNote(beforeCount).getRhythmValue() > startLoc) {
overlappingFirst = true;
overlappingNoteAt = beforeCount;
}
beatCounter += phrase.getNote(beforeCount).getRhythmValue();
}
// is the next note overlapping the end?
if (beforeCount + 1 < phrase.size()) { // make sure we haven't gone through all notes
if (beatCounter < endLoc && beatCounter + phrase.getNote(beforeCount + 1).getRhythmValue() > endLoc) {
overlappingLast = true;
// add partial note
Note partialNote = phrase.getNote(beforeCount).copy();
partialNote.setDuration(partialNote.getDuration() * endLoc - beatCounter / partialNote.getRhythmValue());
partialNote.setRhythmValue(endLoc - beatCounter);
tempPhr.addNote(partialNote);
}
}
// do the repeats
boolean endLoopFlag = false;
for (int r = 0; r < (times - 1); r++) {
for (int j = 0; j < repeatedBit.size(); j++) {
if (!endLoopFlag) tempPhr.addNote(repeatedBit.getNote(j));
}
}
// add the remaining notes
if (overlappingLast) {
// delete lst partial note
tempPhr.removeLastNote();
//beforeCount--;
}
for (int i = beforeCount; i < phrase.size(); i++) {
tempPhr.addNote(phrase.getNote(i));
}
// update this phrase
phrase.setNoteList(tempPhr.getNoteList());
//if(startLoc >= 0.0 && phrase.getStartTime() > startLoc) phrase.setStartTime(startLoc);
}
/**
* increases the dynamic by a certain amount -
* obviously a negative number will decrease it
*
* @param Phrase the phase that is to be affected
* @param int the amount that it is to be affected by
*/
public static void increaseDynamic(Phrase phr, int amount) {
try {
if (phr == null) new NullPointerException();
} catch (NullPointerException e) {
e.toString();
return;
}
Enumeration enum1 = phr.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note n = (Note) enum1.nextElement();
n.setDynamic(n.getDynamic() + amount);
}
}
/**
* Linearly fades in the phrase
.
*
* If phrase
is null; or if fadeLength
is less
* than or equal to zero then this method does nothing.
*
* @param phrase Phrase to be faded
* @param fadelength double describing the number of beats (crotchets) to fade
* over
*/
public static void fadeIn(Phrase phrase, final double fadeLength) {
if (phrase == null || fadeLength <= 0.0) {
return;
}
double rhythmValueCounter = 0.0;
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
if (rhythmValueCounter > fadeLength) {
break;
}
Note nextNote = (Note) enum1.nextElement();
double fadeFactor = rhythmValueCounter / fadeLength;
int dynamic = (int) ((double) nextNote.getDynamic() * fadeFactor);
if (dynamic == 0) {
//start fade in at dynamic of 1 as 0 is MIDI note off
dynamic = 1;
}
nextNote.setDynamic(dynamic);
rhythmValueCounter += nextNote.getRhythmValue();
}
}
/**
* Linearly fades in the phrase
, with the fade beginning before
* the phrase
.
*
* This is mainly used by when fading multiple Phrases of a Part.
*
* If phrase
is null; fadeLength
is less than
* or equal to zero; phraseStart
is less than zero; or
* fadeLength
is less than or equal to phraseStart
then
* this method does nothing.
*
* @param phrase Phrase to be faded
* @param fadeLength double describing the duration of the fade, in
* crotchets
* @param phraseStartTime double describing how far into the fade the
* phrase starts
*/
public static void fadeIn(Phrase phrase, final double fadeLength,
final double phraseStartTime) {
if (phrase == null || fadeLength <= 0.0 || phraseStartTime < 0.0) {
return;
}
double rhythmValueCounter = phraseStartTime;
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
if (rhythmValueCounter >= fadeLength) {
break;
}
Note nextNote = (Note) enum1.nextElement();
double fadeFactor = rhythmValueCounter / fadeLength;
int dynamic = (int) ((double) nextNote.getDynamic() * fadeFactor);
if (dynamic == 0) {
//start fade in at dynamic of 1 as 0 is MIDI note off
dynamic = 1;
}
nextNote.setDynamic(dynamic);
rhythmValueCounter += nextNote.getRhythmValue();
}
}
/**
* Linearly fades out the phrase
.
*
* If phrase
is null; or if fadeLength
is less
* than or equal to zero then this method does nothing.
*
* @param phrase Phrase to be faded
* @param fadeLength double describing the duration of the fade out in
* crotchets
*/
public static void fadeOut(Phrase phrase, final double fadeLength) {
if (phrase == null || fadeLength <= 0.0) {
return;
}
double rhythmValueCounter = 0.0;
int phraseLength = phrase.size() - 1;//-1 due to Vector elements starting at 0
for (int i = 0; i <= phraseLength; i++) {
Note nextNote = (Note) phrase.getNoteList().elementAt(phraseLength - i);
if (rhythmValueCounter > fadeLength) {
break;
}
double fadeFactor = rhythmValueCounter / fadeLength;
int dynamic = (int) ((double) nextNote.getDynamic() * fadeFactor);
if (dynamic == 0) { //only fade out to dynamic of 1 as 0 is note off
dynamic = 1;
}
nextNote.setDynamic(dynamic);
rhythmValueCounter += nextNote.getRhythmValue();
}
}
/**
* Linearly fades out the phrase
, with the fade ending after
* the phrase ends.
*
* This method is mainly used by a Part when fading multiple Phrases.
*
* If phrase
is null; fadeLength
is less than
* or equal to zero; phraseEndTime
is less than zero; or
* fadeLength
is less than phraseEndTime
then this
* method does nothing.
*
* @param phrase Phrase to be faded
* @param fadeLength double describing the duration of the fade out in
* crotchets
* @param phraseEndTime double describing the length of time, in crotchets,
* between the end of the phrase and the end of the
* fade.
*/
public static void fadeOut(Phrase phrase, final double fadeLength,
final double phraseEndTime) {
if (phrase == null || fadeLength <= 0.0 || phraseEndTime < 0.0) {
return;
}
double rhythmValueCounter = phraseEndTime;
int phraseLength = phrase.size() - 1; //-1 due to Vector elements starting at 0
for (int i = 0; i <= phraseLength; i++) {
Note nextNote = (Note) phrase.getNoteList().elementAt(phraseLength - i);
if (rhythmValueCounter >= fadeLength) {
break;
}
double fadeFactor = rhythmValueCounter / fadeLength;
int dynamic = (int) ((double) nextNote.getDynamic() * fadeFactor);
if (dynamic == 0) {
// only fade out to dynamic of 1 as 0 is note off
dynamic = 1;
}
nextNote.setDynamic(dynamic);
rhythmValueCounter += nextNote.getRhythmValue();
}
}
/**
* A compressor/expander routine. Compression ratio numbers between 0 and 1
* compress, values larger than 1 expand. Negative values invert the
* dynamic about the mean.
*
* This compression applies only to the volume, technically the dynamic,
* of the notes. It will multiply the difference between each of the notes'
* dynmaics and the average dynamic, by the compression factor. Thus, a
* ratio
of zero will change every note's volume to the average
* volume; one will leave every note unchanged; and two will make every
* note's volume twice as far from the mean;
*
* Negative values will have a similar affect but leave the dynamic of
* each note on the other side of the mean.
*
* Note also that if the dynamic is expanded to a value greater than
* {@link Note#MAX_DYNAMIC MAX_DYNAMIC} or less than {@link Note#MIN_DYNAMIC
* MIN_DYNAMIC} then the dynamic will be set to a value as described in the
* {@link Note#setDynamic} method.
*
* Finally, if phrase
is null then this method will do
* nothing.
*
* @param phrase Phrase to be expanded/compressed
* @param ratio double describing the compression factor.
*/
public static void compress(Phrase phrase, final double ratio) {
if (phrase == null) {
return;
}
// compress the velocities
Enumeration enum1 = phrase.getNoteList().elements();
// find the max, min, and mean velocities
int max = Note.MIN_DYNAMIC;
int min = Note.MAX_DYNAMIC;
int curr;
int mean;
while (enum1.hasMoreElements()) {
Note note = (Note) enum1.nextElement();
if (note.getPitch() != REST) { // reject rests
curr = note.getDynamic();
if (curr > max) {
max = curr;
}
if (curr < min) {
min = curr;
}
}
}
mean = (min + max) / 2;
// compress the sucker
enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note note = (Note) enum1.nextElement();
curr = (int) (mean + ((note.getDynamic() - mean) * ratio));
note.setDynamic(curr);
}
}
/**
* Adds phrase2 to the end of phrase1.
*
* If either phrase1
or phrase2
is null then
* this method does nothing.
*
* @param phrase1 the base Phrase
* @param phrase2 the Phrase to be appended
*/
public static void append(Phrase phrase1, Phrase phrase2) {
if (phrase1 == null || phrase2 == null) {
return;
}
Enumeration enum1 = phrase2.getNoteList().elements();
while (enum1.hasMoreElements()) {
phrase1.addNote(((Note) enum1.nextElement()).copy());
}
}
/**
* Quantizes the phrase
. See {@link #quantise(Phrase, double)}
* for more details.
*
* @param phrase Phrase to be quantized
* @param qValue the rhythm value to quantize to, in crotchets
*/
public static void quantize(Phrase phrase, double qValue) {
quantise(phrase, qValue);
}
/**
* Quantises the rhythm values of notes in the phrase
. See {@link #quantise(Phrase, double)}
* for more details.
*
* @param phrase Phrase to be quantized
* @param qValue the rhythm value to quantize to, in crotchets
*/
public static void quantise(Phrase phrase, double qValue) {
quantize(phrase, qValue, CHROMATIC_SCALE, 0);
}
/**
* Aligns all rhythmValues of notes in a phrase to the closest beat subdivision
* and all note pitches to be members of the specified mode.
*
* This is a basic quantisation that doesn't take into account a note's
* offset, duration or position within a phrase. Each note is quantised by
* changing it's rhythm value to a integer multiple of qValue
.
*
* As an example of how this might cause unwanted side-effects consider
* quantising the three notes in a quaver triple, to the qValue
* of 0.25. Each note's rhythm value is slightly less than a quaver, so is
* quantised to exactly a quaver. Together the three notes now extend over
* a dotted crotchet, whereas previously they occupied only a crotchet. If
* this triple was in a larger phrase all notes beyond the triplet would
* begin a quaver later. In common time this would have the effect of
* syncopating the rhythm.
*
* If phrase
is null or qValue
is less than
* or equal to zero then this method does nothing.
*
* @param phrase Phrase to be quantised.
* @param qValue the beat subdivision value to quantise to, in crotchets
* @param mode the scale to use for the transposition
* (the JMC has some scale constants)
* @param key the chromatic note to be used as the root
* of the mode. i.e., 0 = C, 1 = C# etc.
*/
public static void quantize(Phrase phrase, final double qValue, final int[] mode, int key) {
if (phrase == null || qValue <= 0.0 || mode == null || key < 0) {
return;
}
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note note = (Note) enum1.nextElement();
double rv = note.getRhythmValue();
//rhythm
note.setRhythmValue((int) Math.round(rv / qValue) * qValue);
// pitch
quantizePitch(note, mode, key);
}
}
/**
* Extends the phrase
by repeating it until it contains the
* number of notes as specified by numNotes
.
*
* The repetitions work in the same manner as {@link #repeat}, except
* that the final repetition will not be a complete copy of the original
* phrase if the note count is reached before the repetition is
* completed.
*
* If phrase
is null or if numNotes
is less
* than or equal to the number of notes in the phrase then this method does
* nothing.
*
* @param phrase Phrase to be cycled
* @param numbNotes the number of notes in the final phrase
*/
public static void cycle(Phrase phrase, final int numNotes) {
if (phrase == null) {
return;
}
// check to see if the argument is legal
int size = phrase.size();
if (numNotes <= size) {
return;
}
Phrase newPhr = new Phrase();
// add extra cycled notes
for (int i = 0; i < numNotes; i++) {
newPhr.addNote(phrase.getNote(i % size).copy());
}
// update the phrase
phrase.getNoteList().removeAllElements();
Enumeration enum1 = newPhr.getNoteList().elements();
while (enum1.hasMoreElements()) {
phrase.getNoteList().addElement(enum1.nextElement());
}
}
/**
* Extends the phrase
by repeating it until it is as long as
* the specified length.
*
* The repetitions work in the same manner as {@link #repeat}, except
* that the final repetition will not be a complete copy of the original
* phrase if the rhythm value count is reached before the repetition is
* completed.
*
* This method does not truncate the last note to make the final phrase
* exactly equal to numBeats
. It guarantee that the final
* phrase is at least numBeats
but may be greater depending on
* the rhythm value of the last note added.
*
* If phrase
is null or if numNotes
is less
* than or equal to the number of notes in the phrase then this method does
* nothing.
*
* @param phrase Phrase to be cycled
* @param numBeats double describing the minimum length of the final
* phrase, in crotchets
*/
public static void cycle(Phrase phrase, final double numBeats) {
// check to see if the argument is legal
if (phrase == null || numBeats <= phrase.getEndTime()) {
return;
}
int size = phrase.size();
Phrase newPhr = new Phrase();
// add extra cycled notes
for (int i = 0; newPhr.getEndTime() < numBeats; i++) {
newPhr.addNote(phrase.getNote(i % size).copy());
}
// update the phrase
phrase.getNoteList().removeAllElements();
Enumeration enum1 = newPhr.getNoteList().elements();
while (enum1.hasMoreElements()) {
phrase.getNoteList().addElement(enum1.nextElement());
}
}
/**
* Randomise the order of notes in the phrase without repeating any note.
*
* If phrase
is null then this method does nothing.
*
* @param phrase Phrase to be shuffled
*/
public static void shuffle(Phrase phrase) {
if (phrase == null) {
return;
}
// set up a new phrase
Phrase newPhr = new Phrase();
// put one note in to start off the new phrase
newPhr.addNote(phrase.getNote((int) (Math.random() * phrase.size())));
// create the rest of the new note order
for (int i = 0; i < phrase.size() - 1; ) {
//select a new note from this phrase
Note n = phrase.getNote((int) (Math.random() * phrase.size()));
//check if it is a note already used
boolean notUsed = true;
for (int j = 0; j < newPhr.size(); j++) {
if (n == newPhr.getNote(j)) {
notUsed = false;
}
}
// if the note is not already present then add it to the phrase
// and move to the next note in the phrase
if (notUsed) {
newPhr.addNote(n);
i++;
}
}
// update the phrase
phrase.getNoteList().removeAllElements();
Enumeration enum1 = newPhr.getNoteList().elements();
while (enum1.hasMoreElements()) {
phrase.getNoteList().addElement(enum1.nextElement());
}
}
/**
* Extend the phrase by adding all notes backwards, repeating the last note
* of the phrase.
*
* If phrase
is null this method does nothing.
*
* @param phrase Phrase to be extended with its mirror
*/
public static void palindrome(Phrase phrase) {
palindrome(phrase, true);
}
/**
* Extend the phrase by adding all notes backwards.
*
* If phrase
is null this method does nothing.
*
* @param phrase Phras to be extended with its mirror
* @param repeatLastNote boolean specifying whether the last note is to
* be repeated
*/
public static void palindrome(Phrase phrase, final boolean repeatLastNote) {
if (phrase == null) {
return;
}
int numbNotes = (repeatLastNote) ? phrase.size() : phrase.size() - 1;
// add the existing note backwards
for (int i = numbNotes - 1; i >= 0; i--) {
phrase.addNote(phrase.getNote(i));
}
}
/**
* Move the notes around the phrase, first becoming second, second becoming
* third, and so forth with last becoming first.
*
* If phrase
is null this method does nothing.
*
* @param phrase Phrase whose notes are to be rotated
*/
public static void rotate(Phrase phrase) {
rotate(phrase, 1);
}
/**
* Move the notes around a number of steps as specified by numSteps
*
which each step involving the first note becoming the second,
* second the third, and so forth with the last becoming first.
*
* If phrase
is null this method does nothing.
*
* @param phrase Phrase whose notes are to be rotated
* @param int number of steps
*/
public static void rotate(Phrase phrase, int numSteps) {
if (phrase == null) {
return;
}
Vector v = phrase.getNoteList();
for (int i = 0; i < numSteps; i++) {
//rotate
v.insertElementAt(v.lastElement(), 0);
v.removeElementAt(v.size() - 1);
}
}
/**
* Reverse the order of notes in the phrase.
*
* If phrase
is null then this method does nothing.
*
* @param phrase Phrase to be mirrored
*/
public static void retrograde(Phrase phrase) {
if (phrase == null) {
return;
}
Phrase backwards = new Phrase();
for (int i = phrase.size(); i > 0; i--) {
backwards.addNote(phrase.getNote(i - 1));
}
// update the phrase
phrase.getNoteList().removeAllElements();
Enumeration enum1 = backwards.getNoteList().elements();
while (enum1.hasMoreElements()) {
phrase.getNoteList().addElement(enum1.nextElement());
}
}
/**
* Mirror the pitch of notes in the phrase around the first note's pitch.
* The order of the notes is not affected it is only the pitches that are
* mirrored. That is, notes which are n semitones above the first pitch
* will be changed to be n semitones below.
*
* For exact details of what happens when pitches are shifted below
* {@link Note#MIN_PITCH MIN_PITCH} or above {@link Note#MAX_PITCH
* MAX_PITCH} see the description for {@link Note#setPitch}
*
* If phrase
is null then this method does nothing.
*
* @param phrase Phrase to be inverted
*/
public static void inversion(Phrase phrase) {
if (phrase == null) {
return;
}
int i = 0;
int firstNote = (int) Note.REST;
// get the first pitch
while (i < phrase.size() && firstNote == Note.REST) {
firstNote = phrase.getNote(i++).getPitch();
}
for (; i < phrase.size(); i++) {
// change the pitch to invert each around the first note
phrase.getNote(i).setPitch(firstNote - (phrase.getNote(i).getPitch()
- firstNote));
}
}
/*
* Mirror the pitch of notes in the phrase around the first note's pitch.
* The order of the notes is not affected it is only the pitches that are
* mirrored. That is, notes which are n semitones above the first pitch
* will be changed to be n semitones below.
*
*
For exact details of what happens when pitches are shifted below
* {@link Note#MIN_PITCH MIN_PITCH} or above {@link Note#MAX_PITCH
* MAX_PITCH} see the description for {@link Note#setPitch}
*
*
If phrase
is null then this method does nothing.
*
* @param phrase Phrase to be inverted
*/
public static void invert(Phrase phrase) {
if (phrase == null) {
return;
}
int i = 0;
int firstNote = (int) Note.REST;
// get the first pitch
while (i < phrase.size() && firstNote == Note.REST) {
firstNote = phrase.getNote(i++).getPitch();
}
for (; i < phrase.size(); i++) {
// change the pitch to invert each around the first note
phrase.getNote(i).setPitch(firstNote - (phrase.getNote(i).getPitch()
- firstNote));
}
}
/*
* Mirror the pitch of notes in the phrase around the first note's pitch
* taking into account a given scale. The order of the notes is not affected
* it is only the pitches that are mirrored. That is, notes which are n diatonic steps
* above the first pitch will be changed to be n steps below.
*
* At present this method will quantise the pitch of non-scale notes to their closest scale degree.
*
*
For exact details of what happens when pitches are shifted below
* {@link Note#MIN_PITCH MIN_PITCH} or above {@link Note#MAX_PITCH
* MAX_PITCH} see the description for {@link Note#setPitch}
*
*
If any of the arguments are invalid or null then this method does nothing.
*
* @param phrase Phrase to be inverted
* @param scale The scale (array of interger values between 0 and 11 assuming a root of 0 i.e., C) to use.
* @param root The pitch to use as a root note (key) of the scale (0 - 11)
*/
public static void diatonicInvert(Phrase phrase, int[] scale, int root) {
if (phrase == null || root < 0 || root > 11 || scale == null) {
return;
}
// base inversion on step within scale and octave
int i = 0;
int firstNotePitch = (int) Note.REST;
int firstNoteOctave = 0;
int firstNoteStep = 0;
int firstNoteStepOffset = 0;
int octave = 0;
int step = 0;
int iSteps = 0;
int nextStep = 0;
int nextOctave = 0;
// get the first pitch
while (i < phrase.size() && firstNotePitch == Note.REST) {
firstNotePitch = phrase.getNote(i++).getPitch();
// calculate first note params
if (root == 0) firstNoteOctave = firstNotePitch / 12;
else firstNoteOctave = (firstNotePitch - 12 + root) / 12;
firstNoteStep = stepIs(firstNotePitch, root, scale);
}
// process the remainder
for (; i < phrase.size(); i++) {
int pitch = phrase.getNote(i).getPitch();
while (pitch == Note.REST) { // process rests
i++;
pitch = phrase.getNote(i).getPitch();
}
// cal caccidental offset
int accidental = 0;
Note n = new Note(pitch, 1);
while (!n.isScale(scale)) {
n.setPitch(n.getPitch() - 1);
accidental++;
}
// calc octave
if (root == 0) octave = pitch / 12;
else octave = (pitch - 12 + root) / 12;
step = stepIs(pitch, root, scale);
// calulate inversion for this note
iSteps = (step - firstNoteStep) + scale.length * (octave - firstNoteOctave);
if (iSteps < 0) {
nextStep = (firstNoteStep + (iSteps * -1)) % scale.length;
nextOctave = firstNoteOctave + ((firstNoteStep + iSteps * -1) / scale.length);
} else {
nextStep = (firstNoteStep - iSteps) % scale.length;
if (nextStep < 0) nextStep = scale.length + nextStep;
int octCalc = (firstNoteStep - iSteps); // / scale.length + 1;
if (octCalc >= 0) {
nextOctave = firstNoteOctave;
} else nextOctave = firstNoteOctave + (octCalc + 1) / scale.length - 1;
}
// change the pitch to invert each around the first note
phrase.getNote(i).setPitch(nextOctave * 12 + scale[nextStep] - accidental);
}
}
// at present this will quantise non scale notes to their closest scale degree
private static int stepIs(int pitch, int root, int[] scale) {
int step = -1;
int closestStep = -1;
int offset = 1000;
for (int i = 0; i < scale.length; i++) {
if (pitch % 12 == (scale[i] + root) % 12) {
step = i;
offset = 0;
} else {
int diff = (pitch % 12) - ((scale[i] + root) % 12);
if (Math.abs(offset) > Math.abs(diff)) {
offset = diff;
closestStep = i;
}
}
}
if (step > -1) {
return step;
} else return closestStep;
}
/**
* Alters the phrase so that it's notes are stretched or compressed until
* the phrase is the length specified. This method works in the same manner
* as elongate, except it takes an absolute length parameter not a ratio.
*
* If phrase
is null or newLength
is less
* than or equal to zero then this method does nothing.
*
* @param phrase Phrase to be lengthened
* @param newLength double describing the number of beats to change the
* phrase to.
*/
public static void changeLength(Phrase phrase, double newLength) {
if (phrase == null || newLength <= 0.0) {
return;
}
final double oldLength = phrase.getEndTime() - phrase.getStartTime();
elongate(phrase, newLength / oldLength);
}
/**
* Stretch the time of each note in the phrase by scaleFactor
*
* If phrase
is null or scaleFactor
is less
* than or equal to zero then this method does nothing.
*
* @param phrase Phrase to be lengthened
* @param scaleFactor double describing the scale factor
*/
public static void elongate(Phrase phrase, double scaleFactor) {
if (phrase == null || scaleFactor <= 0.0) {
return;
}
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note note = (Note) enum1.nextElement();
note.setRhythmValue(note.getRhythmValue() * scaleFactor);
note.setDuration(note.getDuration() * scaleFactor);
}
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter by emphasising the
* first beat of each bar/measure.
*
* The dynamic of each note starting on the beat will be increased by
* 20. If this raises the dynamic above {@link Note#MAX_DYNAMIC MAX_DYNAMIC
* } then the dynamic will be set to a value as described in {@link
* Note#setDynamic}.
*
* If phrase
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param phrase Phrase whose beats are to accented
* @param meter double describing the number of croctets per
* bar/measure
*/
public static void accents(Phrase phrase, double meter) {
double[] beats = {0.0};
accents(phrase, meter, beats);
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter by emphasising the
* first beat of each bar/measure.
*
* The dynamic of each note starting on the beat will be increased by
* 20. If this raises the dynamic above {@link Note#MAX_DYNAMIC MAX_DYNAMIC
* } then the dynamic will be set to a value as described in {@link
* Note#setDynamic}.
*
* If phrase
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param phrase Phrase whose beats are to accented
* @param meter double describing the number of croctets per
* bar/measure
*/
public static void accent(Phrase phrase, double meter) {
double[] beats = {0.0};
accents(phrase, meter, beats);
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter by accenting specified beats
* within each bar/measure.
*
* accentedBeats
is an array of values describing where
* the accents begin relative to the start of each bar, in crochets.
*
* Each accented note's dynamic will be increased by 20.
* If this raises the dynamic above {@link Note#MAX_DYNAMIC MAX_DYNAMIC}
* then the dynamic will be set to a value as described in {@link
* Note#setDynamic}.
*
* Each of the double values in accentedBeats
must be
* greater than or equal to zero and less than meter
or this
* method does nothing.
*
* If phrase
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param phrase Phrase to be accented
* @param meter double describing the number of croctets per
* bar/measure
* @param accentedBeats double array describing the time of the accents in
* the bar.
*/
public static void accents(Phrase phrase, double meter,
double[] accentedBeats) {
accents(phrase, meter, accentedBeats, 20);
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter by accenting specified beats
* within each bar/measure.
*
* accentedBeats
is an array of values describing where
* the accents begin relative to the start of each bar, in crochets.
*
* Each accented note's dynamic will be increased by 20.
* If this raises the dynamic above {@link Note#MAX_DYNAMIC MAX_DYNAMIC}
* then the dynamic will be set to a value as described in {@link
* Note#setDynamic}.
*
* Each of the double values in accentedBeats
must be
* greater than or equal to zero and less than meter
or this
* method does nothing.
*
* If phrase
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param phrase Phrase to be accented
* @param meter double describing the number of croctets per
* bar/measure
* @param accentedBeats double array describing the time of the accents in
* the bar.
*/
public static void accent(Phrase phrase, double meter,
double[] accentedBeats) {
accents(phrase, meter, accentedBeats, 20);
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter. The meter is the total
* beat length of the bar/measure, while the array of accented beats is
* the times within the bar at which an accent should be made.
* For example, 6/8 time would be implied by a meter of 3 and a
* accent array of {0.0, 1.5}.
*
* accentedBeats
is an array of values describing where
* the accents begin relative to the start of each bar, in crochets.
*
* Accented beats will have their dynamic increased by
* accentAmount
. If this causes the dynamic to be above {@link
* Note#MAX_DYNAMIC MAX_DYNAMIC} or below {@link Note#MIN_DYNAMIC
* MIN_DYNAMIC} (when accentAmount
) then the dynamic will be
* set to a value as described by {@link Note#setDynamic}.
*
* Each of the double values in accentedBeats
must be
* greater than or equal to zero and less than meter
or this
* method does nothing.
*
* If phrase
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param phrase Phrase to be accented
* @param meter double describing the number of croctets per
* bar/measure
* @param accentedBeats double array describing the time of the accents in
* the bar.
* @param accentAmount integer describing the value that the dynamic of
* accented beats are increased by.
*/
public static void accents(Phrase phrase, final double meter,
final double[] accentedBeats,
final int accentAmount) {
if (phrase == null || meter <= 0.0) {
return;
}
for (int i = 0; i < accentedBeats.length; i++) {
if (accentedBeats[i] < 0.0 || accentedBeats[i] >= meter) {
return;
}
}
double beatCounter = (phrase.getStartTime() < 0.0)
? 0.0
: phrase.getStartTime();
Vector v = phrase.getNoteList();
for (int i = 0; i < v.size(); i++) {
Note n = (Note) v.elementAt(i);
// check to see if that note occurs on an accented beat
// and if so increase its dynamic level
for (int j = 0; j < accentedBeats.length; j++) {
if (beatCounter % meter == accentedBeats[j]) {
int tempDyn = n.getDynamic();
tempDyn += accentAmount;
n.setDynamic(tempDyn);
}
}
beatCounter += n.getRhythmValue();
}
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter. The meter is the total
* beat length of the bar/measure, while the array of accented beats is
* the times within the bar at which an accent should be made.
* For example, 6/8 time would be implied by a meter of 3 and a
* accent array of {0.0, 1.5}.
*
* accentedBeats
is an array of values describing where
* the accents begin relative to the start of each bar, in crochets.
*
* Accented beats will have their dynamic increased by
* accentAmount
. If this causes the dynamic to be above {@link
* Note#MAX_DYNAMIC MAX_DYNAMIC} or below {@link Note#MIN_DYNAMIC
* MIN_DYNAMIC} (when accentAmount
) then the dynamic will be
* set to a value as described by {@link Note#setDynamic}.
*
* Each of the double values in accentedBeats
must be
* greater than or equal to zero and less than meter
or this
* method does nothing.
*
* If phrase
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param phrase Phrase to be accented
* @param meter double describing the number of croctets per
* bar/measure
* @param accentedBeats double array describing the time of the accents in
* the bar.
* @param accentAmount integer describing the value that the dynamic of
* accented beats are increased by.
*/
public static void accent(Phrase phrase, final double meter,
final double[] accentedBeats,
final int accentAmount) {
if (phrase == null || meter <= 0.0) {
return;
}
for (int i = 0; i < accentedBeats.length; i++) {
if (accentedBeats[i] < 0.0 || accentedBeats[i] >= meter) {
return;
}
}
double beatCounter = (phrase.getStartTime() < 0.0) ? 0.0 : phrase.getStartTime();
Vector v = phrase.getNoteList();
for (int i = 0; i < v.size(); i++) {
Note n = (Note) v.elementAt(i);
// check to see if that note occurs on an accented beat
// and if so increase its dynamic level
for (int j = 0; j < accentedBeats.length; j++) {
if (beatCounter % meter == accentedBeats[j]) {
int tempDyn = n.getDynamic();
tempDyn += accentAmount;
n.setDynamic(tempDyn);
}
}
beatCounter += n.getRhythmValue();
}
}
/**
* Increases dynamic values so that the loudest is at maxiumim level.
*
* This process only effects the dynamic value of the notes in the phrase.
*
* If phrase
is null then
* this method does nothing.
*
* @param phrase the Phrase to be effected
*/
public static void normalise(Phrase phrase) {
if (phrase == null) {
return;
}
// get the curent max
int max = 0;
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note n = (Note) enum1.nextElement();
if (n.getDynamic() > max) max = n.getDynamic();
}
// increase the normalisation
if (max == Note.MAX_DYNAMIC) {
return;
}
int diff = Note.MAX_DYNAMIC - max;
Enumeration enum2 = phrase.getNoteList().elements();
while (enum2.hasMoreElements()) {
Note n = (Note) enum2.nextElement();
n.setDynamic(n.getDynamic() + diff);
}
}
/**
* Randomly adjusts Note dynamic values to create uneven loudness.
* This process only effects the dynamic value of the notes in the phrase.
*
If phrase
is null then
* this method does nothing.
*
* @param phrase the Phrase to be effected
*/
public static void shake(Phrase phrase) {
Mod.shake(phrase, 20);
}
/**
* Randomly adjusts all Notes' dynamic value to create uneven loudness.
*
* This process only effects the dynamic value of the notes in the phrase.
*
* If phrase
is null then
* this method does nothing.
*
* @param phrase The Phrase to be effected
* @param int The amount of effect - e.g., 5 will be +-5 from the current amount
*/
public static void shake(Phrase phrase, int amount) {
if (phrase == null) {
return;
}
int currentValue, newValue;
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note n = (Note) enum1.nextElement();
currentValue = n.getDynamic();
// create new dynamic
do {
newValue = currentValue + (int) (Math.random() * 2 * amount - amount);
} while (newValue < 0 || newValue > 127);
n.setDynamic(newValue);
}
}
/**
* Mutates the phrase
by changing one pitch and one rhythm
* value.
*
* @param Phrase Phrase to be mutated
*/
public static void mutate(Phrase phrase) {
mutate(phrase, 1, 1, CHROMATIC_SCALE, phrase.getLowestPitch(),
phrase.getHighestPitch(), new double[]{0.25, 0.5, 1.0, 1.5, 2.0});
}
/**
* Mutates the phrase
by changing pitches and rhythm
* values.
* The number of pitches and rhythm values to change can be set, and
* the pitches and rhythmValues to select from can be specified. The notes
* within the phrase to change are selected at random. The scale values
* need to be within 0-11 and will typically use the JMC scale constants.
*
* @param Phrase Phrase to be mutated
* @param int pitchCount The number of notes to have pitch altered
* @param double rhythmCount The number of notes to have thier rhythm altered
* @param int[] pitches The scale values from which to select for relacements
* @param int lowestPitch The smallest value a relacement pitch can be
* @param int highestPitch The largest value a replacement pitch can be
* @param double[] rhythms The rhyythm values from which to select replacements
*/
public static void mutate(Phrase phrase, int pitchCount, int[] scale) {
mutate(phrase, 1, 0, scale, phrase.getLowestPitch(),
phrase.getHighestPitch(), new double[]{});
}
/**
* Mutates the phrase
by changing pitches and rhythm
* values.
* The number of pitches and rhythm values to change can be set, and
* the pitches and rhythmValues to select from can be specified. The notes
* within the phrase to change are selected at random. The scale values
* need to be within 0-11 and will typically use the JMC scale constants.
*
* @param Phrase Phrase to be mutated
* @param int pitchCount The number of notes to have pitch altered
* @param double rhythmCount The number of notes to have thier rhythm altered
* @param int[] pitches The scale values from which to select for relacements
* @param int lowestPitch The smallest value a relacement pitch can be
* @param int highestPitch The largest value a replacement pitch can be
* @param double[] rhythms The rhyythm values from which to select replacements
*/
public static void mutate(Phrase phrase, int pitchCount, int rhythmCount,
int[] pitches, int lowestPitch, int highestPitch, double[] rhythms) {
//pitch mutation
for (int i = 0; i < pitchCount; i++) {
int newPitch = (int) (Math.random() * (highestPitch - lowestPitch) + lowestPitch);
int pitchToChange = (int) (Math.random() * phrase.size());
Note noteToChange = phrase.getNote(pitchToChange);
noteToChange.setPitch(newPitch);
while (!noteToChange.isScale(pitches)) {
newPitch = (int) (Math.random() * (highestPitch - lowestPitch) + lowestPitch);
pitchToChange = (int) (Math.random() * phrase.size());
noteToChange = phrase.getNote(pitchToChange);
}
}
// rhythm mutatation
for (int i = 0; i < rhythmCount; i++) {
double newRV = rhythms[(int) (Math.random() * rhythms.length)];
Note noteToChange = phrase.getNote((int) (Math.random() * phrase.size()));
noteToChange.setRhythmValue(newRV);
noteToChange.setDuration(newRV * 0.9);
}
}
/**
* Joins consecutive notes in the phrase
that
* have the same pitch, creating one longer note.
* This is simmilar to the musical function of a tie.
* All note in the phrase meeting the conditions
* are effected.
* This modification may reduce the overall note count.
*
* @param Phrase Phrase to be processed
*/
public static void tiePitches(Phrase phrase) {
for (int i = 0; i < phrase.size() - 1; ) {
Note currNote = phrase.getNote(i);
Note nextNote = phrase.getNote(i + 1);
if (currNote.getPitch() == nextNote.getPitch()) {
currNote.setRhythmValue(currNote.getRhythmValue() + nextNote.getRhythmValue());
currNote.setDuration(currNote.getDuration() + nextNote.getDuration());
phrase.removeNote(i + 1);
} else i++;
}
}
/**
* Joins consecutive rests in the phrase
* creating one longer note.
* This is simmilar to the musical function of a tie.
* This modification may reduce the overall rest count.
*
* @param Phrase Phrase to be processed
*/
public static void tieRests(Phrase phrase) {
for (int i = 0; i < phrase.size() - 1; ) {
Note currNote = phrase.getNote(i);
Note nextNote = phrase.getNote(i + 1);
if (currNote.getPitch() == REST && nextNote.getPitch() == REST) {
currNote.setRhythmValue(currNote.getRhythmValue() + nextNote.getRhythmValue());
currNote.setDuration(currNote.getDuration() + nextNote.getDuration());
phrase.removeNote(i + 1);
} else i++;
}
}
/**
* Lengthens notes followed by a rest in the phrase
* by creating one longer note and deleting the rest.
* This modification may reduce the overall note count in the phrase,
* be careful that use of this method does not to cause
* array out of bounds exceptions.
*
* @param Phrase Phrase to be processed
*/
public static void fillRests(Phrase phrase) {
for (int i = 0; i < phrase.size() - 1; ) {
Note currNote = phrase.getNote(i);
Note nextNote = phrase.getNote(i + 1);
if (currNote.getPitch() != REST && nextNote.getPitch() == REST) {
currNote.setRhythmValue(currNote.getRhythmValue() + nextNote.getRhythmValue());
currNote.setDuration(currNote.getDuration() + nextNote.getDuration());
phrase.removeNote(i + 1);
} else i++;
}
}
/**
* Randomly adjusts all Notes' pan value to create an even spread acroos the stereo spectrum.
* This process only effects the pan value of the notes in the phrase.
*
If phrase
is null then
* this method does nothing.
*
* @param phrase the Phrase to be effected
*/
public static void spread(Phrase phrase) {
if (phrase == null) {
return;
}
int currentValue, newValue;
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note n = (Note) enum1.nextElement();
// create new pan value
n.setPan(Math.random());
}
}
/**
* Adjusts all Note pan values to alternate between extreme left and right from note to note.
* This process only effects the pan value of the notes in the phrase.
*
If phrase
is null then
* this method does nothing.
*
* @param phrase The Phrase to be effected
*/
public static void bounce(Phrase phrase) {
if (phrase == null) {
return;
}
boolean left = true;
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note n = (Note) enum1.nextElement();
// create new pan value
if (left) n.setPan(0.0);
else n.setPan(1.0);
left = !left;
}
}
/**
* Adjusts all Note duration values to vary randomly between specified values from note to note.
* This process only effects the duration attribute of the notes in the phrase.
*
If phrase
is null then this method does nothing.
*
* @param phrase The Phrase to be effected
* @param minlength The shortest possible duration
* @param maxlength The longest possible duration
*/
public static void varyLength(Phrase phrase, double minLength, double maxLength) {
if (phrase == null || maxLength < minLength) {
return;
}
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note n = (Note) enum1.nextElement();
double dur = Math.random() * (maxLength - minLength) + minLength;
n.setDuration(dur);
}
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm & duration, dynamic, and
* pan values of the notes in the phrase.
*
If phrase
is null then this method does nothing.
*
* @param phrase The Phrase to be effected
* @param pitchVariation The degree of pitch change to apply.
*/
public static void randomize(Phrase phrase, int pitchVariation) {
randomize(phrase, pitchVariation, 0.0);
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm & duration, dynamic, and
* pan values of the notes in the phrase.
*
If phrase
is null then this method does nothing.
*
* @param phrase The Phrase to be effected
* @param pitchVariation The degree of pitch change to apply.
* @param rhythmVariation The degree of rhythm value change to apply.
*/
public static void randomize(Phrase phrase, int pitchVariation, double rhythmVariation) {
randomize(phrase, pitchVariation, rhythmVariation, 0);
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm & duration, dynamic, and
* pan values of the notes in the phrase.
*
If phrase
is null then this method does nothing.
*
* @param phrase The Phrase to be effected
* @param pitchVariation The degree of pitch change to apply.
* @param rhythmVariation The degree of rhythm value change to apply.
* @param dynamicVariation The degree of dynamic change to apply.
*/
public static void randomize(Phrase phrase, int pitchVariation, double rhythmVariation, int dynamicVariation) {
if (phrase == null) {
return;
}
boolean left = true;
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note n = (Note) enum1.nextElement();
// create new pitch value
if (pitchVariation > 0) {
n.setPitch(n.getPitch() +
(int) (Math.random() * (pitchVariation * 2) - pitchVariation));
}
// create new rhythm and duration values
if (rhythmVariation > 0.0) {
double var = (Math.random() * (rhythmVariation * 2) - rhythmVariation);
n.setRhythmValue(n.getRhythmValue() + var);
n.setDuration(n.getDuration() + var);
}
// create new dynamic value
if (dynamicVariation > 0) {
n.setDynamic(n.getDynamic() +
(int) (Math.random() * (dynamicVariation * 2) - dynamicVariation));
}
}
}
/**
* All ascending sequences of notes are slured by
* having thier duration extended to 100% of the rhythm value.
* This algorithm assumes notes are arranged in the standard monophonic
* series used by jMusic. Notes with durations longer than rhythm values
* will produce unintended results.
*
* @param Phrase The phrase to be modified.
*/
public static void slurUp(Phrase phrase) {
slurUp(phrase, 2);
}
/**
* All descending sequences of notes are slured by
* having thier duration extended to 100% of the rhythm value.
* This algorithm assumes notes are arranged in the standard monophonic
* series used by jMusic. Notes with durations longer than rhythm values
* will produce unintended results.
*
* @param Phrase The phrase to be modified.
*/
public static void slurDown(Phrase phrase) {
slurDown(phrase, 2);
}
/**
* All ascending sequences of the number of notes or more are slured by
* having thier duration extended to 100% of the rhythm value.
* This algorithm assumes notes are arranged in the standard monophonic
* series used by jMusic. Notes with durations longer than rhythm values
* will produce unintended results.
*
* @param Phrase The phrase to be modified.
* @param int The number of notes in a row to consitute a sequence
*/
public static void slurUp(Phrase phrase, int numberOfNotes) {
if (phrase == null || phrase.size() < numberOfNotes || numberOfNotes < 2) {
System.err.println("jMusic Mod.slurUp error: Arguments not valid.");
return;
}
boolean change = false;
int max = phrase.size() - numberOfNotes;
for (int i = 0; i < max; ) {
for (int j = 0; j < numberOfNotes - 1; j++) {
if ((phrase.getNote(i + j).getPitch() >= 0) &&
(phrase.getNote(i + j).getPitch() < phrase.getNote(i + j + 1).getPitch())) {
change = true;
} else {
change = false;
break;
}
}
if (change) {
for (int k = 0; k < numberOfNotes - 1; k++) {
phrase.getNote(i + k).setDuration(phrase.getNote(i + k).getRhythmValue());
}
i += numberOfNotes - 1;
} else i++;
change = false;
}
}
/**
* All descending sequences of the number of notes or more are slured by
* having the duration of all but the last note extended to
* 100% of the rhythm value.
* This algorithm assumes notes are arranged in the standard monophonic
* series used by jMusic. Notes with durations longer than rhythm values
* will produce unintended results.
*
* @param Phrase The phrase to be modified.
* @param int The number of notes in a row to consitute a sequence
*/
public static void slurDown(Phrase phrase, int numberOfNotes) {
if (phrase == null || phrase.size() < numberOfNotes || numberOfNotes < 2) {
System.err.println("jMusic Mod.slurDown error: Arguments not valid.");
return;
}
boolean change = false;
int max = phrase.size() - numberOfNotes;
for (int i = 0; i < max; ) {
for (int j = 0; j < numberOfNotes - 1; j++) {
if ((phrase.getNote(i + j).getPitch() >= 0) &&
(phrase.getNote(i + j).getPitch() > phrase.getNote(i + j + 1).getPitch())) {
change = true;
} else {
change = false;
break;
}
}
if (change) {
for (int k = 0; k < numberOfNotes - 1; k++) {
phrase.getNote(i + k).setDuration(phrase.getNote(i + k).getRhythmValue());
}
i += numberOfNotes - 1;
} else i++;
change = false;
}
}
/**
* Vary the duration of each note in the phrase by the multiplyer.
*
* @param Phrase The phrase to be modified.
* @param double The amount to multiply the duration by.
*/
public static void increaseDuration(Phrase phrase, double multiplyer) {
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note n = (Note) enum1.nextElement();
n.setDuration(n.getDuration() * multiplyer);
}
}
/**
* Vary the duration of each note in the phrase by the specified amount.
*
* @param Phrase The phrase to be modified.
* @param double The amount to add to the duration.
*/
public static void addToDuration(Phrase phrase, double amount) {
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note n = (Note) enum1.nextElement();
n.setDuration(n.getDuration() + amount);
}
}
/**
* Vary the rhythm value of each note in the phrase by the specified amount.
*
* @param Phrase The phrase to be modified.
* @param double The amount to add.
*/
public static void addToRhythmValue(Phrase phrase, double amount) {
Enumeration enum1 = phrase.getNoteList().elements();
while (enum1.hasMoreElements()) {
Note n = (Note) enum1.nextElement();
n.setRhythmValue(n.getRhythmValue() + amount);
}
}
/**
* Vary both the rhythm value and duration of each note in the phrase by the specified amount.
*
* @param phrase The phrase to be modified.
* @param double The amount to add.
*/
public static void addToLength(Phrase phrase, double amount) {
Enumeration enum1 = phrase.getNoteList().elements();
double articulation = 0.0;
while (enum1.hasMoreElements()) {
Note n = (Note) enum1.nextElement();
articulation = n.getRhythmValue() / n.getDuration();
n.setRhythmValue(n.getRhythmValue() + amount);
n.setDuration(n.getRhythmValue() * articulation);
}
}
/**
* Vary the interval between notes scaling by the specified amount to each interval.
*
* @param phrase - The phrase to be modified.
* @param amount - The scaling multiplyer for the intervals, i.e., 2.0 doubles width.
*/
public static void expandIntervals(Phrase phrase, double amount) {
int phraseSize = phrase.size();
if (phraseSize < 2) return;
Note firstNote = phrase.getNote(0);
for (int i = 1; i < phraseSize; i++) {
Note currNote = phrase.getNote(i);
int newInterval = (int) ((currNote.getPitch() - firstNote.getPitch()) * amount);
currNote.setPitch(currNote.getPitch() + newInterval);
}
}
//---------------------- CPHRASE MODIFICATIONS ---------------------------//
/**
* Transposes the cphrase
.
*
* If cphrase
is null then this method does nothing. If
* the pitch is transposed to a value greater than {@link Note#MAX_PITCH
* MAX_PITCH} or less than {@link Note#MIN_PITCH MIN_DYNAMIC} then the pitch
* will be set to a value as described in the {@link Note#setPitch} method.
*
* @param cphrase CPhrase to be transposed
* @param transpose integer describing the amount to transpose in semitones
*/
public static void transpose(CPhrase cphrase, final int trans) {
if (cphrase == null) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
transpose((Phrase) enum1.nextElement(), trans);
}
}
/**
* Transpose the CPhrase up or down in scale degrees.
*
* If cphrase
is null then this method does nothing. If the
* transposition shifts a pitch above {@link Note#MAX_PITCH MAX_PITCH} or
* below {@link Note#MIN_PITCH MIN_PITCH}, the pitch will probably cap at
* those values. See the description of {@link Note#setPitch} method for
* exact details of what occurs when trying to set the pitch beyond the
* allowed range.
*
* Transposition is in diatonic steps. For example in C major the note C
* transposed 1 will become D, transposed 4 will become G, and transposed
* by 7 will beome C an octave above. This can be somewhat unintuitive
* so be careful.
*
* @param cphrase CPhrase to be transposed
* @param transposition the amount to transpose in semitones
* @param mode the scale to use for the transposition
* @param key the chromatic note to be used as the
* rooth of the mode. i.e., 0 = C, 1 = C# etc.
*/
public static void transpose(CPhrase cphrase, final int transposition, final int[] mode, int key) {
if (cphrase == null) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
transpose((Phrase) enum1.nextElement(), transposition, mode, key);
}
}
/**
* Makes the CPhrase twice as long by repeating it once.
*
* If cphrase
is null this method does nothing.
*
* @param cphrase CPhrase to be repeated
*/
public static void repeat(CPhrase cphrase) {
repeat(cphrase, 2);
}
/**
* Makes the CPhrase n times as long by repeating.
*
* If cphrase
is null or times
is less than
* one this mthod does nothing.
*
* @param cphrase CPhrase to be repeated
* @param times number of repeats (default is 1)
*/
public static void repeat(CPhrase cphrase, final int times) {
if (cphrase == null) {
return;
}
int initialLength = cphrase.getPhraseList().size();
for (int t = 0; t < (times - 1); t++) {
double initialEndTime = cphrase.getEndTime();
for (int i = 0; i < initialLength; i++) {
Phrase phr = (Phrase) cphrase.getPhraseList().elementAt(i);
Phrase phrCopy = phr.copy();
phrCopy.setStartTime(initialEndTime + phr.getStartTime());
cphrase.addPhrase(phrCopy);
}
}
}
/**
* Loops a section of a the CPhrase once
*
* If cphrase
is null; or startLoc
is greater
* than or equal to endLoc
then this method does nothing.
*
* @param cphrase CPhrase to be repeated
* @param startLoc location of the loop start in beats
* @param endLoc location of the loop end in beats
*/
public static void repeat(CPhrase cphrase, final double startLoc,
final double endLoc) {
repeat(cphrase, 2, startLoc, endLoc);
}
/**
* Loops a section of the CPhrase n times.
*
* If cphrase
is null; startLoc
is greater
* than or equal to endLoc
; or n
is less than one
* then this method does nothing.
*
* @param cphrase CPhrase to be repeated
* @param times int number of repeats (default is 1)
* @param double location of the loop start in beats
* @param double location of the loop end in beats
*/
public static void repeat(CPhrase cphrase, final int times,
final double startLoc, final double endLoc) {
if (cphrase == null || startLoc >= endLoc || times < 2) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase nextPhrase = (Phrase) enum1.nextElement();
repeat(nextPhrase, times, startLoc - cphrase.getStartTime(),
endLoc - cphrase.getStartTime());
}
}
/**
* Linearly fades in the CPhrase
*
* If cphrase
is null; or if fadeLength
is
* less than or equal to zero then this method does nothing.
*
* @param cphrase CPhrase to be faded
* @param fadeLength double describing the time of the fade, in crotchets.
*/
public static void fadeIn(CPhrase cphrase, final double fadeLength) {
if (cphrase == null || fadeLength <= 0.0) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase nextPhrase = (Phrase) enum1.nextElement();
//make the correction for phrases that don't start at the same time as the CPhrase does
fadeIn(nextPhrase, fadeLength, nextPhrase.getStartTime());
}
}
/**
* Linearly fades in the CPhrase.
*
* If cphrase
is null; fadeLength
is less than
* or equal to zero; cphraseStartTime
is less than zero; or
* fadeLength
is less than or equal to cphraseStartTime
*
then this method does nothing.
*
* @param cphrase CPhrase to be faded
* @param fadeLength double describing the time of the fade, in
* crotchets
* @param cpharseStartTime double describing how far into the fade the
* phrase starts.
*/
public static void fadeIn(CPhrase cphrase, final double fadeLength,
final double cphraseStartTime) {
if (cphrase == null || fadeLength < 0.0 || cphraseStartTime < 0.0
|| fadeLength <= cphraseStartTime) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase nextPhrase = (Phrase) enum1.nextElement();
// make the correction for phrases that don't start at the same time
// as the Part does (0.0)
fadeIn(nextPhrase, fadeLength, (cphraseStartTime
+ nextPhrase.getStartTime()));
}
}
/**
* Linearly fades out the CPhrase.
*
* If cphrase
is null; or if fadeLength
is
* less than or equal to zero then this method does nothing.
*
* @param cphrase CPhrase to be faded
* @param fadeLength double describing the time of the fade out in
* crotchets
*/
public static void fadeOut(CPhrase cphrase, final double fadeLength) {
if (cphrase == null || fadeLength <= 0) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase nextPhrase = (Phrase) enum1.nextElement();
// make the correction for phrases that don't end at the same time
// as the CPhrase does
fadeOut(nextPhrase, fadeLength, (cphrase.getEndTime()
- nextPhrase.getEndTime()));
}
}
/**
* Linearly fades out the CPhrase.
*
* If cphrase
is null; fadeLength
is less than
* or equal to zero; cphraseEndTime
is less than zero; or
* fadeLength
is less than cphraseEndTime
then
* this method does nothing.
*
* @param cphrase CPhrase to be faded
* @param fadeLength double describing the time of the fade out in
* crotchets
* @param phraseEndTime double describing the length of time, in crothcets,
* between the end of the phrase and the end of the
* fade.
*/
public static void fadeOut(CPhrase cphrase, final double fadeLength,
final double cphraseEndTime) {
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase nextPhrase = (Phrase) enum1.nextElement();
// make the correction for phrases that don't end at the same time
// as the Part does
fadeOut(nextPhrase, fadeLength, (cphraseEndTime
+ cphrase.getEndTime()
- nextPhrase.getEndTime()));
}
}
/**
* A compressor/expander routine. Compression ratio numbers between 0 and 1
* compress, values larger than 1 expand.
*
* See {@link #compress(Phrase, double)} for further details.
*
* If cphrase
is null then this method does nothing.
*
* @param cphrase CPhrase to be compressed
* @param retio double describing the compression factor.
*/
public static void compress(CPhrase cphrase, final double ratio) {
if (cphrase == null) {
return;
}
// find the average dynamic of the part
int curr;
double accum = 0;
int ave;
int counter = 0;
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase tempPhrase = (Phrase) enum1.nextElement();
if (tempPhrase == null) {
break;
}
Enumeration enum2 = tempPhrase.getNoteList().elements();
while (enum2.hasMoreElements()) {
Note note = (Note) enum2.nextElement();
if (note.getPitch() != REST) { // reject rests
curr = note.getDynamic();
accum += curr;
counter++;
}
}
}
ave = (int) (accum / counter);
// compress the sucker
enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase tempPhrase = (Phrase) enum1.nextElement();
if (tempPhrase == null) {
break;
}
Enumeration enum2 = tempPhrase.getNoteList().elements();
while (enum2.hasMoreElements()) {
Note note = (Note) enum2.nextElement();
System.out.println("note was =" + note.getDynamic());
curr = (int) (ave + ((note.getDynamic() - ave) * ratio));
note.setDynamic(curr);
}
}
}
/**
* Adds a second CPhrase to the end of the first.
*
* If cphrase1
or cphrase2
is null then this
* method does nothing.
*
* @param cphrase1 the base CPhrase
* @param cphrase2 CPhrase to be appended
*/
public static void append(CPhrase cphrase1, final CPhrase cphrase2) {
if (cphrase1 == null || cphrase2 == null) {
return;
}
//go through the phrases in the new part
// and add then one by one
double et = cphrase1.getEndTime();
Enumeration enum1 = cphrase2.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase tempPhrase = (Phrase) enum1.nextElement();
tempPhrase.setStartTime(et + tempPhrase.getStartTime());
cphrase1.addPhrase(tempPhrase);
}
}
/**
* Combines the phrases from a second CPhrase into the first.
*
* If cphrase1
or cphrase2
is null then this
* method does nothing.
*
* @param cphrase1 the base CPhrase
* @param cphrase2 the CPhrase to be merged with the first
*/
public static void merge(CPhrase cphrase1, final CPhrase cphrase2) {
if (cphrase1 == null || cphrase2 == null) {
return;
}
// go through the phrases in the provided CPhrase
// and add then one by one
Enumeration enum1 = cphrase2.getPhraseList().elements();
while (enum1.hasMoreElements()) {
cphrase1.addPhrase((Phrase) enum1.nextElement());
}
}
/**
* Quantize the cphrase.
*
* See {@link #quantise(CPhrase, double)}.
*
* @param cphrase CPhrase to be quantized
* @param qValue the amount to quantize to
*/
public static void quantize(CPhrase cphrase, final double qValue) {
quantise(cphrase, qValue);
}
/**
* Quantise the cphrase.
*
* See {@link #quantise(CPhrase, double)}.
*
* @param cphrase CPhrase to be quantized
* @param qValue the amount to quantize to
*/
public static void quantise(CPhrase cphrase, final double qValue) {
quantize(cphrase, qValue, CHROMATIC_SCALE, 0);
}
/**
* Quantise all the phrases in this CPhrase.
*
* See {@link #quantise(Phrase, double)} for further details.
*
* If cphrase
is null or qValue
is less than
* or equal to zero then this method does nothing.
*
* @param cphrase CPhrase to be quantised
* @param qValue the amount to quantise to
*/
public static void quantize(CPhrase cphrase, final double qValue, final int[] mode, int key) {
if (cphrase == null || qValue <= 0.0) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
quantize((Phrase) enum1.nextElement(), qValue, mode, key);
}
}
/**
* Randomly order notes within each phrase.
*
* @param cphrase -The CPhrase that contains phrases to be shuffled
*/
public static void shuffle(CPhrase cphrase) {
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
shuffle((Phrase) enum1.nextElement());
}
}
/**
* A version of Cycle that works for parts
*
* @param Part the part to cycle
* @param double position of where it should cycle to
*/
public static void cycle(Part part, double to) {
// make sure parameters are valid
if (part == null || to <= 0.0 || to == part.getEndTime()) {
return;
}
double endTime = part.getEndTime();
//if to is shorter then the actual part, make a truncated copy
if (to < endTime) {
Part copy = part.copy(0.0, to);
part.empty();
part.addPhraseList(copy.getPhraseArray());
return;
} // other wise add cycles of itself to itself
// go through each whole cycle that needs to be gone through.
int startPoint = 1;
double cycleAt = to;
for (cycleAt = to; (int) (cycleAt / endTime) > 1; cycleAt -= endTime) {
Phrase[] phrases = part.getPhraseArray(); //go through phrases
for (int i = 0; i < phrases.length; i++) {
//setStartTime to make it in phase with that cycle
phrases[i].setStartTime(phrases[i].getStartTime() +
startPoint * endTime);
part.addPhrase(phrases[i]);
}
startPoint++;
}
// copy in the remainding stuff in
double remainder = (to - (startPoint * endTime));
if (remainder > 0.0) {
Part copy = part.copy(0.0, remainder, true, true, false);
Phrase[] phrases = copy.getPhraseArray(); //go through phrases
for (int i = 0; i < phrases.length; i++) {
//setStartTime to make it in phase with that cycle
phrases[i].setStartTime(phrases[i].getStartTime() +
startPoint * endTime);
part.addPhrase(phrases[i]);
}
}
}
/**
* Lengthen each note in the CPhrase.
*
* See {@link #elongate(Phrase, double)} for further details.
*
* If cphrase
is null or scaleFactor
is less
* than or equal to zero then this method does nothing.
*
* @param cphrase CPhrase to be lengthened
* @param scaleFactor double describing the scale factor
*/
public static void elongate(CPhrase cphrase, final double scaleFactor) {
if (cphrase == null || scaleFactor <= 0.0) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = ((Phrase) enum1.nextElement());
elongate(phr, scaleFactor);
phr.setStartTime(phr.getStartTime() * scaleFactor);
}
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter by emphasising the
* first beat of each bar/measure.
*
* The dynamic of each note starting on the beat will be increased by
* 20. If this raises the dynamic above {@link Note#MAX_DYNAMIC MAX_DYNAMIC
* } then the dynamic will be set to a value as described in {@link
* Note#setDynamic}.
*
* If cphrase
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param cphrase CPhrase to be accented
* @param meter double describing the number of croctets per
* bar/measure
*/
public static void accents(CPhrase cphrase, final double meter) {
double[] beats = {0.0};
accents(cphrase, meter, beats);
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter by accenting specified beats
* within each bar/measure.
*
* accentedBeats
is an array of values describing where
* the accents begin relative to the start of each bar, in crochets.
*
* Each accented note's dynamic will be increased by 20.
* If this raises the dynamic above {@link Note#MAX_DYNAMIC MAX_DYNAMIC}
* then the dynamic will be set to a value as described in {@link
* Note#setDynamic}.
*
* Each of the double values in accentedBeats
must be
* greater than or equal to zero and less than meter
or this
* method does nothing.
*
* If cphrase
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param cphrase CPhrase to be accented
* @param meter double describing the number of croctets per
* bar/measure
* @param accentedBeats double array describing the time of the accents in
* the bar.
*/
public static void accents(CPhrase cphrase, final double meter,
final double[] accentedBeats) {
accents(cphrase, meter, accentedBeats, 20);
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter. The meter is the total
* beat length of the bar/measure, while the array of accented beats is
* the times within the bar at which an accent should be made.
* For example, 6/8 time would be implied by a meter of 3 and a
* accent array of {0.0, 1.5}.
*
* accentedBeats
is an array of values describing where
* the accents begin relative to the start of each bar, in crochets.
*
* Accented beats will have their dynamic increased by
* accentAmount
. If this causes the dynamic to be above {@link
* Note#MAX_DYNAMIC MAX_DYNAMIC} or below {@link Note#MIN_DYNAMIC
* MIN_DYNAMIC} (when accentAmount
) then the dynamic will be
* set to a value as described by {@link Note#setDynamic}.
*
* Each of the double values in accentedBeats
must be
* greater than or equal to zero and less than meter
or this
* method does nothing.
*
* If cphrase
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param cphrase CPhrase to be accented
* @param meter double describing the number of croctets per
* bar/measure
* @param accentedBeats double array describing the time of the accents in
* the bar.
* @param accentAmount integer describing the value that the dynamic of
* accented beats are increased by.
*/
public static void accents(CPhrase cphrase, final double meter,
final double[] accentedBeats,
final int accentAmount) {
if (cphrase == null || meter <= 0.0) {
return;
}
for (int i = 0; i < accentedBeats.length; i++) {
if (accentedBeats[i] < 0.0 || accentedBeats[i] >= meter) {
return;
}
}
//go through the phrases one by one and accent them
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
accents(phrase, meter, accentedBeats, accentAmount);
}
}
/**
* Increases dynamic values so that the loudest is at maxiumim level.
*
* If cphrase
is null then
* this method does nothing.
*
* @param cphrase the CPhrase to be effected
*/
public static void normalise(CPhrase cphrase) {
if (cphrase == null) {
return;
}
// get the curent max
int max = 0;
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
Enumeration enum2 = phrase.getNoteList().elements();
while (enum2.hasMoreElements()) {
Note n = (Note) enum2.nextElement();
if (n.getDynamic() > max) max = n.getDynamic();
}
}
// increase the normalisation
if (max == Note.MAX_DYNAMIC) {
return;
}
int diff = Note.MAX_DYNAMIC - max;
Enumeration enum3 = cphrase.getPhraseList().elements();
while (enum3.hasMoreElements()) {
Phrase phrase = (Phrase) enum3.nextElement();
Enumeration enum4 = phrase.getNoteList().elements();
while (enum4.hasMoreElements()) {
Note n = (Note) enum4.nextElement();
n.setDynamic(n.getDynamic() + diff);
}
}
}
/**
* Randomly adjusts all Notes' pan value to create an even spread acroos the stereo spectrum.
* This process only effects the pan value of the notes in the cphrase.
*
If cphrase
is null then
* this method does nothing.
*
* @param cphrase the CPhrase to be effected
*/
public static void spread(CPhrase cphrase) {
if (cphrase == null) {
return;
}
int currentValue, newValue;
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
// create new pan value
Mod.spread(phr);
}
}
/**
* Adjusts all Notes' pan value to alternate between extreme left and right from note to note.
* This process only effects the pan value of the notes in the cphrase.
*
If cphrase
is null then
* this method does nothing.
*
* @param cphrase The CPhrase to be effected
*/
public static void bounce(CPhrase cphrase) {
if (cphrase == null) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
Mod.bounce(phr);
}
}
/**
* Joins consecutive notes in each CPhrase
that
* have the same pitch, creating one longer note.
* This is simmilar to the musical function of a tie.
* All note in the phrase meeting the conditions
* are effected.
* This modification may reduce the overall note count.
*
* @param CPhrase CPhrase to be processed
*/
public static void tiePitches(CPhrase cphrase) {
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
tiePitches(phrase);
}
}
/**
* Joins consecutive rests in each CPhrase
* creating one longer note.
* This is simmilar to the musical function of a tie.
* This modification may reduce the overall rest count.
*
* @param CPhrase CPhrase to be processed
*/
public static void tieRests(CPhrase cphrase) {
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
tieRests(phrase);
}
}
/**
* Lengthens notes followed by a rest in the CPhrase
* by creating one longer note and deleting the rest.
* This modification may reduce the overall note count in the phrase,
* be careful that use of this method does not to cause
* array out of bounds exceptions.
*
* @param CPhrase CPhrase to be processed
*/
public static void fillRests(CPhrase cphrase) {
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
fillRests(phrase);
}
}
/**
* Adjusts all Notes' duration values to vary randomly between specified values from note to note.
* This process only effects the duration attribute of the notes in the phrase.
*
If cphrase
is null then this method does nothing.
*
* @param cphrase The CPhrase to be effected
* @param minlength The shortest possible duration
* @param maxlength The longest possible duration
*/
public static void varyLength(CPhrase cphrase, double minLength, double maxLength) {
if (cphrase == null || maxLength < minLength) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
varyLength(phrase, minLength, maxLength);
}
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm & duration, dynamic, and
* pan values of the notes in the cphrase.
*
If cphrase
is null then this method does nothing.
*
* @param cphrase The CPhrase to be effected
* @param pitchVariation The degree of pitch change to apply.
*/
public static void randomize(CPhrase cphrase, int pitchVariation) {
randomize(cphrase, pitchVariation, 0.0);
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm and duration, dynamic, and
* pan values of the notes in the cphrase.
*
If cphrase
is null then this method does nothing.
*
* @param cphrase The CPhrase to be effected
* @param pitchVariation The degree of pitch change to apply.
* @param rhythmVariation The degree of rhythm value change to apply.
*/
public static void randomize(CPhrase cphrase, int pitchVariation, double rhythmVariation) {
randomize(cphrase, pitchVariation, rhythmVariation, 0);
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm and duration, dynamic, and
* pan values of the notes in the cphrase.
*
If cphrase
is null then this method does nothing.
*
* @param cphrase The CPhrase to be effected
* @param pitchVariation The degree of pitch change to apply.
* @param rhythmVariation The degree of rhythm value change to apply.
* @param dynamicVariation The degree of dynamic change to apply.
*/
public static void randomize(CPhrase cphrase, int pitchVariation, double rhythmVariation, int dynamicVariation) {
if (cphrase == null) {
return;
}
boolean left = true;
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
randomize(phr, pitchVariation, rhythmVariation, dynamicVariation);
}
}
/**
* All ascending sequences of the number of notes or more are slured by
* having thier duration extended to 100% of the rhythm value.
* This algorithm assumes notes are arranged in the standard monophonic
* series used by jMusic. Notes with durations longer than rhythm values
* will produce unintended results.
*
* @param CPhrase The cphrase to be modified.
* @param int The number of notes in a row to consitute a sequence
*/
public static void slurUp(CPhrase cphrase, int numberOfNotes) {
if (cphrase == null) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
slurUp(phr, numberOfNotes);
}
}
/**
* All descending sequences of the number of notes or more are slured by
* having thier duration extended to 100% of the rhythm value.
* This algorithm assumes notes are arranged in the standard monophonic
* series used by jMusic. Notes with durations longer than rhythm values
* will produce unintended results.
*
* @param CPhrase The cphrase to be modified.
* @param int The number of notes in a row to consitute a sequence
*/
public static void slurDown(CPhrase cphrase, int numberOfNotes) {
if (cphrase == null) {
return;
}
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
slurDown(phr, numberOfNotes);
}
}
/**
* Vary the duration of each note in the phrase by the multiplyer.
*
* @param CPhrase The cphrase to be modified.
* @param double The amount to multiply the duration by.
*/
public static void increaseDuration(CPhrase cphrase, double multiplyer) {
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
increaseDuration(phr, multiplyer);
}
}
/**
* Vary the duration of each note in the cphrase by the specified amount.
*
* @param cphrase - The cphrase to be modified.
* @param amount - The number of beats to add to the duration.
*/
public static void addToDuration(CPhrase cphrase, double amount) {
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
addToDuration(phr, amount);
}
}
/**
* Vary the rhythm value of each note in the cphrase by the specified amount.
*
* @param cphrase - The phrase to be modified.
* @param amount - The number of beats to add.
*/
public static void addToRhythmValue(CPhrase cphrase, double amount) {
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
addToRhythmValue(phr, amount);
}
}
/**
* Vary both the rhythm value and duration of each note in the phrase by the specified amount.
*
* @param cphrase - The cphrase to be modified.
* @param amount - The number of beats to add.
*/
public static void addToLength(CPhrase cphrase, double amount) {
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
addToLength(phr, amount);
}
}
/**
* Vary the interval between notes scaling by the specified amount to each interval.
*
* @param cphrase - The CPhrase to be modified.
* @param amount - The scaling multiplyer for the intervals, i.e., 2.0 doubles width.
*/
public static void expandIntervals(CPhrase cphrase, double amount) {
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
expandIntervals(phr, amount);
}
}
/**
* Randomise the dynamic values of notes up to a specified amount either side of the current value.
*
* @param cphrase - The CPhrase to be modified.
* @param amount - The dynamic change possible either side of the curent dynamic.
*/
public static void shake(CPhrase cphrase, int amount) {
Enumeration enum1 = cphrase.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
shake(phr, amount);
}
}
//------------------------ PART MODIFICATIONS ----------------------------//
/**
* Makes the Part twice as long by repeating it once.
*
If part
is null this method does nothing.
*
* @param part Part to be repeated
*/
public static void repeat(Part part) {
repeat(part, 2);
}
/**
* Makes the Part n times as long by repeating.
*
* If part
is null or times
is less than
* one this method does nothing.
*
* @param cphrase Part to be repeated
* @param times number of repeats (default is 1)
*/
public static void repeat(Part part, final int times) {
if (part == null) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase nextPhrase = (Phrase) enum1.nextElement();
repeat(nextPhrase, times);
}
/*
// Alternate logic: Repeats the phase rather than lengthens the phrase
int initialLength = part.getPhraseList().size();
for (int t = 0; t < (times - 1); t++){
double initialEndTime = part.getEndTime();
for(int i = 0; i < initialLength; i++) {
Phrase phr = (Phrase)part.getPhraseList().elementAt(i);
Phrase phrCopy = phr.copy();
phrCopy.setStartTime(initialEndTime + phr.getStartTime());
part.addPhrase(phrCopy);
}
}
*/
}
/**
* Loops a section of a the Part once
*
* If part
is null; or startLoc
is greater
* than or equal to endLoc
then this method does nothing.
*
* @param part Part to be repeated
* @param startLoc location of the loop start in beats
* @param endLoc location of the loop end in beats
*/
public static void repeat(Part part, final double startLoc,
final double endLoc) {
repeat(part, 2, startLoc, endLoc);
}
/**
* Loops a section of the Part n times.
*
* If part
is null; startLoc
is greater
* than or equal to endLoc
; or n
is less than one
* then this method does nothing.
*
* @param part Part to be repeated
* @param times int number of repeats (default is 1)
* @param double location of the loop start in beats
* @param double location of the loop end in beats
*/
public static void repeat(Part part, final int times,
final double startLoc, final double endLoc) {
if (part == null || startLoc >= endLoc || times < 2) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase nextPhrase = (Phrase) enum1.nextElement();
repeat(nextPhrase, times, startLoc, endLoc);
}
}
/**
* Transpose the part up or down in semitone steps. This
* transposition is chromatic, not diatonic.
*
* @param part the Part to be transposed
* @param transposition the number of semitone steps to shift the pitch
*/
public static void transpose(Part part, final int transposition) {
if (part == null || transposition == 0) return;
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
transpose(phr, transposition);
}
}
/**
* Transpose the Part up or down in scale degrees.
*
* If part
is null then this method does nothing. If the
* transposition shifts a pitch above {@link Note#MAX_PITCH MAX_PITCH} or
* below {@link Note#MIN_PITCH MIN_PITCH}, the pitch will probably cap at
* those values. See the description of {@link Note#setPitch} method for
* exact details of what occurs when trying to set the pitch beyond the
* allowed range.
*
* Transposition is in diatonic steps. For example in C major the note C
* transposed 1 will become D, transposed 4 will become G, and transposed
* by 7 will beome C an octave above. This can be somewhat unintuitive
* so be careful.
*
* @param part Part to be transposed
* @param degrees the number of scale degrees to transpose
* @param mode the scale to use for the transposition
* @param key the chromatic note to be used as the
* rooth of the mode. i.e., 0 = C, 1 = C# etc.
*/
public static void transpose(Part part, final int degrees, final int[] mode, int key) {
if (part == null) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
transpose((Phrase) enum1.nextElement(), degrees, mode, key);
}
}
/**
* A compressor/expander routine. Compression ratio numbers between 0 and 1
* compress, values larger than 1 expand.
*
* This is a basic compression routine and is not perfect.
* Specifically, each phrase compresses relative to itself, not to the
* overall part as might be expected.
*
* If part
is null then this method does nothing.
*
* @param part Part to be compressed
* @param ratio double describing the compression factor
*/
public static void compress(Part part, final double ratio) {
if (part == null) {
return;
}
// find the average dynamic of the part
int curr;
double accum = 0;
int ave;
int counter = 0;
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase tempPhrase = (Phrase) enum1.nextElement();
if (tempPhrase == null) {
break;
}
Enumeration enum2 = tempPhrase.getNoteList().elements();
while (enum2.hasMoreElements()) {
Note note = (Note) enum2.nextElement();
if (note.getPitch() != REST) { // reject rests
curr = note.getDynamic();
accum += curr;
counter++;
}
}
}
ave = (int) (accum / counter);
// compress the sucker
enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase tempPhrase = (Phrase) enum1.nextElement();
if (tempPhrase == null) {
break;
}
Enumeration enum2 = tempPhrase.getNoteList().elements();
while (enum2.hasMoreElements()) {
Note note = (Note) enum2.nextElement();
System.out.println("note was =" + note.getDynamic());
curr = (int) (ave + ((note.getDynamic() - ave) * ratio));
note.setDynamic(curr);
}
}
}
/**
* Adds a second part to the end of this one.
*
* If part1
or part2
is null then this method
* does nothing.
*
* @param part1 the base Part
* @param part2 the Part to be appended
*/
public static void append(Part part1, final Part part2) {
append(part1, part2, part1.getEndTime());
}
/**
* Adds a second part to this one starting from a specified location
*
* If part1
or part2
is null
*
* @param part1 the base Part
* @param part2 the Part to be appended
* @param fromLoc double describing the start time for the second part to
* be appended
*/
public static void append(Part part1, final Part part2,
final double fromLoc) {
if (part1 == null || part2 == null) {
return;
}
//go through the phrases in the new part
// and add then one by one
Enumeration enum1 = part2.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase tempPhrase = ((Phrase) enum1.nextElement()).copy();
tempPhrase.setStartTime(fromLoc + tempPhrase.getStartTime());
if (tempPhrase.getInstrument() == part1.getInstrument())
tempPhrase.setInstrument(Phrase.DEFAULT_INSTRUMENT);
part1.addPhrase(tempPhrase);
}
}
/**
* Increases the dynamic by a certain amount -
* obviously a negative number will decrease it.
*
* @param Part the part that is to be affected
* @param int the amount that it is to be affected by
*/
public static void increaseDynamic(Part p, int amount) {
try {
if (p == null) new NullPointerException();
} catch (NullPointerException e) {
e.toString();
return;
}
Enumeration enum1 = p.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
increaseDynamic(phr, amount);
}
}
/**
* Linearly fades in the Part.
*
* If part
is null; or if fadeLength
is less
* than or equal to zero then this method does nothing.
*
* @param part Part to be faded
* @param fadelength double describing the number of crotchets to fade
* over
*/
public static void fadeIn(Part part, final double fadeLength) {
if (part == null || fadeLength <= 0.0) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase nextPhrase = (Phrase) enum1.nextElement();
//make the correction for phrases that don't start at the same time as the Part does (0.0)
fadeIn(nextPhrase, fadeLength, nextPhrase.getStartTime());
}
}
/**
* Linearly fades in the Part.
*
* If part
is null; fadeLength
is less than or
* equal to zero; phraseStart
is less than zero; or
* fadeLength
is less than or equal to phraseStart
then
* this method does nothing.
*
* @param part Part to be faded
* @param fadeLength double describing the duration of the fade, in
* crotchets
* @param partStartTime double describing how far into the fade the part
* starts
*/
public static void fadeIn(Part part, final double fadeLength,
final double partStartTime) {
if (part == null || fadeLength <= 0.0 || partStartTime < 0.0) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase nextPhrase = (Phrase) enum1.nextElement();
//make the correction for phrases that don't start at the same time as the Part does (0.0)
fadeIn(nextPhrase, fadeLength, (partStartTime + nextPhrase.getStartTime()));
}
}
/**
* Linearly fades out the Part.
*
* If part
is null; or if fadeLength
is less
* than or equal to zero then this method does nothing.
*
* @param part Part to be faded
* @param fadeLength double describing the duration of the fade out in
* crotchets
*/
public static void fadeOut(Part part, final double fadeLength) {
if (part == null || fadeLength <= 0.0) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase nextPhrase = (Phrase) enum1.nextElement();
//make the correction for phrases that don't end at the same time as the Part does
fadeOut(nextPhrase, fadeLength, (part.getEndTime() - nextPhrase.getEndTime()));
}
}
/**
* Linearly fades out the Part.
*
* If part
is null; fadeLength
is less than
* or equal to zero; phraseEndTime
is less than zero; or
* fadeLength
is less than phraseEndTime
then this
* method does nothing.
*
* @param part Part to be faded
* @param fadeLength double describing the duration of the fade out in
* crotchets
* @param partEndTime double describing the length of time, in crotchets,
* between the end of the part and the end of the fade.
*/
public static void fadeOut(Part part, final double fadeLength,
final double partEndTime) {
if (part == null || fadeLength <= 0.0 || partEndTime < 0.0) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase nextPhrase = (Phrase) enum1.nextElement();
//make the correction for phrases that don't end at the same time as the Part does
fadeOut(nextPhrase, fadeLength, (partEndTime + part.getEndTime() - nextPhrase.getEndTime()));
}
}
/**
* Combines the phrases from a second part into this one.
*
* If part1
or part2
is null this method does
* nothing.
*
* @param part1 the base Part
* @param part2 the Part to be merged to the first
*/
public static void merge(Part part1, final Part part2) {
if (part1 == null || part2 == null) {
return;
}
//go through the phrases in the new part
// and add then one by one
Enumeration enum1 = part2.getPhraseList().elements();
while (enum1.hasMoreElements()) {
part1.addPhrase((Phrase) enum1.nextElement());
}
}
/**
* Quantize the part
.
*
* See {@link #quantise(Part, double)}.
*
* @param part Part to be quantized
* @param qValue the amount to quantize to
*/
public static void quantize(Part part, final double qValue) {
quantise(part, qValue);
}
/**
* Quantise the part
.
*
* See {@link #quantise(Part, double)}.
*
* @param part Part to be quantized
* @param qValue the amount to quantize to
*/
public static void quantise(Part part, final double qValue) {
quantize(part, qValue, CHROMATIC_SCALE, 0);
}
/**
* Quantize all the phrases in this part, both rhythmic and pitch elements.
*
* See {@link #quantise(Phrase, double)} for details.
*
* If part
is null or qValue
is less than or
* equal to zero then this method does nothing.
*
* @param part Part to be quantised
* @param double - the amount to quantise too
*/
public static void quantize(Part part, final double qValue, final int[] mode, int key) {
if (part == null || qValue <= 0.0 || mode == null || key < 0) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
quantize(phrase, qValue, mode, key);
}
}
/**
* Randomly order notes within each phrase.
*
* @param part -The Part that contains phrases to be shuffled
*/
public static void shuffle(Part part) {
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
shuffle((Phrase) enum1.nextElement());
}
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter by emphasising the
* first beat of each bar/measure.
*
* The dynamic of each note starting on the beat will be increased by
* 20. If this raises the dynamic above {@link Note#MAX_DYNAMIC MAX_DYNAMIC
* } then the dynamic will be set to a value as described in {@link
* Note#setDynamic}.
*
* If part
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param part Part to be accented
* @param meter double describing the number of croctets per bar/measure
*/
public static void accents(Part part, final double meter) {
double[] beats = {0.0};
accents(part, meter, beats);
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter by accenting specified beats
* within each bar/measure.
*
* accentedBeats
is an array of values describing where
* the accents begin relative to the start of each bar, in crochets.
*
* Each accented note's dynamic will be increased by 20.
* If this raises the dynamic above {@link Note#MAX_DYNAMIC MAX_DYNAMIC}
* then the dynamic will be set to a value as described in {@link
* Note#setDynamic}.
*
* Each of the double values in accentedBeats
must be
* greater than or equal to zero and less than meter
or this
* method does nothing.
*
* If part
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param part Part to be accented
* @param meter double describing the number of croctets per
* bar/measure
* @param accentedBeats double array describing the time of the accents in
* the bar.
*/
public static void accents(Part part, double meter,
double[] accentedBeats) {
accents(part, meter, accentedBeats, 20);
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter. The meter is the total
* beat length of the bar/measure, while the array of accented beats is
* the times within the bar at which an accent should be made.
* For example, 6/8 time would be implied by a meter of 3 and a
* accent array of {0.0, 1.5}.
*
* accentedBeats
is an array of values describing where
* the accents begin relative to the start of each bar, in crochets.
*
* Accented beats will have their dynamic increased by
* accentAmount
. If this causes the dynamic to be above {@link
* Note#MAX_DYNAMIC MAX_DYNAMIC} or below {@link Note#MIN_DYNAMIC
* MIN_DYNAMIC} (when accentAmount
) then the dynamic will be
* set to a value as described by {@link Note#setDynamic}.
*
* Each of the double values in accentedBeats
must be
* greater than or equal to zero and less than meter
or this
* method does nothing.
*
* If part
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param part Part to be accented
* @param meter double describing the number of croctets per
* bar/measure
* @param accentedBeats double array describing the time of the accents in
* the bar.
* @param accentAmount integer describing the value that the dynamic of
* accented beats are increased by.
*/
public static void accents(Part part, final double meter,
final double[] accentedBeats,
final int accentAmount) {
if (part == null || meter <= 0.0) {
return;
}
for (int i = 0; i < accentedBeats.length; i++) {
if (accentedBeats[i] < 0.0 || accentedBeats[i] >= meter) {
return;
}
}
//go through the phrases one by one and accent them
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
accents(phrase, meter, accentedBeats, accentAmount);
}
}
/**
* Increases dynamic values so that the loudest is at maxiumim level.
*
* If part
is null then
* this method does nothing.
*
* @param part the Part to be effected
*/
public static void normalise(Part part) {
if (part == null) {
return;
}
// get the curent max
int max = 0;
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
Enumeration enum2 = phrase.getNoteList().elements();
while (enum2.hasMoreElements()) {
Note n = (Note) enum2.nextElement();
if (n.getDynamic() > max) max = n.getDynamic();
}
}
// increase the normalisation
if (max == Note.MAX_DYNAMIC) {
return;
}
int diff = Note.MAX_DYNAMIC - max;
Enumeration enum3 = part.getPhraseList().elements();
while (enum3.hasMoreElements()) {
Phrase phrase = (Phrase) enum3.nextElement();
Enumeration enum4 = phrase.getNoteList().elements();
while (enum4.hasMoreElements()) {
Note n = (Note) enum4.nextElement();
n.setDynamic(n.getDynamic() + diff);
}
}
}
/**
* Stretch the time of each note in each phrase by the scaleFactor
*
* If phrase
is null or scaleFactor
is less
* than or equal to zero then this method does nothing.
*
* @param part Part to be lengthened
* @param scaleFactor double describing the scale factor
*/
public static void elongate(Part part, double scaleFactor) {
if (part == null || scaleFactor <= 0.0) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
elongate(phrase, scaleFactor);
}
}
/**
* Joins consecutive notes in each phrase
that
* have the same pitch, creating one longer note.
* This is simmilar to the musical function of a tie.
* All note in the phrase meeting the conditions
* are effected.
* This modification may reduce the overall note count.
*
* @param Part Part to be processed
*/
public static void tiePitches(Part part) {
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
tiePitches(phrase);
}
}
/**
* Joins consecutive rests in each part
* creating one longer note.
* This is simmilar to the musical function of a tie.
* This modification may reduce the overall rest count.
*
* @param part Part to be processed
*/
public static void tieRests(Part part) {
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
tieRests(phrase);
}
}
/**
* Lengthens notes followed by a rest in the part
* by creating one longer note and deleting the rest.
* This modification may reduce the overall note count in the phrase,
* be careful that use of this method does not to cause
* array out of bounds exceptions.
*
* @param Part Part to be processed
*/
public static void fillRests(Part part) {
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
fillRests(phrase);
}
}
/**
* Randomly adjusts all Notes' pan value to create an even spread acroos the stereo spectrum.
* This process only effects the pan value of the notes in the part.
*
If part
is null then
* this method does nothing.
*
* @param part the Part to be effected
*/
public static void spread(Part part) {
if (part == null) {
return;
}
int currentValue, newValue;
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
// create new pan value
Mod.spread(phr);
}
}
/**
* Adjusts all Notes' pan value to alternate between extreme left and right from note to note.
* This process only effects the pan value of the notes in the part.
*
If part
is null then
* this method does nothing.
*
* @param part The Part to be effected
*/
public static void bounce(Part part) {
if (part == null) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
Mod.bounce(phr);
}
}
/**
* Adjusts all Notes' duration values to vary randomly between specified values from note to note.
* This process only effects the duration attribute of the notes in the phrase.
*
If Part
is null then this method does nothing.
*
* @param part The Part to be effected
* @param minlength The shortest possible duration
* @param maxlength The longest possible duration
*/
public static void varyLength(Part part, double minLength, double maxLength) {
if (part == null || maxLength < minLength) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
varyLength(phrase, minLength, maxLength);
}
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm and duration, dynamic, and
* pan values of the notes in the part.
*
If Part
is null then this method does nothing.
*
* @param part The Part to be effected
* @param pitchVariation The degree of pitch change to apply.
*/
public static void randomize(Part part, int pitchVariation) {
randomize(part, pitchVariation, 0.0);
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm and duration, dynamic, and
* pan values of the notes in the part.
*
If Part
is null then this method does nothing.
*
* @param part The Part to be effected
* @param pitchVariation The degree of pitch change to apply.
* @param rhythmVariation The degree of rhythm value change to apply.
*/
public static void randomize(Part part, int pitchVariation, double rhythmVariation) {
randomize(part, pitchVariation, rhythmVariation, 0);
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm and duration, dynamic, and
* pan values of the notes in the part.
*
If Part
is null then this method does nothing.
*
* @param part The Part to be effected
* @param pitchVariation The degree of pitch change to apply.
* @param rhythmVariation The degree of rhythm value change to apply.
* @param dynamicVariation The degree of dynamic change to apply.
*/
public static void randomize(Part part, int pitchVariation, double rhythmVariation, int dynamicVariation) {
if (part == null) {
return;
}
boolean left = true;
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
randomize(phr, pitchVariation, rhythmVariation, dynamicVariation);
}
}
/**
* All ascending sequences of the number of notes or more are slured by
* having thier duration extended to 100% of the rhythm value.
* This algorithm assumes notes are arranged in the standard monophonic
* series used by jMusic. Notes with durations longer than rhythm values
* will produce unintended results.
*
* @param Part The part to be modified.
* @param int The number of notes in a row to consitute a sequence
*/
public static void slurUp(Part part, int numberOfNotes) {
if (part == null) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
slurUp(phr, numberOfNotes);
}
}
/**
* All descending sequences of the number of notes or more are slured by
* having thier duration extended to 100% of the rhythm value.
* This algorithm assumes notes are arranged in the standard monophonic
* series used by jMusic. Notes with durations longer than rhythm values
* will produce unintended results.
*
* @param Part The part to be modified.
* @param int The number of notes in a row to consitute a sequence
*/
public static void slurDown(Part part, int numberOfNotes) {
if (part == null) {
return;
}
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
slurDown(phr, numberOfNotes);
}
}
/**
* Vary the duration of each note in the part by the multiplyer.
*
* @param Part The part to be modified.
* @param double The amount to multiply the duration by.
*/
public static void increaseDuration(Part part, double multiplyer) {
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
increaseDuration(phr, multiplyer);
}
}
/**
* Vary the duration of each note in the part by the specified amount.
*
* @param part - The Part to be modified.
* @param amount - The number of beats to add to the duration.
*/
public static void addToDuration(Part part, double amount) {
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
addToDuration(phr, amount);
}
}
/**
* Vary the rhythm value of each note in the part by the specified amount.
*
* @param part - The Part to be modified.
* @param amount - The number of beats to add.
*/
public static void addToRhythmValue(Part part, double amount) {
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
addToRhythmValue(phr, amount);
}
}
/**
* Vary both the rhythm value and duration of each note in the part by the specified amount.
*
* @param part - The Part to be modified.
* @param amount - The number of beats to add.
*/
public static void addToLength(Part part, double amount) {
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
addToLength(phr, amount);
}
}
/**
* Vary the interval between notes scaling by the specified amount to each interval.
*
* @param part - The Part to be modified.
* @param amount - The scaling multiplyer for the intervals, i.e., 2.0 doubles width.
*/
public static void expandIntervals(Part part, double amount) {
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
expandIntervals(phr, amount);
}
}
/**
* Randomise the dynamic values of notes up to a specified amount either side of the current value.
*
* @param part - The Part to be modified.
* @param amount - The dynamic change possible either side of the curent dynamic.
*/
public static void shake(Part part, int amount) {
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phr = (Phrase) enum1.nextElement();
shake(phr, amount);
}
}
/**
* packs all of the phrases within this part into a single phrase
*/
public static void consolidate(Part p) {
Phrase[] phr = p.getPhraseArray();
if (phr.length < 2)
return;
// the new phrase has the start time of the earliest one
Phrase nphr = new Phrase(phr[0].getStartTime());
Note n;
boolean finished = false;
double prevsst = phr[0].getStartTime();
while (!finished) {
double sst = Double.POSITIVE_INFINITY; // smallest start time
// a temporary phrase (pointer to the one in the array
Phrase tphr = null;
//find the phrase with the smallest start time
for (int i = 0; i < phr.length; i++) {
if (phr[i].getSize() > 0 && phr[i].getStartTime() < sst) {
sst = phr[i].getStartTime();
tphr = phr[i];
}
}
if (tphr == null) {
finished = true;
break;
}
//get a note out of that phrase and, if it is not a rest,
// put it into the new phrase, adjusting the rhythmValue
// of the previous note accordingly
n = tphr.getNote(0);
if (!n.isRest()) {
if (nphr.getSize() > 0) { // if it is not the first note
nphr.getNote(nphr.getSize() - 1).setRhythmValue(((int) ((sst - prevsst) * 100000 + 0.5)) / 100000.0);
} else {// if it is the first note to go in, set the startime
nphr.setStartTime(sst);
}
nphr.addNote(n);
}
// adjust the start time and remove the note
tphr.setStartTime(((int) ((sst +
n.getRhythmValue()) * 100000 + 0.5)) / 100000.0);
tphr.removeNote(0);
prevsst = sst;
}
p.empty();
p.addPhrase(nphr);
}
//------------------------ SCORE MODIFICATIONS ---------------------------//
/**
* Transpose a Score
*
* @param score Score to be faded
* @param transposition The number of semitones to transpose by
*/
public static void transpose(Score scr, final int transposition) {
if (scr == null || transposition == 0) return;
Enumeration enum1 = scr.getPartList().elements();
while (enum1.hasMoreElements()) {
Part part = (Part) enum1.nextElement();
transpose(part, transposition);
}
}
/**
* Transpose the score up or down in scale degrees.
*
* If score
is null then this method does nothing. If the
* transposition shifts a pitch above {@link Note#MAX_PITCH MAX_PITCH} or
* below {@link Note#MIN_PITCH MIN_PITCH}, the pitch will probably cap at
* those values. See the description of {@link Note#setPitch} method for
* exact details of what occurs when trying to set the pitch beyond the
* allowed range.
*
* Transposition is in diatonic steps. For example in C major the note C
* transposed 1 will become D, transposed 4 will become G, and transposed
* by 7 will beome C an octave above. This can be somewhat unintuitive
* so be careful.
*
* @param score Score to be transposed
* @param degrees the amount to transpose in scale steps
* @param mode the scale to use for the transposition
* @param key the chromatic note to be used as the
* rooth of the mode. i.e., 0 = C, 1 = C# etc.
*/
public static void transpose(Score score, final int degrees, final int[] mode, int key) {
if (score == null) {
return;
}
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
transpose((Part) enum1.nextElement(), degrees, mode, key);
}
}
/**
* Linearly fades in the Score.
*
* See {@link #fadeIn(Phrase, double)}.
*
* If score
is null or fadeLength
is less
* than or equal to zero then this method does nothing.
*
* @param score Score to be faded
* @param fadelength double describing the number of crotchets to fade
* over
*/
public static void fadeIn(Score score, final double fadeLength) {
if (score == null || fadeLength <= 0.0) {
return;
}
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part nextPart = (Part) enum1.nextElement();
fadeIn(nextPart, fadeLength);
}
}
/**
* increases the dynamic by a certain amount -
* obviously a negative number will decrease it
*
* @param Score the score that is to be affected
* @param int the amount
*/
public static void increaseDynamic(Score s, int amount) {
try {
if (s == null) new NullPointerException();
} catch (NullPointerException e) {
e.toString();
return;
}
Enumeration enum1 = s.getPartList().elements();
while (enum1.hasMoreElements()) {
Part p = (Part) enum1.nextElement();
increaseDynamic(p, amount);
}
}
/**
* Linearly fades out the Score.
*
* See {@link #fadeOut(Phrase, double)}.
*
* If score
is null or fadeLength
is less
* than or equal to zero then this method does nothing.
*
* @param score Score to be faded
* @param fadelength double describing the number of crotchets to fade
* over
*/
public static void fadeOut(Score score, final double fadeLength) {
if (score == null || fadeLength <= 0.0) {
return;
}
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part nextPart = (Part) enum1.nextElement();
//make the correction for Parts that don't end at the same time as the Score does
fadeOut(nextPart, fadeLength, (score.getEndTime() - nextPart.getEndTime()));
}
}
/**
* A compressor/expander routine. Compression ratio numbers between 0 and 1
* compress, values larger than 1 expand.
*
* See {@link #compress(Part, double)}.
*
* If score
is null then this method does nothing.
*
* @param score Score to be expanded/compressed
* @param ratio double describing the compression factor
*/
public static void compress(Score score, final double ratio) {
if (score == null) {
return;
}
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part part = (Part) enum1.nextElement();
compress(part, ratio);
}
}
/**
* Makes a the score twice times as long by repeating
*
* If score
is null then this method does nothing.
*
* @param score Score to be repeated
*/
public static void repeat(Score score) {
repeat(score, 2);
}
/**
* Loops the score
n
times.
*
* If score
is null or times
is less than
* one then this method does nothing.
*
* @param score Score to be repeated
* @param times integer representing the number of repeats
*/
public static void repeat(Score score, final int times) {
if (score == null || times < 2) {
return;
}
//get the longest end time
double maxEndTime = 0.0;
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part part = (Part) enum1.nextElement();
if (maxEndTime < part.getEndTime()) maxEndTime = part.getEndTime();
}
for (int i = 0; i < score.getPartList().size(); i++) {
Part p = (Part) score.getPartList().elementAt(i);
int numbOfPhrases = p.getPhraseList().size();
for (int t = 0; t < (times - 1); t++) {
double initialEndTime = maxEndTime * (t + 1);
for (int j = 0; j < numbOfPhrases; j++) {
Phrase phr = (Phrase) p.getPhraseList().elementAt(j);
Phrase phrCopy = phr.copy();
phrCopy.setStartTime(initialEndTime + phr.getStartTime());
p.addPhrase(phrCopy);
}
}
}
}
/**
* Adds a second score to the end of this one.
*
* If score1
or score2
is null then this
* method does nothing.
*
* @param score1 the base Score
* @param score2 the Score to be appended
*/
public static void append(Score score1, final Score score2) {
if (score1 == null || score2 == null) {
return;
}
score2.clean();
if (score2.size() == 0) return;
double endTime = score1.getEndTime();
Enumeration enum1 = score2.getPartList().elements();
while (enum1.hasMoreElements()) {
Part currPart = (Part) enum1.nextElement();
// update start times and program changes
Enumeration enum2 = currPart.getPhraseList().elements();
while (enum2.hasMoreElements()) {
Phrase currPhrase = (Phrase) enum2.nextElement();
currPhrase.setStartTime(currPhrase.getStartTime() + endTime);
if (currPhrase.getInstrument() != 250 && currPhrase.getInstrument() != currPart.getInstrument())
currPhrase.setInstrument(currPart.getInstrument());
if (currPhrase.getInstrument() == currPart.getInstrument())
currPhrase.setInstrument(Phrase.DEFAULT_INSTRUMENT);
}
}
// add to score1
Mod.merge(score1, score2);
}
/**
* Combines the parts from a second score into the first one.
* Parts on an existing channel will have phases extracted and
* added to that part, otherwise a part with a unique channel number
* will be added.
*
If score1
or score2
is null then this
* method does nothing.
*
* @param score1 the base score
* @param score2 the Score to be merged to the base score
*/
public static void merge(Score score1, final Score score2) {
if (score1 == null || score2 == null) {
return;
}
// Go through the parts in the new score
// and merge them one by one
boolean channelExists = false;
Part existingPart;
Part partToMerge;
int s1Size = score1.size();
int s2Size = score2.size();
for (int i = 0; i < s2Size; i++) {
partToMerge = score2.getPart(i);
// check if its channel exists
int chan = partToMerge.getChannel();
for (int j = 0; j < s1Size; j++) {
existingPart = score1.getPart(j);
if (chan == existingPart.getChannel()) {
// transfer phrases
int phraseNumb = partToMerge.size();
for (int k = 0; k < phraseNumb; k++) {
existingPart.addPhrase(partToMerge.getPhrase(k));
}
channelExists = true;
j = s1Size; // get out of loop
}
}
// create a new part with a unique channel, if required
if (!channelExists) {
score1.addPart(partToMerge);
channelExists = false;
}
}
}
/**
* Quantize rhythm values of all the parts in this score. American spelling to save
* frustration!
*
* See {@link #quantise(Score, double)}.
*
* @param score Score to quantize
* @param qValue double describing the amount to quantize to
*/
public static void quantize(Score score, final double qValue) {
quantise(score, qValue);
}
/**
* Quantise all the rhythm values of notes in this score.
*
* See {@link #quantise(Score, double)}.
*
* @param score Score to quantize
* @param qValue double describing the amount to quantize to
*/
public static void quantise(Score score, final double qValue) {
quantize(score, qValue, CHROMATIC_SCALE, 0);
}
/**
* Quantize all the rhythm values and pitches in this score.
*
* See {@link #quantise(Phrase, double)}.
*
* If score
is null or qValue
is less than or
* equal to zero then this method does nothing.
*
* @param score Score to quantise
* @param qValue double describing the amount to quantise to
* @param mode The scale to quantize pitch values to, e.g., MAJOR_SCALE
* @param key The key of the mode. e.g., 0 = C, 1 = C# etc.
*/
public static void quantize(Score score, final double qValue, final int[] mode, int key) {
if (score == null || qValue <= 0.0 || mode == null || key < 0) {
return;
}
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part part = (Part) enum1.nextElement();
quantize(part, qValue, mode, key);
}
}
/**
* Randomly order notes within each phrase.
*
* @param score -The Score that contains phrases to be shuffled
*/
public static void shuffle(Score score) {
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
shuffle((Part) enum1.nextElement());
}
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter by emphasising the
* first beat of each bar/measure.
*
* The dynamic of each note starting on the beat will be increased by
* 20. If this raises the dynamic above {@link Note#MAX_DYNAMIC MAX_DYNAMIC
* } then the dynamic will be set to a value as described in {@link
* Note#setDynamic}.
*
* If score
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param score Score whose beats are to be accented
* @param meter double describing the number of crochets per bar/measure
*/
public static void accents(Score score, final double meter) {
double[] beats = {0.0};
accents(score, meter, beats);
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter by accenting specified beats
* within each bar/measure.
*
* accentedBeats
is an array of values describing where
* the accents begin relative to the start of each bar, in crochets.
*
* Each accented note's dynamic will be increased by 20.
* If this raises the dynamic above {@link Note#MAX_DYNAMIC MAX_DYNAMIC}
* then the dynamic will be set to a value as described in {@link
* Note#setDynamic}.
*
* Each of the double values in accentedBeats
must be
* greater than or equal to zero and less than meter
or this
* method does nothing.
*
* If score
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param score Score to be accented
* @param meter double describing the number of croctets per
* bar/measure
* @param accentedBeats double array describing the time of the accents in
* the bar.
*/
public static void accents(Score score, final double meter,
final double[] accentedBeats) {
accents(score, meter, accentedBeats, 20);
}
/**
* Increase the dynamic of notes at regularly occuring pulse locations
* which generates the sound of regular meter. The meter is the total
* beat length of the bar/measure, while the array of accented beats is
* the times within the bar at which an accent should be made.
* For example, 6/8 time would be implied by a meter of 3 and a
* accent array of {0.0, 1.5}.
*
* accentedBeats
is an array of values describing where
* the accents begin relative to the start of each bar, in crochets.
*
* Accented beats will have their dynamic increased by
* accentAmount
. If this causes the dynamic to be above {@link
* Note#MAX_DYNAMIC MAX_DYNAMIC} or below {@link Note#MIN_DYNAMIC
* MIN_DYNAMIC} (when accentAmount
) then the dynamic will be
* set to a value as described by {@link Note#setDynamic}.
*
* Each of the double values in accentedBeats
must be
* greater than or equal to zero and less than meter
or this
* method does nothing.
*
* If score
is null or meter
is less than or
* equal to zero then this method does nothing.
*
* @param score Score to be accented
* @param meter double describing the number of croctets per
* bar/measure
* @param accentedBeats double array describing the time of the accents in
* the bar.
* @param accentAmount integer describing the value that the dynamic of
* accented beats are increased by.
*/
public static void accents(Score score, final double meter,
final double[] accentedBeats,
final int accentAmount) {
if (score == null || meter <= 0.0) {
return;
}
for (int i = 0; i < accentedBeats.length; i++) {
if (accentedBeats[i] < 0.0 || accentedBeats[i] >= meter) {
return;
}
}
//go through the phrases one by one and accent them
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part part = (Part) enum1.nextElement();
accents(part, meter, accentedBeats, accentAmount);
}
}
/**
* Increases dynamic values so that the loudest is at maxiumim level.
*
* If score
is null then
* this method does nothing.
*
* @param score the Score to be effected
*/
public static void normalise(Score score) {
if (score == null) {
return;
}
// get the curent max
int max = 0;
Enumeration enumS = score.getPartList().elements();
while (enumS.hasMoreElements()) {
Part part = (Part) enumS.nextElement();
Enumeration enum1 = part.getPhraseList().elements();
while (enum1.hasMoreElements()) {
Phrase phrase = (Phrase) enum1.nextElement();
Enumeration enum2 = phrase.getNoteList().elements();
while (enum2.hasMoreElements()) {
Note n = (Note) enum2.nextElement();
if (n.getDynamic() > max) max = n.getDynamic();
}
}
}
// increase the normalisation
if (max == Note.MAX_DYNAMIC) {
return;
}
int diff = Note.MAX_DYNAMIC - max;
Enumeration enumS2 = score.getPartList().elements();
while (enumS2.hasMoreElements()) {
Part part = (Part) enumS2.nextElement();
Enumeration enum3 = part.getPhraseList().elements();
while (enum3.hasMoreElements()) {
Phrase phrase = (Phrase) enum3.nextElement();
Enumeration enum2 = phrase.getNoteList().elements();
while (enum2.hasMoreElements()) {
Note n = (Note) enum2.nextElement();
n.setDynamic(n.getDynamic() + diff);
}
}
}
}
/**
* Pack all phrases from parts with the same channel into one part.
* Data from all parts with the same channel is consolidated into one part.
* This is often useful after appending several scores with
* simmilar instrumentation.
*
* If score
is null then
* this method does nothing.
*
* @param score the Score to be effected
*/
public static void consolidate(Score score) {
if (score == null) {
return;
}
int chan, phraseSize, scoreSize;
for (int c = 0; c < score.size(); c++) {
Part p = score.getPart(c);
chan = p.getChannel();
scoreSize = score.size();
for (int i = scoreSize - 1; i > c; i--) {
//System.out.println("i = " + i + " Part = " + c + " channel = " + chan);
Part p2 = score.getPart(i);
if (p2.getChannel() == chan) {
// move all phrases into the other part
phraseSize = p2.size();
for (int j = 0; j < phraseSize; j++) {
//System.out.println("Moving a phrase");
Phrase phr = p2.getPhrase(j);
phr.setAppend(false);
p.addPhrase(phr);
}
// delete the part
score.removePart(i);
}
}
}
}
/**
* Stretch the time of each note in each phrase by the scaleFactor
*
* If phrase
is null or scaleFactor
is less
* than or equal to zero then this method does nothing.
*
* @param score Score to be lengthened
* @param scaleFactor double describing the scale factor
*/
public static void elongate(Score score, double scaleFactor) {
if (score == null || scaleFactor <= 0.0) {
return;
}
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part part = (Part) enum1.nextElement();
elongate(part, scaleFactor);
}
}
/**
* Joins consecutive notes in each phrase
that
* have the same pitch, creating one longer note.
* This is simmilar to the musical function of a tie.
* All note in the phrase meeting the conditions
* are effected.
* This modification may reduce the overall note count.
*
* @param score Score to be processed
*/
public static void tiePitches(Score score) {
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part part = (Part) enum1.nextElement();
tiePitches(part);
}
}
/**
* Joins consecutive rests in each phrase
* creating one longer note.
* This is simmilar to the musical function of a tie.
* This modification may reduce the overall rest count.
*
* @param score Score to be processed
*/
public static void tieRests(Score score) {
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part part = (Part) enum1.nextElement();
tieRests(part);
}
}
/**
* Lengthens notes followed by a rest in the score
* by creating one longer note and deleting the rest.
* This modification may reduce the overall note count in the phrase,
* be careful that use of this method does not to cause
* array out of bounds exceptions.
*
* @param Score Score to be processed
*/
public static void fillRests(Score score) {
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part part = (Part) enum1.nextElement();
fillRests(part);
}
}
/**
* Randomly adjusts all Notes' pan value to create an even spread acroos the stereo spectrum.
* This process only effects the pan value of the notes in the score.
*
If score
is null then
* this method does nothing.
*
* @param cphrase the CPhrase to be effected
*/
public static void spread(Score score) {
if (score == null) {
return;
}
int currentValue, newValue;
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part p = (Part) enum1.nextElement();
// create new pan value
Mod.spread(p);
}
}
/**
* Adjusts all Notes' pan value to alternate between extreme left and right from note to note.
* This process only effects the pan value of the notes in the score.
*
If score
is null then
* this method does nothing.
*
* @param score The Score to be effected
*/
public static void bounce(Score score) {
if (score == null) {
return;
}
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part p = (Part) enum1.nextElement();
Mod.bounce(p);
}
}
/**
* Adjusts all Notes' duration values to vary randomly between specified values from note to note.
* This process only effects the duration attribute of the notes in the phrase.
*
If the Score
is null then this method does nothing.
*
* @param score The Score to be effected
* @param minlength The shortest possible duration
* @param maxlength The longest possible duration
*/
public static void varyLength(Score score, double minLength, double maxLength) {
if (score == null || maxLength < minLength) {
return;
}
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part part = (Part) enum1.nextElement();
varyLength(part, minLength, maxLength);
}
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm & duration, dynamic, and
* pan values of the notes in the score.
*
If Score
is null then this method does nothing.
*
* @param score The Score to be effected
* @param pitchVariation The degree of pitch change to apply.
*/
public static void randomize(Score score, int pitchVariation) {
randomize(score, pitchVariation, 0.0);
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm & duration, dynamic, and
* pan values of the notes in the score.
*
If score
is null then this method does nothing.
*
* @param score The Score to be effected
* @param pitchVariation The degree of pitch change to apply.
* @param rhythmVariation The degree of rhythm value change to apply.
*/
public static void randomize(Score score, int pitchVariation, double rhythmVariation) {
randomize(score, pitchVariation, rhythmVariation, 0);
}
/**
* Adjusts Note values to any value plus or minus a specified amount.
* This process can effect pitch, rhythm & duration, dynamic, and
* pan values of the notes in the score.
*
If Score
is null then this method does nothing.
*
* @param score The Score to be effected
* @param pitchVariation The degree of pitch change to apply.
* @param rhythmVariation The degree of rhythm value change to apply.
* @param dynamicVariation The degree of dynamic change to apply.
*/
public static void randomize(Score score, int pitchVariation, double rhythmVariation, int dynamicVariation) {
if (score == null) {
return;
}
boolean left = true;
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part p = (Part) enum1.nextElement();
randomize(p, pitchVariation, rhythmVariation, dynamicVariation);
}
}
/**
* All ascending sequences of the number of notes or more are slured by
* having thier duration extended to 100% of the rhythm value.
* This algorithm assumes notes are arranged in the standard monophonic
* series used by jMusic. Notes with durations longer than rhythm values
* will produce unintended results.
*
* @param Score The score to be modified.
* @param int The number of notes in a row to consitute a sequence
*/
public static void slurUp(Score score, int numberOfNotes) {
if (score == null) {
return;
}
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part p = (Part) enum1.nextElement();
slurUp(p, numberOfNotes);
}
}
/**
* All descending sequences of the number of notes or more are slured by
* having thier duration extended to 100% of the rhythm value.
* This algorithm assumes notes are arranged in the standard monophonic
* series used by jMusic. Notes with durations longer than rhythm values
* will produce unintended results.
*
* @param Score The score to be modified.
* @param int The number of notes in a row to consitute a sequence
*/
public static void slurDown(Score score, int numberOfNotes) {
if (score == null) {
return;
}
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part p = (Part) enum1.nextElement();
slurDown(p, numberOfNotes);
}
}
/**
* Vary the duration of each note in the score by the multiplyer.
*
* @param Score The score to be modified.
* @param double The amount to multiply the duration by.
*/
public static void increaseDuration(Score score, double multiplyer) {
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part p = (Part) enum1.nextElement();
increaseDuration(p, multiplyer);
}
}
/**
* Vary the duration of each note in the score by the specified amount.
*
* @param score - The Score to be modified.
* @param amount - The number of beats to add to the duration.
*/
public static void addToDuration(Score score, double amount) {
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part p = (Part) enum1.nextElement();
addToDuration(p, amount);
}
}
/**
* Vary the rhythm value of each note in the Score by the specified amount.
*
* @param score - The Score to be modified.
* @param amount - The number of beats to add.
*/
public static void addToRhythmValue(Score score, double amount) {
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part p = (Part) enum1.nextElement();
addToRhythmValue(p, amount);
}
}
/**
* Vary both the rhythm value and duration of each note in the Score by the specified amount.
*
* @param score - The Score to be modified.
* @param amount - The number of beats to add.
*/
public static void addToLength(Score score, double amount) {
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part p = (Part) enum1.nextElement();
addToLength(p, amount);
}
}
/**
* Vary the interval between notes scaling by the specified amount to each interval.
*
* @param score - The Score to be modified.
* @param amount - The scaling multiplyer for the intervals, i.e., 2.0 doubles width.
*/
public static void expandIntervals(Score score, double amount) {
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part p = (Part) enum1.nextElement();
expandIntervals(p, amount);
}
}
/**
* Randomise the dynamic values of notes up to a specified amount either side of the current value.
*
* @param score - The score to be modified.
* @param amount - The dynamic change possible either side of the curent dynamic.
*/
public static void shake(Score score, int amount) {
Enumeration enum1 = score.getPartList().elements();
while (enum1.hasMoreElements()) {
Part part = (Part) enum1.nextElement();
shake(part, amount);
}
}
}