annotate util/matlab_midi/midiInfo.m @ 93:43d9c4a22189

Mehrdad's comment on installing SMALLbox.
author Mehrdad <myvaigha@staffmail.ed.ac.uk>
date Tue, 12 Apr 2011 15:31:16 +0100
parents a30e8bd6d948
children
rev   line source
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