【MID音频读取和分析】基于matlab的MID音频文件读取和分析

时间:2022-10-10 15:58:12


目录

​1.软件版本​

​2.理论知识​

​3.核心代码​

​4.操作步骤与仿真结论​

​5.参考文献​

​6.完整源码获得方式​


1.软件版本

matlab2013b

2.理论知识

这个部分,不涉及什么原理,主要是对MIDI音频信号需要了解其主要的文件结构特点,MIDI文件的主要结构如下:

​http://wenku.baidu.com/view/298f6f758e9951e79b8927fa.html​

3.核心代码

function midi = readmidi(filename, rawbytes)



if (nargin<2)
rawbytes=0;
end

fid = fopen(filename);
[A count] = fread(fid,'uint8');
fclose(fid);

if (rawbytes) midi.rawbytes_all = A; end



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


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

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
function [PR,t,nn] = piano_roll(Notes,vel,ts)
%
% Inputs:
% Notes: A 'notes' matrix as returned from midiInfo.m
% vel: (optional) if vel==1, set value to note velocity instead of 1. (default 0)
% ts: (optional) time step of one 'pixel' in seconds (default 0.01)
%
% Outputs:
% PR: PR(ni,ti): value at note index ni, time index ti
% t: t(ti): time value in seconds at time index ti
% nn: nn(ni): note number at note index ti
%


if nargin < 2
vel = 0;
end
if nargin < 3
ts = 0.01;
end

Nnotes = size(Notes,1);

n1 = round(Notes(:,5)/ts)+1;
n2 = round(Notes(:,6)/ts)+1;

if vel == 0
vals = ones(Nnotes,1);
else
vals = Notes(:,4); % velocity
end

for i=1:Nnotes
PR(Notes(i,3), n1(i):n2(i)) = vals(i);
end

% create quantized time axis:
t = linspace(0,max(Notes(:,6)),size(PR,2));
% note axis:
nn = min(Notes(:,3)):max(Notes(:,3));
% truncate to notes used:
PR = PR(nn,:);

4.操作步骤与仿真结论

【MID音频读取和分析】基于matlab的MID音频文件读取和分析

5.参考文献

​MIDI文件格式分析 - 百度文库​

A03-11