y@0
|
1 function [nmat,n] = midi2nmat(filename)
|
y@0
|
2 % nmat = midi2nmat(filename)
|
y@0
|
3 % Read midi file FILENAME into Matlab variable NMAT (Beta)
|
y@0
|
4 % Based on Ken Schutte's m-files (readmidi, midiInfo, getTempoChanges)
|
y@0
|
5 % This beta might replace the mex-files used in the previous version of the toolbox as
|
y@0
|
6 % newer versions of Matlab (7.4+) and various OS's need new compilations
|
y@0
|
7 % of the mex files. Using the C sources and the compiled mex files provides
|
y@0
|
8 % faster reading of midi files but because the compatibility is limited, this
|
y@0
|
9 % simple workaround is offered. This beta version is very primitive,
|
y@0
|
10 % though. - Tuomas Eerola
|
y@0
|
11 %
|
y@0
|
12 % KNOWN PROBLEMS: - Tempo changes are handled in a simple way
|
y@0
|
13 % - Extra messages are not retained
|
y@0
|
14 % - Channels may not be handled correctly
|
y@0
|
15 %
|
y@0
|
16 % For more information on Ken Schutte's functions, see
|
y@0
|
17 % http://www.kenschutte.com/software
|
y@0
|
18 %
|
y@0
|
19 % CREATED ON 31.12.2007 BY TE (MATLAB 7.4 MacOSX 10.4)
|
y@0
|
20 %
|
y@0
|
21
|
y@0
|
22 %% USE KEN SCHUTTE'S FUNCTIONS
|
y@0
|
23 m = readmidi(filename);
|
y@0
|
24 n = midiInfo(m,0);
|
y@0
|
25 [tempos, tempos_time] = getTempoChanges(m);
|
y@0
|
26
|
y@0
|
27 %% CONVERT OUTPUT INTO MIDI TOOLBOX NMAT FORMAT
|
y@0
|
28 nmat(:,6) = n(:,5);
|
y@0
|
29 nmat(:,7) = n(:,6)-n(:,5); % duration
|
y@0
|
30 nmat(:,3) = n(:,2);
|
y@0
|
31 nmat(:,4) = n(:,3);
|
y@0
|
32 nmat(:,5) = n(:,4);
|
y@0
|
33
|
y@0
|
34 %% CHECK IF MULTIPLE TEMPI
|
y@0
|
35 if isempty(tempos), tempos=500000;end % some files may not contain the tempo, use default in that case
|
y@0
|
36 if length(tempos)>1
|
y@0
|
37 % here for diff tempi....
|
y@0
|
38
|
y@0
|
39 disp('WARNING: Multiple tempi detected, simple recoding of timing (seconds, not beats!)');
|
y@0
|
40 disp(num2str(60./(tempos./1000000)))
|
y@0
|
41
|
y@0
|
42
|
y@0
|
43 tc_ind=0;
|
y@0
|
44 starttime=0;
|
y@0
|
45 for i=1:length(tempos_time)-1
|
y@0
|
46 tempo_begin = (tempos_time(i+1)-tempos_time(i))/m.ticks_per_quarter_note*tempos(i)/1000000;
|
y@0
|
47 tempo_begin = tempo_begin+starttime;
|
y@0
|
48 starttime = tempo_begin;
|
y@0
|
49 tc_ind=[tc_ind tempo_begin];
|
y@0
|
50 end
|
y@0
|
51 nmat2=nmat;
|
y@0
|
52 for i=1:length(tc_ind)
|
y@0
|
53 if i==length(tc_ind)
|
y@0
|
54 time_index = nmat(:,6)>=tc_ind(i);
|
y@0
|
55 else
|
y@0
|
56 time_index = nmat(:,6)>=tc_ind(i); %& nmat(:,6)<=tc_ind(i+1);
|
y@0
|
57 end
|
y@0
|
58
|
y@0
|
59 timeratio = tempos(i)/tempos(1);
|
y@0
|
60
|
y@0
|
61 % realign timing after tempo changes
|
y@0
|
62 tmp1 = nmat2(time_index,:);
|
y@0
|
63 tmp2 = n(time_index,5).*timeratio; tmp2=tmp2(1);
|
y@0
|
64 realign = tmp1(1,6)-tmp2; % previous onset time
|
y@0
|
65 nmat2(time_index,6) = (n(time_index,5).*timeratio)+realign;
|
y@0
|
66 nmat2(time_index,7) = nmat2(time_index,7).*timeratio;
|
y@0
|
67 end
|
y@0
|
68 nmat(:,1) = n(:,5)/(tempos(1)/1000000); % beats are not modified according to tempi
|
y@0
|
69 nmat(:,2) = (n(:,6)-n(:,5))/(tempos(1)/1000000);
|
y@0
|
70
|
y@0
|
71 else
|
y@0
|
72 nmat(:,1) = n(:,5)/(tempos(1)/1000000);
|
y@0
|
73 nmat(:,2) = (n(:,6)-n(:,5))/(tempos(1)/1000000);
|
y@0
|
74 end
|
y@0
|
75
|
y@0
|
76 % postprocessing...
|
y@0
|
77
|
y@0
|
78 % IF CHANNEL IS EMPTY
|
y@0
|
79
|
y@0
|
80 if nmat(:,3)==0
|
y@0
|
81 nmat(:,3)=1;
|
y@0
|
82 end
|
y@0
|
83
|
y@0
|
84 %% FUNCTIONS
|
y@0
|
85
|
y@0
|
86
|
y@0
|
87 function [Notes,endtime] = midiInfo(midi,outputFormat,tracklist)
|
y@0
|
88 % [Notes,endtime] = midiInfo(midi,outputFormat,tracklist)
|
y@0
|
89 %
|
y@0
|
90 % Takes a midi structre and generates info on notes and messages
|
y@0
|
91 % Can return a matrix of note parameters and/or output/display
|
y@0
|
92 % formatted table of messages
|
y@0
|
93 %
|
y@0
|
94 % Inputs:
|
y@0
|
95 % midi - Matlab structure (created by readmidi.m)
|
y@0
|
96 % tracklist - which tracks to show ([] for all)
|
y@0
|
97 % outputFormat
|
y@0
|
98 % - if it's a string write the formated output to the file
|
y@0
|
99 % - if 0, don't display or write formatted output
|
y@0
|
100 % - if 1, just display (default)
|
y@0
|
101 %
|
y@0
|
102 % outputs:
|
y@0
|
103 % Notes - a matrix containing a list of notes, ordered by start time
|
y@0
|
104 % column values are:
|
y@0
|
105 % 1 2 3 4 5 6 7 8
|
y@0
|
106 % [track chan nn vel t1 t2 msgNum1 msgNum2]
|
y@0
|
107 % endtime - time of end of track message
|
y@0
|
108 %
|
y@0
|
109 %---------------------------------------------------------------
|
y@0
|
110 % Subversion Revision: 14 (2006-11-28)
|
y@0
|
111 % minor alterations by TE 2.1.2008
|
y@0
|
112 % This software can be used freely for non-commerical use.
|
y@0
|
113 % Visit http://www.kenschutte.com/software for more
|
y@0
|
114 % documentation, copyright info, and updates.
|
y@0
|
115 %---------------------------------------------------------------
|
y@0
|
116
|
y@0
|
117
|
y@0
|
118 if nargin<3
|
y@0
|
119 tracklist=[];
|
y@0
|
120 if nargin<2
|
y@0
|
121 outputFormat=1;
|
y@0
|
122 end
|
y@0
|
123 end
|
y@0
|
124 if (isempty(tracklist))
|
y@0
|
125 tracklist = 1:length(midi.track);
|
y@0
|
126 end
|
y@0
|
127
|
y@0
|
128 [tempos, tempos_time] = getTempoChanges(midi);
|
y@0
|
129
|
y@0
|
130 current_tempo = 500000; % default tempo
|
y@0
|
131
|
y@0
|
132 fid = -1;
|
y@0
|
133 if (ischar(outputFormat))
|
y@0
|
134 fid = fopen(outputFormat,'w');
|
y@0
|
135 end
|
y@0
|
136
|
y@0
|
137 endtime = -1;
|
y@0
|
138
|
y@0
|
139 % each row:
|
y@0
|
140 % 1 2 3 4 5 6 7 8
|
y@0
|
141 % [track chan nn vel t1 t2 msgNum1 msgNum2]
|
y@0
|
142 Notes = zeros(0,8);
|
y@0
|
143
|
y@0
|
144 for i=1:length(tracklist)
|
y@0
|
145 tracknum = tracklist(i);
|
y@0
|
146 cumtime=0;
|
y@0
|
147 seconds=0;
|
y@0
|
148
|
y@0
|
149 Msg = cell(0);
|
y@0
|
150 Msg{1,1} = 'chan';
|
y@0
|
151 Msg{1,2} = 'deltatime';
|
y@0
|
152 Msg{1,3} = 'time';
|
y@0
|
153 Msg{1,4} = 'name';
|
y@0
|
154 Msg{1,5} = 'data';
|
y@0
|
155
|
y@0
|
156 runnro=1;
|
y@0
|
157
|
y@0
|
158 for msgNum=1:length(midi.track(tracknum).messages)
|
y@0
|
159
|
y@0
|
160 currMsg = midi.track(tracknum).messages(msgNum);
|
y@0
|
161
|
y@0
|
162 midimeta = currMsg.midimeta;
|
y@0
|
163 deltatime = currMsg.deltatime;
|
y@0
|
164 data = currMsg.data;
|
y@0
|
165 type = currMsg.type;
|
y@0
|
166 chan = currMsg.chan;
|
y@0
|
167
|
y@0
|
168 cumtime = cumtime + deltatime;
|
y@0
|
169 %current_tempo/midi.ticks_per_quarter_note;
|
y@0
|
170 %debug = deltatime*1e-6*current_tempo/midi.ticks_per_quarter_note;
|
y@0
|
171
|
y@0
|
172 seconds = seconds + deltatime*1e-6*current_tempo/midi.ticks_per_quarter_note;
|
y@0
|
173 [mx ind] = max(find(cumtime >= tempos_time));
|
y@0
|
174
|
y@0
|
175 %% % ADDED BY TE 1.1.2008
|
y@0
|
176 if isempty(ind)
|
y@0
|
177 % do nothing
|
y@0
|
178 else
|
y@0
|
179 current_tempo = tempos(ind);
|
y@0
|
180 end
|
y@0
|
181 %% end
|
y@0
|
182
|
y@0
|
183 % find start/stop of notes:
|
y@0
|
184 % if (strcmp(name,'Note on') && (data(2)>0))
|
y@0
|
185 % note on with vel>0:
|
y@0
|
186 if (midimeta==1 && type==144 && data(2)>0)
|
y@0
|
187 % note on:
|
y@0
|
188
|
y@0
|
189 if isempty(Notes)
|
y@0
|
190 Notes(1,:) = [tracknum chan data(1) data(2) seconds 0 runnro -1];
|
y@0
|
191 runnro=runnro+1;
|
y@0
|
192 else
|
y@0
|
193 % Notes(end+1,:) = [tracknum chan data(1) data(2) seconds 0 msgNum -1]
|
y@0
|
194 Notes(end+1,:) = [tracknum chan data(1) data(2) seconds 0 runnro -1];
|
y@0
|
195 runnro=runnro+1;
|
y@0
|
196 end
|
y@0
|
197 % elseif ((strcmp(name,'Note on') && (data(2)==0)) || strcmp(name,'Note off'))
|
y@0
|
198 % note on with vel==0 or note off:
|
y@0
|
199
|
y@0
|
200 elseif (midimeta==1 && ( (type==144 && data(2)==0) || type==128 ))
|
y@0
|
201
|
y@0
|
202 % note off:
|
y@0
|
203 % % find index, wther tr,chan,and nn match, and not complete
|
y@0
|
204
|
y@0
|
205 ind = find((...
|
y@0
|
206 (Notes(:,1)==tracknum) + ...
|
y@0
|
207 (Notes(:,2)==chan) + ...
|
y@0
|
208 (Notes(:,3)==data(1)) + ...
|
y@0
|
209 (Notes(:,8)==-1)...
|
y@0
|
210 )==4);
|
y@0
|
211
|
y@0
|
212 if (length(ind)==0)
|
y@0
|
213 error('ending non-open note?');
|
y@0
|
214 elseif (length(ind)>1)
|
y@0
|
215 disp('warning: found multiple matches in endNote, taking first...');
|
y@0
|
216 ind = ind(1);
|
y@0
|
217 end
|
y@0
|
218
|
y@0
|
219 % set info on ending:
|
y@0
|
220 Notes(ind,6) = seconds;
|
y@0
|
221 Notes(ind,8) = msgNum;
|
y@0
|
222
|
y@0
|
223 % end of track:
|
y@0
|
224 elseif (midimeta==0 && type==47)
|
y@0
|
225 if (endtime == -1)
|
y@0
|
226 endtime = seconds;
|
y@0
|
227 else
|
y@0
|
228 disp('two "end of track" messages?');
|
y@0
|
229 endtime(end+1) = seconds;
|
y@0
|
230 end
|
y@0
|
231
|
y@0
|
232
|
y@0
|
233 end
|
y@0
|
234
|
y@0
|
235 % we could check to make sure it ends with
|
y@0
|
236 % 'end of track'
|
y@0
|
237
|
y@0
|
238
|
y@0
|
239 if (outputFormat ~= 0)
|
y@0
|
240 % get some specific descriptions:
|
y@0
|
241 name = num2str(type);
|
y@0
|
242 dataStr = num2str(data);
|
y@0
|
243
|
y@0
|
244 if (isempty(chan))
|
y@0
|
245 Msg{msgNum,1} = '-';
|
y@0
|
246 else
|
y@0
|
247 Msg{msgNum,1} = num2str(chan);
|
y@0
|
248 end
|
y@0
|
249
|
y@0
|
250 Msg{msgNum,2} = num2str(deltatime);
|
y@0
|
251 Msg{msgNum,3} = formatTime(seconds);
|
y@0
|
252
|
y@0
|
253 if (midimeta==0)
|
y@0
|
254 Msg{msgNum,4} = 'meta';
|
y@0
|
255 else
|
y@0
|
256 Msg{msgNum,4} = '';
|
y@0
|
257 end
|
y@0
|
258
|
y@0
|
259 [name,dataStr] = getMsgInfo(midimeta, type, data);
|
y@0
|
260 Msg{msgNum,5} = name;
|
y@0
|
261 Msg{msgNum,6} = dataStr;
|
y@0
|
262 end
|
y@0
|
263
|
y@0
|
264
|
y@0
|
265 end
|
y@0
|
266
|
y@0
|
267 if (outputFormat ~= 0)
|
y@0
|
268 printTrackInfo(Msg,tracknum,fid);
|
y@0
|
269 end
|
y@0
|
270
|
y@0
|
271 end
|
y@0
|
272
|
y@0
|
273 % make this an option!!!
|
y@0
|
274 % - I'm not sure why it's needed...
|
y@0
|
275 % remove start silence:
|
y@0
|
276
|
y@0
|
277 %_ removed by TE
|
y@0
|
278 %_first_t = min(Notes(:,5));
|
y@0
|
279 %_Notes(:,5) = Notes(:,5) - first_t;
|
y@0
|
280 %_Notes(:,6) = Notes(:,6) - first_t;
|
y@0
|
281
|
y@0
|
282 % sort Notes by start time:
|
y@0
|
283 [junk,ord] = sort(Notes(:,5));
|
y@0
|
284 Notes = Notes(ord,:);
|
y@0
|
285
|
y@0
|
286
|
y@0
|
287 if (fid ~= -1)
|
y@0
|
288 fclose(fid);
|
y@0
|
289 end
|
y@0
|
290
|
y@0
|
291
|
y@0
|
292
|
y@0
|
293
|
y@0
|
294
|
y@0
|
295
|
y@0
|
296
|
y@0
|
297
|
y@0
|
298
|
y@0
|
299
|
y@0
|
300
|
y@0
|
301 function printTrackInfo(Msg,tracknum,fid)
|
y@0
|
302
|
y@0
|
303
|
y@0
|
304 % make cols same length instead of just using \t
|
y@0
|
305 for i=1:size(Msg,2)
|
y@0
|
306 maxLen(i)=0;
|
y@0
|
307 for j=1:size(Msg,1)
|
y@0
|
308 if (length(Msg{j,i})>maxLen(i))
|
y@0
|
309 maxLen(i) = length(Msg{j,i});
|
y@0
|
310 end
|
y@0
|
311 end
|
y@0
|
312 end
|
y@0
|
313
|
y@0
|
314
|
y@0
|
315 s='';
|
y@0
|
316 s=[s sprintf('--------------------------------------------------\n')];
|
y@0
|
317 s=[s sprintf('Track %d\n',tracknum)];
|
y@0
|
318 s=[s sprintf('--------------------------------------------------\n')];
|
y@0
|
319
|
y@0
|
320 if (fid == -1)
|
y@0
|
321 disp(s)
|
y@0
|
322 else
|
y@0
|
323 fprintf(fid,'%s',s);
|
y@0
|
324 end
|
y@0
|
325
|
y@0
|
326
|
y@0
|
327 for i=1:size(Msg,1)
|
y@0
|
328 line='';
|
y@0
|
329 for j=1:size(Msg,2)
|
y@0
|
330 sp = repmat(' ',1,5+maxLen(j)-length(Msg{i,j}));
|
y@0
|
331 m = Msg{i,j};
|
y@0
|
332 m = m(:)'; % ensure column vector
|
y@0
|
333 % line = [line Msg{i,j} sp];
|
y@0
|
334 line = [line m sp];
|
y@0
|
335 end
|
y@0
|
336
|
y@0
|
337 if (fid == -1)
|
y@0
|
338 disp(line)
|
y@0
|
339 else
|
y@0
|
340 fprintf(fid,'%s\n',line);
|
y@0
|
341 end
|
y@0
|
342
|
y@0
|
343 end
|
y@0
|
344
|
y@0
|
345
|
y@0
|
346
|
y@0
|
347 function s=formatTime(seconds)
|
y@0
|
348
|
y@0
|
349 minutes = floor(seconds/60);
|
y@0
|
350 secs = seconds - 60*minutes;
|
y@0
|
351
|
y@0
|
352 s = sprintf('%d:%2.3f',minutes,secs);
|
y@0
|
353
|
y@0
|
354
|
y@0
|
355
|
y@0
|
356 function [name,dataStr]=getMsgInfo(midimeta, type, data)
|
y@0
|
357
|
y@0
|
358 % meta events:
|
y@0
|
359 if (midimeta==0)
|
y@0
|
360 if (type==0); name = 'Sequence Number'; len=2; dataStr = num2str(data);
|
y@0
|
361 elseif (type==1); name = 'Text Events'; len=-1; dataStr = char(data);
|
y@0
|
362 elseif (type==2); name = 'Copyright Notice'; len=-1; dataStr = char(data);
|
y@0
|
363 elseif (type==3); name = 'Sequence/Track Name'; len=-1; dataStr = char(data);
|
y@0
|
364 elseif (type==4); name = 'Instrument Name'; len=-1; dataStr = char(data);
|
y@0
|
365 elseif (type==5); name = 'Lyric'; len=-1; dataStr = char(data);
|
y@0
|
366 elseif (type==6); name = 'Marker'; len=-1; dataStr = char(data);
|
y@0
|
367 elseif (type==7); name = 'Cue Point'; len=-1; dataStr = char(data);
|
y@0
|
368 elseif (type==32); name = 'MIDI Channel Prefix'; len=1; dataStr = num2str(data);
|
y@0
|
369 elseif (type==47); name = 'End of Track'; len=0; dataStr = '';
|
y@0
|
370 elseif (type==81); name = 'Set Tempo'; len=3;
|
y@0
|
371 val = data(1)*16^4+data(2)*16^2+data(3); dataStr = ['microsec per quarter note: ' num2str(val)];
|
y@0
|
372 elseif (type==84); name = 'SMPTE Offset'; len=5;
|
y@0
|
373 if size(data)==5
|
y@0
|
374 dataStr = ['[hh mm ss fr ff]=' num2str(data)];
|
y@0
|
375 else
|
y@0
|
376 dataStr = ['[hh mm ss fr ff]=' num2str(data')];
|
y@0
|
377 end
|
y@0
|
378 elseif (type==88); name = 'Time Signature'; len=4;
|
y@0
|
379 dataStr = [num2str(data(1)) '/' num2str(data(2)) ', clock ticks and notated 32nd notes=' num2str(data(3)) '/' num2str(data(4))];
|
y@0
|
380 elseif (type==89); name = 'Key Signature'; len=2;
|
y@0
|
381 % num sharps/flats (flats negative)
|
y@0
|
382 if (data(1)>=0)
|
y@0
|
383 % 1 2 3 4 5 6 7
|
y@0
|
384 ss={'C','G','D', 'A', 'E','B', 'F#', 'C#'};
|
y@0
|
385 if data(1)>7
|
y@0
|
386 dataStr='C'; % ADDED BY TE 1.1.2008
|
y@0
|
387 else
|
y@0
|
388 dataStr = ss{data(1)+1};
|
y@0
|
389 end
|
y@0
|
390
|
y@0
|
391 else
|
y@0
|
392 % 1 2 3 4 5 6 7
|
y@0
|
393 ss={'F','Bb','Eb','Ab','Db','Gb','Cb'};
|
y@0
|
394 dataStr = ss{abs(data(1))};
|
y@0
|
395 end
|
y@0
|
396 if (data(2)==0)
|
y@0
|
397 dataStr = [dataStr ' Major'];
|
y@0
|
398 else
|
y@0
|
399 dataStr = [dataStr ' Minor'];
|
y@0
|
400 end
|
y@0
|
401
|
y@0
|
402 elseif (type==89); name = 'Sequencer-Specific Meta-event'; len=-1;
|
y@0
|
403 dataStr = char(data);
|
y@0
|
404 % !! last two conflict...
|
y@0
|
405
|
y@0
|
406 else
|
y@0
|
407 name = ['UNKNOWN META EVENT: ' num2str(type)]; dataStr = num2str(data);
|
y@0
|
408 end
|
y@0
|
409
|
y@0
|
410 % meta 0x21 = MIDI port number, length 1 (? perhaps)
|
y@0
|
411 else
|
y@0
|
412
|
y@0
|
413 % channel voice messages:
|
y@0
|
414 % (from event byte with chan removed, eg 0x8n -> 0x80 = 128 for
|
y@0
|
415 % note off)
|
y@0
|
416 if (type==128); name = 'Note off'; len=2; dataStr = ['nn=' num2str(data(1)) ' vel=' num2str(data(2))];
|
y@0
|
417 elseif (type==144); name = 'Note on'; len=2; dataStr = ['nn=' num2str(data(1)) ' vel=' num2str(data(2))];
|
y@0
|
418 elseif (type==160); name = 'Polyphonic Key Pressure'; len=2; dataStr = ['nn=' num2str(data(1)) ' vel=' num2str(data(2))];
|
y@0
|
419 elseif (type==176); name = 'Controller Change'; len=2; dataStr = ['ctrl=' controllers(data(1)) ' value=' num2str(data(2))];
|
y@0
|
420 elseif (type==192); name = 'Program Change'; len=1; dataStr = ['instr=' num2str(data)];
|
y@0
|
421 elseif (type==208); name = 'Channel Key Pressure'; len=1; dataStr = ['vel=' num2str(data)];
|
y@0
|
422 elseif (type==224); name = 'Pitch Bend'; len=2;
|
y@0
|
423 val = data(1)+data(2)*256;
|
y@0
|
424 val = base2dec('2000',16) - val;
|
y@0
|
425 dataStr = ['change=' num2str(val) '?'];
|
y@0
|
426
|
y@0
|
427 % channel mode messages:
|
y@0
|
428 % ... unsure about data for these... (do some have a data byte and
|
y@0
|
429 % others not?)
|
y@0
|
430 %
|
y@0
|
431 % 0xC1 .. 0xC8
|
y@0
|
432 elseif (type==193); name = 'All Sounds Off'; dataStr = num2str(data);
|
y@0
|
433 elseif (type==194); name = 'Reset All Controllers'; dataStr = num2str(data);
|
y@0
|
434 elseif (type==195); name = 'Local Control'; dataStr = num2str(data);
|
y@0
|
435 elseif (type==196); name = 'All Notes Off'; dataStr = num2str(data);
|
y@0
|
436 elseif (type==197); name = 'Omni Mode Off'; dataStr = num2str(data);
|
y@0
|
437 elseif (type==198); name = 'Omni Mode On'; dataStr = num2str(data);
|
y@0
|
438 elseif (type==199); name = 'Mono Mode On'; dataStr = num2str(data);
|
y@0
|
439 elseif (type==200); name = 'Poly Mode On'; dataStr = num2str(data);
|
y@0
|
440
|
y@0
|
441 % sysex, F0->F7
|
y@0
|
442 elseif (type==240); name = 'Sysex 0xF0'; dataStr = num2str(data);
|
y@0
|
443 elseif (type==241); name = 'Sysex 0xF1'; dataStr = num2str(data);
|
y@0
|
444 elseif (type==242); name = 'Sysex 0xF2'; dataStr = num2str(data);
|
y@0
|
445 elseif (type==243); name = 'Sysex 0xF3'; dataStr = num2str(data);
|
y@0
|
446 elseif (type==244); name = 'Sysex 0xF4'; dataStr = num2str(data);
|
y@0
|
447 elseif (type==245); name = 'Sysex 0xF5'; dataStr = num2str(data);
|
y@0
|
448 elseif (type==246); name = 'Sysex 0xF6'; dataStr = num2str(data);
|
y@0
|
449 elseif (type==247); name = 'Sysex 0xF7'; dataStr = num2str(data);
|
y@0
|
450
|
y@0
|
451 % realtime
|
y@0
|
452 % (i think have no data..?)
|
y@0
|
453 elseif (type==248); name = 'Real-time 0xF8 - Timing clock'; dataStr = num2str(data);
|
y@0
|
454 elseif (type==249); name = 'Real-time 0xF9'; dataStr = num2str(data);
|
y@0
|
455 elseif (type==250); name = 'Real-time 0xFA - Start a sequence'; dataStr = num2str(data);
|
y@0
|
456 elseif (type==251); name = 'Real-time 0xFB - Continue a sequence'; dataStr = num2str(data);
|
y@0
|
457 elseif (type==252); name = 'Real-time 0xFC - Stop a sequence'; dataStr = num2str(data);
|
y@0
|
458 elseif (type==253); name = 'Real-time 0xFD'; dataStr = num2str(data);
|
y@0
|
459 elseif (type==254); name = 'Real-time 0xFE'; dataStr = num2str(data);
|
y@0
|
460 elseif (type==255); name = 'Real-time 0xFF'; dataStr = num2str(data);
|
y@0
|
461
|
y@0
|
462
|
y@0
|
463 else
|
y@0
|
464 name = ['UNKNOWN MIDI EVENT: ' num2str(type)]; dataStr = num2str(data);
|
y@0
|
465 end
|
y@0
|
466
|
y@0
|
467
|
y@0
|
468 end
|
y@0
|
469
|
y@0
|
470 function s=controllers(n)
|
y@0
|
471 if (n==1); s='Mod Wheel';
|
y@0
|
472 elseif (n==2); s='Breath Controllery';
|
y@0
|
473 elseif (n==4); s='Foot Controller';
|
y@0
|
474 elseif (n==5); s='Portamento Time';
|
y@0
|
475 elseif (n==6); s='Data Entry MSB';
|
y@0
|
476 elseif (n==7); s='Volume';
|
y@0
|
477 elseif (n==8); s='Balance';
|
y@0
|
478 elseif (n==10); s='Pan';
|
y@0
|
479 elseif (n==11); s='Expression Controller';
|
y@0
|
480 elseif (n==16); s='General Purpose 1';
|
y@0
|
481 elseif (n==17); s='General Purpose 2';
|
y@0
|
482 elseif (n==18); s='General Purpose 3';
|
y@0
|
483 elseif (n==19); s='General Purpose 4';
|
y@0
|
484 elseif (n==64); s='Sustain';
|
y@0
|
485 elseif (n==65); s='Portamento';
|
y@0
|
486 elseif (n==66); s='Sustenuto';
|
y@0
|
487 elseif (n==67); s='Soft Pedal';
|
y@0
|
488 elseif (n==69); s='Hold 2';
|
y@0
|
489 elseif (n==80); s='General Purpose 5';
|
y@0
|
490 elseif (n==81); s='Temp Change (General Purpose 6)';
|
y@0
|
491 elseif (n==82); s='General Purpose 7';
|
y@0
|
492 elseif (n==83); s='General Purpose 8';
|
y@0
|
493 elseif (n==91); s='Ext Effects Depth';
|
y@0
|
494 elseif (n==92); s='Tremelo Depthy';
|
y@0
|
495 elseif (n==93); s='Chorus Depth';
|
y@0
|
496 elseif (n==94); s='Detune Depth (Celeste Depth)';
|
y@0
|
497 elseif (n==95); s='Phaser Depth';
|
y@0
|
498 elseif (n==96); s='Data Increment (Data Entry +1)';
|
y@0
|
499 elseif (n==97); s='Data Decrement (Data Entry -1)';
|
y@0
|
500 elseif (n==98); s='Non-Registered Param LSB';
|
y@0
|
501 elseif (n==99); s='Non-Registered Param MSB';
|
y@0
|
502 elseif (n==100); s='Registered Param LSB';
|
y@0
|
503 elseif (n==101); s='Registered Param MSB';
|
y@0
|
504 else
|
y@0
|
505 s='UNKNOWN CONTROLLER';
|
y@0
|
506 end
|
y@0
|
507
|
y@0
|
508 %Channel mode message values
|
y@0
|
509 %Reset All Controllers 79 121 Val ??
|
y@0
|
510 %Local Control 7A 122 Val 0 = off, 7F (127) = on
|
y@0
|
511 %All Notes Off 7B 123 Val must be 0
|
y@0
|
512 %Omni Mode Off 7C 124 Val must be 0
|
y@0
|
513 %Omni Mode On 7D 125 Val must be 0
|
y@0
|
514 %Mono Mode On 7E 126 Val = # of channels, or 0 if # channels equals # voices in receiver
|
y@0
|
515 %Poly Mode On 7F 127 Val must be 0
|
y@0
|
516
|
y@0
|
517
|
y@0
|
518 function [tempos,tempos_time]=getTempoChanges(midi)
|
y@0
|
519 % [tempos,tempos_time]=getTempoChanges(midi)
|
y@0
|
520 %
|
y@0
|
521 % input: a midi struct from readmidi.m
|
y@0
|
522 % output:
|
y@0
|
523 % tempos = tempo values indexed by tempos_time
|
y@0
|
524 % tempos_time is in units of ticks
|
y@0
|
525 %
|
y@0
|
526 % should tempo changes effect across tracks? across channels?
|
y@0
|
527 %
|
y@0
|
528 %---------------------------------------------------------------
|
y@0
|
529 % Subversion Revision: 14 (2006-01-24)
|
y@0
|
530 %
|
y@0
|
531 % This software can be used freely for non-commerical use.
|
y@0
|
532 % Visit http://www.kenschutte.com/software for more
|
y@0
|
533 % documentation, copyright info, and updates.
|
y@0
|
534 %---------------------------------------------------------------
|
y@0
|
535
|
y@0
|
536
|
y@0
|
537 tempos = [];
|
y@0
|
538 tempos_time = [];
|
y@0
|
539 %tempos_index = [];
|
y@0
|
540 for i=1:length(midi.track)
|
y@0
|
541 cumtime=0;
|
y@0
|
542 for j=1:length(midi.track(i).messages)
|
y@0
|
543 cumtime = cumtime+midi.track(i).messages(j).deltatime;
|
y@0
|
544 % if (strcmp(midi.track(i).messages(j).name,'Set Tempo'))
|
y@0
|
545 if (midi.track(i).messages(j).midimeta==0 && midi.track(i).messages(j).type==81)
|
y@0
|
546
|
y@0
|
547 tempos_time(end+1) = cumtime;
|
y@0
|
548 d = midi.track(i).messages(j).data;
|
y@0
|
549 tempos(end+1) = d(1)*16^4 + d(2)*16^2 + d(3);
|
y@0
|
550 end
|
y@0
|
551 end
|
y@0
|
552 end
|
y@0
|
553
|
y@0
|
554
|
y@0
|
555 function midi = readmidi(filename, rawbytes)
|
y@0
|
556 % midi = readmidi(filename, rawbytes)
|
y@0
|
557 % midi = readmidi(filename)
|
y@0
|
558 %
|
y@0
|
559 % Read MIDI file and store in a Matlab structure
|
y@0
|
560 % (use midiInfo.m to see structure detail)
|
y@0
|
561 %
|
y@0
|
562 % Inputs:
|
y@0
|
563 % filename - input MIDI file
|
y@0
|
564 % rawbytes - 0 or 1: Include raw bytes in structure
|
y@0
|
565 % This info is redundant, but can be
|
y@0
|
566 % useful for debugging. default=0
|
y@0
|
567 %
|
y@0
|
568 %---------------------------------------------------------------
|
y@0
|
569 % Subversion Revision: 14 (2006-12-03)
|
y@0
|
570 %
|
y@0
|
571 % This software can be used freely for non-commerical use.
|
y@0
|
572 % Visit http://www.kenschutte.com/software for more
|
y@0
|
573 % documentation, copyright info, and updates.
|
y@0
|
574 %---------------------------------------------------------------
|
y@0
|
575
|
y@0
|
576
|
y@0
|
577
|
y@0
|
578 if (nargin<2)
|
y@0
|
579 rawbytes=0;
|
y@0
|
580 end
|
y@0
|
581
|
y@0
|
582 fid = fopen(filename);
|
y@0
|
583 %[A count] = fread(fid,'char');
|
y@0
|
584 [A count] = fread(fid,'uint8');
|
y@0
|
585 fclose(fid);
|
y@0
|
586
|
y@0
|
587 midi.filename = filename;
|
y@0
|
588 if (rawbytes) midi.rawbytes_all = A; end
|
y@0
|
589
|
y@0
|
590
|
y@0
|
591 % realtime events: status: [F8, FF]. no data bytes
|
y@0
|
592 %clock, undefined, start, continue, stop, undefined, active
|
y@0
|
593 %sensing, systerm reset
|
y@0
|
594
|
y@0
|
595 % file consists of "header chunk" and "track chunks"
|
y@0
|
596 % 4B 'MThd' (header) or 'MTrk' (track)
|
y@0
|
597 % 4B 32-bit unsigned int = number of bytes in chunk, not
|
y@0
|
598 % counting these first 8
|
y@0
|
599
|
y@0
|
600
|
y@0
|
601 % HEADER CHUNK --------------------------------------------------------
|
y@0
|
602 % 4B 'Mthd'
|
y@0
|
603 % 4B length
|
y@0
|
604 % 2B file format
|
y@0
|
605 % 0=single track, 1=multitrack synchronous, 2=multitrack asynchronous
|
y@0
|
606 % Synchronous formats start all tracks at the same time, while asynchronous formats can start and end any track at any time during the score.
|
y@0
|
607 % 2B track cout (must be 1 for format 0)
|
y@0
|
608 % 2B num delta-time ticks per quarter note
|
y@0
|
609 %
|
y@0
|
610
|
y@0
|
611 if ~isequal(A(1:4)',[77 84 104 100]) % double('MThd')
|
y@0
|
612 error('File does not begin with header ID (MThd)');
|
y@0
|
613 end
|
y@0
|
614
|
y@0
|
615 header_len = decode_int(A(5:8));
|
y@0
|
616 if (header_len == 6)
|
y@0
|
617 else
|
y@0
|
618 error('Header length != 6 bytes.');
|
y@0
|
619 end
|
y@0
|
620
|
y@0
|
621 format = decode_int(A(9:10));
|
y@0
|
622 if (format==0 || format==1 || format==2)
|
y@0
|
623 midi.format = format;
|
y@0
|
624 else
|
y@0
|
625 error('Format does not equal 0,1,or 2');
|
y@0
|
626 end
|
y@0
|
627
|
y@0
|
628 num_tracks = decode_int(A(11:12));
|
y@0
|
629 if (format==0 && num_tracks~=1)
|
y@0
|
630 error('File is format 0, but num_tracks != 1');
|
y@0
|
631 end
|
y@0
|
632
|
y@0
|
633 time_unit = decode_int(A(13:14));
|
y@0
|
634 if (bitand(time_unit,2^15)==0)
|
y@0
|
635 midi.ticks_per_quarter_note = time_unit;
|
y@0
|
636 else
|
y@0
|
637 error('Header: SMPTE time format found - not currently supported');
|
y@0
|
638 end
|
y@0
|
639
|
y@0
|
640 if (rawbytes), midi.rawbytes_header = A(1:14); end
|
y@0
|
641
|
y@0
|
642 % end header parse ----------------------------------------------------
|
y@0
|
643
|
y@0
|
644
|
y@0
|
645
|
y@0
|
646
|
y@0
|
647
|
y@0
|
648
|
y@0
|
649 % BREAK INTO SEPARATE TRACKS ------------------------------------------
|
y@0
|
650 % midi.track(1).data = [byte byte byte ...];
|
y@0
|
651 % midi.track(2).date = ...
|
y@0
|
652 % ...
|
y@0
|
653 %
|
y@0
|
654 % Track Chunks---------
|
y@0
|
655 % 4B 'MTrk'
|
y@0
|
656 % 4B length (after first 8B)
|
y@0
|
657 %
|
y@0
|
658 ctr = 15;
|
y@0
|
659 for i=1:num_tracks
|
y@0
|
660
|
y@0
|
661 if ~isequal(A(ctr:ctr+3)',[77 84 114 107]) % double('MTrk')
|
y@0
|
662 error(['Track ' num2str(i) ' does not begin with track ID=MTrk']);
|
y@0
|
663 end
|
y@0
|
664 ctr = ctr+4;
|
y@0
|
665
|
y@0
|
666 track_len = decode_int(A(ctr:ctr+3));
|
y@0
|
667 ctr = ctr+4;
|
y@0
|
668
|
y@0
|
669 % have track.rawbytes hold initial 8B also...
|
y@0
|
670 track_rawbytes{i} = A((ctr-8):(ctr+track_len-1));
|
y@0
|
671
|
y@0
|
672 if (rawbytes)
|
y@0
|
673 midi.track(i).rawbytes_header = A(ctr-8:ctr-1);
|
y@0
|
674 end
|
y@0
|
675
|
y@0
|
676 ctr = ctr+track_len;
|
y@0
|
677 end
|
y@0
|
678 % ----------------------------------------------------------------------
|
y@0
|
679
|
y@0
|
680
|
y@0
|
681
|
y@0
|
682
|
y@0
|
683
|
y@0
|
684
|
y@0
|
685 % Events:
|
y@0
|
686 % - meta events: start with 'FF'
|
y@0
|
687 % - MIDI events: all others
|
y@0
|
688
|
y@0
|
689 % MIDI events:
|
y@0
|
690 % optional command byte + 0,1,or 2 bytes of parameters
|
y@0
|
691 % "running mode": command byte omitted.
|
y@0
|
692 %
|
y@0
|
693 % all midi command bytes have MSB=1
|
y@0
|
694 % all data for inside midi command have value <= 127 (ie MSB=0)
|
y@0
|
695 % -> so can determine running mode
|
y@0
|
696 %
|
y@0
|
697 % meta events' data may have any values (meta events have to set
|
y@0
|
698 % len)
|
y@0
|
699 %
|
y@0
|
700
|
y@0
|
701
|
y@0
|
702
|
y@0
|
703 % 'Fn' MIDI commands:
|
y@0
|
704 % no chan. control the entire system
|
y@0
|
705 %F8 Timing Clock
|
y@0
|
706 %FA start a sequence
|
y@0
|
707 %FB continue a sequence
|
y@0
|
708 %FC stop a sequence
|
y@0
|
709
|
y@0
|
710 % Meta events:
|
y@0
|
711 % 1B 0xFF
|
y@0
|
712 % 1B event type
|
y@0
|
713 % 1B length of additional data
|
y@0
|
714 % ?? additional data
|
y@0
|
715 %
|
y@0
|
716
|
y@0
|
717
|
y@0
|
718 % "channel mode messages"
|
y@0
|
719 % have same code as "control change": 0xBn
|
y@0
|
720 % but uses reserved controller numbers 120-127
|
y@0
|
721 %
|
y@0
|
722
|
y@0
|
723
|
y@0
|
724 %Midi events consist of an optional command byte
|
y@0
|
725 % followed by zero, one or two bytes of parameters.
|
y@0
|
726 % In running mode, the command can be omitted, in
|
y@0
|
727 % which case the last MIDI command specified is
|
y@0
|
728 % assumed. The first bit of a command byte is 1,
|
y@0
|
729 % while the first bit of a parameter is always 0.
|
y@0
|
730 % In addition, the last 4 bits of a command
|
y@0
|
731 % indicate the channel to which the event should
|
y@0
|
732 % be sent; therefore, there are 6 possible
|
y@0
|
733 % commands (really 7, but we will discuss the x'Fn'
|
y@0
|
734 % commands later) that can be specified. They are:
|
y@0
|
735
|
y@0
|
736
|
y@0
|
737 % parse tracks -----------------------------------------
|
y@0
|
738 for i=1:num_tracks
|
y@0
|
739
|
y@0
|
740 track = track_rawbytes{i};
|
y@0
|
741
|
y@0
|
742 if (rawbytes); midi.track(i).rawbytes = track; end
|
y@0
|
743
|
y@0
|
744 msgCtr = 1;
|
y@0
|
745 ctr=9; % first 8B were MTrk and length
|
y@0
|
746 while (ctr < length(track_rawbytes{i}))
|
y@0
|
747
|
y@0
|
748 clear currMsg;
|
y@0
|
749 currMsg.used_running_mode = 0;
|
y@0
|
750 % note:
|
y@0
|
751 % .used_running_mode is necessary only to
|
y@0
|
752 % be able to reconstruct a file _exactly_ from
|
y@0
|
753 % the 'midi' structure. this is helpful for
|
y@0
|
754 % debugging since write(read(filename)) can be
|
y@0
|
755 % tested for exact replication...
|
y@0
|
756 %
|
y@0
|
757
|
y@0
|
758 ctr_start_msg = ctr;
|
y@0
|
759
|
y@0
|
760 [deltatime,ctr] = decode_var_length(track, ctr);
|
y@0
|
761
|
y@0
|
762 % ?
|
y@0
|
763 %if (rawbytes)
|
y@0
|
764 % currMsg.rawbytes_deltatime = track(ctr_start_msg:ctr-1);
|
y@0
|
765 %end
|
y@0
|
766
|
y@0
|
767 % deltaime must be 1-4 bytes long.
|
y@0
|
768 % could check here...
|
y@0
|
769
|
y@0
|
770
|
y@0
|
771 % CHECK FOR META EVENTS ------------------------
|
y@0
|
772 % 'FF'
|
y@0
|
773 if track(ctr)==255
|
y@0
|
774
|
y@0
|
775 type = track(ctr+1);
|
y@0
|
776
|
y@0
|
777 ctr = ctr+2;
|
y@0
|
778
|
y@0
|
779 % get variable length 'length' field
|
y@0
|
780 [len,ctr] = decode_var_length(track, ctr);
|
y@0
|
781
|
y@0
|
782 % note: some meta events have pre-determined lengths...
|
y@0
|
783 % we could try verifiying they are correct here.
|
y@0
|
784
|
y@0
|
785 thedata = track(ctr:ctr+len-1);
|
y@0
|
786 chan = [];
|
y@0
|
787
|
y@0
|
788 ctr = ctr + len;
|
y@0
|
789
|
y@0
|
790 midimeta = 0;
|
y@0
|
791
|
y@0
|
792 else
|
y@0
|
793 midimeta = 1;
|
y@0
|
794 % MIDI EVENT ---------------------------
|
y@0
|
795
|
y@0
|
796
|
y@0
|
797
|
y@0
|
798
|
y@0
|
799 % check for running mode:
|
y@0
|
800 if (track(ctr)<128)
|
y@0
|
801
|
y@0
|
802 % make it re-do last command:
|
y@0
|
803 %ctr = ctr - 1;
|
y@0
|
804 %track(ctr) = last_byte;
|
y@0
|
805 currMsg.used_running_mode = 1;
|
y@0
|
806
|
y@0
|
807 B = last_byte;
|
y@0
|
808 nB = track(ctr); % ?
|
y@0
|
809
|
y@0
|
810 else
|
y@0
|
811
|
y@0
|
812 B = track(ctr);
|
y@0
|
813 nB = track(ctr+1);
|
y@0
|
814
|
y@0
|
815 ctr = ctr + 1;
|
y@0
|
816
|
y@0
|
817 end
|
y@0
|
818
|
y@0
|
819 % nibbles:
|
y@0
|
820 %B = track(ctr);
|
y@0
|
821 %nB = track(ctr+1);
|
y@0
|
822
|
y@0
|
823
|
y@0
|
824 Hn = bitshift(B,-4);
|
y@0
|
825 Ln = bitand(B,15);
|
y@0
|
826
|
y@0
|
827 chan = [];
|
y@0
|
828
|
y@0
|
829 msg_type = midi_msg_type(B,nB);
|
y@0
|
830
|
y@0
|
831 % DEBUG:
|
y@0
|
832 if (i==2)
|
y@0
|
833 if (msgCtr==1)
|
y@0
|
834 disp(msg_type);
|
y@0
|
835 end
|
y@0
|
836 end
|
y@0
|
837
|
y@0
|
838
|
y@0
|
839 switch msg_type
|
y@0
|
840
|
y@0
|
841 case 'channel_mode'
|
y@0
|
842
|
y@0
|
843 % UNSURE: if all channel mode messages have 2 data byes (?)
|
y@0
|
844 type = bitshift(Hn,4) + (nB-120+1);
|
y@0
|
845 thedata = track(ctr:ctr+1);
|
y@0
|
846 chan = Ln;
|
y@0
|
847
|
y@0
|
848 ctr = ctr + 2;
|
y@0
|
849
|
y@0
|
850 % ---- channel voice messages:
|
y@0
|
851 case 'channel_voice'
|
y@0
|
852
|
y@0
|
853 type = bitshift(Hn,4);
|
y@0
|
854 len = channel_voice_msg_len(type); % var length data:
|
y@0
|
855 thedata = track(ctr:ctr+len-1);
|
y@0
|
856 chan = Ln;
|
y@0
|
857
|
y@0
|
858 % DEBUG:
|
y@0
|
859 if (i==2)
|
y@0
|
860 if (msgCtr==1)
|
y@0
|
861 disp([999 Hn type])
|
y@0
|
862 end
|
y@0
|
863 end
|
y@0
|
864
|
y@0
|
865 ctr = ctr + len;
|
y@0
|
866
|
y@0
|
867 case 'sysex'
|
y@0
|
868
|
y@0
|
869 % UNSURE: do sysex events (F0-F7) have
|
y@0
|
870 % variable length 'length' field?
|
y@0
|
871
|
y@0
|
872 [len,ctr] = decode_var_length(track, ctr);
|
y@0
|
873
|
y@0
|
874 type = B;
|
y@0
|
875 thedata = track(ctr:ctr+len-1);
|
y@0
|
876 chan = [];
|
y@0
|
877
|
y@0
|
878 ctr = ctr + len;
|
y@0
|
879
|
y@0
|
880 case 'sys_realtime'
|
y@0
|
881
|
y@0
|
882 % UNSURE: I think these are all just one byte
|
y@0
|
883 type = B;
|
y@0
|
884 thedata = [];
|
y@0
|
885 chan = [];
|
y@0
|
886
|
y@0
|
887 end
|
y@0
|
888
|
y@0
|
889 last_byte = Ln + bitshift(Hn,4);
|
y@0
|
890
|
y@0
|
891 end % end midi event 'if'
|
y@0
|
892
|
y@0
|
893
|
y@0
|
894 currMsg.deltatime = deltatime;
|
y@0
|
895 currMsg.midimeta = midimeta;
|
y@0
|
896 currMsg.type = type;
|
y@0
|
897 currMsg.data = thedata;
|
y@0
|
898 currMsg.chan = chan;
|
y@0
|
899
|
y@0
|
900 if (rawbytes)
|
y@0
|
901 currMsg.rawbytes = track(ctr_start_msg:ctr-1);
|
y@0
|
902 end
|
y@0
|
903
|
y@0
|
904 midi.track(i).messages(msgCtr) = currMsg;
|
y@0
|
905 msgCtr = msgCtr + 1;
|
y@0
|
906
|
y@0
|
907
|
y@0
|
908 end % end loop over rawbytes
|
y@0
|
909 end % end loop over tracks
|
y@0
|
910
|
y@0
|
911
|
y@0
|
912 function val=decode_int(A)
|
y@0
|
913
|
y@0
|
914 val = 0;
|
y@0
|
915 for i=1:length(A)
|
y@0
|
916 val = val + bitshift(A(length(A)-i+1), 8*(i-1));
|
y@0
|
917 end
|
y@0
|
918
|
y@0
|
919
|
y@0
|
920 function len=channel_voice_msg_len(type)
|
y@0
|
921
|
y@0
|
922 if (type==128); len=2;
|
y@0
|
923 elseif (type==144); len=2;
|
y@0
|
924 elseif (type==160); len=2;
|
y@0
|
925 elseif (type==176); len=2;
|
y@0
|
926 elseif (type==192); len=1;
|
y@0
|
927 elseif (type==208); len=1;
|
y@0
|
928 elseif (type==224); len=2;
|
y@0
|
929 else
|
y@0
|
930 disp(type); error('bad channel voice message type');
|
y@0
|
931 end
|
y@0
|
932
|
y@0
|
933
|
y@0
|
934 %
|
y@0
|
935 % decode variable length field (often deltatime)
|
y@0
|
936 %
|
y@0
|
937 % return value and new position of pointer into 'bytes'
|
y@0
|
938 %
|
y@0
|
939 function [val,ptr] = decode_var_length(bytes, ptr)
|
y@0
|
940
|
y@0
|
941 keepgoing=1;
|
y@0
|
942 binarystring = '';
|
y@0
|
943 while (keepgoing)
|
y@0
|
944 % check MSB:
|
y@0
|
945 % if MSB=1, then delta-time continues into next byte...
|
y@0
|
946 if(~bitand(bytes(ptr),128))
|
y@0
|
947 keepgoing=0;
|
y@0
|
948 end
|
y@0
|
949 % keep appending last 7 bits from each byte in the deltatime:
|
y@0
|
950 binbyte = ['00000000' dec2base(bytes(ptr),2)];
|
y@0
|
951 binarystring = [binarystring binbyte(end-6:end)];
|
y@0
|
952 ptr=ptr+1;
|
y@0
|
953 end
|
y@0
|
954 val = base2dec(binarystring,2);
|
y@0
|
955
|
y@0
|
956
|
y@0
|
957
|
y@0
|
958
|
y@0
|
959 %
|
y@0
|
960 % Read first 2 bytes of msg and
|
y@0
|
961 % determine the type
|
y@0
|
962 % (most require only 1st byte)
|
y@0
|
963 %
|
y@0
|
964 % str is one of:
|
y@0
|
965 % 'channel_mode'
|
y@0
|
966 % 'channel_voice'
|
y@0
|
967 % 'sysex'
|
y@0
|
968 % 'sys_realtime'
|
y@0
|
969 %
|
y@0
|
970 function str=midi_msg_type(B,nB)
|
y@0
|
971
|
y@0
|
972 Hn = bitshift(B,-4);
|
y@0
|
973 Ln = bitand(B,7);
|
y@0
|
974
|
y@0
|
975 % ---- channel mode messages:
|
y@0
|
976 %if (Hn==11 && nB>=120 && nB<=127)
|
y@0
|
977 if (Hn==11 && nB>=122 && nB<=127)
|
y@0
|
978 str = 'channel_mode';
|
y@0
|
979
|
y@0
|
980 % ---- channel voice messages:
|
y@0
|
981 elseif (Hn>=8 && Hn<=14)
|
y@0
|
982 str = 'channel_voice';
|
y@0
|
983
|
y@0
|
984 % ---- sysex events:
|
y@0
|
985 elseif (Hn==15 && Ln>=0 && Ln<=7)
|
y@0
|
986 str = 'sysex';
|
y@0
|
987
|
y@0
|
988 % system real-time messages
|
y@0
|
989 elseif (Hn==15 && Ln>=8 && Ln<=15)
|
y@0
|
990 % UNSURE: how can you tell between 0xFF system real-time
|
y@0
|
991 % message and 0xFF meta event?
|
y@0
|
992 % (now, it will always be processed by meta)
|
y@0
|
993 str = 'sys_realtime';
|
y@0
|
994
|
y@0
|
995 else
|
y@0
|
996 % don't think it can get here...
|
y@0
|
997 error('bad midi message');
|
y@0
|
998 end
|
y@0
|
999
|