samer@0
|
1 /*
|
samer@0
|
2 * Copyright (c) 2000, Samer Abdallah, King's College London.
|
samer@0
|
3 * All rights reserved.
|
samer@0
|
4 *
|
samer@0
|
5 * This software is provided AS iS and WITHOUT ANY WARRANTY;
|
samer@0
|
6 * without even the implied warranty of MERCHANTABILITY or
|
samer@0
|
7 * FITNESS FOR A PARTICULAR PURPOSE.
|
samer@0
|
8 */
|
samer@0
|
9
|
samer@0
|
10 package samer.midi;
|
samer@0
|
11 import samer.core.*;
|
samer@0
|
12 import samer.core.types.*;
|
samer@0
|
13 import samer.core.util.*;
|
samer@0
|
14 import samer.tools.*;
|
samer@0
|
15 import samer.maths.*;
|
samer@0
|
16 import java.util.*;
|
samer@0
|
17 import java.io.*;
|
samer@0
|
18 import javax.sound.midi.*;
|
samer@0
|
19
|
samer@0
|
20
|
samer@0
|
21 /**
|
samer@0
|
22 A class which generates midi events from the
|
samer@0
|
23 values of an input vector. Each element of the vector
|
samer@0
|
24 corresponds to a certain midi note number. The loudness
|
samer@0
|
25 of a note depends on the value of the corresponding
|
samer@0
|
26 vector element.
|
samer@0
|
27
|
samer@0
|
28 Parameters:
|
samer@0
|
29 trigger note on events.
|
samer@0
|
30
|
samer@0
|
31 offset is added to the vector element number to get a
|
samer@0
|
32 midi note number.
|
samer@0
|
33
|
samer@0
|
34 pedal control whether or not note off events are generated
|
samer@0
|
35 */
|
samer@0
|
36
|
samer@0
|
37 public class MidiSynth extends Viewable implements Agent, Task, Saveable
|
samer@0
|
38 {
|
samer@2
|
39 VVector in;
|
samer@2
|
40 Synthesizer synthesizer=null;
|
samer@2
|
41
|
samer@0
|
42 protected int n;
|
samer@0
|
43 protected double [] x;
|
samer@0
|
44 protected int [] nnums;
|
samer@0
|
45 protected int [] chans;
|
samer@0
|
46 protected MidiChannel [] cc;
|
samer@0
|
47
|
samer@0
|
48 protected VDouble factor;
|
samer@0
|
49 protected VInteger offset;
|
samer@0
|
50 protected VBoolean pedal;
|
samer@0
|
51
|
samer@2
|
52 public MidiSynth(VVector input)
|
samer@0
|
53 {
|
samer@0
|
54 super("midisynth");
|
samer@0
|
55
|
samer@0
|
56 Shell.push(node);
|
samer@0
|
57 factor = new VDouble("factor",128,0);
|
samer@0
|
58 offset = new VInteger("offset",0,0);
|
samer@0
|
59 pedal = new VBoolean("pedal",false,0);
|
samer@0
|
60 setAgent(this);
|
samer@0
|
61 addAgent(new Saver(this));
|
samer@0
|
62 Shell.pop();
|
samer@0
|
63
|
samer@2
|
64 in = input;
|
samer@2
|
65 n = in.size();
|
samer@2
|
66 x = in.array();
|
samer@0
|
67
|
samer@0
|
68 nnums = new int[n];
|
samer@0
|
69 chans = new int[n];
|
samer@0
|
70 for (int i=0; i<n; i++) { nnums[i]=i; chans[i]=0; }
|
samer@0
|
71
|
samer@0
|
72 try { getAgent().execute("load", Shell.env()); open(); }
|
samer@0
|
73 catch (Exception ex) { ex.printStackTrace(); }
|
samer@0
|
74 }
|
samer@0
|
75
|
samer@0
|
76 public Viewer getViewer() {
|
samer@0
|
77 return new BaseViewer(this) {
|
samer@0
|
78 {
|
samer@0
|
79 panel().setLayout( new StackLayout(3));
|
samer@0
|
80 add(factor);
|
samer@0
|
81 add(offset);
|
samer@0
|
82 add(pedal);
|
samer@0
|
83 add(Shell.createButtonsFor(MidiSynth.this));
|
samer@0
|
84 }
|
samer@0
|
85 };
|
samer@0
|
86 }
|
samer@0
|
87
|
samer@2
|
88 public void open() throws Exception {
|
samer@2
|
89 if (synthesizer!=null) { Shell.print("already open"); return; }
|
samer@2
|
90 if ((synthesizer = MidiSystem.getSynthesizer()) == null) {
|
samer@2
|
91 throw new Exception("getSynthesizer() failed");
|
samer@2
|
92 }
|
samer@2
|
93 Shell.print("opening synthesizer");
|
samer@2
|
94 synthesizer.open();
|
samer@2
|
95
|
samer@2
|
96 cc = synthesizer.getChannels();
|
samer@2
|
97 Shell.print("got synth and channel:"+synthesizer+cc);
|
samer@2
|
98 }
|
samer@2
|
99
|
samer@2
|
100 public void close() {
|
samer@2
|
101 if (synthesizer != null) {
|
samer@2
|
102 Shell.print("closing synthesiser");
|
samer@2
|
103 synthesizer.close();
|
samer@2
|
104 synthesizer = null;
|
samer@2
|
105 }
|
samer@2
|
106 }
|
samer@2
|
107
|
samer@0
|
108 public void dispose() {
|
samer@0
|
109 close();
|
samer@0
|
110 factor.dispose();
|
samer@0
|
111 offset.dispose();
|
samer@0
|
112 pedal.dispose();
|
samer@0
|
113 super.dispose();
|
samer@0
|
114 }
|
samer@0
|
115
|
samer@0
|
116 public void silence() {
|
samer@0
|
117 for (int i=0; i<n; i++) {
|
samer@0
|
118 cc[chans[i]].noteOff(nnums[i]+offset.value, 0);
|
samer@0
|
119 }
|
samer@0
|
120 }
|
samer@0
|
121
|
samer@0
|
122 public void starting() {}
|
samer@0
|
123 public void stopping() {}
|
samer@0
|
124 public void run()
|
samer@0
|
125 {
|
samer@2
|
126 if (synthesizer==null) return;
|
samer@2
|
127
|
samer@0
|
128 for (int i=0; i<n; i++) {
|
samer@0
|
129 if (x[i]>0) {
|
samer@0
|
130 cc[chans[i]].noteOn(nnums[i]+offset.value,mapVelocity(x[i]));
|
samer@0
|
131 } else if (x[i]<0 && !pedal.value) {
|
samer@0
|
132 cc[chans[i]].noteOff(nnums[i]+offset.value, 0);
|
samer@0
|
133 }
|
samer@0
|
134 }
|
samer@0
|
135 }
|
samer@0
|
136
|
samer@0
|
137 public void getCommands(Agent.Registry r) {
|
samer@0
|
138 r.add("open").add("close").add("silence").add("edit");
|
samer@0
|
139 }
|
samer@0
|
140
|
samer@0
|
141 public void execute(String cmd, Environment env) throws Exception
|
samer@0
|
142 {
|
samer@0
|
143 if (cmd.equals("open")) open();
|
samer@0
|
144 else if (cmd.equals("close")) close();
|
samer@0
|
145 else if (cmd.equals("silence")) silence();
|
samer@0
|
146 else if (cmd.equals("edit"))
|
samer@0
|
147 Shell.expose( new Editor(),"MidiSynthEditor");
|
samer@0
|
148 }
|
samer@0
|
149
|
samer@0
|
150 public void save(OutputStream s) throws Exception {
|
samer@0
|
151 ObjectOutputStream o=new ObjectOutputStream(s);
|
samer@0
|
152 o.writeObject(nnums);
|
samer@0
|
153 o.writeObject(chans);
|
samer@0
|
154 o.flush();
|
samer@0
|
155 }
|
samer@0
|
156
|
samer@0
|
157 public void load(InputStream s) throws Exception {
|
samer@0
|
158 ObjectInputStream o=new ObjectInputStream(s);
|
samer@0
|
159 int [] newnotes = (int [])o.readObject();
|
samer@0
|
160 int [] newchans = (int [])o.readObject();
|
samer@0
|
161 safecopy( newnotes, nnums);
|
samer@0
|
162 safecopy( newchans,chans);
|
samer@0
|
163 }
|
samer@0
|
164
|
samer@0
|
165 public void read(Reader r) throws Exception { throw new Exception("unsupported"); }
|
samer@0
|
166 public void write(Writer w) throws Exception { throw new Exception("unsupported"); }
|
samer@0
|
167
|
samer@0
|
168 private static void safecopy( int [] src, int [] dest) {
|
samer@0
|
169 int n=src.length; if (dest.length<n) n=dest.length;
|
samer@0
|
170 System.arraycopy( src, 0, dest, 0, n);
|
samer@0
|
171 }
|
samer@0
|
172
|
samer@0
|
173 public int mapVelocity(double x) {
|
samer@0
|
174 int vel=(int)(factor.value*x);
|
samer@0
|
175 if (vel>127) vel=127;
|
samer@0
|
176 return vel;
|
samer@0
|
177 }
|
samer@0
|
178
|
samer@0
|
179 class Editor extends BaseViewer
|
samer@0
|
180 {
|
samer@0
|
181 VInteger i=new VInteger("input number",0,Variable.NOINIT);
|
samer@0
|
182 VInteger n=new VInteger("note number",nnums[0],Variable.NOINIT);
|
samer@0
|
183 VInteger c=new VInteger("channel number",chans[0],Variable.NOINIT);
|
samer@0
|
184 VInteger p=new VInteger("program",0,0);
|
samer@0
|
185 VBoolean l1=new VBoolean("lock note",false,Variable.NOINIT);
|
samer@0
|
186 VBoolean l2=new VBoolean("lock channel",false,Variable.NOINIT);
|
samer@0
|
187
|
samer@0
|
188 public Editor() {
|
samer@0
|
189 super(MidiSynth.this);
|
samer@0
|
190
|
samer@0
|
191 i.setRange(0,nnums.length-1);
|
samer@0
|
192 n.setRange(0,127);
|
samer@0
|
193 c.setRange(0,15);
|
samer@0
|
194 n.setRange(0,127);
|
samer@0
|
195 p.setRange(0,127);
|
samer@0
|
196 setLayout( new StackLayout());
|
samer@0
|
197 add(i); add(n); add(c);
|
samer@0
|
198 add(p); add(l1); add(l2);
|
samer@0
|
199 i.addObserver(this);
|
samer@0
|
200 n.addObserver(this);
|
samer@0
|
201 c.addObserver(this);
|
samer@0
|
202 p.addObserver(this);
|
samer@0
|
203 }
|
samer@0
|
204
|
samer@0
|
205 public void update(Observable o, Object arg)
|
samer@0
|
206 {
|
samer@0
|
207 disposeFilter(o,arg);
|
samer@0
|
208 if (arg==this) return; // &&&
|
samer@0
|
209 if (o==i) {
|
samer@0
|
210 if (l1.value) { nnums[i.value]=n.value; }
|
samer@0
|
211 else { n.value = nnums[i.value]; n.changed(this); }
|
samer@0
|
212
|
samer@0
|
213 if (l2.value) { chans[i.value]=c.value; }
|
samer@0
|
214 else { c.value = chans[i.value]; c.changed(this); }
|
samer@0
|
215
|
samer@0
|
216 p.value = cc[c.value].getProgram();
|
samer@0
|
217 p.changed(this);
|
samer@0
|
218 } else if (o==n) {
|
samer@0
|
219 nnums[i.value]=n.value;
|
samer@0
|
220 } else if (o==c) {
|
samer@0
|
221 chans[i.value]=c.value;
|
samer@0
|
222 } else if (o==p) {
|
samer@0
|
223 cc[c.value].programChange(p.value);
|
samer@0
|
224 }
|
samer@0
|
225 }
|
samer@0
|
226 }
|
samer@0
|
227 }
|