ivan@81
|
1 function [Notes,endtime] = midiInfo(midi,outputFormat,tracklist)
|
ivan@81
|
2 % [Notes,endtime] = midiInfo(midi,outputFormat,tracklist)
|
ivan@81
|
3 %
|
ivan@81
|
4 % Takes a midi structre and generates info on notes and messages
|
ivan@81
|
5 % Can return a matrix of note parameters and/or output/display
|
ivan@81
|
6 % formatted table of messages
|
ivan@81
|
7 %
|
ivan@81
|
8 % Inputs:
|
ivan@81
|
9 % midi - Matlab structure (created by readmidi.m)
|
ivan@81
|
10 % tracklist - which tracks to show ([] for all)
|
ivan@81
|
11 % outputFormat
|
ivan@81
|
12 % - if it's a string write the formated output to the file
|
ivan@81
|
13 % - if 0, don't display or write formatted output
|
ivan@81
|
14 % - if 1, just display (default)
|
ivan@81
|
15 %
|
ivan@81
|
16 % outputs:
|
ivan@81
|
17 % Notes - a matrix containing a list of notes, ordered by start time
|
ivan@81
|
18 % column values are:
|
ivan@81
|
19 % 1 2 3 4 5 6 7 8
|
ivan@81
|
20 % [track chan nn vel t1 t2 msgNum1 msgNum2]
|
ivan@81
|
21 % endtime - time of end of track message
|
ivan@81
|
22 %
|
ivan@81
|
23
|
ivan@81
|
24 % Copyright (c) 2009 Ken Schutte
|
ivan@81
|
25 % more info at: http://www.kenschutte.com/midi
|
ivan@81
|
26
|
ivan@81
|
27 if nargin<3
|
ivan@81
|
28 tracklist=[];
|
ivan@81
|
29 if nargin<2
|
ivan@81
|
30 outputFormat=1;
|
ivan@81
|
31 end
|
ivan@81
|
32 end
|
ivan@81
|
33 if (isempty(tracklist))
|
ivan@81
|
34 tracklist = 1:length(midi.track);
|
ivan@81
|
35 end
|
ivan@81
|
36
|
ivan@81
|
37 [tempos, tempos_time] = getTempoChanges(midi);
|
ivan@81
|
38
|
ivan@81
|
39 current_tempo = 500000; % default tempo
|
ivan@81
|
40
|
ivan@81
|
41 fid = -1;
|
ivan@81
|
42 if (ischar(outputFormat))
|
ivan@81
|
43 fid = fopen(outputFormat,'w');
|
ivan@81
|
44 end
|
ivan@81
|
45
|
ivan@81
|
46 endtime = -1;
|
ivan@81
|
47
|
ivan@81
|
48 % each row:
|
ivan@81
|
49 % 1 2 3 4 5 6 7 8
|
ivan@81
|
50 % [track chan nn vel t1 t2 msgNum1 msgNum2]
|
ivan@81
|
51 Notes = zeros(0,8);
|
ivan@81
|
52
|
ivan@81
|
53 for i=1:length(tracklist)
|
ivan@81
|
54 tracknum = tracklist(i);
|
ivan@81
|
55
|
ivan@81
|
56 cumtime=0;
|
ivan@81
|
57 seconds=0;
|
ivan@81
|
58
|
ivan@81
|
59 Msg = cell(0);
|
ivan@81
|
60 Msg{1,1} = 'chan';
|
ivan@81
|
61 Msg{1,2} = 'deltatime';
|
ivan@81
|
62 Msg{1,3} = 'time';
|
ivan@81
|
63 Msg{1,4} = 'name';
|
ivan@81
|
64 Msg{1,5} = 'data';
|
ivan@81
|
65
|
ivan@81
|
66 for msgNum=1:length(midi.track(tracknum).messages)
|
ivan@81
|
67
|
ivan@81
|
68 currMsg = midi.track(tracknum).messages(msgNum);
|
ivan@81
|
69
|
ivan@81
|
70 midimeta = currMsg.midimeta;
|
ivan@81
|
71 deltatime = currMsg.deltatime;
|
ivan@81
|
72 data = currMsg.data;
|
ivan@81
|
73 type = currMsg.type;
|
ivan@81
|
74 chan = currMsg.chan;
|
ivan@81
|
75
|
ivan@81
|
76 cumtime = cumtime + deltatime;
|
ivan@81
|
77 seconds = seconds + deltatime*1e-6*current_tempo/midi.ticks_per_quarter_note;
|
ivan@81
|
78
|
ivan@81
|
79 [mx ind] = max(find(cumtime >= tempos_time));
|
ivan@81
|
80 current_tempo = tempos(ind);
|
ivan@81
|
81
|
ivan@81
|
82 % find start/stop of notes:
|
ivan@81
|
83 % if (strcmp(name,'Note on') && (data(2)>0))
|
ivan@81
|
84 % note on with vel>0:
|
ivan@81
|
85 if (midimeta==1 && type==144 && data(2)>0)
|
ivan@81
|
86 % note on:
|
ivan@81
|
87 Notes(end+1,:) = [tracknum chan data(1) data(2) seconds 0 msgNum -1];
|
ivan@81
|
88 % elseif ((strcmp(name,'Note on') && (data(2)==0)) || strcmp(name,'Note off'))
|
ivan@81
|
89 % note on with vel==0 or note off:
|
ivan@81
|
90 elseif (midimeta==1 && ( (type==144 && data(2)==0) || type==128 ))
|
ivan@81
|
91
|
ivan@81
|
92 % note off:
|
ivan@81
|
93 % % find index, wther tr,chan,and nn match, and not complete
|
ivan@81
|
94
|
ivan@81
|
95 ind = find((...
|
ivan@81
|
96 (Notes(:,1)==tracknum) + ...
|
ivan@81
|
97 (Notes(:,2)==chan) + ...
|
ivan@81
|
98 (Notes(:,3)==data(1)) + ...
|
ivan@81
|
99 (Notes(:,8)==-1)...
|
ivan@81
|
100 )==4);
|
ivan@81
|
101
|
ivan@81
|
102 if (length(ind)==0)
|
ivan@81
|
103 error('ending non-open note?');
|
ivan@81
|
104 elseif (length(ind)>1)
|
ivan@81
|
105 %% ??? not sure about this...
|
ivan@81
|
106 %%disp('warning: found mulitple matches in endNote, taking first...');
|
ivan@81
|
107 ind = ind(1);
|
ivan@81
|
108 end
|
ivan@81
|
109
|
ivan@81
|
110 % set info on ending:
|
ivan@81
|
111 Notes(ind,6) = seconds;
|
ivan@81
|
112 Notes(ind,8) = msgNum;
|
ivan@81
|
113
|
ivan@81
|
114 % end of track:
|
ivan@81
|
115 elseif (midimeta==0 && type==47)
|
ivan@81
|
116 if (endtime == -1)
|
ivan@81
|
117 endtime = seconds;
|
ivan@81
|
118 else
|
ivan@81
|
119 disp('two "end of track" messages?');
|
ivan@81
|
120 endtime(end+1) = seconds;
|
ivan@81
|
121 end
|
ivan@81
|
122
|
ivan@81
|
123
|
ivan@81
|
124 end
|
ivan@81
|
125
|
ivan@81
|
126 % we could check to make sure it ends with
|
ivan@81
|
127 % 'end of track'
|
ivan@81
|
128
|
ivan@81
|
129
|
ivan@81
|
130 if (outputFormat ~= 0)
|
ivan@81
|
131 % get some specific descriptions:
|
ivan@81
|
132 name = num2str(type);
|
ivan@81
|
133 dataStr = num2str(data);
|
ivan@81
|
134
|
ivan@81
|
135 if (isempty(chan))
|
ivan@81
|
136 Msg{msgNum,1} = '-';
|
ivan@81
|
137 else
|
ivan@81
|
138 Msg{msgNum,1} = num2str(chan);
|
ivan@81
|
139 end
|
ivan@81
|
140
|
ivan@81
|
141 Msg{msgNum,2} = num2str(deltatime);
|
ivan@81
|
142 Msg{msgNum,3} = formatTime(seconds);
|
ivan@81
|
143
|
ivan@81
|
144 if (midimeta==0)
|
ivan@81
|
145 Msg{msgNum,4} = 'meta';
|
ivan@81
|
146 else
|
ivan@81
|
147 Msg{msgNum,4} = '';
|
ivan@81
|
148 end
|
ivan@81
|
149
|
ivan@81
|
150 [name,dataStr] = getMsgInfo(midimeta, type, data);
|
ivan@81
|
151 Msg{msgNum,5} = name;
|
ivan@81
|
152 Msg{msgNum,6} = dataStr;
|
ivan@81
|
153 end
|
ivan@81
|
154
|
ivan@81
|
155
|
ivan@81
|
156 end
|
ivan@81
|
157
|
ivan@81
|
158 if (outputFormat ~= 0)
|
ivan@81
|
159 printTrackInfo(Msg,tracknum,fid);
|
ivan@81
|
160 end
|
ivan@81
|
161
|
ivan@81
|
162 end
|
ivan@81
|
163
|
ivan@81
|
164 % make this an option!!!
|
ivan@81
|
165 % - I'm not sure why it's needed...
|
ivan@81
|
166 % remove start silence:
|
ivan@81
|
167 first_t = min(Notes(:,5));
|
ivan@81
|
168 Notes(:,5) = Notes(:,5) - first_t;
|
ivan@81
|
169 Notes(:,6) = Notes(:,6) - first_t;
|
ivan@81
|
170
|
ivan@81
|
171 % sort Notes by start time:
|
ivan@81
|
172 [junk,ord] = sort(Notes(:,5));
|
ivan@81
|
173 Notes = Notes(ord,:);
|
ivan@81
|
174
|
ivan@81
|
175
|
ivan@81
|
176 if (fid ~= -1)
|
ivan@81
|
177 fclose(fid);
|
ivan@81
|
178 end
|
ivan@81
|
179
|
ivan@81
|
180
|
ivan@81
|
181
|
ivan@81
|
182
|
ivan@81
|
183
|
ivan@81
|
184
|
ivan@81
|
185
|
ivan@81
|
186
|
ivan@81
|
187
|
ivan@81
|
188
|
ivan@81
|
189
|
ivan@81
|
190 function printTrackInfo(Msg,tracknum,fid)
|
ivan@81
|
191
|
ivan@81
|
192
|
ivan@81
|
193 % make cols same length instead of just using \t
|
ivan@81
|
194 for i=1:size(Msg,2)
|
ivan@81
|
195 maxLen(i)=0;
|
ivan@81
|
196 for j=1:size(Msg,1)
|
ivan@81
|
197 if (length(Msg{j,i})>maxLen(i))
|
ivan@81
|
198 maxLen(i) = length(Msg{j,i});
|
ivan@81
|
199 end
|
ivan@81
|
200 end
|
ivan@81
|
201 end
|
ivan@81
|
202
|
ivan@81
|
203
|
ivan@81
|
204 s='';
|
ivan@81
|
205 s=[s sprintf('--------------------------------------------------\n')];
|
ivan@81
|
206 s=[s sprintf('Track %d\n',tracknum)];
|
ivan@81
|
207 s=[s sprintf('--------------------------------------------------\n')];
|
ivan@81
|
208
|
ivan@81
|
209 if (fid == -1)
|
ivan@81
|
210 disp(s)
|
ivan@81
|
211 else
|
ivan@81
|
212 fprintf(fid,'%s',s);
|
ivan@81
|
213 end
|
ivan@81
|
214
|
ivan@81
|
215
|
ivan@81
|
216 for i=1:size(Msg,1)
|
ivan@81
|
217 line='';
|
ivan@81
|
218 for j=1:size(Msg,2)
|
ivan@81
|
219 sp = repmat(' ',1,5+maxLen(j)-length(Msg{i,j}));
|
ivan@81
|
220 m = Msg{i,j};
|
ivan@81
|
221 m = m(:)'; % ensure column vector
|
ivan@81
|
222 % line = [line Msg{i,j} sp];
|
ivan@81
|
223 line = [line m sp];
|
ivan@81
|
224 end
|
ivan@81
|
225
|
ivan@81
|
226 if (fid == -1)
|
ivan@81
|
227 disp(line)
|
ivan@81
|
228 else
|
ivan@81
|
229 fprintf(fid,'%s\n',line);
|
ivan@81
|
230 end
|
ivan@81
|
231
|
ivan@81
|
232 end
|
ivan@81
|
233
|
ivan@81
|
234
|
ivan@81
|
235
|
ivan@81
|
236 function s=formatTime(seconds)
|
ivan@81
|
237
|
ivan@81
|
238 minutes = floor(seconds/60);
|
ivan@81
|
239 secs = seconds - 60*minutes;
|
ivan@81
|
240
|
ivan@81
|
241 s = sprintf('%d:%2.3f',minutes,secs);
|
ivan@81
|
242
|
ivan@81
|
243
|
ivan@81
|
244
|
ivan@81
|
245 function [name,dataStr]=getMsgInfo(midimeta, type, data);
|
ivan@81
|
246
|
ivan@81
|
247 % meta events:
|
ivan@81
|
248 if (midimeta==0)
|
ivan@81
|
249 if (type==0); name = 'Sequence Number'; len=2; dataStr = num2str(data);
|
ivan@81
|
250 elseif (type==1); name = 'Text Events'; len=-1; dataStr = char(data);
|
ivan@81
|
251 elseif (type==2); name = 'Copyright Notice'; len=-1; dataStr = char(data);
|
ivan@81
|
252 elseif (type==3); name = 'Sequence/Track Name'; len=-1; dataStr = char(data);
|
ivan@81
|
253 elseif (type==4); name = 'Instrument Name'; len=-1; dataStr = char(data);
|
ivan@81
|
254 elseif (type==5); name = 'Lyric'; len=-1; dataStr = char(data);
|
ivan@81
|
255 elseif (type==6); name = 'Marker'; len=-1; dataStr = char(data);
|
ivan@81
|
256 elseif (type==7); name = 'Cue Point'; len=-1; dataStr = char(data);
|
ivan@81
|
257 elseif (type==32); name = 'MIDI Channel Prefix'; len=1; dataStr = num2str(data);
|
ivan@81
|
258 elseif (type==47); name = 'End of Track'; len=0; dataStr = '';
|
ivan@81
|
259 elseif (type==81); name = 'Set Tempo'; len=3;
|
ivan@81
|
260 val = data(1)*16^4+data(2)*16^2+data(3); dataStr = ['microsec per quarter note: ' num2str(val)];
|
ivan@81
|
261 elseif (type==84); name = 'SMPTE Offset'; len=5;
|
ivan@81
|
262 dataStr = ['[hh mm ss fr ff]=' num2str(data)];
|
ivan@81
|
263 elseif (type==88); name = 'Time Signature'; len=4;
|
ivan@81
|
264 dataStr = [num2str(data(1)) '/' num2str(data(2)) ', clock ticks and notated 32nd notes=' num2str(data(3)) '/' num2str(data(4))];
|
ivan@81
|
265 elseif (type==89); name = 'Key Signature'; len=2;
|
ivan@81
|
266 % num sharps/flats (flats negative)
|
ivan@81
|
267 if (data(1)>=0)
|
ivan@81
|
268 % 1 2 3 4 5 6 7
|
ivan@81
|
269 ss={'C','G','D', 'A', 'E','B', 'F#', 'C#'};
|
ivan@81
|
270 dataStr = ss{data(1)+1};
|
ivan@81
|
271 else
|
ivan@81
|
272 % 1 2 3 4 5 6 7
|
ivan@81
|
273 ss={'F','Bb','Eb','Ab','Db','Gb','Cb'};
|
ivan@81
|
274 dataStr = ss{abs(data(1))};
|
ivan@81
|
275 end
|
ivan@81
|
276 if (data(2)==0)
|
ivan@81
|
277 dataStr = [dataStr ' Major'];
|
ivan@81
|
278 else
|
ivan@81
|
279 dataStr = [dataStr ' Minor'];
|
ivan@81
|
280 end
|
ivan@81
|
281
|
ivan@81
|
282 elseif (type==89); name = 'Sequencer-Specific Meta-event'; len=-1;
|
ivan@81
|
283 dataStr = char(data);
|
ivan@81
|
284 % !! last two conflict...
|
ivan@81
|
285
|
ivan@81
|
286 else
|
ivan@81
|
287 name = ['UNKNOWN META EVENT: ' num2str(type)]; dataStr = num2str(data);
|
ivan@81
|
288 end
|
ivan@81
|
289
|
ivan@81
|
290 % meta 0x21 = MIDI port number, length 1 (? perhaps)
|
ivan@81
|
291 else
|
ivan@81
|
292
|
ivan@81
|
293 % channel voice messages:
|
ivan@81
|
294 % (from event byte with chan removed, eg 0x8n -> 0x80 = 128 for
|
ivan@81
|
295 % note off)
|
ivan@81
|
296 if (type==128); name = 'Note off'; len=2; dataStr = ['nn=' num2str(data(1)) ' vel=' num2str(data(2))];
|
ivan@81
|
297 elseif (type==144); name = 'Note on'; len=2; dataStr = ['nn=' num2str(data(1)) ' vel=' num2str(data(2))];
|
ivan@81
|
298 elseif (type==160); name = 'Polyphonic Key Pressure'; len=2; dataStr = ['nn=' num2str(data(1)) ' vel=' num2str(data(2))];
|
ivan@81
|
299 elseif (type==176); name = 'Controller Change'; len=2; dataStr = ['ctrl=' controllers(data(1)) ' value=' num2str(data(2))];
|
ivan@81
|
300 elseif (type==192); name = 'Program Change'; len=1; dataStr = ['instr=' num2str(data)];
|
ivan@81
|
301 elseif (type==208); name = 'Channel Key Pressure'; len=1; dataStr = ['vel=' num2str(data)];
|
ivan@81
|
302 elseif (type==224); name = 'Pitch Bend'; len=2;
|
ivan@81
|
303 val = data(1)+data(2)*256;
|
ivan@81
|
304 val = base2dec('2000',16) - val;
|
ivan@81
|
305 dataStr = ['change=' num2str(val) '?'];
|
ivan@81
|
306
|
ivan@81
|
307 % channel mode messages:
|
ivan@81
|
308 % ... unsure about data for these... (do some have a data byte and
|
ivan@81
|
309 % others not?)
|
ivan@81
|
310 %
|
ivan@81
|
311 % 0xC1 .. 0xC8
|
ivan@81
|
312 elseif (type==193); name = 'All Sounds Off'; dataStr = num2str(data);
|
ivan@81
|
313 elseif (type==194); name = 'Reset All Controllers'; dataStr = num2str(data);
|
ivan@81
|
314 elseif (type==195); name = 'Local Control'; dataStr = num2str(data);
|
ivan@81
|
315 elseif (type==196); name = 'All Notes Off'; dataStr = num2str(data);
|
ivan@81
|
316 elseif (type==197); name = 'Omni Mode Off'; dataStr = num2str(data);
|
ivan@81
|
317 elseif (type==198); name = 'Omni Mode On'; dataStr = num2str(data);
|
ivan@81
|
318 elseif (type==199); name = 'Mono Mode On'; dataStr = num2str(data);
|
ivan@81
|
319 elseif (type==200); name = 'Poly Mode On'; dataStr = num2str(data);
|
ivan@81
|
320
|
ivan@81
|
321 % sysex, F0->F7
|
ivan@81
|
322 elseif (type==240); name = 'Sysex 0xF0'; dataStr = num2str(data);
|
ivan@81
|
323 elseif (type==241); name = 'Sysex 0xF1'; dataStr = num2str(data);
|
ivan@81
|
324 elseif (type==242); name = 'Sysex 0xF2'; dataStr = num2str(data);
|
ivan@81
|
325 elseif (type==243); name = 'Sysex 0xF3'; dataStr = num2str(data);
|
ivan@81
|
326 elseif (type==244); name = 'Sysex 0xF4'; dataStr = num2str(data);
|
ivan@81
|
327 elseif (type==245); name = 'Sysex 0xF5'; dataStr = num2str(data);
|
ivan@81
|
328 elseif (type==246); name = 'Sysex 0xF6'; dataStr = num2str(data);
|
ivan@81
|
329 elseif (type==247); name = 'Sysex 0xF7'; dataStr = num2str(data);
|
ivan@81
|
330
|
ivan@81
|
331 % realtime
|
ivan@81
|
332 % (i think have no data..?)
|
ivan@81
|
333 elseif (type==248); name = 'Real-time 0xF8 - Timing clock'; dataStr = num2str(data);
|
ivan@81
|
334 elseif (type==249); name = 'Real-time 0xF9'; dataStr = num2str(data);
|
ivan@81
|
335 elseif (type==250); name = 'Real-time 0xFA - Start a sequence'; dataStr = num2str(data);
|
ivan@81
|
336 elseif (type==251); name = 'Real-time 0xFB - Continue a sequence'; dataStr = num2str(data);
|
ivan@81
|
337 elseif (type==252); name = 'Real-time 0xFC - Stop a sequence'; dataStr = num2str(data);
|
ivan@81
|
338 elseif (type==253); name = 'Real-time 0xFD'; dataStr = num2str(data);
|
ivan@81
|
339 elseif (type==254); name = 'Real-time 0xFE'; dataStr = num2str(data);
|
ivan@81
|
340 elseif (type==255); name = 'Real-time 0xFF'; dataStr = num2str(data);
|
ivan@81
|
341
|
ivan@81
|
342
|
ivan@81
|
343 else
|
ivan@81
|
344 name = ['UNKNOWN MIDI EVENT: ' num2str(type)]; dataStr = num2str(data);
|
ivan@81
|
345 end
|
ivan@81
|
346
|
ivan@81
|
347
|
ivan@81
|
348 end
|
ivan@81
|
349
|
ivan@81
|
350 function s=controllers(n)
|
ivan@81
|
351 if (n==1); s='Mod Wheel';
|
ivan@81
|
352 elseif (n==2); s='Breath Controllery';
|
ivan@81
|
353 elseif (n==4); s='Foot Controller';
|
ivan@81
|
354 elseif (n==5); s='Portamento Time';
|
ivan@81
|
355 elseif (n==6); s='Data Entry MSB';
|
ivan@81
|
356 elseif (n==7); s='Volume';
|
ivan@81
|
357 elseif (n==8); s='Balance';
|
ivan@81
|
358 elseif (n==10); s='Pan';
|
ivan@81
|
359 elseif (n==11); s='Expression Controller';
|
ivan@81
|
360 elseif (n==16); s='General Purpose 1';
|
ivan@81
|
361 elseif (n==17); s='General Purpose 2';
|
ivan@81
|
362 elseif (n==18); s='General Purpose 3';
|
ivan@81
|
363 elseif (n==19); s='General Purpose 4';
|
ivan@81
|
364 elseif (n==64); s='Sustain';
|
ivan@81
|
365 elseif (n==65); s='Portamento';
|
ivan@81
|
366 elseif (n==66); s='Sustenuto';
|
ivan@81
|
367 elseif (n==67); s='Soft Pedal';
|
ivan@81
|
368 elseif (n==69); s='Hold 2';
|
ivan@81
|
369 elseif (n==80); s='General Purpose 5';
|
ivan@81
|
370 elseif (n==81); s='Temp Change (General Purpose 6)';
|
ivan@81
|
371 elseif (n==82); s='General Purpose 7';
|
ivan@81
|
372 elseif (n==83); s='General Purpose 8';
|
ivan@81
|
373 elseif (n==91); s='Ext Effects Depth';
|
ivan@81
|
374 elseif (n==92); s='Tremelo Depthy';
|
ivan@81
|
375 elseif (n==93); s='Chorus Depth';
|
ivan@81
|
376 elseif (n==94); s='Detune Depth (Celeste Depth)';
|
ivan@81
|
377 elseif (n==95); s='Phaser Depth';
|
ivan@81
|
378 elseif (n==96); s='Data Increment (Data Entry +1)';
|
ivan@81
|
379 elseif (n==97); s='Data Decrement (Data Entry -1)';
|
ivan@81
|
380 elseif (n==98); s='Non-Registered Param LSB';
|
ivan@81
|
381 elseif (n==99); s='Non-Registered Param MSB';
|
ivan@81
|
382 elseif (n==100); s='Registered Param LSB';
|
ivan@81
|
383 elseif (n==101); s='Registered Param MSB';
|
ivan@81
|
384 else
|
ivan@81
|
385 s='UNKNOWN CONTROLLER';
|
ivan@81
|
386 end
|
ivan@81
|
387
|
ivan@81
|
388 %Channel mode message values
|
ivan@81
|
389 %Reset All Controllers 79 121 Val ??
|
ivan@81
|
390 %Local Control 7A 122 Val 0 = off, 7F (127) = on
|
ivan@81
|
391 %All Notes Off 7B 123 Val must be 0
|
ivan@81
|
392 %Omni Mode Off 7C 124 Val must be 0
|
ivan@81
|
393 %Omni Mode On 7D 125 Val must be 0
|
ivan@81
|
394 %Mono Mode On 7E 126 Val = # of channels, or 0 if # channels equals # voices in receiver
|
ivan@81
|
395 %Poly Mode On 7F 127 Val must be 0
|