libcm is a C development framework with an emphasis on audio signal processing applications.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

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. }