libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmMidiFile.c 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443
  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() and cmMidiFileWrite())
  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. unsigned nextUid; // next available msg uid
  40. } _cmMidiFile_t;
  41. cmMidiFileH_t cmMidiFileNullHandle = cmSTATIC_NULL_HANDLE;
  42. _cmMidiFile_t* _cmMidiFileHandleToPtr( cmMidiFileH_t h )
  43. {
  44. _cmMidiFile_t* p = (_cmMidiFile_t*)h.h;
  45. assert( p != NULL );
  46. return p;
  47. }
  48. void* _cmMidiFileMalloc( _cmMidiFile_t* mfp, unsigned byteN )
  49. { return cmLHeapAllocZ(mfp->lhH,byteN); }
  50. cmMfRC_t _cmMidiFileRead8( _cmMidiFile_t* mfp, cmMidiByte_t* p )
  51. {
  52. if( cmFileReadUChar(mfp->fh,p,1) != kOkFileRC )
  53. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI byte read failed.");
  54. return kOkMfRC;
  55. }
  56. cmMfRC_t _cmMidiFileRead16( _cmMidiFile_t* mfp, unsigned short* p )
  57. {
  58. if( cmFileReadUShort(mfp->fh,p,1) != kOkFileRC )
  59. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI short read failed.");
  60. *p = mfSwap16(*p);
  61. return kOkMfRC;
  62. }
  63. cmMfRC_t _cmMidiFileRead24( _cmMidiFile_t* mfp, unsigned* p )
  64. {
  65. *p = 0;
  66. int i = 0;
  67. for(; i<3; ++i)
  68. {
  69. unsigned char c;
  70. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  71. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI 24 bit integer read failed.");
  72. *p = (*p << 8) + c;
  73. }
  74. //*p =mfSwap32(*p);
  75. return kOkMfRC;
  76. }
  77. cmMfRC_t _cmMidiFileRead32( _cmMidiFile_t* mfp, unsigned* p )
  78. {
  79. if( cmFileReadUInt(mfp->fh,p,1) != kOkFileRC )
  80. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI integer read failed.");
  81. *p = mfSwap32(*p);
  82. return kOkMfRC;
  83. }
  84. cmMfRC_t _cmMidiFileReadText( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  85. {
  86. if( byteN == 0 )
  87. return kOkMfRC;
  88. char* t = (char*)_cmMidiFileMalloc(mfp,byteN+1);
  89. t[byteN] = 0;
  90. if( cmFileReadChar(mfp->fh,t,byteN) != kOkFileRC )
  91. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read text failed.");
  92. tmp->u.text = t;
  93. tmp->byteCnt = byteN;
  94. return kOkMfRC;
  95. }
  96. cmMfRC_t _cmMidiFileReadRecd( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  97. {
  98. char* t = (char*)_cmMidiFileMalloc(mfp,byteN);
  99. if( cmFileReadChar(mfp->fh,t,byteN) != kOkFileRC )
  100. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read record failed.");
  101. tmp->byteCnt = byteN;
  102. tmp->u.voidPtr = t;
  103. return kOkMfRC;
  104. }
  105. cmMfRC_t _cmMidiFileReadVarLen( _cmMidiFile_t* mfp, unsigned* p )
  106. {
  107. unsigned char c;
  108. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  109. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read variable length integer failed.");
  110. if( !(c & 0x80) )
  111. *p = c;
  112. else
  113. {
  114. *p = c & 0x7f;
  115. do
  116. {
  117. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  118. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read variable length integer failed.");
  119. *p = (*p << 7) + (c & 0x7f);
  120. }while( c & 0x80 );
  121. }
  122. return kOkMfRC;
  123. }
  124. cmMfRC_t _cmMidiFileAppendTrackMsg( _cmMidiFile_t* mfp, unsigned short trkIdx, unsigned dtick, cmMidiByte_t status, cmMidiTrackMsg_t** trkMsgPtrPtr )
  125. {
  126. cmMidiTrackMsg_t* tmp = (cmMidiTrackMsg_t*)_cmMidiFileMalloc(mfp, sizeof(cmMidiTrackMsg_t) );
  127. // link new record onto track record chain
  128. if( mfp->trkV[trkIdx].base == NULL )
  129. mfp->trkV[trkIdx].base = tmp;
  130. else
  131. mfp->trkV[trkIdx].last->link = tmp;
  132. mfp->trkV[trkIdx].last = tmp;
  133. mfp->trkV[trkIdx].cnt++;
  134. // set the generic track record fields
  135. tmp->dtick = dtick;
  136. tmp->status = status;
  137. tmp->metaId = kInvalidMetaMdId;
  138. tmp->trkIdx = trkIdx;
  139. tmp->byteCnt = 0;
  140. *trkMsgPtrPtr = tmp;
  141. return kOkMfRC;
  142. }
  143. cmMfRC_t _cmMidiFileReadSysEx( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  144. {
  145. cmMfRC_t rc = kOkMfRC;
  146. cmMidiByte_t b = 0;
  147. if( byteN == cmInvalidCnt )
  148. {
  149. long offs;
  150. if( cmFileTell(mfp->fh,&offs) != kOkFileRC )
  151. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI File 'tell' failed.");
  152. byteN = 0;
  153. // get the length of the sys-ex msg
  154. while( !cmFileEof(mfp->fh) && (b != kSysComEoxMdId) )
  155. {
  156. if((rc = _cmMidiFileRead8(mfp,&b)) != kOkMfRC )
  157. return rc;
  158. ++byteN;
  159. }
  160. // verify that the EOX byte was found
  161. if( b != kSysComEoxMdId )
  162. return cmErrMsg(&mfp->err,kMissingEoxMfRC,"MIDI file missing 'end-of-sys-ex'.");
  163. // rewind to the beginning of the msg
  164. if( cmFileSeek(mfp->fh,kBeginFileFl,offs) != kOkFileRC )
  165. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file seek failed on sys-ex read.");
  166. }
  167. // allocate memory to hold the sys-ex msg
  168. cmMidiByte_t* mp = (cmMidiByte_t *)_cmMidiFileMalloc(mfp, byteN );
  169. // read the sys-ex msg from the file into msg memory
  170. if( cmFileReadUChar(mfp->fh,mp,byteN) != kOkFileRC )
  171. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI sys-ex read failed.");
  172. tmp->byteCnt = byteN;
  173. tmp->u.sysExPtr = mp;
  174. return rc;
  175. }
  176. cmMfRC_t _cmMidiFileReadChannelMsg( _cmMidiFile_t* mfp, cmMidiByte_t* rsPtr, cmMidiByte_t status, cmMidiTrackMsg_t* tmp )
  177. {
  178. cmMfRC_t rc = kOkMfRC;
  179. cmMidiChMsg_t* p = (cmMidiChMsg_t*)_cmMidiFileMalloc(mfp,sizeof(cmMidiChMsg_t));
  180. unsigned useRsFl = status <= 0x7f;
  181. cmMidiByte_t statusCh = useRsFl ? *rsPtr : status;
  182. if( useRsFl )
  183. p->d0 = status;
  184. else
  185. *rsPtr = status;
  186. tmp->byteCnt = sizeof(cmMidiChMsg_t);
  187. tmp->status = statusCh & 0xf0;
  188. p->ch = statusCh & 0x0f;
  189. p->durTicks = 0;
  190. unsigned byteN = cmMidiStatusToByteCount(tmp->status);
  191. if( byteN==kInvalidMidiByte || byteN > 2 )
  192. return cmErrMsg(&mfp->err,kInvalidStatusMfRC,"Invalid status:0x%x %i.",tmp->status,tmp->status);
  193. unsigned i;
  194. for(i=useRsFl; i<byteN; ++i)
  195. {
  196. cmMidiByte_t* b = i==0 ? &p->d0 : &p->d1;
  197. if((rc = _cmMidiFileRead8(mfp,b)) != kOkMfRC )
  198. return rc;
  199. }
  200. // convert note-on velocity=0 to note off
  201. if( tmp->status == kNoteOnMdId && p->d1==0 )
  202. tmp->status = kNoteOffMdId;
  203. tmp->u.chMsgPtr = p;
  204. return rc;
  205. }
  206. cmMfRC_t _cmMidiFileReadMetaMsg( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp )
  207. {
  208. cmMidiByte_t metaId;
  209. cmMfRC_t rc;
  210. unsigned byteN = 0;
  211. if((rc = _cmMidiFileRead8(mfp,&metaId)) != kOkMfRC )
  212. return rc;
  213. if((rc = _cmMidiFileReadVarLen(mfp,&byteN)) != kOkMfRC )
  214. return rc;
  215. //printf("mt: %i 0x%x n:%i\n",metaId,metaId,byteN);
  216. switch( metaId )
  217. {
  218. case kSeqNumbMdId: rc = _cmMidiFileRead16(mfp,&tmp->u.sVal); break;
  219. case kTextMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  220. case kCopyMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  221. case kTrkNameMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  222. case kInstrNameMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  223. case kLyricsMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  224. case kMarkerMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  225. case kCuePointMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  226. case kMidiChMdId: rc = _cmMidiFileRead8(mfp,&tmp->u.bVal); break;
  227. case kEndOfTrkMdId: break;
  228. case kTempoMdId: rc = _cmMidiFileRead24(mfp,&tmp->u.iVal); break;
  229. case kSmpteMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiSmpte_t)); break;
  230. case kTimeSigMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiTimeSig_t)); break;
  231. case kKeySigMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiKeySig_t)); break;
  232. case kSeqSpecMdId: rc = _cmMidiFileReadSysEx(mfp,tmp,byteN); break;
  233. default:
  234. cmFileSeek(mfp->fh,kCurFileFl,byteN);
  235. rc = cmErrMsg(&mfp->err,kUnknownMetaIdMfRC,"Unknown meta status:0x%x %i.",metaId,metaId);
  236. }
  237. tmp->metaId = metaId;
  238. return rc;
  239. }
  240. cmMfRC_t _cmMidiFileReadTrack( _cmMidiFile_t* mfp, unsigned short trkIdx )
  241. {
  242. cmMfRC_t rc = kOkMfRC;
  243. unsigned dticks = 0;
  244. cmMidiByte_t status;
  245. cmMidiByte_t runstatus = 0;
  246. bool contFl = true;
  247. while( contFl && (rc==kOkMfRC))
  248. {
  249. cmMidiTrackMsg_t* tmp = NULL;
  250. // read the tick count
  251. if((rc = _cmMidiFileReadVarLen(mfp,&dticks)) != kOkMfRC )
  252. return rc;
  253. // read the status byte
  254. if((rc = _cmMidiFileRead8(mfp,&status)) != kOkMfRC )
  255. return rc;
  256. //printf("st:%i 0x%x\n",status,status);
  257. // append a track msg
  258. if((rc = _cmMidiFileAppendTrackMsg( mfp, trkIdx, dticks, status, &tmp )) != kOkMfRC )
  259. return rc;
  260. // switch on status
  261. switch( status )
  262. {
  263. // handle sys-ex msg
  264. case kSysExMdId:
  265. rc = _cmMidiFileReadSysEx(mfp,tmp,cmInvalidCnt);
  266. break;
  267. // handle meta msg
  268. case kMetaStId:
  269. rc = _cmMidiFileReadMetaMsg(mfp,tmp);
  270. // ignore unknown meta messages
  271. if( rc == kUnknownMetaIdMfRC )
  272. rc = kOkMfRC;
  273. contFl = tmp->metaId != kEndOfTrkMdId;
  274. break;
  275. default:
  276. // handle channel msg
  277. rc = _cmMidiFileReadChannelMsg(mfp,&runstatus,status,tmp);
  278. }
  279. }
  280. return rc;
  281. }
  282. cmMfRC_t _cmMidiFileReadHdr( _cmMidiFile_t* mfp )
  283. {
  284. cmMfRC_t rc;
  285. unsigned fileId;
  286. unsigned chunkByteN;
  287. // read the file id
  288. if((rc = _cmMidiFileRead32(mfp,&fileId)) != kOkMfRC )
  289. return rc;
  290. // verify the file id
  291. if( fileId != 'MThd' )
  292. return cmErrMsg(&mfp->err,kNotAMidiFileMfRC,"");
  293. // read the file chunk byte count
  294. if((rc = _cmMidiFileRead32(mfp,&chunkByteN)) != kOkMfRC )
  295. return rc;
  296. // read the format id
  297. if((rc = _cmMidiFileRead16(mfp,&mfp->fmtId)) != kOkMfRC )
  298. return rc;
  299. // read the track count
  300. if((rc = _cmMidiFileRead16(mfp,&mfp->trkN)) != kOkMfRC )
  301. return rc;
  302. // read the ticks per quarter note
  303. if((rc = _cmMidiFileRead16(mfp,&mfp->ticksPerQN)) != kOkMfRC )
  304. return rc;
  305. // if the division field was given in smpte
  306. if( mfp->ticksPerQN & 0x8000 )
  307. {
  308. mfp->smpteFmtId = (mfp->ticksPerQN & 0x7f00) >> 8;
  309. mfp->smpteTicksPerFrame = (mfp->ticksPerQN & 0xFF);
  310. mfp->ticksPerQN = 0;
  311. }
  312. // allocate and zero the track array
  313. if( mfp->trkN )
  314. mfp->trkV = _cmMidiFileMalloc( mfp, sizeof(_cmMidiTrack_t)*mfp->trkN);
  315. return rc;
  316. }
  317. int _cmMidiFileSortFunc( const void *p0, const void* p1 )
  318. {
  319. //printf("%i %i\n",(*(cmMidiTrackMsg_t**)p0)->dticks,(*(cmMidiTrackMsg_t**)p1)->dticks);
  320. if( (*(cmMidiTrackMsg_t**)p0)->atick == (*(cmMidiTrackMsg_t**)p1)->atick )
  321. return 0;
  322. return (*(cmMidiTrackMsg_t**)p0)->atick < (*(cmMidiTrackMsg_t**)p1)->atick ? -1 : 1;
  323. }
  324. cmMfRC_t _cmMidiFileClose( _cmMidiFile_t* mfp )
  325. {
  326. cmMfRC_t rc = kOkMfRC;
  327. if( mfp == NULL )
  328. return rc;
  329. cmMemPtrFree(&mfp->msgV);
  330. if( cmFileIsValid( mfp->fh ) )
  331. if( cmFileClose( &mfp->fh ) != kOkFileRC )
  332. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file close failed.");
  333. if( cmLHeapIsValid( mfp->lhH ) )
  334. cmLHeapDestroy(&mfp->lhH);
  335. cmMemPtrFree(&mfp);
  336. return rc;
  337. }
  338. cmMfRC_t cmMidiFileOpen( const char* fn, cmMidiFileH_t* hPtr, cmCtx_t* ctx )
  339. {
  340. cmMfRC_t rc = kOkMfRC;
  341. _cmMidiFile_t* mfp = NULL;
  342. unsigned short trkIdx = 0;
  343. cmErr_t err;
  344. if( cmMidiFileIsValid(*hPtr) )
  345. if((rc = _cmMidiFileClose(_cmMidiFileHandleToPtr(*hPtr))) != kOkMfRC )
  346. return rc;
  347. cmErrSetup(&err,&ctx->rpt,"MIDI File");
  348. // allocate the midi file object
  349. if(( mfp = cmMemAllocZ( _cmMidiFile_t, 1)) == NULL )
  350. return rc = cmErrMsg(&err,kMemAllocFailMfRC,"MIDI file memory allocation failed.");
  351. cmErrClone(&mfp->err,&err);
  352. // allocate the linked heap
  353. if( cmLHeapIsValid( mfp->lhH = cmLHeapCreate( 1024, ctx )) == false )
  354. {
  355. rc = cmErrMsg(&err,kMemAllocFailMfRC,"MIDI heap allocation failed.");
  356. goto errLabel;
  357. }
  358. // open the file
  359. if(cmFileOpen(&mfp->fh,fn,kReadFileFl | kBinaryFileFl,mfp->err.rpt) != kOkFileRC )
  360. {
  361. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file open failed.");
  362. goto errLabel;
  363. }
  364. // read header and setup track array
  365. if(( rc = _cmMidiFileReadHdr(mfp)) != kOkMfRC )
  366. goto errLabel;
  367. while( !cmFileEof(mfp->fh) && trkIdx < mfp->trkN )
  368. {
  369. unsigned chkId=0,chkN=0;
  370. // read the chunk id
  371. if((rc = _cmMidiFileRead32(mfp,&chkId)) != kOkMfRC )
  372. goto errLabel;
  373. // read the chunk size
  374. if((rc = _cmMidiFileRead32(mfp,&chkN)) != kOkMfRC )
  375. goto errLabel;
  376. // if this is not a trk chunk then skip it
  377. if( chkId != (unsigned)'MTrk')
  378. {
  379. //if( fseek( mfp->fp, chkN, SEEK_CUR) != 0 )
  380. if( cmFileSeek(mfp->fh,kCurFileFl,chkN) != kOkFileRC )
  381. {
  382. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file seek failed.");
  383. goto errLabel;
  384. }
  385. }
  386. else
  387. {
  388. if((rc = _cmMidiFileReadTrack(mfp,trkIdx)) != kOkMfRC )
  389. goto errLabel;
  390. ++trkIdx;
  391. }
  392. }
  393. // get the total trk msg count
  394. mfp->msgN = 0;
  395. for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx)
  396. mfp->msgN += mfp->trkV[ trkIdx ].cnt;
  397. // allocate the trk msg index vector: msgV[]
  398. mfp->msgV = cmMemAllocZ(cmMidiTrackMsg_t*, mfp->msgN);
  399. // store a pointer to every trk msg in msgV[]
  400. // and convert tick to absolute tick
  401. mfp->nextUid = 0;
  402. unsigned i = 0;
  403. for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx)
  404. {
  405. unsigned tick = 0;
  406. cmMidiTrackMsg_t* tmp = mfp->trkV[ trkIdx ].base;
  407. while( tmp != NULL )
  408. {
  409. assert( i < mfp->msgN);
  410. tick += tmp->dtick; // convert delta-ticks to absolute ticks
  411. tmp->atick = tick;
  412. tmp->uid = mfp->nextUid++; // assign the msg uid
  413. mfp->msgV[i] = tmp;
  414. tmp = tmp->link;
  415. ++i;
  416. }
  417. }
  418. // sort msgV[] in ascending order on atick
  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. hPtr->h = mfp;
  426. errLabel:
  427. if( cmFileClose(&mfp->fh) != kOkFileRC )
  428. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file close failed.");
  429. if( rc != kOkMfRC )
  430. _cmMidiFileClose(mfp);
  431. return rc;
  432. }
  433. cmMfRC_t cmMidiFileClose( cmMidiFileH_t* h )
  434. {
  435. cmMfRC_t rc;
  436. if( cmMidiFileIsNull(*h) )
  437. return kOkMfRC;
  438. if((rc = _cmMidiFileClose(_cmMidiFileHandleToPtr(*h))) == kOkMfRC )
  439. return rc;
  440. h->h = NULL;
  441. return rc;
  442. }
  443. cmMfRC_t _cmMidiFileWrite8( _cmMidiFile_t* mfp, unsigned char v )
  444. {
  445. cmMfRC_t rc = kOkMfRC;
  446. if( cmFileWriteUChar(mfp->fh,&v,1) != kOkFileRC )
  447. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file byte write failed.");
  448. return rc;
  449. }
  450. cmMfRC_t _cmMidiFileWrite16( _cmMidiFile_t* mfp, unsigned short v )
  451. {
  452. cmMfRC_t rc = kOkMfRC;
  453. v = mfSwap16(v);
  454. if( cmFileWriteUShort(mfp->fh,&v,1) != kOkFileRC )
  455. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file short integer write failed.");
  456. return rc;
  457. }
  458. cmMfRC_t _cmMidiFileWrite24( _cmMidiFile_t* mfp, unsigned v )
  459. {
  460. cmMfRC_t rc = kOkMfRC;
  461. unsigned mask = 0xff0000;
  462. int i;
  463. for(i=2; i>=0; --i)
  464. {
  465. unsigned char c = (v & mask) >> (i*8);
  466. mask >>= 8;
  467. if( cmFileWriteUChar(mfp->fh,&c,1) != kOkFileRC )
  468. {
  469. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file 24 bit integer write failed.");
  470. goto errLabel;
  471. }
  472. }
  473. errLabel:
  474. return rc;
  475. }
  476. cmMfRC_t _cmMidiFileWrite32( _cmMidiFile_t* mfp, unsigned v )
  477. {
  478. cmMfRC_t rc = kOkMfRC;
  479. v = mfSwap32(v);
  480. if( cmFileWriteUInt(mfp->fh,&v,1) != kOkFileRC )
  481. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file integer write failed.");
  482. return rc;
  483. }
  484. cmMfRC_t _cmMidiFileWriteRecd( _cmMidiFile_t* mfp, const void* v, unsigned byteCnt )
  485. {
  486. cmMfRC_t rc = kOkMfRC;
  487. if( cmFileWriteChar(mfp->fh,v,byteCnt) != kOkFileRC )
  488. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file write record failed.");
  489. return rc;
  490. }
  491. cmMfRC_t _cmMidiFileWriteVarLen( _cmMidiFile_t* mfp, unsigned v )
  492. {
  493. cmMfRC_t rc = kOkMfRC;
  494. unsigned buf = v & 0x7f;
  495. while((v >>= 7) > 0 )
  496. {
  497. buf <<= 8;
  498. buf |= 0x80;
  499. buf += (v & 0x7f);
  500. }
  501. while(1)
  502. {
  503. unsigned char c = (unsigned char)(buf & 0xff);
  504. if( cmFileWriteUChar(mfp->fh,&c,1) != kOkFileRC )
  505. {
  506. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file variable length integer write failed.");
  507. goto errLabel;
  508. }
  509. if( buf & 0x80 )
  510. buf >>= 8;
  511. else
  512. break;
  513. }
  514. errLabel:
  515. return rc;
  516. }
  517. cmMfRC_t _cmMidiFileWriteHdr( _cmMidiFile_t* mfp )
  518. {
  519. cmMfRC_t rc;
  520. unsigned fileId = 'MThd';
  521. unsigned chunkByteN = 6;
  522. // write the file id ('MThd')
  523. if((rc = _cmMidiFileWrite32(mfp,fileId)) != kOkMfRC )
  524. return rc;
  525. // write the file chunk byte count (always 6)
  526. if((rc = _cmMidiFileWrite32(mfp,chunkByteN)) != kOkMfRC )
  527. return rc;
  528. // write the MIDI file format id (0,1,2)
  529. if((rc = _cmMidiFileWrite16(mfp,mfp->fmtId)) != kOkMfRC )
  530. return rc;
  531. // write the track count
  532. if((rc = _cmMidiFileWrite16(mfp,mfp->trkN)) != kOkMfRC )
  533. return rc;
  534. unsigned short v = 0;
  535. // if the ticks per quarter note field is valid ...
  536. if( mfp->ticksPerQN )
  537. v = mfp->ticksPerQN;
  538. else
  539. {
  540. // ... otherwise the division field was given in smpte
  541. v = mfp->smpteFmtId << 8;
  542. v += mfp->smpteTicksPerFrame;
  543. }
  544. if((rc = _cmMidiFileWrite16(mfp,v)) != kOkMfRC )
  545. return rc;
  546. return rc;
  547. }
  548. cmMfRC_t _cmMidiFileWriteSysEx( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp )
  549. {
  550. cmMfRC_t rc = kOkMfRC;
  551. if((rc = _cmMidiFileWrite8(mfp,kSysExMdId)) != kOkMfRC )
  552. goto errLabel;
  553. if( cmFileWriteUChar(mfp->fh,tmp->u.sysExPtr,tmp->byteCnt) != kOkFileRC )
  554. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"Sys-ex msg write failed.");
  555. errLabel:
  556. return rc;
  557. }
  558. cmMfRC_t _cmMidiFileWriteChannelMsg( _cmMidiFile_t* mfp, const cmMidiTrackMsg_t* tmp, cmMidiByte_t* runStatus )
  559. {
  560. cmMfRC_t rc = kOkMfRC;
  561. unsigned byteN = cmMidiStatusToByteCount(tmp->status);
  562. cmMidiByte_t status = tmp->status + tmp->u.chMsgPtr->ch;
  563. if( status != *runStatus )
  564. {
  565. *runStatus = status;
  566. if((rc = _cmMidiFileWrite8(mfp,status)) != kOkMfRC )
  567. goto errLabel;
  568. }
  569. if(byteN>=1)
  570. if((rc = _cmMidiFileWrite8(mfp,tmp->u.chMsgPtr->d0)) != kOkMfRC )
  571. goto errLabel;
  572. if(byteN>=2)
  573. if((rc = _cmMidiFileWrite8(mfp,tmp->u.chMsgPtr->d1)) != kOkMfRC )
  574. goto errLabel;
  575. errLabel:
  576. return rc;
  577. }
  578. cmMfRC_t _cmMidiFileWriteMetaMsg( _cmMidiFile_t* mfp, const cmMidiTrackMsg_t* tmp )
  579. {
  580. cmMfRC_t rc;
  581. if((rc = _cmMidiFileWrite8(mfp,kMetaStId)) != kOkMfRC )
  582. return rc;
  583. if((rc = _cmMidiFileWrite8(mfp,tmp->metaId)) != kOkMfRC )
  584. return rc;
  585. switch( tmp->metaId )
  586. {
  587. case kSeqNumbMdId:
  588. if((rc = _cmMidiFileWrite8(mfp,sizeof(tmp->u.sVal))) == kOkMfRC )
  589. rc = _cmMidiFileWrite16(mfp,tmp->u.sVal);
  590. break;
  591. case kTempoMdId:
  592. if((rc = _cmMidiFileWrite8(mfp,3)) == kOkMfRC )
  593. rc = _cmMidiFileWrite24(mfp,tmp->u.iVal);
  594. break;
  595. case kSmpteMdId:
  596. if((rc = _cmMidiFileWrite8(mfp,sizeof(cmMidiSmpte_t))) == kOkMfRC )
  597. rc = _cmMidiFileWriteRecd(mfp,tmp->u.smptePtr,sizeof(cmMidiSmpte_t));
  598. break;
  599. case kTimeSigMdId:
  600. if((rc = _cmMidiFileWrite8(mfp,sizeof(cmMidiTimeSig_t))) == kOkMfRC )
  601. rc = _cmMidiFileWriteRecd(mfp,tmp->u.timeSigPtr,sizeof(cmMidiTimeSig_t));
  602. break;
  603. case kKeySigMdId:
  604. if((rc = _cmMidiFileWrite8(mfp,sizeof(cmMidiKeySig_t))) == kOkMfRC )
  605. rc = _cmMidiFileWriteRecd(mfp,tmp->u.keySigPtr,sizeof(cmMidiKeySig_t));
  606. break;
  607. case kSeqSpecMdId:
  608. if((rc = _cmMidiFileWriteVarLen(mfp,sizeof(tmp->byteCnt))) == kOkMfRC )
  609. rc = _cmMidiFileWriteRecd(mfp,tmp->u.sysExPtr,tmp->byteCnt);
  610. break;
  611. case kMidiChMdId:
  612. if((rc = _cmMidiFileWrite8(mfp,sizeof(tmp->u.bVal))) == kOkMfRC )
  613. rc = _cmMidiFileWrite8(mfp,tmp->u.bVal);
  614. break;
  615. case kEndOfTrkMdId:
  616. rc = _cmMidiFileWrite8(mfp,0);
  617. break;
  618. case kTextMdId:
  619. case kCopyMdId:
  620. case kTrkNameMdId:
  621. case kInstrNameMdId:
  622. case kLyricsMdId:
  623. case kMarkerMdId:
  624. case kCuePointMdId:
  625. {
  626. unsigned n = tmp->u.text==NULL ? 0 : strlen(tmp->u.text);
  627. if((rc = _cmMidiFileWriteVarLen(mfp,n)) == kOkMfRC && n>0 )
  628. rc = _cmMidiFileWriteRecd(mfp,tmp->u.text,n);
  629. }
  630. break;
  631. default:
  632. {
  633. // ignore unknown meta messages
  634. }
  635. }
  636. return rc;
  637. }
  638. cmMfRC_t _cmMidiFileWriteTrack( _cmMidiFile_t* mfp, unsigned trkIdx )
  639. {
  640. cmMfRC_t rc = kOkMfRC;
  641. cmMidiTrackMsg_t* tmp = mfp->trkV[trkIdx].base;
  642. cmMidiByte_t runStatus = 0;
  643. for(; tmp!=NULL; tmp=tmp->link)
  644. {
  645. // write the msg tick count
  646. if((rc = _cmMidiFileWriteVarLen(mfp,tmp->dtick)) != kOkMfRC )
  647. return rc;
  648. // switch on status
  649. switch( tmp->status )
  650. {
  651. // handle sys-ex msg
  652. case kSysExMdId:
  653. rc = _cmMidiFileWriteSysEx(mfp,tmp);
  654. break;
  655. // handle meta msg
  656. case kMetaStId:
  657. rc = _cmMidiFileWriteMetaMsg(mfp,tmp);
  658. break;
  659. default:
  660. // handle channel msg
  661. rc = _cmMidiFileWriteChannelMsg(mfp,tmp,&runStatus);
  662. }
  663. }
  664. return rc;
  665. }
  666. cmMfRC_t cmMidiFileWrite( cmMidiFileH_t h, const char* fn )
  667. {
  668. cmMfRC_t rc = kOkMfRC;
  669. _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  670. unsigned i;
  671. // create the output file
  672. if( cmFileOpen(&mfp->fh,fn,kWriteFileFl,mfp->err.rpt) != kOkFileRC )
  673. return cmErrMsg(&mfp->err,kFileFailMfRC,"The MIDI file '%s' could not be created.",cmStringNullGuard(fn));
  674. // write the file header
  675. if((rc = _cmMidiFileWriteHdr(mfp)) != kOkMfRC )
  676. {
  677. rc = cmErrMsg(&mfp->err,rc,"The file header write failed on the MIDI file '%s'.",cmStringNullGuard(fn));
  678. goto errLabel;
  679. }
  680. for(i=0; i < mfp->trkN; ++i )
  681. {
  682. unsigned chkId='MTrk';
  683. long offs0,offs1;
  684. // write the track chunk id ('MTrk')
  685. if((rc = _cmMidiFileWrite32(mfp,chkId)) != kOkMfRC )
  686. goto errLabel;
  687. cmFileTell(mfp->fh,&offs0);
  688. // write the track chunk size as zero
  689. if((rc = _cmMidiFileWrite32(mfp,0)) != kOkMfRC )
  690. goto errLabel;
  691. if((rc = _cmMidiFileWriteTrack(mfp,i)) != kOkMfRC )
  692. goto errLabel;
  693. cmFileTell(mfp->fh,&offs1);
  694. cmFileSeek(mfp->fh,kBeginFileFl,offs0);
  695. _cmMidiFileWrite32(mfp,offs1-offs0-4);
  696. cmFileSeek(mfp->fh,kBeginFileFl,offs1);
  697. }
  698. errLabel:
  699. cmFileClose(&mfp->fh);
  700. return rc;
  701. }
  702. bool cmMidiFileIsValid( cmMidiFileH_t h )
  703. { return !cmMidiFileIsNull(h); }
  704. unsigned cmMidiFileTrackCount( cmMidiFileH_t h )
  705. {
  706. _cmMidiFile_t* mfp;
  707. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  708. return cmInvalidCnt;
  709. return mfp->trkN;
  710. }
  711. unsigned cmMidiFileType( cmMidiFileH_t h )
  712. {
  713. _cmMidiFile_t* mfp;
  714. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  715. return cmInvalidId;
  716. return mfp->fmtId;
  717. }
  718. const char* cmMidiFileName( cmMidiFileH_t h )
  719. {
  720. _cmMidiFile_t* mfp;
  721. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  722. return NULL;
  723. return mfp->fn;
  724. }
  725. unsigned cmMidiFileTicksPerQN( cmMidiFileH_t h )
  726. {
  727. _cmMidiFile_t* mfp;
  728. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  729. return cmInvalidCnt;
  730. return mfp->ticksPerQN;
  731. }
  732. cmMidiByte_t cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h )
  733. {
  734. _cmMidiFile_t* mfp;
  735. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  736. return kInvalidMidiByte;
  737. if( mfp->ticksPerQN != 0 )
  738. return 0;
  739. return mfp->smpteTicksPerFrame;
  740. }
  741. cmMidiByte_t cmMidiFileSmpteFormatId( cmMidiFileH_t h )
  742. {
  743. _cmMidiFile_t* mfp;
  744. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  745. return kInvalidMidiByte;
  746. if( mfp->ticksPerQN != 0 )
  747. return 0;
  748. return mfp->smpteFmtId;
  749. }
  750. unsigned cmMidiFileTrackMsgCount( cmMidiFileH_t h, unsigned trackIdx )
  751. {
  752. _cmMidiFile_t* mfp;
  753. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  754. return cmInvalidCnt;
  755. return mfp->trkV[trackIdx].cnt;
  756. }
  757. const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx )
  758. {
  759. _cmMidiFile_t* mfp;
  760. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  761. return NULL;
  762. return mfp->trkV[trackIdx].base;
  763. }
  764. unsigned cmMidiFileMsgCount( cmMidiFileH_t h )
  765. {
  766. _cmMidiFile_t* mfp;
  767. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  768. return cmInvalidCnt;
  769. return mfp->msgN;
  770. }
  771. const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h )
  772. {
  773. _cmMidiFile_t* mfp;
  774. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  775. return NULL;
  776. // this cast is needed to eliminate an apparently needless 'incompatible type' warning
  777. return (const cmMidiTrackMsg_t**)mfp->msgV;
  778. }
  779. unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned offsUSecs, unsigned* msgUsecsPtr, unsigned* microsPerTickPtr )
  780. {
  781. _cmMidiFile_t* p;
  782. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  783. return cmInvalidIdx;
  784. if( p->msgN == 0 )
  785. return cmInvalidIdx;
  786. unsigned mi;
  787. double microsPerQN = 60000000.0/120.0;
  788. double microsPerTick = microsPerQN / p->ticksPerQN;
  789. double accUSecs = 0;
  790. for(mi=0; mi<p->msgN; ++mi)
  791. {
  792. const cmMidiTrackMsg_t* mp = p->msgV[mi];
  793. if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
  794. microsPerTick = mp->u.iVal / p->ticksPerQN;
  795. accUSecs += mp->dtick * microsPerTick ;
  796. if( accUSecs >= offsUSecs )
  797. break;
  798. }
  799. if( mi == p->msgN )
  800. return cmInvalidIdx;
  801. if( msgUsecsPtr != NULL )
  802. *msgUsecsPtr = round(accUSecs - offsUSecs);
  803. if( microsPerTickPtr != NULL )
  804. *microsPerTickPtr = round(microsPerTick);
  805. return mi;
  806. }
  807. double cmMidiFileDurSecs( cmMidiFileH_t h )
  808. {
  809. _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  810. unsigned mi;
  811. double durSecs = 0;
  812. double r = 1.0; //1000.0/(1000-.8);
  813. double microsPerQN = r*60000000.0/120.0;
  814. double microsPerTick = microsPerQN / mfp->ticksPerQN;
  815. for(mi=0; mi<mfp->msgN; ++mi)
  816. {
  817. cmMidiTrackMsg_t* mp = mfp->msgV[mi];
  818. if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
  819. microsPerTick = r*mp->u.iVal / mfp->ticksPerQN;
  820. // update the accumulated seconds
  821. durSecs += (mp->dtick * microsPerTick) / 1000000.0;
  822. }
  823. return durSecs;
  824. }
  825. void cmMidiFileTickToMicros( cmMidiFileH_t h )
  826. {
  827. _cmMidiFile_t* p;
  828. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  829. return;
  830. if( p->msgN == 0 )
  831. return;
  832. unsigned mi;
  833. double r = 1.0; //1000.0/(1000-.8);
  834. double microsPerQN = r*60000000/120; // default tempo
  835. double microsPerTick = microsPerQN / p->ticksPerQN;
  836. for(mi=0; mi<p->msgN; ++mi)
  837. {
  838. cmMidiTrackMsg_t* mp = p->msgV[mi];
  839. if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
  840. microsPerTick = r*mp->u.iVal / p->ticksPerQN;
  841. mp->dtick = round(microsPerTick*mp->dtick);
  842. }
  843. }
  844. void cmMidiFileTickToSamples( cmMidiFileH_t h, double srate, bool absFl )
  845. {
  846. _cmMidiFile_t* p;
  847. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  848. return;
  849. if( p->msgN == 0 )
  850. return;
  851. unsigned mi;
  852. double r = 1.0; //1000.0/(1000-.8);
  853. double microsPerQN = r*60000000/120; // default tempo
  854. double microsPerTick = microsPerQN / p->ticksPerQN;
  855. double absSmp = 0;
  856. for(mi=0; mi<p->msgN; ++mi)
  857. {
  858. cmMidiTrackMsg_t* mp = p->msgV[mi];
  859. if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
  860. microsPerTick = r*mp->u.iVal / p->ticksPerQN;
  861. double delta = microsPerTick*mp->dtick*srate/1000000.0;
  862. absSmp += delta;
  863. mp->dtick = round(absFl ? absSmp : delta);
  864. }
  865. }
  866. typedef struct _cmMidiVoice_str
  867. {
  868. const cmMidiTrackMsg_t* mp;
  869. unsigned durTicks;
  870. bool sustainFl;
  871. struct _cmMidiVoice_str* link;
  872. } _cmMidiVoice_t;
  873. void _cmMidFileCalcNoteDurationReleaseNote( _cmMidiVoice_t** listPtrPtr, _cmMidiVoice_t* pp, _cmMidiVoice_t* vp )
  874. {
  875. assert( (pp==NULL && vp==*listPtrPtr) || pp->link==vp);
  876. // store the duration of the note into the track msg
  877. // assoc'd with the note-on msg
  878. cmMidiChMsg_t* cmp = (cmMidiChMsg_t*)vp->mp->u.chMsgPtr; // cast away const
  879. cmp->durTicks = vp->durTicks;
  880. _cmMidiVoice_t* np = vp->link;
  881. // release the voice msg
  882. cmMemFree(vp);
  883. // unlink the active voice msg
  884. if( pp == NULL )
  885. *listPtrPtr = np;
  886. else
  887. pp->link = np;
  888. }
  889. void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
  890. {
  891. _cmMidiFile_t* p;
  892. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  893. return;
  894. if( p->msgN == 0 )
  895. return;
  896. unsigned mi = cmInvalidId;
  897. _cmMidiVoice_t* list = NULL; // list of active voices
  898. _cmMidiVoice_t* vp = NULL;
  899. bool sustainFlagV[ kMidiChCnt ];
  900. // clear the sustain pedal flag
  901. for(mi=0; mi<kMidiChCnt; ++mi)
  902. sustainFlagV[mi]=false;
  903. for(mi=0; mi<p->msgN; ++mi)
  904. {
  905. cmMidiTrackMsg_t* mp = p->msgV[mi];
  906. // update the duration of the sounding notes
  907. for(vp = list; vp!=NULL; vp=vp->link)
  908. vp->durTicks += mp->dtick;
  909. //
  910. // If this is sustain pedal msg
  911. //
  912. if( mp->status==kCtlMdId && mp->u.chMsgPtr->d0 == kSustainCtlMdId )
  913. {
  914. unsigned chIdx = mp->u.chMsgPtr->ch;
  915. assert( chIdx < kMidiChCnt );
  916. // set the state of the sustain pedal flags
  917. sustainFlagV[chIdx] = mp->u.chMsgPtr->d1 >= 64;
  918. // if the pedal went up ...
  919. if( sustainFlagV[chIdx] == false )
  920. {
  921. // ... then release sustaining notes
  922. _cmMidiVoice_t* pp = NULL;
  923. for(vp=list; vp != NULL; )
  924. {
  925. _cmMidiVoice_t* np = vp->link;
  926. if( vp->sustainFl && (vp->mp->u.chMsgPtr->ch == chIdx) )
  927. _cmMidFileCalcNoteDurationReleaseNote(&list,pp,vp);
  928. else
  929. pp = vp;
  930. vp = np;
  931. }
  932. }
  933. }
  934. //
  935. // if this is a note-on msg
  936. //
  937. if( mp->status==kNoteOnMdId && mp->u.chMsgPtr->d1>0 )
  938. {
  939. vp = cmMemAllocZ(_cmMidiVoice_t,1);
  940. vp->mp = mp;
  941. vp->sustainFl = false;
  942. vp->link = list;
  943. list = vp;
  944. }
  945. else
  946. //
  947. // if this is a note-off msg
  948. //
  949. if( (mp->status==kNoteOnMdId && mp->u.chMsgPtr->d1==0) || (mp->status==kNoteOffMdId) )
  950. {
  951. _cmMidiVoice_t* pp = NULL;
  952. unsigned chIdx = mp->u.chMsgPtr->ch;
  953. assert( chIdx < kMidiChCnt );
  954. // for each active voice
  955. for(vp=list; vp!=NULL; vp=vp->link )
  956. {
  957. // if this active voice ch/pitch matches the note-off msg ch pitch
  958. if( (vp->mp->u.chMsgPtr->d0==mp->u.chMsgPtr->d0) && (vp->mp->u.chMsgPtr->ch==chIdx) )
  959. {
  960. // if the sustain pedal is down for this channel - then defer turning the note off
  961. if( sustainFlagV[chIdx] )
  962. vp->sustainFl = true;
  963. else
  964. _cmMidFileCalcNoteDurationReleaseNote(&list,pp,vp);
  965. break;
  966. }
  967. pp = vp;
  968. } // end for
  969. } // end if
  970. } // end-for
  971. // check for hung notes
  972. _cmMidiVoice_t* np = NULL;
  973. vp = list;
  974. while( vp != NULL )
  975. {
  976. np = vp->link;
  977. cmErrMsg(&p->err,kMissingNoteOffMfRC,"Missing note-off for note-on:%s",cmMidiToSciPitch(vp->mp->u.chMsgPtr->d0,NULL,0));
  978. cmMemFree(vp);
  979. vp = np;
  980. }
  981. }
  982. void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks )
  983. {
  984. _cmMidiFile_t* p;
  985. unsigned mi;
  986. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  987. return;
  988. if( p->msgN == 0 )
  989. return;
  990. for(mi=0; mi<p->msgN; ++mi)
  991. {
  992. cmMidiTrackMsg_t* mp = p->msgV[mi];
  993. // locate the first msg which has a non-zero delta tick
  994. if( mp->dtick > 0 )
  995. {
  996. mp->dtick = ticks;
  997. break;
  998. }
  999. }
  1000. }
  1001. unsigned cmMidiFilePackTrackMsgBufByteCount( const cmMidiTrackMsg_t* m )
  1002. { return sizeof(cmMidiTrackMsg_t) + m->byteCnt; }
  1003. cmMidiTrackMsg_t* cmMidiFilePackTrackMsg( const cmMidiTrackMsg_t* m, void* buf, unsigned bufByteCnt )
  1004. {
  1005. unsigned n = sizeof(cmMidiTrackMsg_t) + m->byteCnt;
  1006. if( n < bufByteCnt )
  1007. {
  1008. assert(0);
  1009. return NULL;
  1010. }
  1011. // copy the cmMidiTrackMsg_t into the buffer
  1012. memcpy(buf, m, sizeof(cmMidiTrackMsg_t));
  1013. if( m->byteCnt > 0 )
  1014. {
  1015. // copy any linked data into the buffer
  1016. memcpy(buf + sizeof(cmMidiTrackMsg_t), m->u.voidPtr, m->byteCnt );
  1017. // fixup the linked data ptr
  1018. cmMidiTrackMsg_t* mp = (cmMidiTrackMsg_t*)buf;
  1019. mp->u.voidPtr = buf + sizeof(cmMidiTrackMsg_t);
  1020. }
  1021. return (cmMidiTrackMsg_t*)buf;
  1022. }
  1023. void cmMidiFilePrint( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt )
  1024. {
  1025. const _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  1026. if( mfp->fn != NULL )
  1027. cmRptPrintf(rpt,"%s ",mfp->fn);
  1028. cmRptPrintf(rpt,"fmt:%i ticksPerQN:%i tracks:%i\n",mfp->fmtId,mfp->ticksPerQN,mfp->trkN);
  1029. int i = trkIdx == cmInvalidIdx ? 0 : trkIdx;
  1030. int n = trkIdx == cmInvalidIdx ? mfp->trkN : trkIdx+1;
  1031. for(; i<n; ++i)
  1032. {
  1033. cmRptPrintf(rpt,"Track:%i\n",i);
  1034. cmMidiTrackMsg_t* tmp = mfp->trkV[i].base;
  1035. while( tmp != NULL )
  1036. {
  1037. cmRptPrintf(rpt,"%5i ", tmp->dtick );
  1038. if( tmp->status == kMetaStId )
  1039. cmRptPrintf(rpt,"%s ", cmMidiMetaStatusToLabel(tmp->metaId));
  1040. else
  1041. {
  1042. cmRptPrintf(rpt,"%4s %3i %3i %3i", cmMidiStatusToLabel(tmp->status),tmp->u.chMsgPtr->ch,tmp->u.chMsgPtr->d0,tmp->u.chMsgPtr->d1);
  1043. }
  1044. cmRptPrintf(rpt,"\n");
  1045. tmp = tmp->link;
  1046. }
  1047. }
  1048. }
  1049. bool cmMidiFileIsNull( cmMidiFileH_t h )
  1050. { return (_cmMidiFile_t*)h.h == NULL; }
  1051. void cmMidiFileTestPrint( void* printDataPtr, const char* fmt, va_list vl )
  1052. { vprintf(fmt,vl); }
  1053. void cmMidiFileTest( const char* fn, cmCtx_t* ctx )
  1054. {
  1055. cmMfRC_t rc;
  1056. cmMidiFileH_t h = cmMidiFileNullHandle;
  1057. if((rc = cmMidiFileOpen(fn,&h,ctx)) != kOkMfRC )
  1058. {
  1059. printf("Error:%i Unable to open the cmMidi file: %s\n",rc,fn);
  1060. return;
  1061. }
  1062. if(1)
  1063. {
  1064. //cmMidiFilePrint(h,cmMidiFileTrackCount(h)-1,&ctx->rpt);
  1065. cmMidiFilePrint(h,cmInvalidIdx,&ctx->rpt);
  1066. }
  1067. if( 0 )
  1068. {
  1069. printf("Tracks:%i\n",cmMidiFileTrackCount(h));
  1070. unsigned i = 0;
  1071. for(i=0; i<cmMidiFileMsgCount(h); ++i)
  1072. {
  1073. cmMidiTrackMsg_t* tmp = (cmMidiTrackMsg_t*)cmMidiFileMsgArray(h)[i];
  1074. if( tmp->status==kMetaStId && tmp->metaId == kTempoMdId )
  1075. {
  1076. double bpm = 60000000.0/tmp->u.iVal;
  1077. printf("Tempo:%i %f\n",tmp->u.iVal,bpm);
  1078. tmp->u.iVal = floor( 60000000.0/69.0 );
  1079. break;
  1080. }
  1081. }
  1082. cmMidiFileWrite(h,"/home/kevin/temp/test0.mid");
  1083. }
  1084. cmMidiFileClose(&h);
  1085. }