midird3.m

来自「非常好的数字处理教程」· M 代码 · 共 909 行 · 第 1/3 页

M
909
字号
function [midi,info,ext,ms_per_quarter,ms_per_tick] = midird3(fname,txtname)
% function [midi,info,ext,ms_per_quarter,ms_per_tick] = midird3(fname,txtname)
%
%  This function reads Standard MIDI Files (SMFs) and returns their contents
%  in the two structures midi and info.
%
%     midi     is a struct array containing one field for each track of the
%              midi file. Within each field, the midi notes are given in
%              the format
%
%              (delta time,absolute time, midi note number, note duration, note velocity,
%               channel)
%
%     info     contains all sideinfo from the header chunk as well as meter and time
%              information from the MIDI meta events
%
%     ext      is all extended information besides notes contained in the MIDI file.
%              This is needed, e.g., to reproduce the binary MIDI files
%              using midiwr.m
%              
%              -> All system exclusive data are stored in ext
%
%              Format of ext:  (track abstime [binary MIDI sequence])
%
%     ms_per_quarter     milliseconds per quarter note, if indicated by
%              MIDI-file (set to 0, if no information on time-coding is
%              found in MIDI-file)
%
%     ms_per_tick       milliseconds per MIDI tick, if indicated by
%              MIDI-file (set to 0, if no information on time-coding is
%              found in MIDI-file)
%
%  Inputs:    fname     Name of input MIDI-File
%
%			  (optional) txtname   Name of Output-Textfile
%
%             If the optional second argument txtname is used, a text
%             version of the MIDI-file is written to a file named textname
%
%   Changes:  * Frank Kurth, Feb., 26th, 2004: Rework to cope with wrongly
%             formatted SMFs
%             * Andreas Ribbrock, Apr., 14th, 2004: Stabilized w.r.t. wrongly
%             formatted SMFiles
%             * Frank Kurth, May, 11th, 2004: Incorporated functionality to
%             read time-coding from SMF (by Hinnerk Feldwisch and Meinard M黮ler)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%   Last changes by Frank Kurth, May, 11th, 2004
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
VLQLEN = 4;    % Length of variable length quantities, currently == 4 
TUPLEN = 6;

% The following are define global for use in the subfunctions within
% this file:

global smf;
global abstime;
%global ext;
%global extptr;

% some constants

UNDEF = -1; NOTEON = 0; NOTEOFF = 1; POLYAT = 2; CONTROLCHANGE = 3; PROGCHANGE = 4;
CHANNELAT = 5; PITCHWHEEL = 6; EXCEPT = 7;

% -fk 25.02.04

data = zeros(1,2);

% open input file

fid = fopen(fname,'r');

if fid < 0
   disp(['midird3: Error opening MIDI file: ' fname]);
   return;
end

% check if output file (MIDIasText) is requested

if nargin > 1
   
   TXT = 1;
   fidtxt = fopen(txtname,'w');
   if fid < 0
   	disp('midird3: Error opening output file!');
      fclose(fid);
      return;
	end

else
   TXT = 0;
end

% read full MIDI file

