[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