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