libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

cmMidiFile.c 26KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmFile.h"
  7. #include "cmMem.h"
  8. #include "cmMallocDebug.h"
  9. #include "cmLinkedHeap.h"
  10. #include "cmMidi.h"
  11. #include "cmMidiFile.h"
  12. #ifdef cmBIG_ENDIAN
  13. #define mfSwap16(v) (v)
  14. #define mfSwap32(v) (v)
  15. #else
  16. #define mfSwap16(v) cmSwap16(v)
  17. #define mfSwap32(v) cmSwap32(v)
  18. #endif
  19. typedef struct
  20. {
  21. unsigned cnt; // count of track records
  22. cmMidiTrackMsg_t* base; // pointer to first track recd
  23. cmMidiTrackMsg_t* last; // pointer to last track recd
  24. } _cmMidiTrack_t;
  25. typedef struct
  26. {
  27. cmErr_t err; // this objects error object
  28. cmLHeapH_t lhH; // linked heap used for all dynamically alloc'd data space
  29. cmFileH_t fh; // cmFile handle (only used in fmMidiFileOpen())
  30. unsigned short fmtId; // midi file type id: 0,1,2
  31. unsigned short ticksPerQN; // ticks per quarter note or 0 if smpteFmtId is valid
  32. cmMidiByte_t smpteFmtId; // smpte format or 0 if ticksPerQN is valid
  33. cmMidiByte_t smpteTicksPerFrame; // smpte ticks per frame or 0 if ticksPerQN is valid
  34. unsigned short trkN; // track count
  35. _cmMidiTrack_t* trkV; // track vector
  36. char* fn; // file name or NULL if this object did not originate from a file
  37. unsigned msgN; // count of msg's in msgV[]
  38. cmMidiTrackMsg_t** msgV; // sorted msg list
  39. } _cmMidiFile_t;
  40. #define _cmMidiFileError( err, rc ) _cmMidiFileOnError(err, rc, __LINE__,__FILE__,__FUNCTION__ )
  41. cmMidiFileH_t cmMidiFileNullHandle = cmSTATIC_NULL_HANDLE;
  42. cmMfRC_t _cmMidiFileOnError( cmErr_t* err, cmMfRC_t rc, int line, const char* fn, const char* func )
  43. {
  44. return cmErrMsg(err,rc,"rc:%i line:%i %s %s\n",rc,line,func,fn);
  45. }
  46. _cmMidiFile_t* _cmMidiFileHandleToPtr( cmMidiFileH_t h )
  47. {
  48. _cmMidiFile_t* p = (_cmMidiFile_t*)h.h;
  49. assert( p != NULL );
  50. return p;
  51. }
  52. void* _cmMidiFileMalloc( _cmMidiFile_t* mfp, unsigned byteN )
  53. { return cmLHeapAllocZ(mfp->lhH,byteN); }
  54. cmMfRC_t _cmMidiFileRead8( _cmMidiFile_t* mfp, cmMidiByte_t* p )
  55. {
  56. if( cmFileReadUChar(mfp->fh,p,1) != kOkFileRC )
  57. return _cmMidiFileError(&mfp->err,kSysFreadFailMfRC);
  58. return kOkMfRC;
  59. }
  60. cmMfRC_t _cmMidiFileRead16( _cmMidiFile_t* mfp, unsigned short* p )
  61. {
  62. if( cmFileReadUShort(mfp->fh,p,1) != kOkFileRC )
  63. return _cmMidiFileError(&mfp->err,kSysFreadFailMfRC);
  64. *p = mfSwap16(*p);
  65. return kOkMfRC;
  66. }
  67. cmMfRC_t _cmMidiFileRead24( _cmMidiFile_t* mfp, unsigned* p )
  68. {
  69. *p = 0;
  70. int i = 0;
  71. for(; i<3; ++i)
  72. {
  73. unsigned char c;
  74. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  75. return _cmMidiFileError(&mfp->err,kSysFreadFailMfRC);
  76. *p = (*p << 8) + c;
  77. }
  78. //*p =mfSwap32(*p);
  79. return kOkMfRC;
  80. }
  81. cmMfRC_t _cmMidiFileRead32( _cmMidiFile_t* mfp, unsigned* p )
  82. {
  83. if( cmFileReadUInt(mfp->fh,p,1) != kOkFileRC )
  84. return _cmMidiFileError(&mfp->err,kSysFreadFailMfRC);
  85. *p = mfSwap32(*p);
  86. return kOkMfRC;
  87. }
  88. cmMfRC_t _cmMidiFileReadText( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  89. {
  90. if( byteN == 0 )
  91. return kOkMfRC;
  92. char* t = (char*)_cmMidiFileMalloc(mfp,byteN+1);
  93. t[byteN] = 0;
  94. if( cmFileReadChar(mfp->fh,t,byteN) != kOkFileRC )
  95. return _cmMidiFileError(&mfp->err,kSysFreadFailMfRC);
  96. tmp->u.text = t;
  97. tmp->byteCnt = byteN;
  98. return kOkMfRC;
  99. }
  100. cmMfRC_t _cmMidiFileReadRecd( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  101. {
  102. char* t = (char*)_cmMidiFileMalloc(mfp,byteN);
  103. if( cmFileReadChar(mfp->fh,t,byteN) != kOkFileRC )
  104. return _cmMidiFileError(&mfp->err,kSysFreadFailMfRC);
  105. tmp->byteCnt = byteN;
  106. tmp->u.voidPtr = t;
  107. return kOkMfRC;
  108. }
  109. cmMfRC_t _cmMidiFileReadVarLen( _cmMidiFile_t* mfp, unsigned* p )
  110. {
  111. unsigned char c;
  112. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  113. return _cmMidiFileError(&mfp->err,kSysFreadFailMfRC);
  114. if( !(c & 0x80) )
  115. *p = c;
  116. else
  117. {
  118. *p = c & 0x7f;
  119. do
  120. {
  121. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  122. return _cmMidiFileError(&mfp->err,kSysFreadFailMfRC);
  123. *p = (*p << 7) + (c & 0x7f);
  124. }while( c & 0x80 );
  125. }
  126. return kOkMfRC;
  127. }
  128. cmMfRC_t _cmMidiFileAppendTrackMsg( _cmMidiFile_t* mfp, unsigned short trkIdx, unsigned dtick, cmMidiByte_t status, cmMidiTrackMsg_t** trkMsgPtrPtr )
  129. {
  130. cmMidiTrackMsg_t* tmp = (cmMidiTrackMsg_t*)_cmMidiFileMalloc(mfp, sizeof(cmMidiTrackMsg_t) );
  131. // link new record onto track record chain
  132. if( mfp->trkV[trkIdx].base == NULL )
  133. mfp->trkV[trkIdx].base = tmp;
  134. else
  135. mfp->trkV[trkIdx].last->link = tmp;
  136. mfp->trkV[trkIdx].last = tmp;
  137. mfp->trkV[trkIdx].cnt++;
  138. // set the generic track record fields
  139. tmp->dtick = dtick;
  140. tmp->status = status;
  141. tmp->metaId = kInvalidMetaMdId;
  142. tmp->trkIdx = trkIdx;
  143. tmp->byteCnt = 0;
  144. *trkMsgPtrPtr = tmp;
  145. return kOkMfRC;
  146. }
  147. cmMfRC_t _cmMidiFileReadSysEx( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  148. {
  149. cmMfRC_t rc = kOkMfRC;
  150. cmMidiByte_t b = 0;
  151. if( byteN == cmInvalidCnt )
  152. {
  153. long offs;
  154. if( cmFileTell(mfp->fh,&offs) != kOkFileRC )
  155. return _cmMidiFileError(&mfp->err,kSysFtellFailMfRC);
  156. byteN = 0;
  157. // get the length of the sys-ex msg
  158. while( !cmFileEof(mfp->fh) && (b != kSysComEoxMdId) )
  159. {
  160. if((rc = _cmMidiFileRead8(mfp,&b)) != kOkMfRC )
  161. return rc;
  162. ++byteN;
  163. }
  164. // verify that the EOX byte was found
  165. if( b != kSysComEoxMdId )
  166. return _cmMidiFileError(&mfp->err,kMissingEoxMfRC);
  167. // rewind to the beginning of the msg
  168. if( cmFileSeek(mfp->fh,kBeginFileFl,offs) != kOkFileRC )
  169. return _cmMidiFileError(&mfp->err,kSysFseekFailMfRC);
  170. }
  171. // allocate memory to hold the sys-ex msg
  172. cmMidiByte_t* mp = (cmMidiByte_t *)_cmMidiFileMalloc(mfp, byteN );
  173. // read the sys-ex msg from the file into msg memory
  174. if( cmFileReadUChar(mfp->fh,mp,byteN) != kOkFileRC )
  175. return _cmMidiFileError(&mfp->err,kSysFreadFailMfRC);
  176. tmp->byteCnt = byteN;
  177. tmp->u.sysExPtr = mp;
  178. return rc;
  179. }
  180. cmMfRC_t _cmMidiFileReadChannelMsg( _cmMidiFile_t* mfp, cmMidiByte_t* rsPtr, cmMidiByte_t status, cmMidiTrackMsg_t* tmp )
  181. {
  182. cmMfRC_t rc = kOkMfRC;
  183. cmMidiChMsg_t* p = (cmMidiChMsg_t*)_cmMidiFileMalloc(mfp,sizeof(cmMidiChMsg_t));
  184. unsigned useRsFl = status <= 0x7f;
  185. cmMidiByte_t statusCh = useRsFl ? *rsPtr : status;
  186. if( useRsFl )
  187. p->d0 = status;
  188. else
  189. *rsPtr = status;
  190. tmp->byteCnt = sizeof(cmMidiChMsg_t);
  191. tmp->status = statusCh & 0xf0;
  192. p->ch = statusCh & 0x0f;
  193. p->durTicks = 0;
  194. unsigned byteN = cmMidiStatusToByteCount(tmp->status);
  195. if( byteN==kInvalidMidiByte || byteN > 2 )
  196. return cmErrMsg(&mfp->err,kInvalidStatusMfRC,"Invalid status:0x%x %i.",tmp->status,tmp->status);
  197. unsigned i;
  198. for(i=useRsFl; i<byteN; ++i)
  199. {
  200. cmMidiByte_t* b = i==0 ? &p->d0 : &p->d1;
  201. if((rc = _cmMidiFileRead8(mfp,b)) != kOkMfRC )
  202. return rc;
  203. }
  204. // convert note-on velocity=0 to note off
  205. if( tmp->status == kNoteOnMdId && p->d1==0 )
  206. tmp->status = kNoteOffMdId;
  207. tmp->u.chMsgPtr = p;
  208. return rc;
  209. }
  210. cmMfRC_t _cmMidiFileReadMetaMsg( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp )
  211. {
  212. cmMidiByte_t metaId;
  213. cmMfRC_t rc;
  214. unsigned byteN = 0;
  215. if((rc = _cmMidiFileRead8(mfp,&metaId)) != kOkMfRC )
  216. return rc;
  217. if((rc = _cmMidiFileReadVarLen(mfp,&byteN)) != kOkMfRC )
  218. return rc;
  219. //printf("mt: %i 0x%x n:%i\n",metaId,metaId,byteN);
  220. switch( metaId )
  221. {
  222. case kSeqNumbMdId: rc = _cmMidiFileRead16(mfp,&tmp->u.sVal); break;
  223. case kTextMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  224. case kCopyMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  225. case kTrkNameMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  226. case kInstrNameMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  227. case kLyricsMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  228. case kMarkerMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  229. case kCuePointMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  230. case kMidiChMdId: rc = _cmMidiFileRead8(mfp,&tmp->u.bVal); break;
  231. case kEndOfTrkMdId: break;
  232. case kTempoMdId: rc = _cmMidiFileRead24(mfp,&tmp->u.iVal); break;
  233. case kSmpteMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiSmpte_t)); break;
  234. case kTimeSigMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiTimeSig_t)); break;
  235. case kKeySigMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiKeySig_t)); break;
  236. case kSeqSpecMdId: rc = _cmMidiFileReadSysEx(mfp,tmp,byteN); break;
  237. default:
  238. cmFileSeek(mfp->fh,kCurFileFl,byteN);
  239. cmErrMsg(&mfp->err,kUnknownMetaIdMfRC,"Unknown meta status:0x%x %i.",metaId,metaId);
  240. rc = _cmMidiFileError(&mfp->err,kUnknownMetaIdMfRC);
  241. }
  242. tmp->metaId = metaId;
  243. return rc;
  244. }
  245. cmMfRC_t _cmMidiFileReadTrack( _cmMidiFile_t* mfp, unsigned short trkIdx )
  246. {
  247. cmMfRC_t rc = kOkMfRC;
  248. unsigned dticks = 0;
  249. cmMidiByte_t status;
  250. cmMidiByte_t runstatus = 0;
  251. bool contFl = true;
  252. while( contFl && (rc==kOkMfRC))
  253. {
  254. cmMidiTrackMsg_t* tmp = NULL;
  255. // read the tick count
  256. if((rc = _cmMidiFileReadVarLen(mfp,&dticks)) != kOkMfRC )
  257. return rc;
  258. // read the status byte
  259. if((rc = _cmMidiFileRead8(mfp,&status)) != kOkMfRC )
  260. return rc;
  261. //printf("st:%i 0x%x\n",status,status);
  262. // append a track msg
  263. if((rc = _cmMidiFileAppendTrackMsg( mfp, trkIdx, dticks, status, &tmp )) != kOkMfRC )
  264. return rc;
  265. // switch on status
  266. switch( status )
  267. {
  268. // handle sys-ex msg
  269. case kSysExMdId:
  270. rc = _cmMidiFileReadSysEx(mfp,tmp,cmInvalidCnt);
  271. break;
  272. // handle meta msg
  273. case kMetaStId:
  274. rc = _cmMidiFileReadMetaMsg(mfp,tmp);
  275. // ignore unknown meta messages
  276. if( rc == kUnknownMetaIdMfRC )
  277. rc = kOkMfRC;
  278. contFl = tmp->metaId != kEndOfTrkMdId;
  279. break;
  280. default:
  281. // handle channel msg
  282. rc = _cmMidiFileReadChannelMsg(mfp,&runstatus,status,tmp);
  283. }
  284. }
  285. return rc;
  286. }
  287. cmMfRC_t _cmMidiFileReadHdr( _cmMidiFile_t* mfp )
  288. {
  289. cmMfRC_t rc;
  290. unsigned fileId;
  291. unsigned chunkByteN;
  292. // read the file id
  293. if((rc = _cmMidiFileRead32(mfp,&fileId)) != kOkMfRC )
  294. return rc;
  295. // verify the file id
  296. if( fileId != 'MThd' )
  297. return _cmMidiFileError(&mfp->err,kNotAMidiFileMfRC);
  298. // read the file chunk byte count
  299. if((rc = _cmMidiFileRead32(mfp,&chunkByteN)) != kOkMfRC )
  300. return rc;
  301. // read the format id
  302. if((rc = _cmMidiFileRead16(mfp,&mfp->fmtId)) != kOkMfRC )
  303. return rc;
  304. // read the track count
  305. if((rc = _cmMidiFileRead16(mfp,&mfp->trkN)) != kOkMfRC )
  306. return rc;
  307. // read the ticks per quarter note
  308. if((rc = _cmMidiFileRead16(mfp,&mfp->ticksPerQN)) != kOkMfRC )
  309. return rc;
  310. // if the division field was given in smpte
  311. if( mfp->ticksPerQN & 0x8000 )
  312. {
  313. mfp->smpteFmtId = (mfp->ticksPerQN & 0x7f00) >> 8;
  314. mfp->smpteTicksPerFrame = (mfp->ticksPerQN & 0xFF);
  315. mfp->ticksPerQN = 0;
  316. }
  317. // allocate and zero the track array
  318. if( mfp->trkN )
  319. mfp->trkV = _cmMidiFileMalloc( mfp, sizeof(_cmMidiTrack_t)*mfp->trkN);
  320. return rc;
  321. }
  322. int _cmMidiFileSortFunc( const void *p0, const void* p1 )
  323. {
  324. //printf("%i %i\n",(*(cmMidiTrackMsg_t**)p0)->dticks,(*(cmMidiTrackMsg_t**)p1)->dticks);
  325. if( (*(cmMidiTrackMsg_t**)p0)->dtick == (*(cmMidiTrackMsg_t**)p1)->dtick )
  326. return 0;
  327. return (*(cmMidiTrackMsg_t**)p0)->dtick < (*(cmMidiTrackMsg_t**)p1)->dtick ? -1 : 1;
  328. }
  329. cmMfRC_t _cmMidiFileClose( _cmMidiFile_t* mfp )
  330. {
  331. cmMfRC_t rc = kOkMfRC;
  332. if( mfp == NULL )
  333. return rc;
  334. cmMemPtrFree(&mfp->msgV);
  335. if( cmFileIsValid( mfp->fh ) )
  336. if( cmFileClose( &mfp->fh ) != kOkFileRC )
  337. rc = _cmMidiFileError(&mfp->err,kSysFcloseFailMfRC);
  338. if( cmLHeapIsValid( mfp->lhH ) )
  339. cmLHeapDestroy(&mfp->lhH);
  340. cmMemPtrFree(&mfp);
  341. return rc;
  342. }
  343. cmMfRC_t cmMidiFileOpen( const char* fn, cmMidiFileH_t* hPtr, cmCtx_t* ctx )
  344. {
  345. cmMfRC_t rc = kOkMfRC;
  346. _cmMidiFile_t* mfp = NULL;
  347. unsigned short trkIdx = 0;
  348. cmErr_t err;
  349. cmErrSetup(&err,&ctx->rpt,"MIDI File");
  350. // allocate the midi file object
  351. if(( mfp = cmMemAllocZ( _cmMidiFile_t, 1)) == NULL )
  352. return rc = _cmMidiFileError(&err,kMemAllocFailMfRC);
  353. cmErrClone(&mfp->err,&err);
  354. // allocate the linked heap
  355. if( cmLHeapIsValid( mfp->lhH = cmLHeapCreate( 1024, ctx )) == false )
  356. {
  357. rc = _cmMidiFileError(&err,kMemAllocFailMfRC);
  358. goto errLabel;
  359. }
  360. // open the file
  361. if(cmFileOpen(&mfp->fh,fn,kReadFileFl | kBinaryFileFl,mfp->err.rpt) != kOkFileRC )
  362. {
  363. rc = _cmMidiFileError(&mfp->err,kSysFopenFailMfRC);
  364. goto errLabel;
  365. }
  366. // read header and setup track array
  367. if(( rc = _cmMidiFileReadHdr(mfp)) != kOkMfRC )
  368. goto errLabel;
  369. while( !cmFileEof(mfp->fh) && trkIdx < mfp->trkN )
  370. {
  371. unsigned chkId=0,chkN=0;
  372. // read the chunk id
  373. if((rc = _cmMidiFileRead32(mfp,&chkId)) != kOkMfRC )
  374. goto errLabel;
  375. // read the chunk size
  376. if((rc = _cmMidiFileRead32(mfp,&chkN)) != kOkMfRC )
  377. goto errLabel;
  378. // if this is not a trk chunk then skip it
  379. if( chkId != (unsigned)'MTrk')
  380. {
  381. //if( fseek( mfp->fp, chkN, SEEK_CUR) != 0 )
  382. if( cmFileSeek(mfp->fh,kCurFileFl,chkN) != kOkFileRC )
  383. {
  384. rc = _cmMidiFileError(&mfp->err,kSysFseekFailMfRC);
  385. goto errLabel;
  386. }
  387. }
  388. else
  389. {
  390. if((rc = _cmMidiFileReadTrack(mfp,trkIdx)) != kOkMfRC )
  391. goto errLabel;
  392. ++trkIdx;
  393. }
  394. }
  395. // get the total trk msg count
  396. mfp->msgN = 0;
  397. for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx)
  398. mfp->msgN += mfp->trkV[ trkIdx ].cnt;
  399. // allocate the trk msg index vector: msgV[]
  400. mfp->msgV = cmMemAllocZ(cmMidiTrackMsg_t*, mfp->msgN);
  401. // store a pointer to every trk msg in msgV[]
  402. // and convert tick to absolute tick
  403. unsigned i = 0;
  404. for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx)
  405. {
  406. unsigned tick = 0;
  407. cmMidiTrackMsg_t* tmp = mfp->trkV[ trkIdx ].base;
  408. while( tmp != NULL )
  409. {
  410. assert( i < mfp->msgN);
  411. tick += tmp->dtick; // convert delta-ticks to absolute ticks
  412. tmp->dtick = tick;
  413. mfp->msgV[i] = tmp;
  414. tmp = tmp->link;
  415. ++i;
  416. }
  417. }
  418. // sort msgV[] in ascending order on dtick
  419. qsort( mfp->msgV, mfp->msgN, sizeof(cmMidiTrackMsg_t*), _cmMidiFileSortFunc );
  420. //for(i=0; i<25; ++i)
  421. // printf("%i 0x%x 0x%x\n",mfp->msgV[i]->tick,mfp->msgV[i]->status,mfp->msgV[i]->metaId);
  422. mfp->fn = _cmMidiFileMalloc(mfp,strlen(fn)+1);
  423. assert( mfp->fn != NULL );
  424. strcpy(mfp->fn,fn);
  425. //
  426. // calculate the total duration of the MIDI file and convert absolute ticks back to delta ticks
  427. //
  428. unsigned mi;
  429. unsigned prvTick = 0;
  430. for(mi=0; mi<mfp->msgN; ++mi)
  431. {
  432. cmMidiTrackMsg_t* mp = mfp->msgV[mi];
  433. // convert absolute ticks back to delta ticks
  434. unsigned dtick = mp->dtick - prvTick;
  435. prvTick = mp->dtick;
  436. mp->dtick = dtick;
  437. }
  438. hPtr->h = mfp;
  439. errLabel:
  440. if( cmFileClose(&mfp->fh) != kOkFileRC )
  441. rc = _cmMidiFileError(&mfp->err,kCloseFailFileRC);
  442. if( rc != kOkMfRC )
  443. _cmMidiFileClose(mfp);
  444. return rc;
  445. }
  446. cmMfRC_t cmMidiFileClose( cmMidiFileH_t* h )
  447. {
  448. cmMfRC_t rc;
  449. if( cmMidiFileIsNull(*h) )
  450. return kOkMfRC;
  451. if((rc = _cmMidiFileClose(_cmMidiFileHandleToPtr(*h))) == kOkMfRC )
  452. return rc;
  453. h->h = NULL;
  454. return rc;
  455. }
  456. unsigned cmMidiFileTrackCount( cmMidiFileH_t h )
  457. {
  458. _cmMidiFile_t* mfp;
  459. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  460. return cmInvalidCnt;
  461. return mfp->trkN;
  462. }
  463. unsigned cmMidiFileType( cmMidiFileH_t h )
  464. {
  465. _cmMidiFile_t* mfp;
  466. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  467. return cmInvalidId;
  468. return mfp->fmtId;
  469. }
  470. const char* cmMidiFileName( cmMidiFileH_t h )
  471. {
  472. _cmMidiFile_t* mfp;
  473. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  474. return NULL;
  475. return mfp->fn;
  476. }
  477. unsigned cmMidiFileTicksPerQN( cmMidiFileH_t h )
  478. {
  479. _cmMidiFile_t* mfp;
  480. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  481. return cmInvalidCnt;
  482. return mfp->ticksPerQN;
  483. }
  484. cmMidiByte_t cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h )
  485. {
  486. _cmMidiFile_t* mfp;
  487. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  488. return kInvalidMidiByte;
  489. if( mfp->ticksPerQN != 0 )
  490. return 0;
  491. return mfp->smpteTicksPerFrame;
  492. }
  493. cmMidiByte_t cmMidiFileSmpteFormatId( cmMidiFileH_t h )
  494. {
  495. _cmMidiFile_t* mfp;
  496. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  497. return kInvalidMidiByte;
  498. if( mfp->ticksPerQN != 0 )
  499. return 0;
  500. return mfp->smpteFmtId;
  501. }
  502. unsigned cmMidiFileTrackMsgCount( cmMidiFileH_t h, unsigned trackIdx )
  503. {
  504. _cmMidiFile_t* mfp;
  505. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  506. return cmInvalidCnt;
  507. return mfp->trkV[trackIdx].cnt;
  508. }
  509. const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx )
  510. {
  511. _cmMidiFile_t* mfp;
  512. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  513. return NULL;
  514. return mfp->trkV[trackIdx].base;
  515. }
  516. unsigned cmMidiFileMsgCount( cmMidiFileH_t h )
  517. {
  518. _cmMidiFile_t* mfp;
  519. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  520. return cmInvalidCnt;
  521. return mfp->msgN;
  522. }
  523. const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h )
  524. {
  525. _cmMidiFile_t* mfp;
  526. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  527. return NULL;
  528. // this cast is needed to eliminate an apparently needless 'incompatible type' warning
  529. return (const cmMidiTrackMsg_t**)mfp->msgV;
  530. }
  531. unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned offsUSecs, unsigned* msgUsecsPtr, unsigned* microsPerTickPtr )
  532. {
  533. _cmMidiFile_t* p;
  534. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  535. return cmInvalidIdx;
  536. if( p->msgN == 0 )
  537. return cmInvalidIdx;
  538. unsigned mi;
  539. unsigned microsPerQN = 60000000/120;
  540. unsigned microsPerTick = microsPerQN / p->ticksPerQN;
  541. unsigned accUSecs = 0;
  542. for(mi=0; mi<p->msgN; ++mi)
  543. {
  544. const cmMidiTrackMsg_t* mp = p->msgV[mi];
  545. if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
  546. microsPerTick = mp->u.iVal / p->ticksPerQN;
  547. accUSecs += mp->dtick * microsPerTick ;
  548. if( accUSecs >= offsUSecs )
  549. break;
  550. }
  551. if( mi == p->msgN )
  552. return cmInvalidIdx;
  553. if( msgUsecsPtr != NULL )
  554. *msgUsecsPtr = accUSecs - offsUSecs;
  555. if( microsPerTickPtr != NULL )
  556. *microsPerTickPtr = microsPerTick;
  557. return mi;
  558. }
  559. double cmMidiFileDurSecs( cmMidiFileH_t h )
  560. {
  561. _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  562. unsigned mi;
  563. double durSecs = 0;
  564. unsigned microsPerQN = 60000000/120;
  565. unsigned microsPerTick = microsPerQN / mfp->ticksPerQN;
  566. for(mi=0; mi<mfp->msgN; ++mi)
  567. {
  568. cmMidiTrackMsg_t* mp = mfp->msgV[mi];
  569. if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
  570. microsPerTick = mp->u.iVal / mfp->ticksPerQN;
  571. // update the accumulated seconds
  572. durSecs += (mp->dtick * microsPerTick) / 1000000.0;
  573. }
  574. return durSecs;
  575. }
  576. void cmMidiFileTickToMicros( cmMidiFileH_t h )
  577. {
  578. _cmMidiFile_t* p;
  579. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  580. return;
  581. if( p->msgN == 0 )
  582. return;
  583. unsigned mi;
  584. unsigned microsPerQN = 60000000/120; // default tempo
  585. unsigned microsPerTick = microsPerQN / p->ticksPerQN;
  586. for(mi=0; mi<p->msgN; ++mi)
  587. {
  588. cmMidiTrackMsg_t* mp = p->msgV[mi];
  589. if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
  590. microsPerTick = mp->u.iVal / p->ticksPerQN;
  591. mp->dtick *= microsPerTick;
  592. }
  593. }
  594. void cmMidiFileTickToSamples( cmMidiFileH_t h, double srate, bool absFl )
  595. {
  596. _cmMidiFile_t* p;
  597. unsigned mi;
  598. //bool fl = true;
  599. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  600. return;
  601. cmMidiFileTickToMicros(h);
  602. unsigned absSmp = 0;
  603. for(mi=0; mi<p->msgN; ++mi)
  604. {
  605. cmMidiTrackMsg_t* mp = p->msgV[mi];
  606. unsigned delta = floor((mp->dtick*srate)/1000000.0);
  607. mp->dtick = absFl ? absSmp : delta;
  608. absSmp += delta;
  609. }
  610. }
  611. typedef struct _cmMidiVoice_str
  612. {
  613. const cmMidiTrackMsg_t* mp;
  614. unsigned durTicks;
  615. bool sustainFl;
  616. struct _cmMidiVoice_str* link;
  617. } _cmMidiVoice_t;
  618. void _cmMidFileCalcNoteDurationReleaseNote( _cmMidiVoice_t** listPtrPtr, _cmMidiVoice_t* pp, _cmMidiVoice_t* vp )
  619. {
  620. assert( (pp==NULL && vp==*listPtrPtr) || pp->link==vp);
  621. // store the duration of the note into the track msg
  622. // assoc'd with the note-on msg
  623. cmMidiChMsg_t* cmp = (cmMidiChMsg_t*)vp->mp->u.chMsgPtr; // cast away const
  624. cmp->durTicks = vp->durTicks;
  625. _cmMidiVoice_t* np = vp->link;
  626. // release the voice msg
  627. cmMemFree(vp);
  628. // unlink the active voice msg
  629. if( pp == NULL )
  630. *listPtrPtr = np;
  631. else
  632. pp->link = np;
  633. }
  634. void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
  635. {
  636. _cmMidiFile_t* p;
  637. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  638. return;
  639. if( p->msgN == 0 )
  640. return;
  641. unsigned mi = cmInvalidId;
  642. _cmMidiVoice_t* list = NULL; // list of active voices
  643. _cmMidiVoice_t* vp = NULL;
  644. bool sustainFlagV[ kMidiChCnt ];
  645. // clear the sustain pedal flag
  646. for(mi=0; mi<kMidiChCnt; ++mi)
  647. sustainFlagV[mi]=false;
  648. for(mi=0; mi<p->msgN; ++mi)
  649. {
  650. cmMidiTrackMsg_t* mp = p->msgV[mi];
  651. // update the duration of the sounding notes
  652. for(vp = list; vp!=NULL; vp=vp->link)
  653. vp->durTicks += mp->dtick;
  654. //
  655. // If this is sustain pedal msg
  656. //
  657. if( mp->status==kCtlMdId && mp->u.chMsgPtr->d0 == kSustainCtlMdId )
  658. {
  659. unsigned chIdx = mp->u.chMsgPtr->ch;
  660. assert( chIdx < kMidiChCnt );
  661. // set the state of the sustain pedal flags
  662. sustainFlagV[chIdx] = mp->u.chMsgPtr->d1 >= 64;
  663. // if the pedal went up ...
  664. if( sustainFlagV[chIdx] == false )
  665. {
  666. // ... then release sustaining notes
  667. _cmMidiVoice_t* pp = NULL;
  668. for(vp=list; vp != NULL; )
  669. {
  670. _cmMidiVoice_t* np = vp->link;
  671. if( vp->sustainFl && (vp->mp->u.chMsgPtr->ch == chIdx) )
  672. _cmMidFileCalcNoteDurationReleaseNote(&list,pp,vp);
  673. else
  674. pp = vp;
  675. vp = np;
  676. }
  677. }
  678. }
  679. //
  680. // if this is a note-on msg
  681. //
  682. if( mp->status==kNoteOnMdId && mp->u.chMsgPtr->d1>0 )
  683. {
  684. vp = cmMemAllocZ(_cmMidiVoice_t,1);
  685. vp->mp = mp;
  686. vp->sustainFl = false;
  687. vp->link = list;
  688. list = vp;
  689. }
  690. else
  691. //
  692. // if this is a note-off msg
  693. //
  694. if( (mp->status==kNoteOnMdId && mp->u.chMsgPtr->d1==0) || (mp->status==kNoteOffMdId) )
  695. {
  696. _cmMidiVoice_t* pp = NULL;
  697. unsigned chIdx = mp->u.chMsgPtr->ch;
  698. assert( chIdx < kMidiChCnt );
  699. // for each active voice
  700. for(vp=list; vp!=NULL; vp=vp->link )
  701. {
  702. // if this active voice ch/pitch matches the note-off msg ch pitch
  703. if( (vp->mp->u.chMsgPtr->d0==mp->u.chMsgPtr->d0) && (vp->mp->u.chMsgPtr->ch==chIdx) )
  704. {
  705. // if the sustain pedal is down for this channel - then defer turning the note off
  706. if( sustainFlagV[chIdx] )
  707. vp->sustainFl = true;
  708. else
  709. _cmMidFileCalcNoteDurationReleaseNote(&list,pp,vp);
  710. break;
  711. }
  712. pp = vp;
  713. } // end for
  714. } // end if
  715. } // end-for
  716. // check for hung notes
  717. _cmMidiVoice_t* np = NULL;
  718. vp = list;
  719. while( vp != NULL )
  720. {
  721. np = vp->link;
  722. cmErrMsg(&p->err,kMissingNoteOffMfRC,"Missing note-off for note-on:%s",cmMidiToSciPitch(vp->mp->u.chMsgPtr->d0,NULL,0));
  723. cmMemFree(vp);
  724. vp = np;
  725. }
  726. }
  727. void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks )
  728. {
  729. _cmMidiFile_t* p;
  730. unsigned mi;
  731. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  732. return;
  733. if( p->msgN == 0 )
  734. return;
  735. for(mi=0; mi<p->msgN; ++mi)
  736. {
  737. cmMidiTrackMsg_t* mp = p->msgV[mi];
  738. // locate the first msg which has a non-zero delta tick
  739. if( mp->dtick > 0 )
  740. {
  741. mp->dtick = ticks;
  742. break;
  743. }
  744. }
  745. }
  746. unsigned cmMidiFilePackTrackMsgBufByteCount( const cmMidiTrackMsg_t* m )
  747. { return sizeof(cmMidiTrackMsg_t) + m->byteCnt; }
  748. cmMidiTrackMsg_t* cmMidiFilePackTrackMsg( const cmMidiTrackMsg_t* m, void* buf, unsigned bufByteCnt )
  749. {
  750. unsigned n = sizeof(cmMidiTrackMsg_t) + m->byteCnt;
  751. if( n < bufByteCnt )
  752. {
  753. assert(0);
  754. return NULL;
  755. }
  756. // copy the cmMidiTrackMsg_t into the buffer
  757. memcpy(buf, m, sizeof(cmMidiTrackMsg_t));
  758. if( m->byteCnt > 0 )
  759. {
  760. // copy any linked data into the buffer
  761. memcpy(buf + sizeof(cmMidiTrackMsg_t), m->u.voidPtr, m->byteCnt );
  762. // fixup the linked data ptr
  763. cmMidiTrackMsg_t* mp = (cmMidiTrackMsg_t*)buf;
  764. mp->u.voidPtr = buf + sizeof(cmMidiTrackMsg_t);
  765. }
  766. return (cmMidiTrackMsg_t*)buf;
  767. }
  768. void cmMidiFilePrint( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt )
  769. {
  770. const _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  771. if( mfp->fn != NULL )
  772. cmRptPrintf(rpt,"%s ",mfp->fn);
  773. cmRptPrintf(rpt,"fmt:%i ticksPerQN:%i tracks:%i\n",mfp->fmtId,mfp->ticksPerQN,mfp->trkN);
  774. int i = trkIdx == cmInvalidIdx ? 0 : trkIdx;
  775. int n = trkIdx == cmInvalidIdx ? mfp->trkN : trkIdx+1;
  776. for(; i<n; ++i)
  777. {
  778. cmRptPrintf(rpt,"Track:%i\n",i);
  779. cmMidiTrackMsg_t* tmp = mfp->trkV[i].base;
  780. while( tmp != NULL )
  781. {
  782. cmRptPrintf(rpt,"%5i ", tmp->dtick );
  783. if( tmp->status == kMetaStId )
  784. cmRptPrintf(rpt,"%s ", cmMidiMetaStatusToLabel(tmp->metaId));
  785. else
  786. {
  787. cmRptPrintf(rpt,"%4s %3i %3i %3i", cmMidiStatusToLabel(tmp->status),tmp->u.chMsgPtr->ch,tmp->u.chMsgPtr->d0,tmp->u.chMsgPtr->d1);
  788. }
  789. cmRptPrintf(rpt,"\n");
  790. tmp = tmp->link;
  791. }
  792. }
  793. }
  794. bool cmMidiFileIsNull( cmMidiFileH_t h )
  795. { return (_cmMidiFile_t*)h.h == NULL; }
  796. void cmMidiFileTestPrint( void* printDataPtr, const char* fmt, va_list vl )
  797. { vprintf(fmt,vl); }
  798. void cmMidiFileTest( const char* fn, cmCtx_t* ctx )
  799. {
  800. cmMfRC_t rc;
  801. cmMidiFileH_t h;
  802. if((rc = cmMidiFileOpen(fn,&h,ctx)) != kOkMfRC )
  803. {
  804. printf("Error:%i Unable to open the cmMidi file: %s\n",rc,fn);
  805. return;
  806. }
  807. cmMidiFilePrint(h,cmMidiFileTrackCount(h)-1,&ctx->rpt);
  808. printf("Tracks:%i\n",cmMidiFileTrackCount(h));
  809. cmMidiFileClose(&h);
  810. }