[jmsl] various MusicShape functions
jmsl at music.columbia.edu
jmsl at music.columbia.edu
Tue Jan 8 19:41:11 EST 2008
Here's a little class that does a couple of utility tasks with
musicShapes, such as snapping short notes to the next long note, or
averaging the durations of short notes. There's also a method for
finding contiguous regions in vectors, which could be useful in a
number of other tasks, such as for beaming, slurs, transcription, etc.
Peter McCulloch
package com.petermcculloch.megalo;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JLabel;
import com.softsynth.jmsl.JMSL;
import com.softsynth.jmsl.JMSLRandom;
import com.softsynth.jmsl.MusicDevice;
import com.softsynth.jmsl.MusicShape;
import com.softsynth.jmsl.midi.MidiIO_MidiShare;
import com.softsynth.jmsl.midi.MidiInstrument;
import com.softsynth.jmsl.util.EventDistributions;
/**
* @author peter
*
*/
public class MusicShapeUtility {
/**
*@paramargs
*/
public static void main(String[] args) {
MusicShape shape = new MusicShape(4);
JMSLRandom.randomize();
for (int j = 0; j < 10; j++) {
// First part has long notes, should be unaffected by
evenOutShortNotes
for (int i = 0; i < 5; i++) {
double randVal = EventDistributions.genEntryDelayMyhill(20, 10);
shape.add(randVal, 48 + (i * 5), 127., 1);
}
shape.add(2., 36., 127., 1);
// Second part will be affected by evenOutShortNotes
for (int i = 0; i < 5; i++) {
double randVal = EventDistributions.genEntryDelayMyhill(10, 10.);
shape.add(randVal, 58 + (i * 5), 127., 0.1);
}
shape.add(2., 46., 127., 0.1);
}
// Before...
shape.print();
MusicShape s = evenOutShortNotes(shape, 0.03, 0.2, 0.1, 1);
s = alignShortNotesToChord(s, 0.03, 0.1);
// After...
s.print();
MusicDevice dev = MidiIO_MidiShare.instance();
dev.open();
MidiInstrument midiIns = new MidiInstrument(1);
midiIns.setChannel(1);
shape.setInstrument(midiIns);
JFrame frame = new JFrame();
frame.getContentPane().add(new JLabel("Close me to quit!"));
frame.setLocationRelativeTo(null);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
JMSL.closeMusicDevices(); // be nice!
System.exit(0);
}
});
frame.pack();
frame.setVisible(true);
Vector<Integer> contig = new Vector<Integer>();
JMSLRandom.randomize();
for (int i = 0; i < 40; i++) {
if (JMSLRandom.choose(100.) < 70.) {
contig.add(i);
}
}
Vector<Vector<Integer>> v = findContiguousRegions(contig, false);
int regionCount = 0;
System.out.print("\n=============REGIONS:============");
for (Iterator<Vector<Integer>> f = v.iterator(); f.hasNext();) {
Vector<Integer> vec = f.next();
System.out.print("\nRegion " + regionCount + ":\t");
for (Iterator<Integer> g = vec.iterator(); g.hasNext();) {
Integer i = g.next();
System.out.print(i + " ");
}
regionCount++;
}
try {
shape.play(JMSL.now());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
*SameasalignShortNotesToChordwithboththresholdssettosamevalue
*
*@params
*@paramtimeThresh
*@return
*/
public static MusicShape alignShortNotesToChord(MusicShape s, double
timeThresh) {
return alignShortNotesToChord(s, timeThresh, timeThresh);
}
/**
*Alignnotesin{@link MusicShape}thathaveadurationbelow
*lowTimeThreshtonearestfollowingnotethathasadurationabove
*highTimeThresh.
*
*@params
*@paramlowTimeThresh
*@paramhighTimeThresh
*@return
*/
// TODO add optional search limiting (e.g. up to 3 notes away)
public static MusicShape alignShortNotesToChord(MusicShape s, double
lowTimeThresh,
double highTimeThresh) {
Vector<Integer> smallValues = new Vector<Integer>();
// Add all the values below a specified time threshold
for (int i = 0; i < s.size(); i++) {
if (s.get(i, 0) < lowTimeThresh) {
smallValues.add(new Integer(i));
// System.out.println("Added:\t" + i + "\ttime:" + s.get(i, 0));
}
}
for (int i = 0; i < smallValues.size(); i++) {
// System.out.println("================\nNote:\t" +
// smallValues.get(i));
// Go through the musicShape to find the next note that has a value
// over the threshold
for (int j = smallValues.get(i); j < s.size(); j++) {
double tempTime = s.get(j, 0);
// System.out.println("\tchecked time:\t"+tempTime);
if (tempTime >= highTimeThresh) {
// System.out.println("\tItem works:\t" + j);
s.sumSet(s.get(smallValues.get(i), 0), j, 0);
s.set(0., smallValues.get(i), 0);
// break out of the loop
j = s.size();
}
}
}
return (MusicShape) s.clone();
}
public static MusicShape evenOutShortNotes(MusicShape s, double
minTimeThresh,
double maxTimeThresh, double maxDuration, double adjustmentFactor) {
// Make sure adjustment factor is in a safe range
adjustmentFactor = Math.min(Math.max(adjustmentFactor, 0.), 1.);
Vector<Integer> inRange = new Vector<Integer>();
for (int i = 0; i < s.size(); i++) {
double onset = s.get(i, 0);
double dur = s.get(i, 3);
if (onset >= minTimeThresh && onset <= maxTimeThresh && dur <=
maxDuration) {
inRange.add(i);
}
}
Vector regions = findContiguousRegions(inRange, false);
int regionCount = 0;
// System.out.print("\n=============REGIONS:============");
for (Iterator<Vector<Integer>> f = regions.iterator(); f.hasNext();) {
Vector<Integer> vec = f.next();
// System.out.print("\nRegion " + regionCount + ":\t");
for (Iterator<Integer> g = vec.iterator(); g.hasNext();) {
Integer i = g.next();
// System.out.print(i + " ");
}
regionCount++;
}
double meanOnset = 0.;
for (int i = 0; i < regions.size(); i++) {
Vector vec = (Vector<Integer>) regions.get(i);
double onsetSum = 0;
for (Iterator f = vec.iterator(); f.hasNext();) {
// Add the values of the onset times
onsetSum += s.get(((Integer) f.next()).intValue(), 0);
}
// Store the mean value
meanOnset = onsetSum / vec.size();
for (Iterator f = vec.iterator(); f.hasNext();) {
Integer index = (Integer) f.next();
double oldValue = s.get(index.intValue(), 0);
double newValue =
(adjustmentFactor * meanOnset) +
((1 - adjustmentFactor) * s.get(index.intValue(), 0));
// System.out.println("Old Value: "+oldValue+"\tNew Value:
// "+newValue);
s.set(newValue, index.intValue(), 0);
}
}
return s;
}
public static Vector<Vector<Integer>> findContiguousRegions
(Vector<Integer> inVec,
boolean includeSingletons) {
Collections.sort(inVec);
Vector<Integer> uniqueSorted = new Vector<Integer>();
// System.out.print("Elements:\t");
for (Integer element : inVec) {
Integer i = (Integer) element;
if (!uniqueSorted.contains(i)) {
uniqueSorted.add(i);
// System.out.print(i + " ");
}
}
Vector<Integer> startPoints = new Vector<Integer>();
Vector<Integer> endPoints = new Vector<Integer>();
// System.out.println("\n====x========POINTS:============");
// Add beginning point
startPoints.add(uniqueSorted.get(0));
// Add points in middle
for (int i = 0; i < uniqueSorted.size(); i++) {
// If the difference between the previous value and this one is not
// 1,
// mark it as a point
if (i < uniqueSorted.size() - 1) {
int diff = uniqueSorted.get(i + 1).intValue() - uniqueSorted.get
(i).intValue();
if (diff != 1) {
endPoints.add(uniqueSorted.get(i));
}
}
if (i > 0) {
int prevDiff = uniqueSorted.get(i).intValue() - uniqueSorted.get
(i - 1).intValue();
if (prevDiff != 1) {
if (!startPoints.contains(uniqueSorted.get(i))) {
startPoints.add(uniqueSorted.get(i));
}
}
}
}
if (!endPoints.contains(uniqueSorted.get(uniqueSorted.size() - 1))) {
endPoints.add(uniqueSorted.get(uniqueSorted.size() - 1));
}
// System.out.print("StartPoints:\t");
for (Object element : startPoints) {
Integer integer = (Integer) element;
// System.out.print(integer + "\t");
}
// System.out.print("\nEndPoints:\t");
for (Object element : endPoints) {
Integer integer = (Integer) element;
// System.out.print(integer + "\t");
}
Vector<Vector<Integer>> v = new Vector<Vector<Integer>>();
if (startPoints.size() != endPoints.size()) {
return null;
} else {
for (int i = 0; i < startPoints.size(); i++) {
Vector<Integer> subVec = new Vector<Integer>();
Integer start = startPoints.get(i);
Integer end = endPoints.get(i);
if ((start.compareTo(end) != 0)) {
for (int j = start.intValue(); j <= end.intValue(); j++) {
subVec.add(j);
}
v.add(subVec);
} else {
if (includeSingletons) {
subVec.add(start);
v.add(subVec);
}
}
}
}
return v;
}
}
More information about the jmsl
mailing list