Find_PPQN.m
上传用户:abcwxk
上传日期:2016-05-20
资源大小:434k
文件大小:4k
源码类别:

midi

开发平台:

Visual C++

  1. function [tempo, time_signature, PPQN] = Find_PPQN(file);
  2. % 验证文件格式是否是midi,是否是format0,提取MThd头中的Division
  3. % 形参:mid文件名,这里使用的mid文件必须是format0,会有验证部分的代码
  4. % 返回值:PPQN(Pulses Per Quarter Note)每1/4音符所含的时钟数
  5. % delta-time的单位就是clock(Pulse)
  6. % 知识点:参考 Borg.com+Midi+Specification.pdf  Page151 : MThd chunk
  7. %     MThd chunk在C中的定义:
  8. %     struct MTHD_CHUNK
  9. %     { 
  10. %     /* Here's the 8 byte header that all chunks must have */ 
  11. %     char   ID[4]; /* This will be 'M','T','h','d' */ 
  12. %     unsigned long  Length; /* This will be 6 */ 
  13. %     /* Here are the 6 bytes */ 
  14. %     unsigned short Format;  /*只有0、1、2三种*/
  15. %     unsigned short  NumTracks;  /*Track的个数*/
  16. %     unsigned short Division; /*PPQN*/
  17. %     };
  18. %     Division表示每个1/4音符是多少个midi clocks,
  19. %     即PPQN(Pulses ie clocks Per Quarter Note),
  20. %     delta-time的单位是1个clock。
  21. %     Division第一字节如果为负,-24, -25, -29, or -30
  22. %     对应于4种SMPTE的标准,每秒多少帧。
  23. %     第二字节就表示每帧的精度,分成多少子帧。
  24. % 作者:刘涛 08年9月19日 15:37
  25. % 中国传媒大学 智能信息处理实验室
  26. % 1、文件格式验证 MThd chunk验证
  27. % (1)打开文件
  28. fid = fopen(file);
  29. if fid == -1
  30.     error('打开文件失败');
  31. end
  32. % (2)读取MThd chunk中的数据
  33. MThd_Chunk = fread(fid, 12, '*uint8');
  34. % (3)验证MThd chunk中的前面12个字节数据
  35. % 是不是'M' 'T' 'h' 'd' 0 0 0 6,验证是不是fomat 0,只有1个track
  36. if sum(MThd_Chunk(1:12) - uint8([77;84;104;100;0;0;0;6;0;0;0;1]))...
  37.         ~= 0
  38.     error('这不是midi文件');
  39. end
  40. % 2、读取Division并判断PPQN
  41. Division = fread(fid, 2, '*uint8');
  42. SMPTE = 0;  % 每秒多少帧,当division的data1为负的时候
  43.                         % data1表示SMPTE,只有4个值
  44.                         % -24,-25,-29 or -30
  45. PPQN = 0; % 每四分音符有几个clock
  46. deltatime = 0; % 1个clock的时间,单位是毫秒
  47. if Division(1) >127
  48.     switch Division(1)
  49.         case 232, % E8 -24
  50.             SMPTE = 24;
  51.         case 231, % E7 -25
  52.             SMPTE = 25;
  53.         case  227, % E3 -29
  54.             SMPTE = 29;
  55.         case 226, % E2 -30
  56.             SMPTE = 30;
  57.         otherwise
  58.             error('Division第一字节为负,但不属于-24-25-29-30这四种SMPTE标准格式');
  59.     end
  60.     deltatime = 1000 / SMPTE / Division(2);
  61.     error('没有找到PPQN,找到了SMPTE')
  62. else
  63.     PPQN = double(Division(1))*256 + double(Division(2));
  64. end
  65. % 3、遍历events data,寻找tempo和time sigature
  66. % 读取MThd chunk 、mtrk头 和 长度
  67. temp = fread(fid,4+4, '*uint8');
  68. MTrk = temp(1:4);
  69. if sum(char(MTrk) == ['M'; 'T'; 'r'; 'k']) ~= 4
  70.     error('不是MTrk头');
  71. end
  72. mtrkLength = double(temp(8)) + double(temp(7))*16^2 ...
  73.     + double(temp(6))*16^4 + double(temp(5))*16^6;
  74. % 全部读入MTrk chunk中的数据,以1个字节为单位循环分析数据
  75. eventsData = fread(fid,mtrkLength,'*uint8');
  76. fclose(fid);
  77. meta_data = 255; tempo_meta = 81;
  78. time_meta = 88;
  79. tempo = 500000; time_signature = [4 4];
  80. tempo_change = false; time_change = false;
  81. for i = 1:length(eventsData)
  82.     if eventsData(i) == meta_data
  83.         switch eventsData(i+1)
  84.             case tempo_meta, % Tempo
  85.                 if tempo_change
  86.                     disp('tempo被改了');
  87.                 else
  88.                     tempo_change = true;
  89.                 end
  90.                 tempo = double(eventsData(i+3))*16^4 + ...
  91.                     double(eventsData(i+4))*16^2 + ...
  92.                     double(eventsData(i+5));
  93.             case time_meta, % Time Signature
  94.                 if time_change
  95.                     disp('time signature被改了');
  96.                 else
  97.                     time_change = true;
  98.                 end
  99.                 time_signature(1) = double(eventsData(i+3));
  100.                 time_signature(2) = double(2^double(eventsData(i+4)));
  101.         end
  102.     end
  103. end  
  104. return;