ivan@81: function midi = readmidi(filename, rawbytes) ivan@81: % midi = readmidi(filename, rawbytes) ivan@81: % midi = readmidi(filename) ivan@81: % ivan@81: % Read MIDI file and store in a Matlab structure ivan@81: % (use midiInfo.m to see structure detail) ivan@81: % ivan@81: % Inputs: ivan@81: % filename - input MIDI file ivan@81: % rawbytes - 0 or 1: Include raw bytes in structure ivan@81: % This info is redundant, but can be ivan@81: % useful for debugging. default=0 ivan@81: % ivan@81: ivan@81: % Copyright (c) 2009 Ken Schutte ivan@81: % more info at: http://www.kenschutte.com/midi ivan@81: ivan@81: ivan@81: if (nargin<2) ivan@81: rawbytes=0; ivan@81: end ivan@81: ivan@81: fid = fopen(filename); ivan@81: [A count] = fread(fid,'uint8'); ivan@81: fclose(fid); ivan@81: ivan@81: if (rawbytes) midi.rawbytes_all = A; end ivan@81: ivan@81: % realtime events: status: [F8, FF]. no data bytes ivan@81: %clock, undefined, start, continue, stop, undefined, active ivan@81: %sensing, systerm reset ivan@81: ivan@81: % file consists of "header chunk" and "track chunks" ivan@81: % 4B 'MThd' (header) or 'MTrk' (track) ivan@81: % 4B 32-bit unsigned int = number of bytes in chunk, not ivan@81: % counting these first 8 ivan@81: ivan@81: ivan@81: % HEADER CHUNK -------------------------------------------------------- ivan@81: % 4B 'Mthd' ivan@81: % 4B length ivan@81: % 2B file format ivan@81: % 0=single track, 1=multitrack synchronous, 2=multitrack asynchronous ivan@81: % Synchronous formats start all tracks at the same time, while asynchronous formats can start and end any track at any time during the score. ivan@81: % 2B track cout (must be 1 for format 0) ivan@81: % 2B num delta-time ticks per quarter note ivan@81: % ivan@81: ivan@81: if ~isequal(A(1:4)',[77 84 104 100]) % double('MThd') ivan@81: error('File does not begin with header ID (MThd)'); ivan@81: end ivan@81: ivan@81: header_len = decode_int(A(5:8)); ivan@81: if (header_len == 6) ivan@81: else ivan@81: error('Header length != 6 bytes.'); ivan@81: end ivan@81: ivan@81: format = decode_int(A(9:10)); ivan@81: if (format==0 || format==1 || format==2) ivan@81: midi.format = format; ivan@81: else ivan@81: error('Format does not equal 0,1,or 2'); ivan@81: end ivan@81: ivan@81: num_tracks = decode_int(A(11:12)); ivan@81: if (format==0 && num_tracks~=1) ivan@81: error('File is format 0, but num_tracks != 1'); ivan@81: end ivan@81: ivan@81: time_unit = decode_int(A(13:14)); ivan@81: if (bitand(time_unit,2^15)==0) ivan@81: midi.ticks_per_quarter_note = time_unit; ivan@81: else ivan@81: error('Header: SMPTE time format found - not currently supported'); ivan@81: end ivan@81: ivan@81: if (rawbytes) ivan@81: midi.rawbytes_header = A(1:14); ivan@81: end ivan@81: ivan@81: % end header parse ---------------------------------------------------- ivan@81: ivan@81: ivan@81: ivan@81: ivan@81: ivan@81: ivan@81: % BREAK INTO SEPARATE TRACKS ------------------------------------------ ivan@81: % midi.track(1).data = [byte byte byte ...]; ivan@81: % midi.track(2).date = ... ivan@81: % ... ivan@81: % ivan@81: % Track Chunks--------- ivan@81: % 4B 'MTrk' ivan@81: % 4B length (after first 8B) ivan@81: % ivan@81: ctr = 15; ivan@81: for i=1:num_tracks ivan@81: ivan@81: if ~isequal(A(ctr:ctr+3)',[77 84 114 107]) % double('MTrk') ivan@81: error(['Track ' num2str(i) ' does not begin with track ID=MTrk']); ivan@81: end ivan@81: ctr = ctr+4; ivan@81: ivan@81: track_len = decode_int(A(ctr:ctr+3)); ivan@81: ctr = ctr+4; ivan@81: ivan@81: % have track.rawbytes hold initial 8B also... ivan@81: track_rawbytes{i} = A((ctr-8):(ctr+track_len-1)); ivan@81: ivan@81: if (rawbytes) ivan@81: midi.track(i).rawbytes_header = A(ctr-8:ctr-1); ivan@81: end ivan@81: ivan@81: ctr = ctr+track_len; ivan@81: end ivan@81: % ---------------------------------------------------------------------- ivan@81: ivan@81: ivan@81: ivan@81: ivan@81: ivan@81: ivan@81: % Events: ivan@81: % - meta events: start with 'FF' ivan@81: % - MIDI events: all others ivan@81: ivan@81: % MIDI events: ivan@81: % optional command byte + 0,1,or 2 bytes of parameters ivan@81: % "running mode": command byte omitted. ivan@81: % ivan@81: % all midi command bytes have MSB=1 ivan@81: % all data for inside midi command have value <= 127 (ie MSB=0) ivan@81: % -> so can determine running mode ivan@81: % ivan@81: % meta events' data may have any values (meta events have to set ivan@81: % len) ivan@81: % ivan@81: ivan@81: ivan@81: ivan@81: % 'Fn' MIDI commands: ivan@81: % no chan. control the entire system ivan@81: %F8 Timing Clock ivan@81: %FA start a sequence ivan@81: %FB continue a sequence ivan@81: %FC stop a sequence ivan@81: ivan@81: % Meta events: ivan@81: % 1B 0xFF ivan@81: % 1B event type ivan@81: % 1B length of additional data ivan@81: % ?? additional data ivan@81: % ivan@81: ivan@81: ivan@81: % "channel mode messages" ivan@81: % have same code as "control change": 0xBn ivan@81: % but uses reserved controller numbers 120-127 ivan@81: % ivan@81: ivan@81: ivan@81: %Midi events consist of an optional command byte ivan@81: % followed by zero, one or two bytes of parameters. ivan@81: % In running mode, the command can be omitted, in ivan@81: % which case the last MIDI command specified is ivan@81: % assumed. The first bit of a command byte is 1, ivan@81: % while the first bit of a parameter is always 0. ivan@81: % In addition, the last 4 bits of a command ivan@81: % indicate the channel to which the event should ivan@81: % be sent; therefore, there are 6 possible ivan@81: % commands (really 7, but we will discuss the x'Fn' ivan@81: % commands later) that can be specified. They are: ivan@81: ivan@81: ivan@81: % parse tracks ----------------------------------------- ivan@81: for i=1:num_tracks ivan@81: ivan@81: track = track_rawbytes{i}; ivan@81: ivan@81: if (rawbytes); midi.track(i).rawbytes = track; end ivan@81: ivan@81: msgCtr = 1; ivan@81: ctr=9; % first 8B were MTrk and length ivan@81: while (ctr < length(track_rawbytes{i})) ivan@81: ivan@81: clear currMsg; ivan@81: currMsg.used_running_mode = 0; ivan@81: % note: ivan@81: % .used_running_mode is necessary only to ivan@81: % be able to reconstruct a file _exactly_ from ivan@81: % the 'midi' structure. this is helpful for ivan@81: % debugging since write(read(filename)) can be ivan@81: % tested for exact replication... ivan@81: % ivan@81: ivan@81: ctr_start_msg = ctr; ivan@81: ivan@81: [deltatime,ctr] = decode_var_length(track, ctr); ivan@81: ivan@81: % ? ivan@81: %if (rawbytes) ivan@81: % currMsg.rawbytes_deltatime = track(ctr_start_msg:ctr-1); ivan@81: %end ivan@81: ivan@81: % deltaime must be 1-4 bytes long. ivan@81: % could check here... ivan@81: ivan@81: ivan@81: % CHECK FOR META EVENTS ------------------------ ivan@81: % 'FF' ivan@81: if track(ctr)==255 ivan@81: ivan@81: type = track(ctr+1); ivan@81: ivan@81: ctr = ctr+2; ivan@81: ivan@81: % get variable length 'length' field ivan@81: [len,ctr] = decode_var_length(track, ctr); ivan@81: ivan@81: % note: some meta events have pre-determined lengths... ivan@81: % we could try verifiying they are correct here. ivan@81: ivan@81: thedata = track(ctr:ctr+len-1); ivan@81: chan = []; ivan@81: ivan@81: ctr = ctr + len; ivan@81: ivan@81: midimeta = 0; ivan@81: ivan@81: else ivan@81: midimeta = 1; ivan@81: % MIDI EVENT --------------------------- ivan@81: ivan@81: ivan@81: ivan@81: ivan@81: % check for running mode: ivan@81: if (track(ctr)<128) ivan@81: ivan@81: % make it re-do last command: ivan@81: %ctr = ctr - 1; ivan@81: %track(ctr) = last_byte; ivan@81: currMsg.used_running_mode = 1; ivan@81: ivan@81: B = last_byte; ivan@81: nB = track(ctr); % ? ivan@81: ivan@81: else ivan@81: ivan@81: B = track(ctr); ivan@81: nB = track(ctr+1); ivan@81: ivan@81: ctr = ctr + 1; ivan@81: ivan@81: end ivan@81: ivan@81: % nibbles: ivan@81: %B = track(ctr); ivan@81: %nB = track(ctr+1); ivan@81: ivan@81: ivan@81: Hn = bitshift(B,-4); ivan@81: Ln = bitand(B,15); ivan@81: ivan@81: chan = []; ivan@81: ivan@81: msg_type = midi_msg_type(B,nB); ivan@81: ivan@81: % DEBUG: ivan@81: if (i==2) ivan@81: if (msgCtr==1) ivan@81: disp(msg_type); ivan@81: end ivan@81: end ivan@81: ivan@81: ivan@81: switch msg_type ivan@81: ivan@81: case 'channel_mode' ivan@81: ivan@81: % UNSURE: if all channel mode messages have 2 data byes (?) ivan@81: type = bitshift(Hn,4) + (nB-120+1); ivan@81: thedata = track(ctr:ctr+1); ivan@81: chan = Ln; ivan@81: ivan@81: ctr = ctr + 2; ivan@81: ivan@81: % ---- channel voice messages: ivan@81: case 'channel_voice' ivan@81: ivan@81: type = bitshift(Hn,4); ivan@81: len = channel_voice_msg_len(type); % var length data: ivan@81: thedata = track(ctr:ctr+len-1); ivan@81: chan = Ln; ivan@81: ivan@81: % DEBUG: ivan@81: if (i==2) ivan@81: if (msgCtr==1) ivan@81: disp([999 Hn type]) ivan@81: end ivan@81: end ivan@81: ivan@81: ctr = ctr + len; ivan@81: ivan@81: case 'sysex' ivan@81: ivan@81: % UNSURE: do sysex events (F0-F7) have ivan@81: % variable length 'length' field? ivan@81: ivan@81: [len,ctr] = decode_var_length(track, ctr); ivan@81: ivan@81: type = B; ivan@81: thedata = track(ctr:ctr+len-1); ivan@81: chan = []; ivan@81: ivan@81: ctr = ctr + len; ivan@81: ivan@81: case 'sys_realtime' ivan@81: ivan@81: % UNSURE: I think these are all just one byte ivan@81: type = B; ivan@81: thedata = []; ivan@81: chan = []; ivan@81: ivan@81: end ivan@81: ivan@81: last_byte = Ln + bitshift(Hn,4); ivan@81: ivan@81: end % end midi event 'if' ivan@81: ivan@81: ivan@81: currMsg.deltatime = deltatime; ivan@81: currMsg.midimeta = midimeta; ivan@81: currMsg.type = type; ivan@81: currMsg.data = thedata; ivan@81: currMsg.chan = chan; ivan@81: ivan@81: if (rawbytes) ivan@81: currMsg.rawbytes = track(ctr_start_msg:ctr-1); ivan@81: end ivan@81: ivan@81: midi.track(i).messages(msgCtr) = currMsg; ivan@81: msgCtr = msgCtr + 1; ivan@81: ivan@81: ivan@81: end % end loop over rawbytes ivan@81: end % end loop over tracks ivan@81: ivan@81: ivan@81: function val=decode_int(A) ivan@81: ivan@81: val = 0; ivan@81: for i=1:length(A) ivan@81: val = val + bitshift(A(length(A)-i+1), 8*(i-1)); ivan@81: end ivan@81: ivan@81: ivan@81: function len=channel_voice_msg_len(type) ivan@81: ivan@81: if (type==128); len=2; ivan@81: elseif (type==144); len=2; ivan@81: elseif (type==160); len=2; ivan@81: elseif (type==176); len=2; ivan@81: elseif (type==192); len=1; ivan@81: elseif (type==208); len=1; ivan@81: elseif (type==224); len=2; ivan@81: else ivan@81: disp(type); error('bad channel voice message type'); ivan@81: end ivan@81: ivan@81: ivan@81: % ivan@81: % decode variable length field (often deltatime) ivan@81: % ivan@81: % return value and new position of pointer into 'bytes' ivan@81: % ivan@81: function [val,ptr] = decode_var_length(bytes, ptr) ivan@81: ivan@81: keepgoing=1; ivan@81: binarystring = ''; ivan@81: while (keepgoing) ivan@81: % check MSB: ivan@81: % if MSB=1, then delta-time continues into next byte... ivan@81: if(~bitand(bytes(ptr),128)) ivan@81: keepgoing=0; ivan@81: end ivan@81: % keep appending last 7 bits from each byte in the deltatime: ivan@81: binbyte = ['00000000' dec2base(bytes(ptr),2)]; ivan@81: binarystring = [binarystring binbyte(end-6:end)]; ivan@81: ptr=ptr+1; ivan@81: end ivan@81: val = base2dec(binarystring,2); ivan@81: ivan@81: ivan@81: ivan@81: ivan@81: % ivan@81: % Read first 2 bytes of msg and ivan@81: % determine the type ivan@81: % (most require only 1st byte) ivan@81: % ivan@81: % str is one of: ivan@81: % 'channel_mode' ivan@81: % 'channel_voice' ivan@81: % 'sysex' ivan@81: % 'sys_realtime' ivan@81: % ivan@81: function str=midi_msg_type(B,nB) ivan@81: ivan@81: Hn = bitshift(B,-4); ivan@81: Ln = bitand(B,7); ivan@81: ivan@81: % ---- channel mode messages: ivan@81: %if (Hn==11 && nB>=120 && nB<=127) ivan@81: if (Hn==11 && nB>=122 && nB<=127) ivan@81: str = 'channel_mode'; ivan@81: ivan@81: % ---- channel voice messages: ivan@81: elseif (Hn>=8 && Hn<=14) ivan@81: str = 'channel_voice'; ivan@81: ivan@81: % ---- sysex events: ivan@81: elseif (Hn==15 && Ln>=0 && Ln<=7) ivan@81: str = 'sysex'; ivan@81: ivan@81: % system real-time messages ivan@81: elseif (Hn==15 && Ln>=8 && Ln<=15) ivan@81: % UNSURE: how can you tell between 0xFF system real-time ivan@81: % message and 0xFF meta event? ivan@81: % (now, it will always be processed by meta) ivan@81: str = 'sys_realtime'; ivan@81: ivan@81: else ivan@81: % don't think it can get here... ivan@81: error('bad midi message'); ivan@81: end