samer@0: /* samer@0: * Copyright (c) 2000, Samer Abdallah, King's College London. samer@0: * All rights reserved. samer@0: * samer@0: * This software is provided AS iS and WITHOUT ANY WARRANTY; samer@0: * without even the implied warranty of MERCHANTABILITY or samer@0: * FITNESS FOR A PARTICULAR PURPOSE. samer@0: */ samer@0: samer@0: package samer.midi; samer@0: import samer.core.*; samer@0: import samer.core.types.*; samer@0: import samer.core.util.*; samer@0: import samer.tools.*; samer@0: import samer.maths.*; samer@0: import java.util.*; samer@0: import java.io.*; samer@0: import javax.sound.midi.*; samer@0: samer@0: samer@0: /** samer@0: A class which generates midi events from the samer@0: values of an input vector. Each element of the vector samer@0: corresponds to a certain midi note number. The loudness samer@0: of a note depends on the value of the corresponding samer@0: vector element. samer@0: samer@0: Parameters: samer@0: trigger note on events. samer@0: samer@0: offset is added to the vector element number to get a samer@0: midi note number. samer@0: samer@0: pedal control whether or not note off events are generated samer@0: */ samer@0: samer@0: public class MidiSynth extends Viewable implements Agent, Task, Saveable samer@0: { samer@2: VVector in; samer@2: Synthesizer synthesizer=null; samer@2: samer@0: protected int n; samer@0: protected double [] x; samer@0: protected int [] nnums; samer@0: protected int [] chans; samer@0: protected MidiChannel [] cc; samer@0: samer@0: protected VDouble factor; samer@0: protected VInteger offset; samer@0: protected VBoolean pedal; samer@0: samer@2: public MidiSynth(VVector input) samer@0: { samer@0: super("midisynth"); samer@0: samer@0: Shell.push(node); samer@0: factor = new VDouble("factor",128,0); samer@0: offset = new VInteger("offset",0,0); samer@0: pedal = new VBoolean("pedal",false,0); samer@0: setAgent(this); samer@0: addAgent(new Saver(this)); samer@0: Shell.pop(); samer@0: samer@2: in = input; samer@2: n = in.size(); samer@2: x = in.array(); samer@0: samer@0: nnums = new int[n]; samer@0: chans = new int[n]; samer@0: for (int i=0; i0) { samer@0: cc[chans[i]].noteOn(nnums[i]+offset.value,mapVelocity(x[i])); samer@0: } else if (x[i]<0 && !pedal.value) { samer@0: cc[chans[i]].noteOff(nnums[i]+offset.value, 0); samer@0: } samer@0: } samer@0: } samer@0: samer@0: public void getCommands(Agent.Registry r) { samer@0: r.add("open").add("close").add("silence").add("edit"); samer@0: } samer@0: samer@0: public void execute(String cmd, Environment env) throws Exception samer@0: { samer@0: if (cmd.equals("open")) open(); samer@0: else if (cmd.equals("close")) close(); samer@0: else if (cmd.equals("silence")) silence(); samer@0: else if (cmd.equals("edit")) samer@0: Shell.expose( new Editor(),"MidiSynthEditor"); samer@0: } samer@0: samer@0: public void save(OutputStream s) throws Exception { samer@0: ObjectOutputStream o=new ObjectOutputStream(s); samer@0: o.writeObject(nnums); samer@0: o.writeObject(chans); samer@0: o.flush(); samer@0: } samer@0: samer@0: public void load(InputStream s) throws Exception { samer@0: ObjectInputStream o=new ObjectInputStream(s); samer@0: int [] newnotes = (int [])o.readObject(); samer@0: int [] newchans = (int [])o.readObject(); samer@0: safecopy( newnotes, nnums); samer@0: safecopy( newchans,chans); samer@0: } samer@0: samer@0: public void read(Reader r) throws Exception { throw new Exception("unsupported"); } samer@0: public void write(Writer w) throws Exception { throw new Exception("unsupported"); } samer@0: samer@0: private static void safecopy( int [] src, int [] dest) { samer@0: int n=src.length; if (dest.length127) vel=127; samer@0: return vel; samer@0: } samer@0: samer@0: class Editor extends BaseViewer samer@0: { samer@0: VInteger i=new VInteger("input number",0,Variable.NOINIT); samer@0: VInteger n=new VInteger("note number",nnums[0],Variable.NOINIT); samer@0: VInteger c=new VInteger("channel number",chans[0],Variable.NOINIT); samer@0: VInteger p=new VInteger("program",0,0); samer@0: VBoolean l1=new VBoolean("lock note",false,Variable.NOINIT); samer@0: VBoolean l2=new VBoolean("lock channel",false,Variable.NOINIT); samer@0: samer@0: public Editor() { samer@0: super(MidiSynth.this); samer@0: samer@0: i.setRange(0,nnums.length-1); samer@0: n.setRange(0,127); samer@0: c.setRange(0,15); samer@0: n.setRange(0,127); samer@0: p.setRange(0,127); samer@0: setLayout( new StackLayout()); samer@0: add(i); add(n); add(c); samer@0: add(p); add(l1); add(l2); samer@0: i.addObserver(this); samer@0: n.addObserver(this); samer@0: c.addObserver(this); samer@0: p.addObserver(this); samer@0: } samer@0: samer@0: public void update(Observable o, Object arg) samer@0: { samer@0: disposeFilter(o,arg); samer@0: if (arg==this) return; // &&& samer@0: if (o==i) { samer@0: if (l1.value) { nnums[i.value]=n.value; } samer@0: else { n.value = nnums[i.value]; n.changed(this); } samer@0: samer@0: if (l2.value) { chans[i.value]=c.value; } samer@0: else { c.value = chans[i.value]; c.changed(this); } samer@0: samer@0: p.value = cc[c.value].getProgram(); samer@0: p.changed(this); samer@0: } else if (o==n) { samer@0: nnums[i.value]=n.value; samer@0: } else if (o==c) { samer@0: chans[i.value]=c.value; samer@0: } else if (o==p) { samer@0: cc[c.value].programChange(p.value); samer@0: } samer@0: } samer@0: } samer@0: }