smf = [fread(fid,inf,'uint8')' zeros(1,10)];
ptr = 1;

extptr = 1;
status = 255;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Read MIDI Track header
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

ptr = NextValidHeader(ptr);

if(ptr <0 | ptr+4 > length(smf) | ptr+7 > length(smf))
   disp('Midi file format error on line 99 of midird3');
   fclose(fid);
   info = cell(0,0);
   midi = cell(0,0);
   ext  = cell(0,0);
   return;
end

len = Read32(smf(ptr+4:ptr+7));

ptr = ptr + 8;                % 'MThd' 

if(ptr <0 | ptr+1 > length(smf))
   disp('Midi file format error on line 110 of midird3');
   fclose(fid);
   info = cell(0,0);
   midi = cell(0,0);
   ext  = cell(0,0);
   return;
end
info.format = Read16(smf(ptr:ptr+1));

if(ptr <0 | ptr+2 > length(smf) | ptr+3 > length(smf))
   disp('Midi file format error on line 118 of midird3');
   fclose(fid);
   info = cell(0,0);
   midi = cell(0,0);
   ext  = cell(0,0);
   return;
end
info.ntrks  = Read16(smf(ptr+2:ptr+3));
if smf(ptr+4) < 0
   info.division.up = double(uint8(smf(ptr+4)));
   info.division.low = smf(ptr+5);
   info.division.main = inf;
else
   info.division.main = Read16(smf(ptr+4:ptr+5));
   info.division.up = inf;
   info.division.low = inf;
end

if TXT
   
   fprintf(fidtxt,'MIDI Header Chunk\n\n');
   fprintf(fidtxt,'MIDI File Format: %d \n',info.format);
   fprintf(fidtxt,'No. of Tracks: %d\n',info.ntrks);
   if smf(ptr+4) < 0
   	fprintf(fidtxt,'Time Division: (%d,%d)\n',[info.division.up info.division.low]);   
   else
      fprintf(fidtxt,'Time Division: %d\n',info.division.main);         
   end
   
end

ptr = ptr + len;					% 32BitLength,  the length should be 6 in SMFs

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Read Tracks
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

midi = cell(1,info.ntrks);

abstime = 0;

for k = 1:info.ntrks
   
   if(ptr < 1 | ptr > length(smf))
      disp('Midi file format error on line 162 of midird3');
      fclose(fid);
      info = cell(0,0);
      midi = cell(0,0);
      ext  = cell(0,0);
      return;
   end
   
   ptr = NextValidHeader(ptr);
   
   if(ptr < 1 | ptr > length(smf))
      disp('Midi file format error on line 171 of midird3');
      fclose(fid);
      info = cell(0,0);
      midi = cell(0,0);
      ext  = cell(0,0);
      return;
   end

   
   ptrackstart = ptr;
   
   if TXT
      
      fprintf(fidtxt,'---------------------------------------------------------------------------\n');
      fprintf(fidtxt,'MIDI Track Chunk #%d\n\n',k-1);
      
   end
   
   s = char(smf(ptr:ptr+3));
   %s = sprintf('%s',smf(ptr:ptr+3))
   if(~strcmp(s,'MTrk'))
       s = sprintf('midird3: MTrk expected, %s found!',smf(ptr:ptr+3));
      disp(s);
      return;
   end
   tracklen = Read32(smf(ptr+4:ptr+7));    % track length
   ptr = ptr + 8;									 % track start after header
   
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   % start: process track
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   
   notes = zeros(tracklen,TUPLEN);				 % faster to create array at once
   notepos = 1;
   runningstatus.cmd = UNDEF;
   runningstatus.channel = UNDEF;
   actnotes = ones(1,TUPLEN)*-1;
   uniqueID = -2;
   
   if info.format ~= 2							 % Note: abstime (here) is independent of the
      abstime = 0;                         %       time signature which is eventually
   end												 %       present in the MIDI file (Meta Event)
   													 %       abstime only counts delta ticks
                                           
   while ptr <= ptrackstart + 8 + tracklen % start: track loop
      
      if(ptr+VLQLEN-1 <0 | ptr+VLQLEN-1 > length(smf)) 
         disp('Midi file format error on line 215 of midird3');
      	fclose(fid);
      	info = cell(0,0);
        midi = cell(0,0);
        ext  = cell(0,0);
	      return;
      end
      
      
      if(ptr <1 | (ptr+VLQLEN-1) > length(smf))
         info = cell(0,0);
         midi = cell(0,0);
         ext  = cell(0,0);
			disp('Midi file format error on line 224 of midird3');
         fclose(fid);
      	return;   
      end
      
      
      [delta,offs] = readvarlenintern(smf(ptr:ptr+VLQLEN-1));
      
      if(delta == 0 & offs == 0)
         info = cell(0,0);
         midi = cell(0,0);
         ext  = cell(0,0);
	     disp('Midi file format error on line 234 of midird3');
         fclose(fid);
      	return;   
      end
      
      abstime = abstime + delta;
      ptr = ptr+offs;
    
      eventstring = sprintf('%d\t\t\t\t',delta);         
         
      next = double(uint8(smf(ptr)));
      
      if(bitand(next,128))	 							 % status byte found if expn TRUE
         										    % evaluate new command (->running status)
         status = next;                                  
         if TXT                                 
             eventstring = [eventstring sprintf('Status %x ',next)];                                  
         end    
         channel = bitand(next,15);
                        
         switch bitand(next,240)					 % &F0
            
            case 144,				             % $90, NOTEON
               runningstatus.cmd = NOTEON;
               runningstatus.channel = channel;
               ptr = ptr + 1;
            case 128				             % $80, NOTEOFF
               runningstatus.cmd = NOTEOFF;  
               runningstatus.channel = channel;  
               ptr = ptr + 1;
            case 160,				 % $A0, POLYAT
               runningstatus.cmd = POLYAT;
               runningstatus.channel = channel;
               ptr = ptr + 1;
            case 176,				 % $B0, CONTROLCHANGE
               runningstatus.cmd = CONTROLCHANGE;
               runningstatus.channel = channel;
               ptr = ptr + 1;
            case 192,            % $C0, PROGCHANGE

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?