JMSL Tutorial: JScore
Implementing your own Unary Copy Buffer Transform, part 2


Converge To Mean Transform

Here is another example of a UnaryCopyBufferTransform. The previous tutorial reversed the order of the Notes in the buffer. Here, we will change the pitches of the Notes in the buffer.

IMPORTANT: Any Note's pitch can be changed algorithmically with note.setPitchData(double newPitch), but you must make a call to NoteFactory.updateFromPitch(note) to ensure that the new pitch data is properly reflected by the Note's graphic properties.
Similarly, any note's duration can be changed algorithmically with note.setDurationData(double newDur), followed by a call to NoteFactory.updateFromDur(note)

The following transform, suggested by Phil Burk, converges the pitches of a melody to the original melody's mean pitch. Two passes through the copy buffer are required: one to calculate the mean pitch, and a second one to change the pitch of each note and draw it closer to the mean. So the first note of the melody will be unchanged, the last will equal the mean pitch, and all notes in between will converge closer and closer to the mean.

package jmsltutorial;
import com.softsynth.jmsl.score.*;
import com.softsynth.jmsl.util.*;
import java.util.*;

public class ConvergeToMeanTransform extends UnaryCopyBufferTransform {
        public ConvergeToMeanTransform() {
                setName("Converge To Mean");
public void operate(CopyBuffer copyBuffer) {
        // First calculate the mean pitch
        double pitchSum=0;
        double pitchCount=0;
        for (Enumeration e=copyBuffer.elements(); e.hasMoreElements(); ) {
                Note note = (Note)e.nextElement();
                if (!note.isRest()) {
                        pitchSum += note.getPitchData();
        double mean = pitchSum / pitchCount;
        // Now make an interpolator to provide a weighting for every note, starts at 1, down to 0
        LinearInterpolator weightInterpolator = new LinearInterpolator(0, 1.0, pitchCount, 0);
        // now make a second pass, and scale each note according to its position in the copy buffer,:
        // early minimally, late maximally
        int pitchPosition = 0;
        for (Enumeration e=copyBuffer.elements(); e.hasMoreElements(); ) {
                Note note = (Note)e.nextElement();
                if (!note.isRest()) {
                        double weight = weightInterpolator.interp(pitchPosition);
                        double newPitch = weight * note.getPitchData() + (1-weight) * mean;

Adding your custom transform to ScoreFrame's menu

You may add a custom Transform to a scoreFrame easily with a call to public void addUnaryCopyBufferTransform(UnaryCopyBufferTransform transform). Consult the documentation for other versions of this method which allow you to add to your own submenus, and add shortcut keys.
scoreFrame.addUnaryCopyBufferTransform(new ConvergeToMeanTransform());

NOTE: As of JMSL v1.02 there is an even easier way to add transforms.  Compile them and drop the package-named folder containing the .class file into the jmsl_plugins directory.  For more about JMSL plugins, see JMSL Plugins Tutorial

Try it!

This applet fills two measures with a wide interval random melody. Copy the melody, and select Converge To Mean transform from the Transforms Menu (which was added to the menu in the applet's init() method). Then paste and observe the tapering of the melody to the mean pitch at the end.

Note: If you cannot easily draw a rectangle around the melody without missing one or two of the Notes, click once on the first Note, then Shift-Click on the last, which will select the entire range.


View the applet source and the transform source.

You need Java

Previous Tutorial Index Tutorial Contents Next

  (C) 2000 Nick Didkovsky and Phil Burk, All Rights Reserved
  JMSL is based upon HMSL (C) Phil Burk, Larry Polansky and David Rosenboom.