[jmsl] QuarterTone MidiScoreInstrument save problem
jmsl at music.columbia.edu
jmsl at music.columbia.edu
Mon Jan 7 23:03:49 EST 2008
Hi Peter
What an excellent problem. If I ever write a JMSL textbook, this would
be in it as a highly motivating example of how to design an Instrument
that saves and loads properly from a Score file.
Here's some principles that should be followed when designing an
instrument that can be saved and loaded to/from a JMSL Score file:
1) provide a no-args constructor. This will be called when the
instrument is first encountered in the score when it is loaded. You were
getting an instantiation exception because JMSL was trying to make a new
instrument but could not find a no-args constructor.
2) do not open() the music device in any instrument methods. The
instrument should provide its MusicDevice to the score loader with
getMusicDevice(). The loader will check if the device is open, and if it
is not, it will open it.
3) do nothing in the constructor that assumes the device is open. Do
anything that depends on the device being open (like building a JSyn
SynthNote for example), in buildFromAttributes (see next point)
4) The instrument should implement AttributeBuildable, and do everything
that is device-specific in buildFromAttributes(). This method is called
by the score loader after the no-args constructor fires, after the
musicdevice is opened, and after all the instrument's field values are
are set with the primitive values found in the score (for example midi
channel, program, etc). So the device is opened, all its attributes are
set, it is ready for final building in this method.
When you create one of these instruments in main(), a good way to test
it is:
1) make a new one with the no-args constructor
2) set its values (in your case set Midi channel and program)
3) request its MusicDevice and edit() it and open() it
4) call buildFromAttributes()
If it functions OK after this, then it will load() ok from a score.
Here are the relevant mods, pasted below
Thanks
Nick Didkovsky
PS since you are extending MidiScoreInstrument, you only need one
additional MidiInstrument class variable (not two) since this() could
play one voice and ins1 could play the other.
public class QuarterToneTwoChannelMidiInstrument extends
MidiScoreInstrument implements AttributeBuildable {
private static final MidiIO MIDI = JMSL.midi;
MidiInstrument ins1, ins2;
public QuarterToneTwoChannelMidiInstrument() {
this(1);
}
public QuarterToneTwoChannelMidiInstrument(int channel1) {
super(channel1);
ins1 = new MidiInstrument(channel1);
ins2 = new MidiInstrument(channel1 + 1);
}
/**
*
*/
public void buildFromAttributes() {
System.out.println("Got here, buildFromAttributes()");
super.buildFromAttributes();
MIDI.bendPitch(getChannel(), MidiIO.PITCH_BEND_CENTER);
MIDI.bendPitch(getChannel() + 1, MidiIO.PITCH_BEND_CENTER + 2048);
System.out.println("Pitch Bend " + MIDI.PITCH_BEND + "\t\tPitch
Bend Center" + MIDI.PITCH_BEND_CENTER);
}
// ETC ETC ETC
// then in main()...
QuarterToneTwoChannelMidiInstrument dc = new
QuarterToneTwoChannelMidiInstrument();
dc.setChannel(1);
dc.getMusicDevice().open();
dc.buildFromAttributes();
//...
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
JMSL.closeMusicDevices(); // be nice!
System.exit(0);
}
});
jmsl at music.columbia.edu wrote:
> Here's a MidiScoreInstrument that can play quarter-tones. It does it
> by dividing the input into two channels instead of one. The second
> channel has a pitchbend set to a quarter-tone.
>
> The only problem is that I can't save and load it. It's probably
> something minor with the path, but I can't quite sort it out, and if
> anyone can give me a nudge in the right direction, I'd greatly
> appreciate it.
>
> Also, I may have gone overboard with some of the getters and setters;
> any tips, Nick?
>
> Thanks,
> Peter McCulloch
>
>
> Here's the error I get:
>
> Save instrument
> java.lang.ClassNotFoundException:
> com/petermcculloch/megalo/QuarterToneTwoChannelMidiInstrument
> at java.lang.Class.forName0(Native Method)
> at java.lang.Class.forName(Class.java:164)
> at com.softsynth.jmsl.util.JMSLClassLoader.load(JMSLClassLoader.java)
> at
> com.softsynth.jmsl.util.BeanInspector.setClassName(BeanInspector.java)
> at com.softsynth.jmsl.util.SimpleXMLSaver.<init>(SimpleXMLSaver.java)
> at com.softsynth.jmsl.score.ScoreFrame.handleSaveIns(ScoreFrame.java)
> at
> com.softsynth.jmsl.score.ScoreFrame.actionPerformed(ScoreFrame.java)
> at java.awt.MenuItem.processActionEvent(MenuItem.java:597)
> at java.awt.MenuItem.processEvent(MenuItem.java:556)
> at java.awt.MenuComponent.dispatchEventImpl(MenuComponent.java:298)
> at java.awt.MenuComponent.dispatchEvent(MenuComponent.java:286)
> at java.awt.EventQueue.dispatchEvent(EventQueue.java:466)
> at
> java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:269)
>
> at
> java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190)
>
> at
> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:184)
> at
> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:176)
> at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)
> JMSLClassLoader, Class.forName(
> com.petermcculloch.megalo.QuarterToneTwoChannelMidiInstrument) failed,
> trying System classloader
>
>
> Code for QuarterToneTwoChannelMidiInstrument:
>
>
> package com.petermcculloch.megalo;
>
> import com.softsynth.jmsl.DimensionNameSpace;
> import com.softsynth.jmsl.Interpreter;
> import com.softsynth.jmsl.JMSL;
> import com.softsynth.jmsl.MusicDevice;
> import com.softsynth.jmsl.midi.MidiIO;
> import com.softsynth.jmsl.midi.MidiIO_MidiShare;
> import com.softsynth.jmsl.midi.MidiInstrument;
> import com.softsynth.jmsl.score.Note;
> import com.softsynth.jmsl.score.NoteFactory;
> import com.softsynth.jmsl.score.Score;
> import com.softsynth.jmsl.score.ScoreFrame;
> import com.softsynth.jmsl.score.midi.MidiScoreInstrument;
>
> public class QuarterToneTwoChannelMidiInstrument extends
> MidiScoreInstrument {
>
> private static final MidiIO MIDI = JMSL.midi;
> MidiInstrument ins1, ins2;
>
> public QuarterToneTwoChannelMidiInstrument(int channel1) {
> MusicDevice dev = MidiIO_MidiShare.instance();
> dev.open();
> System.out.println("Got here");
> ins1 = new MidiInstrument(channel1);
> ins2 = new MidiInstrument(channel1 + 1);
> MIDI.bendPitch(channel1, MidiIO.PITCH_BEND_CENTER);
> MIDI.bendPitch(channel1 + 1, MidiIO.PITCH_BEND_CENTER + 2048);
> System.out.println("Pitch Bend " + MIDI.PITCH_BEND +
> "\t\tPitch Bend Center"
> + MIDI.PITCH_BEND_CENTER);
> }
>
> @Override
> public String getName() {
> return "Quarter-Tone MIDI Instrument (2 Ch.)";
> }
>
> @Override
> public double play(double playtime, double timestretch, double[]
> dim) {
> double pitch = dim[1];
> double roundedPitch = Math.round(pitch * 2) * 0.5;
> double[] scaledVelDim = dim;
> scaledVelDim[2] = dim[2] * 127.;
>
> if (roundedPitch - Math.floor(roundedPitch) == 0) {
> return ins1.play(playtime, timestretch, scaledVelDim);
> } else {
> return ins2.play(playtime, timestretch, scaledVelDim);
> }
> }
>
> @Override
> public int getProgram() {
> return ins1.getProgram();
> }
>
> @Override
> public void setProgram(int program) {
> ins1.setProgram(program);
> ins2.setProgram(program);
> }
>
> @Override
> public Interpreter getInterpreter() {
> return ins1.getInterpreter();
> }
>
> @Override
> public void setInterpreter(Interpreter interpreter) {
> if ((ins1 != null) && (ins2 != null)) {
> ins1.setInterpreter(interpreter);
> ins2.setInterpreter(interpreter);
> }
> super.setInterpreter(interpreter);
> }
>
> @Override
> public DimensionNameSpace getDimensionNameSpace() {
> return ins1.getDimensionNameSpace();
> }
>
> @Override
> public void setDimensionNameSpace(DimensionNameSpace dns) {
> if ((ins1 != null) && (ins2 != null)) {
> ins1.setDimensionNameSpace(dns);
> ins2.setDimensionNameSpace(dns);
> }
> super.setDimensionNameSpace(dns);
> }
>
> @Override
> public double update(double playtime, double timestretch, double[]
> dim) {
> double pitch = dim[1];
> double roundedPitch = Math.round(pitch * 2) * 0.5;
> double[] scaledVelDim = dim;
> scaledVelDim[2] = dim[2] * 127.;
>
> if (roundedPitch - Math.floor(roundedPitch) == 0) {
> return ins1.update(playtime, timestretch, scaledVelDim);
> } else {
> return ins2.update(playtime, timestretch, scaledVelDim);
> }
> }
>
> public MidiInstrument getIns1() {
> return ins1;
> }
>
> public void setIns1(MidiInstrument ins1) {
> this.ins1 = ins1;
> }
>
> public MidiInstrument getIns2() {
> return ins2;
> }
>
> public void setIns2(MidiInstrument ins2) {
> this.ins2 = ins2;
> }
>
> @Override
> public double getTransposition() {
> return ins1.getTransposition();
> }
>
> @Override
> public void setTransposition(double transposition) {
> // TODO Auto-generated method stub
> ins1.setTransposition(transposition);
> ins2.setTransposition(transposition);
> }
>
> @Override
> public int getNumOutputs() {
> return 2;
> }
>
> @Override
> public int getChannel() {
> return ins1.getChannel();
> }
>
> @Override
> public void setChannel(int channel) {
> ins1.setChannel(channel);
> if (channel + 1 == 10) {
> ins2.setChannel(11);
> } else {
> ins2.setChannel(channel + 1);
> }
> }
>
> @Override
> public double close(double time) throws InterruptedException {
> ins1.close(time);
> return ins2.close(time);
> }
>
> @Override
> public double open(double time) throws InterruptedException {
> ins1.open(time);
> return ins2.open(time);
> }
>
> @Override
> public Object getOutput() {
> // TODO Auto-generated method stub
> return ins1.getOutput();
> }
>
> @Override
> public void setMusicDevice(MusicDevice arg0) {
> if ((ins1 != null) && (ins2 != null)) {
> ins1.setMusicDevice(arg0);
> ins2.setMusicDevice(arg0);
> }
> super.setMusicDevice(arg0);
> }
>
> @Override
> public void setMixerClassName(String mixerClassName) {
> if ((ins1 != null) && (ins2 != null)) {
> ins1.setMixerClassName(mixerClassName);
> ins2.setMixerClassName(mixerClassName);
> }
> super.setMixerClassName(mixerClassName);
> }
>
> public static void main(String args[]) {
> // plays a quarter-tone scale, and then a chord to demonstrate
> playback.
>
> ScoreFrame frame = new ScoreFrame();
>
> Score score = new Score(1);
> score.addMeasures(10, 3, 4);
>
> for (double x = 0.; x < 12.; x = x + 0.5) {
> // System.out.println(x + 60.);
> Note n = NoteFactory.makeNote(0.25, x + 60., 1, 0.25);
> score.addNote(n);
> if (x % 2. != 1.5) {
> n.setBeamedOut(true);
> }
> }
>
> for (int i = 0; i < 6; i++) {
> Note n = NoteFactory.makeNote(0.5, 60., 1., 0.5);
> score.addNote(n);
> n.addInterval(64.);
> n.addInterval(67);
> n.addInterval(72.5);
> if (i % 4 != 1) {
> n.setBeamedOut(true);
> }
>
> }
>
> frame.addScore(score);
>
> QuarterToneTwoChannelMidiInstrument dc = new
> QuarterToneTwoChannelMidiInstrument(1);
> frame.addInstrument(dc);
>
> frame.pack();
> frame.setVisible(true);
>
> }
>
> }
>
>
>
>
> _______________________________________________
> jmsl mailing list
> jmsl at music.columbia.edu
> http://music.columbia.edu/mailman/listinfo/jmsl
More information about the jmsl
mailing list