annotate Sirtassa/midi2nmat.m @ 2:13ec2fa02a26 tip

(none)
author Yannick JACOB <y.jacob@se12.qmul.ac.uk>
date Tue, 03 Sep 2013 15:33:42 +0100
parents 2cd427e000b0
children
rev   line source
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