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 47KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837
  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 "cmTime.h"
  11. #include "cmMidi.h"
  12. #include "cmMidiFile.h"
  13. #ifdef cmBIG_ENDIAN
  14. #define mfSwap16(v) (v)
  15. #define mfSwap32(v) (v)
  16. #else
  17. #define mfSwap16(v) cmSwap16(v)
  18. #define mfSwap32(v) cmSwap32(v)
  19. #endif
  20. typedef struct
  21. {
  22. unsigned cnt; // count of track records
  23. cmMidiTrackMsg_t* base; // pointer to first track recd
  24. cmMidiTrackMsg_t* last; // pointer to last track recd
  25. } _cmMidiTrack_t;
  26. typedef struct
  27. {
  28. cmErr_t err; // this objects error object
  29. cmLHeapH_t lhH; // linked heap used for all dynamically alloc'd data space
  30. cmFileH_t fh; // cmFile handle (only used in fmMidiFileOpen() and cmMidiFileWrite())
  31. unsigned short fmtId; // midi file type id: 0,1,2
  32. unsigned short ticksPerQN; // ticks per quarter note or 0 if smpteFmtId is valid
  33. cmMidiByte_t smpteFmtId; // smpte format or 0 if ticksPerQN is valid
  34. cmMidiByte_t smpteTicksPerFrame; // smpte ticks per frame or 0 if ticksPerQN is valid
  35. unsigned short trkN; // track count
  36. _cmMidiTrack_t* trkV; // track vector
  37. char* fn; // file name or NULL if this object did not originate from a file
  38. unsigned msgN; // count of msg's in msgV[]
  39. cmMidiTrackMsg_t** msgV; // sorted msg list
  40. unsigned nextUid; // next available msg uid
  41. } _cmMidiFile_t;
  42. cmMidiFileH_t cmMidiFileNullHandle = cmSTATIC_NULL_HANDLE;
  43. _cmMidiFile_t* _cmMidiFileHandleToPtr( cmMidiFileH_t h )
  44. {
  45. _cmMidiFile_t* p = (_cmMidiFile_t*)h.h;
  46. assert( p != NULL );
  47. return p;
  48. }
  49. void* _cmMidiFileMalloc( _cmMidiFile_t* mfp, unsigned byteN )
  50. { return cmLHeapAllocZ(mfp->lhH,byteN); }
  51. cmMfRC_t _cmMidiFileRead8( _cmMidiFile_t* mfp, cmMidiByte_t* p )
  52. {
  53. if( cmFileReadUChar(mfp->fh,p,1) != kOkFileRC )
  54. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI byte read failed.");
  55. return kOkMfRC;
  56. }
  57. cmMfRC_t _cmMidiFileRead16( _cmMidiFile_t* mfp, unsigned short* p )
  58. {
  59. if( cmFileReadUShort(mfp->fh,p,1) != kOkFileRC )
  60. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI short read failed.");
  61. *p = mfSwap16(*p);
  62. return kOkMfRC;
  63. }
  64. cmMfRC_t _cmMidiFileRead24( _cmMidiFile_t* mfp, unsigned* p )
  65. {
  66. *p = 0;
  67. int i = 0;
  68. for(; i<3; ++i)
  69. {
  70. unsigned char c;
  71. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  72. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI 24 bit integer read failed.");
  73. *p = (*p << 8) + c;
  74. }
  75. //*p =mfSwap32(*p);
  76. return kOkMfRC;
  77. }
  78. cmMfRC_t _cmMidiFileRead32( _cmMidiFile_t* mfp, unsigned* p )
  79. {
  80. if( cmFileReadUInt(mfp->fh,p,1) != kOkFileRC )
  81. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI integer read failed.");
  82. *p = mfSwap32(*p);
  83. return kOkMfRC;
  84. }
  85. cmMfRC_t _cmMidiFileReadText( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  86. {
  87. if( byteN == 0 )
  88. return kOkMfRC;
  89. char* t = (char*)_cmMidiFileMalloc(mfp,byteN+1);
  90. t[byteN] = 0;
  91. if( cmFileReadChar(mfp->fh,t,byteN) != kOkFileRC )
  92. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read text failed.");
  93. tmp->u.text = t;
  94. tmp->byteCnt = byteN;
  95. return kOkMfRC;
  96. }
  97. cmMfRC_t _cmMidiFileReadRecd( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  98. {
  99. char* t = (char*)_cmMidiFileMalloc(mfp,byteN);
  100. if( cmFileReadChar(mfp->fh,t,byteN) != kOkFileRC )
  101. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read record failed.");
  102. tmp->byteCnt = byteN;
  103. tmp->u.voidPtr = t;
  104. return kOkMfRC;
  105. }
  106. cmMfRC_t _cmMidiFileReadVarLen( _cmMidiFile_t* mfp, unsigned* p )
  107. {
  108. unsigned char c;
  109. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  110. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read variable length integer failed.");
  111. if( !(c & 0x80) )
  112. *p = c;
  113. else
  114. {
  115. *p = c & 0x7f;
  116. do
  117. {
  118. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  119. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read variable length integer failed.");
  120. *p = (*p << 7) + (c & 0x7f);
  121. }while( c & 0x80 );
  122. }
  123. return kOkMfRC;
  124. }
  125. cmMfRC_t _cmMidiFileAppendTrackMsg( _cmMidiFile_t* mfp, unsigned short trkIdx, unsigned dtick, cmMidiByte_t status, cmMidiTrackMsg_t** trkMsgPtrPtr )
  126. {
  127. cmMidiTrackMsg_t* tmp = (cmMidiTrackMsg_t*)_cmMidiFileMalloc(mfp, sizeof(cmMidiTrackMsg_t) );
  128. // link new record onto track record chain
  129. if( mfp->trkV[trkIdx].base == NULL )
  130. mfp->trkV[trkIdx].base = tmp;
  131. else
  132. mfp->trkV[trkIdx].last->link = tmp;
  133. mfp->trkV[trkIdx].last = tmp;
  134. mfp->trkV[trkIdx].cnt++;
  135. // set the generic track record fields
  136. tmp->dtick = dtick;
  137. tmp->status = status;
  138. tmp->metaId = kInvalidMetaMdId;
  139. tmp->trkIdx = trkIdx;
  140. tmp->byteCnt = 0;
  141. *trkMsgPtrPtr = tmp;
  142. return kOkMfRC;
  143. }
  144. cmMfRC_t _cmMidiFileReadSysEx( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  145. {
  146. cmMfRC_t rc = kOkMfRC;
  147. cmMidiByte_t b = 0;
  148. if( byteN == cmInvalidCnt )
  149. {
  150. long offs;
  151. if( cmFileTell(mfp->fh,&offs) != kOkFileRC )
  152. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI File 'tell' failed.");
  153. byteN = 0;
  154. // get the length of the sys-ex msg
  155. while( !cmFileEof(mfp->fh) && (b != kSysComEoxMdId) )
  156. {
  157. if((rc = _cmMidiFileRead8(mfp,&b)) != kOkMfRC )
  158. return rc;
  159. ++byteN;
  160. }
  161. // verify that the EOX byte was found
  162. if( b != kSysComEoxMdId )
  163. return cmErrMsg(&mfp->err,kMissingEoxMfRC,"MIDI file missing 'end-of-sys-ex'.");
  164. // rewind to the beginning of the msg
  165. if( cmFileSeek(mfp->fh,kBeginFileFl,offs) != kOkFileRC )
  166. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file seek failed on sys-ex read.");
  167. }
  168. // allocate memory to hold the sys-ex msg
  169. cmMidiByte_t* mp = (cmMidiByte_t *)_cmMidiFileMalloc(mfp, byteN );
  170. // read the sys-ex msg from the file into msg memory
  171. if( cmFileReadUChar(mfp->fh,mp,byteN) != kOkFileRC )
  172. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI sys-ex read failed.");
  173. tmp->byteCnt = byteN;
  174. tmp->u.sysExPtr = mp;
  175. return rc;
  176. }
  177. cmMfRC_t _cmMidiFileReadChannelMsg( _cmMidiFile_t* mfp, cmMidiByte_t* rsPtr, cmMidiByte_t status, cmMidiTrackMsg_t* tmp )
  178. {
  179. cmMfRC_t rc = kOkMfRC;
  180. cmMidiChMsg_t* p = (cmMidiChMsg_t*)_cmMidiFileMalloc(mfp,sizeof(cmMidiChMsg_t));
  181. unsigned useRsFl = status <= 0x7f;
  182. cmMidiByte_t statusCh = useRsFl ? *rsPtr : status;
  183. if( useRsFl )
  184. p->d0 = status;
  185. else
  186. *rsPtr = status;
  187. tmp->byteCnt = sizeof(cmMidiChMsg_t);
  188. tmp->status = statusCh & 0xf0;
  189. p->ch = statusCh & 0x0f;
  190. p->durMicros = 0;
  191. unsigned byteN = cmMidiStatusToByteCount(tmp->status);
  192. if( byteN==kInvalidMidiByte || byteN > 2 )
  193. return cmErrMsg(&mfp->err,kInvalidStatusMfRC,"Invalid status:0x%x %i.",tmp->status,tmp->status);
  194. unsigned i;
  195. for(i=useRsFl; i<byteN; ++i)
  196. {
  197. cmMidiByte_t* b = i==0 ? &p->d0 : &p->d1;
  198. if((rc = _cmMidiFileRead8(mfp,b)) != kOkMfRC )
  199. return rc;
  200. }
  201. // convert note-on velocity=0 to note off
  202. if( tmp->status == kNoteOnMdId && p->d1==0 )
  203. tmp->status = kNoteOffMdId;
  204. tmp->u.chMsgPtr = p;
  205. return rc;
  206. }
  207. cmMfRC_t _cmMidiFileReadMetaMsg( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp )
  208. {
  209. cmMidiByte_t metaId;
  210. cmMfRC_t rc;
  211. unsigned byteN = 0;
  212. if((rc = _cmMidiFileRead8(mfp,&metaId)) != kOkMfRC )
  213. return rc;
  214. if((rc = _cmMidiFileReadVarLen(mfp,&byteN)) != kOkMfRC )
  215. return rc;
  216. //printf("mt: %i 0x%x n:%i\n",metaId,metaId,byteN);
  217. switch( metaId )
  218. {
  219. case kSeqNumbMdId: rc = _cmMidiFileRead16(mfp,&tmp->u.sVal); break;
  220. case kTextMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  221. case kCopyMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  222. case kTrkNameMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  223. case kInstrNameMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  224. case kLyricsMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  225. case kMarkerMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  226. case kCuePointMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  227. case kMidiChMdId: rc = _cmMidiFileRead8(mfp,&tmp->u.bVal); break;
  228. case kEndOfTrkMdId: break;
  229. case kTempoMdId: rc = _cmMidiFileRead24(mfp,&tmp->u.iVal); break;
  230. case kSmpteMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiSmpte_t)); break;
  231. case kTimeSigMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiTimeSig_t)); break;
  232. case kKeySigMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiKeySig_t)); break;
  233. case kSeqSpecMdId: rc = _cmMidiFileReadSysEx(mfp,tmp,byteN); break;
  234. default:
  235. cmFileSeek(mfp->fh,kCurFileFl,byteN);
  236. rc = cmErrMsg(&mfp->err,kUnknownMetaIdMfRC,"Unknown meta status:0x%x %i.",metaId,metaId);
  237. }
  238. tmp->metaId = metaId;
  239. return rc;
  240. }
  241. cmMfRC_t _cmMidiFileReadTrack( _cmMidiFile_t* mfp, unsigned short trkIdx )
  242. {
  243. cmMfRC_t rc = kOkMfRC;
  244. unsigned dticks = 0;
  245. cmMidiByte_t status;
  246. cmMidiByte_t runstatus = 0;
  247. bool contFl = true;
  248. while( contFl && (rc==kOkMfRC))
  249. {
  250. cmMidiTrackMsg_t* tmp = NULL;
  251. // read the tick count
  252. if((rc = _cmMidiFileReadVarLen(mfp,&dticks)) != kOkMfRC )
  253. return rc;
  254. // read the status byte
  255. if((rc = _cmMidiFileRead8(mfp,&status)) != kOkMfRC )
  256. return rc;
  257. //printf("st:%i 0x%x\n",status,status);
  258. // append a track msg
  259. if((rc = _cmMidiFileAppendTrackMsg( mfp, trkIdx, dticks, status, &tmp )) != kOkMfRC )
  260. return rc;
  261. // switch on status
  262. switch( status )
  263. {
  264. // handle sys-ex msg
  265. case kSysExMdId:
  266. rc = _cmMidiFileReadSysEx(mfp,tmp,cmInvalidCnt);
  267. break;
  268. // handle meta msg
  269. case kMetaStId:
  270. rc = _cmMidiFileReadMetaMsg(mfp,tmp);
  271. // ignore unknown meta messages
  272. if( rc == kUnknownMetaIdMfRC )
  273. rc = kOkMfRC;
  274. contFl = tmp->metaId != kEndOfTrkMdId;
  275. break;
  276. default:
  277. // handle channel msg
  278. rc = _cmMidiFileReadChannelMsg(mfp,&runstatus,status,tmp);
  279. }
  280. }
  281. return rc;
  282. }
  283. cmMfRC_t _cmMidiFileReadHdr( _cmMidiFile_t* mfp )
  284. {
  285. cmMfRC_t rc;
  286. unsigned fileId;
  287. unsigned chunkByteN;
  288. // read the file id
  289. if((rc = _cmMidiFileRead32(mfp,&fileId)) != kOkMfRC )
  290. return rc;
  291. // verify the file id
  292. if( fileId != 'MThd' )
  293. return cmErrMsg(&mfp->err,kNotAMidiFileMfRC,"");
  294. // read the file chunk byte count
  295. if((rc = _cmMidiFileRead32(mfp,&chunkByteN)) != kOkMfRC )
  296. return rc;
  297. // read the format id
  298. if((rc = _cmMidiFileRead16(mfp,&mfp->fmtId)) != kOkMfRC )
  299. return rc;
  300. // read the track count
  301. if((rc = _cmMidiFileRead16(mfp,&mfp->trkN)) != kOkMfRC )
  302. return rc;
  303. // read the ticks per quarter note
  304. if((rc = _cmMidiFileRead16(mfp,&mfp->ticksPerQN)) != kOkMfRC )
  305. return rc;
  306. // if the division field was given in smpte
  307. if( mfp->ticksPerQN & 0x8000 )
  308. {
  309. mfp->smpteFmtId = (mfp->ticksPerQN & 0x7f00) >> 8;
  310. mfp->smpteTicksPerFrame = (mfp->ticksPerQN & 0xFF);
  311. mfp->ticksPerQN = 0;
  312. }
  313. // allocate and zero the track array
  314. if( mfp->trkN )
  315. mfp->trkV = _cmMidiFileMalloc( mfp, sizeof(_cmMidiTrack_t)*mfp->trkN);
  316. return rc;
  317. }
  318. int _cmMidiFileSortFunc( const void *p0, const void* p1 )
  319. {
  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. // Set the absolute accumulated ticks (atick) value of each track message.
  325. // The absolute accumulated ticks gives a global time ordering for all
  326. // messages in the file.
  327. void _cmMidiFileSetAccumulateTicks( _cmMidiFile_t* p )
  328. {
  329. cmMidiTrackMsg_t* nextTrkMsg[ p->trkN ]; // next msg in each track
  330. unsigned long long atick = 0;
  331. unsigned i;
  332. bool fl = true;
  333. // iniitalize nextTrkTick[] and nextTrkMsg[].
  334. for(i=0; i<p->trkN; ++i)
  335. if((nextTrkMsg[i] = p->trkV[i].base) != NULL )
  336. nextTrkMsg[i]->atick = nextTrkMsg[i]->dtick;
  337. while(1)
  338. {
  339. unsigned k = cmInvalidIdx;
  340. // find the trk which has the next msg (min atick time)
  341. for(i=0; i<p->trkN; ++i)
  342. if( nextTrkMsg[i]!=NULL && (k==cmInvalidIdx || nextTrkMsg[i]->atick < nextTrkMsg[k]->atick) )
  343. k = i;
  344. // no next msg was found - we're done
  345. if( k == cmInvalidIdx )
  346. break;
  347. if( fl && nextTrkMsg[k]->dtick > 0 )
  348. {
  349. fl = false;
  350. nextTrkMsg[k]->dtick = 1;
  351. nextTrkMsg[k]->atick = 1;
  352. }
  353. // store the current atick
  354. atick = nextTrkMsg[k]->atick;
  355. // advance the selected track to it's next message
  356. nextTrkMsg[k] = nextTrkMsg[k]->link;
  357. // set the selected tracks next atick time
  358. if( nextTrkMsg[k] != NULL )
  359. nextTrkMsg[k]->atick = atick + nextTrkMsg[k]->dtick;
  360. }
  361. }
  362. void _cmMidiFileSetAbsoluteTime( _cmMidiFile_t* mfp )
  363. {
  364. double microsPerQN = 60000000/120; // default tempo;
  365. double microsPerTick = microsPerQN / mfp->ticksPerQN;
  366. unsigned long long amicro = 0;
  367. unsigned i;
  368. for(i=0; i<mfp->msgN; ++i)
  369. {
  370. cmMidiTrackMsg_t* mp = mfp->msgV[i];
  371. unsigned dtick = 0;
  372. if( i > 0 )
  373. {
  374. // atick must have already been set and sorted
  375. assert( mp->atick >= mfp->msgV[i-1]->atick );
  376. dtick = mp->atick - mfp->msgV[i-1]->atick;
  377. }
  378. amicro += microsPerTick * dtick;
  379. mp->amicro = amicro;
  380. // track tempo changes
  381. if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
  382. microsPerTick = mp->u.iVal / mfp->ticksPerQN;
  383. }
  384. }
  385. cmMfRC_t _cmMidiFileClose( _cmMidiFile_t* mfp )
  386. {
  387. cmMfRC_t rc = kOkMfRC;
  388. if( mfp == NULL )
  389. return rc;
  390. cmMemPtrFree(&mfp->msgV);
  391. if( cmFileIsValid( mfp->fh ) )
  392. if( cmFileClose( &mfp->fh ) != kOkFileRC )
  393. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file close failed.");
  394. if( cmLHeapIsValid( mfp->lhH ) )
  395. cmLHeapDestroy(&mfp->lhH);
  396. cmMemPtrFree(&mfp);
  397. return rc;
  398. }
  399. cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* hPtr, const char* fn )
  400. {
  401. cmMfRC_t rc = kOkMfRC;
  402. _cmMidiFile_t* mfp = NULL;
  403. unsigned short trkIdx = 0;
  404. cmErr_t err;
  405. unsigned i,j;
  406. if( cmMidiFileIsValid(*hPtr) )
  407. if((rc = _cmMidiFileClose(_cmMidiFileHandleToPtr(*hPtr))) != kOkMfRC )
  408. return rc;
  409. cmErrSetup(&err,&ctx->rpt,"MIDI File");
  410. // allocate the midi file object
  411. if(( mfp = cmMemAllocZ( _cmMidiFile_t, 1)) == NULL )
  412. return rc = cmErrMsg(&err,kMemAllocFailMfRC,"MIDI file memory allocation failed.");
  413. cmErrClone(&mfp->err,&err);
  414. // allocate the linked heap
  415. if( cmLHeapIsValid( mfp->lhH = cmLHeapCreate( 1024, ctx )) == false )
  416. {
  417. rc = cmErrMsg(&err,kMemAllocFailMfRC,"MIDI heap allocation failed.");
  418. goto errLabel;
  419. }
  420. // open the file
  421. if(cmFileOpen(&mfp->fh,fn,kReadFileFl | kBinaryFileFl,mfp->err.rpt) != kOkFileRC )
  422. {
  423. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file open failed.");
  424. goto errLabel;
  425. }
  426. // read header and setup track array
  427. if(( rc = _cmMidiFileReadHdr(mfp)) != kOkMfRC )
  428. goto errLabel;
  429. while( !cmFileEof(mfp->fh) && trkIdx < mfp->trkN )
  430. {
  431. unsigned chkId=0,chkN=0;
  432. // read the chunk id
  433. if((rc = _cmMidiFileRead32(mfp,&chkId)) != kOkMfRC )
  434. goto errLabel;
  435. // read the chunk size
  436. if((rc = _cmMidiFileRead32(mfp,&chkN)) != kOkMfRC )
  437. goto errLabel;
  438. // if this is not a trk chunk then skip it
  439. if( chkId != (unsigned)'MTrk')
  440. {
  441. //if( fseek( mfp->fp, chkN, SEEK_CUR) != 0 )
  442. if( cmFileSeek(mfp->fh,kCurFileFl,chkN) != kOkFileRC )
  443. {
  444. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file seek failed.");
  445. goto errLabel;
  446. }
  447. }
  448. else
  449. {
  450. if((rc = _cmMidiFileReadTrack(mfp,trkIdx)) != kOkMfRC )
  451. goto errLabel;
  452. ++trkIdx;
  453. }
  454. }
  455. // get the total trk msg count
  456. mfp->msgN = 0;
  457. for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx)
  458. mfp->msgN += mfp->trkV[ trkIdx ].cnt;
  459. // allocate the trk msg index vector: msgV[]
  460. mfp->msgV = cmMemAllocZ(cmMidiTrackMsg_t*, mfp->msgN);
  461. mfp->nextUid = 0;
  462. // store a pointer to every trk msg in msgV[] and set 'uid'
  463. for(i=0,j=0; i<mfp->trkN; ++i)
  464. {
  465. cmMidiTrackMsg_t* m = mfp->trkV[i].base;
  466. for(; m!=NULL; m=m->link)
  467. {
  468. assert( j < mfp->msgN );
  469. mfp->msgV[j++] = m;
  470. m->uid = mfp->nextUid++;
  471. }
  472. }
  473. mfp->fn = _cmMidiFileMalloc(mfp,strlen(fn)+1);
  474. assert( mfp->fn != NULL );
  475. strcpy(mfp->fn,fn);
  476. // set the atick value in each msg
  477. _cmMidiFileSetAccumulateTicks(mfp);
  478. // sort msgV[] in ascending order on atick
  479. qsort( mfp->msgV, mfp->msgN, sizeof(cmMidiTrackMsg_t*), _cmMidiFileSortFunc );
  480. // set the amicro value in each msg
  481. _cmMidiFileSetAbsoluteTime(mfp);
  482. hPtr->h = mfp;
  483. errLabel:
  484. if( cmFileClose(&mfp->fh) != kOkFileRC )
  485. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file close failed.");
  486. if( rc != kOkMfRC )
  487. _cmMidiFileClose(mfp);
  488. return rc;
  489. }
  490. cmMfRC_t cmMidiFileClose( cmMidiFileH_t* h )
  491. {
  492. cmMfRC_t rc;
  493. if( cmMidiFileIsNull(*h) )
  494. return kOkMfRC;
  495. if((rc = _cmMidiFileClose(_cmMidiFileHandleToPtr(*h))) == kOkMfRC )
  496. return rc;
  497. h->h = NULL;
  498. return rc;
  499. }
  500. cmMfRC_t _cmMidiFileWrite8( _cmMidiFile_t* mfp, unsigned char v )
  501. {
  502. cmMfRC_t rc = kOkMfRC;
  503. if( cmFileWriteUChar(mfp->fh,&v,1) != kOkFileRC )
  504. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file byte write failed.");
  505. return rc;
  506. }
  507. cmMfRC_t _cmMidiFileWrite16( _cmMidiFile_t* mfp, unsigned short v )
  508. {
  509. cmMfRC_t rc = kOkMfRC;
  510. v = mfSwap16(v);
  511. if( cmFileWriteUShort(mfp->fh,&v,1) != kOkFileRC )
  512. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file short integer write failed.");
  513. return rc;
  514. }
  515. cmMfRC_t _cmMidiFileWrite24( _cmMidiFile_t* mfp, unsigned v )
  516. {
  517. cmMfRC_t rc = kOkMfRC;
  518. unsigned mask = 0xff0000;
  519. int i;
  520. for(i=2; i>=0; --i)
  521. {
  522. unsigned char c = (v & mask) >> (i*8);
  523. mask >>= 8;
  524. if( cmFileWriteUChar(mfp->fh,&c,1) != kOkFileRC )
  525. {
  526. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file 24 bit integer write failed.");
  527. goto errLabel;
  528. }
  529. }
  530. errLabel:
  531. return rc;
  532. }
  533. cmMfRC_t _cmMidiFileWrite32( _cmMidiFile_t* mfp, unsigned v )
  534. {
  535. cmMfRC_t rc = kOkMfRC;
  536. v = mfSwap32(v);
  537. if( cmFileWriteUInt(mfp->fh,&v,1) != kOkFileRC )
  538. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file integer write failed.");
  539. return rc;
  540. }
  541. cmMfRC_t _cmMidiFileWriteRecd( _cmMidiFile_t* mfp, const void* v, unsigned byteCnt )
  542. {
  543. cmMfRC_t rc = kOkMfRC;
  544. if( cmFileWriteChar(mfp->fh,v,byteCnt) != kOkFileRC )
  545. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file write record failed.");
  546. return rc;
  547. }
  548. cmMfRC_t _cmMidiFileWriteVarLen( _cmMidiFile_t* mfp, unsigned v )
  549. {
  550. cmMfRC_t rc = kOkMfRC;
  551. unsigned buf = v & 0x7f;
  552. while((v >>= 7) > 0 )
  553. {
  554. buf <<= 8;
  555. buf |= 0x80;
  556. buf += (v & 0x7f);
  557. }
  558. while(1)
  559. {
  560. unsigned char c = (unsigned char)(buf & 0xff);
  561. if( cmFileWriteUChar(mfp->fh,&c,1) != kOkFileRC )
  562. {
  563. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file variable length integer write failed.");
  564. goto errLabel;
  565. }
  566. if( buf & 0x80 )
  567. buf >>= 8;
  568. else
  569. break;
  570. }
  571. errLabel:
  572. return rc;
  573. }
  574. cmMfRC_t _cmMidiFileWriteHdr( _cmMidiFile_t* mfp )
  575. {
  576. cmMfRC_t rc;
  577. unsigned fileId = 'MThd';
  578. unsigned chunkByteN = 6;
  579. // write the file id ('MThd')
  580. if((rc = _cmMidiFileWrite32(mfp,fileId)) != kOkMfRC )
  581. return rc;
  582. // write the file chunk byte count (always 6)
  583. if((rc = _cmMidiFileWrite32(mfp,chunkByteN)) != kOkMfRC )
  584. return rc;
  585. // write the MIDI file format id (0,1,2)
  586. if((rc = _cmMidiFileWrite16(mfp,mfp->fmtId)) != kOkMfRC )
  587. return rc;
  588. // write the track count
  589. if((rc = _cmMidiFileWrite16(mfp,mfp->trkN)) != kOkMfRC )
  590. return rc;
  591. unsigned short v = 0;
  592. // if the ticks per quarter note field is valid ...
  593. if( mfp->ticksPerQN )
  594. v = mfp->ticksPerQN;
  595. else
  596. {
  597. // ... otherwise the division field was given in smpte
  598. v = mfp->smpteFmtId << 8;
  599. v += mfp->smpteTicksPerFrame;
  600. }
  601. if((rc = _cmMidiFileWrite16(mfp,v)) != kOkMfRC )
  602. return rc;
  603. return rc;
  604. }
  605. cmMfRC_t _cmMidiFileWriteSysEx( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp )
  606. {
  607. cmMfRC_t rc = kOkMfRC;
  608. if((rc = _cmMidiFileWrite8(mfp,kSysExMdId)) != kOkMfRC )
  609. goto errLabel;
  610. if( cmFileWriteUChar(mfp->fh,tmp->u.sysExPtr,tmp->byteCnt) != kOkFileRC )
  611. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"Sys-ex msg write failed.");
  612. errLabel:
  613. return rc;
  614. }
  615. cmMfRC_t _cmMidiFileWriteChannelMsg( _cmMidiFile_t* mfp, const cmMidiTrackMsg_t* tmp, cmMidiByte_t* runStatus )
  616. {
  617. cmMfRC_t rc = kOkMfRC;
  618. unsigned byteN = cmMidiStatusToByteCount(tmp->status);
  619. cmMidiByte_t status = tmp->status + tmp->u.chMsgPtr->ch;
  620. if( status != *runStatus )
  621. {
  622. *runStatus = status;
  623. if((rc = _cmMidiFileWrite8(mfp,status)) != kOkMfRC )
  624. goto errLabel;
  625. }
  626. if(byteN>=1)
  627. if((rc = _cmMidiFileWrite8(mfp,tmp->u.chMsgPtr->d0)) != kOkMfRC )
  628. goto errLabel;
  629. if(byteN>=2)
  630. if((rc = _cmMidiFileWrite8(mfp,tmp->u.chMsgPtr->d1)) != kOkMfRC )
  631. goto errLabel;
  632. errLabel:
  633. return rc;
  634. }
  635. cmMfRC_t _cmMidiFileWriteMetaMsg( _cmMidiFile_t* mfp, const cmMidiTrackMsg_t* tmp )
  636. {
  637. cmMfRC_t rc;
  638. if((rc = _cmMidiFileWrite8(mfp,kMetaStId)) != kOkMfRC )
  639. return rc;
  640. if((rc = _cmMidiFileWrite8(mfp,tmp->metaId)) != kOkMfRC )
  641. return rc;
  642. switch( tmp->metaId )
  643. {
  644. case kSeqNumbMdId:
  645. if((rc = _cmMidiFileWrite8(mfp,sizeof(tmp->u.sVal))) == kOkMfRC )
  646. rc = _cmMidiFileWrite16(mfp,tmp->u.sVal);
  647. break;
  648. case kTempoMdId:
  649. if((rc = _cmMidiFileWrite8(mfp,3)) == kOkMfRC )
  650. rc = _cmMidiFileWrite24(mfp,tmp->u.iVal);
  651. break;
  652. case kSmpteMdId:
  653. if((rc = _cmMidiFileWrite8(mfp,sizeof(cmMidiSmpte_t))) == kOkMfRC )
  654. rc = _cmMidiFileWriteRecd(mfp,tmp->u.smptePtr,sizeof(cmMidiSmpte_t));
  655. break;
  656. case kTimeSigMdId:
  657. if((rc = _cmMidiFileWrite8(mfp,sizeof(cmMidiTimeSig_t))) == kOkMfRC )
  658. rc = _cmMidiFileWriteRecd(mfp,tmp->u.timeSigPtr,sizeof(cmMidiTimeSig_t));
  659. break;
  660. case kKeySigMdId:
  661. if((rc = _cmMidiFileWrite8(mfp,sizeof(cmMidiKeySig_t))) == kOkMfRC )
  662. rc = _cmMidiFileWriteRecd(mfp,tmp->u.keySigPtr,sizeof(cmMidiKeySig_t));
  663. break;
  664. case kSeqSpecMdId:
  665. if((rc = _cmMidiFileWriteVarLen(mfp,sizeof(tmp->byteCnt))) == kOkMfRC )
  666. rc = _cmMidiFileWriteRecd(mfp,tmp->u.sysExPtr,tmp->byteCnt);
  667. break;
  668. case kMidiChMdId:
  669. if((rc = _cmMidiFileWrite8(mfp,sizeof(tmp->u.bVal))) == kOkMfRC )
  670. rc = _cmMidiFileWrite8(mfp,tmp->u.bVal);
  671. break;
  672. case kEndOfTrkMdId:
  673. rc = _cmMidiFileWrite8(mfp,0);
  674. break;
  675. case kTextMdId:
  676. case kCopyMdId:
  677. case kTrkNameMdId:
  678. case kInstrNameMdId:
  679. case kLyricsMdId:
  680. case kMarkerMdId:
  681. case kCuePointMdId:
  682. {
  683. unsigned n = tmp->u.text==NULL ? 0 : strlen(tmp->u.text);
  684. if((rc = _cmMidiFileWriteVarLen(mfp,n)) == kOkMfRC && n>0 )
  685. rc = _cmMidiFileWriteRecd(mfp,tmp->u.text,n);
  686. }
  687. break;
  688. default:
  689. {
  690. // ignore unknown meta messages
  691. }
  692. }
  693. return rc;
  694. }
  695. cmMfRC_t _cmMidiFileWriteTrack( _cmMidiFile_t* mfp, unsigned trkIdx )
  696. {
  697. cmMfRC_t rc = kOkMfRC;
  698. cmMidiTrackMsg_t* tmp = mfp->trkV[trkIdx].base;
  699. cmMidiByte_t runStatus = 0;
  700. for(; tmp!=NULL; tmp=tmp->link)
  701. {
  702. // write the msg tick count
  703. if((rc = _cmMidiFileWriteVarLen(mfp,tmp->dtick)) != kOkMfRC )
  704. return rc;
  705. // switch on status
  706. switch( tmp->status )
  707. {
  708. // handle sys-ex msg
  709. case kSysExMdId:
  710. rc = _cmMidiFileWriteSysEx(mfp,tmp);
  711. break;
  712. // handle meta msg
  713. case kMetaStId:
  714. rc = _cmMidiFileWriteMetaMsg(mfp,tmp);
  715. break;
  716. default:
  717. // handle channel msg
  718. rc = _cmMidiFileWriteChannelMsg(mfp,tmp,&runStatus);
  719. }
  720. }
  721. return rc;
  722. }
  723. cmMfRC_t cmMidiFileWrite( cmMidiFileH_t h, const char* fn )
  724. {
  725. cmMfRC_t rc = kOkMfRC;
  726. _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  727. unsigned i;
  728. // create the output file
  729. if( cmFileOpen(&mfp->fh,fn,kWriteFileFl,mfp->err.rpt) != kOkFileRC )
  730. return cmErrMsg(&mfp->err,kFileFailMfRC,"The MIDI file '%s' could not be created.",cmStringNullGuard(fn));
  731. // write the file header
  732. if((rc = _cmMidiFileWriteHdr(mfp)) != kOkMfRC )
  733. {
  734. rc = cmErrMsg(&mfp->err,rc,"The file header write failed on the MIDI file '%s'.",cmStringNullGuard(fn));
  735. goto errLabel;
  736. }
  737. for(i=0; i < mfp->trkN; ++i )
  738. {
  739. unsigned chkId='MTrk';
  740. long offs0,offs1;
  741. // write the track chunk id ('MTrk')
  742. if((rc = _cmMidiFileWrite32(mfp,chkId)) != kOkMfRC )
  743. goto errLabel;
  744. cmFileTell(mfp->fh,&offs0);
  745. // write the track chunk size as zero
  746. if((rc = _cmMidiFileWrite32(mfp,0)) != kOkMfRC )
  747. goto errLabel;
  748. if((rc = _cmMidiFileWriteTrack(mfp,i)) != kOkMfRC )
  749. goto errLabel;
  750. cmFileTell(mfp->fh,&offs1);
  751. cmFileSeek(mfp->fh,kBeginFileFl,offs0);
  752. _cmMidiFileWrite32(mfp,offs1-offs0-4);
  753. cmFileSeek(mfp->fh,kBeginFileFl,offs1);
  754. }
  755. errLabel:
  756. cmFileClose(&mfp->fh);
  757. return rc;
  758. }
  759. bool cmMidiFileIsValid( cmMidiFileH_t h )
  760. { return !cmMidiFileIsNull(h); }
  761. unsigned cmMidiFileTrackCount( cmMidiFileH_t h )
  762. {
  763. _cmMidiFile_t* mfp;
  764. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  765. return cmInvalidCnt;
  766. return mfp->trkN;
  767. }
  768. unsigned cmMidiFileType( cmMidiFileH_t h )
  769. {
  770. _cmMidiFile_t* mfp;
  771. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  772. return cmInvalidId;
  773. return mfp->fmtId;
  774. }
  775. const char* cmMidiFileName( cmMidiFileH_t h )
  776. {
  777. _cmMidiFile_t* mfp;
  778. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  779. return NULL;
  780. return mfp->fn;
  781. }
  782. unsigned cmMidiFileTicksPerQN( cmMidiFileH_t h )
  783. {
  784. _cmMidiFile_t* mfp;
  785. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  786. return cmInvalidCnt;
  787. return mfp->ticksPerQN;
  788. }
  789. cmMidiByte_t cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h )
  790. {
  791. _cmMidiFile_t* mfp;
  792. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  793. return kInvalidMidiByte;
  794. if( mfp->ticksPerQN != 0 )
  795. return 0;
  796. return mfp->smpteTicksPerFrame;
  797. }
  798. cmMidiByte_t cmMidiFileSmpteFormatId( cmMidiFileH_t h )
  799. {
  800. _cmMidiFile_t* mfp;
  801. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  802. return kInvalidMidiByte;
  803. if( mfp->ticksPerQN != 0 )
  804. return 0;
  805. return mfp->smpteFmtId;
  806. }
  807. unsigned cmMidiFileTrackMsgCount( cmMidiFileH_t h, unsigned trackIdx )
  808. {
  809. _cmMidiFile_t* mfp;
  810. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  811. return cmInvalidCnt;
  812. return mfp->trkV[trackIdx].cnt;
  813. }
  814. const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx )
  815. {
  816. _cmMidiFile_t* mfp;
  817. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  818. return NULL;
  819. return mfp->trkV[trackIdx].base;
  820. }
  821. unsigned cmMidiFileMsgCount( cmMidiFileH_t h )
  822. {
  823. _cmMidiFile_t* mfp;
  824. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  825. return cmInvalidCnt;
  826. return mfp->msgN;
  827. }
  828. const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h )
  829. {
  830. _cmMidiFile_t* mfp;
  831. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  832. return NULL;
  833. // this cast is needed to eliminate an apparently needless 'incompatible type' warning
  834. return (const cmMidiTrackMsg_t**)mfp->msgV;
  835. }
  836. cmMidiTrackMsg_t* _cmMidiFileUidToMsg( _cmMidiFile_t* mfp, unsigned uid )
  837. {
  838. unsigned i;
  839. for(i=0; i<mfp->msgN; ++i)
  840. if( mfp->msgV[i]->uid == uid )
  841. return mfp->msgV[i];
  842. return NULL;
  843. }
  844. cmMfRC_t cmMidiFileSetVelocity( cmMidiFileH_t h, unsigned uid, cmMidiByte_t vel )
  845. {
  846. cmMidiTrackMsg_t* r;
  847. _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  848. assert( mfp != NULL );
  849. if((r = _cmMidiFileUidToMsg(mfp,uid)) == NULL )
  850. return cmErrMsg(&mfp->err,kUidNotFoundMfRC,"The MIDI file uid %i could not be found.",uid);
  851. if( cmMidiIsNoteOn(r->status)==false && cmMidiIsNoteOff(r->status,0)==false )
  852. return cmErrMsg(&mfp->err,kUidNotANoteMsgMfRC,"Cannot set velocity on a non-Note-On/Off msg.");
  853. cmMidiChMsg_t* chm = (cmMidiChMsg_t*)r->u.chMsgPtr;
  854. chm->d1 = vel;
  855. return kOkMfRC;
  856. }
  857. unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long offsUSecs, unsigned* msgUsecsPtr, unsigned* microsPerTickPtr )
  858. {
  859. _cmMidiFile_t* p;
  860. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  861. return cmInvalidIdx;
  862. if( p->msgN == 0 )
  863. return cmInvalidIdx;
  864. unsigned mi;
  865. double microsPerQN = 60000000.0/120.0;
  866. double microsPerTick = microsPerQN / p->ticksPerQN;
  867. double accUSecs = 0;
  868. for(mi=0; mi<p->msgN; ++mi)
  869. {
  870. const cmMidiTrackMsg_t* mp = p->msgV[mi];
  871. if( mp->amicro >= offsUSecs )
  872. break;
  873. }
  874. if( mi == p->msgN )
  875. return cmInvalidIdx;
  876. if( msgUsecsPtr != NULL )
  877. *msgUsecsPtr = round(accUSecs - offsUSecs);
  878. if( microsPerTickPtr != NULL )
  879. *microsPerTickPtr = round(microsPerTick);
  880. return mi;
  881. }
  882. double cmMidiFileDurSecs( cmMidiFileH_t h )
  883. {
  884. _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  885. if( mfp->msgN == 0 )
  886. return 0;
  887. return mfp->msgV[ mfp->msgN-1 ]->amicro / 1000000.0;
  888. }
  889. typedef struct _cmMidiVoice_str
  890. {
  891. const cmMidiTrackMsg_t* mp;
  892. unsigned durMicros;
  893. bool sustainFl;
  894. struct _cmMidiVoice_str* link;
  895. } _cmMidiVoice_t;
  896. void _cmMidFileCalcNoteDurationReleaseNote( _cmMidiVoice_t** listPtrPtr, _cmMidiVoice_t* pp, _cmMidiVoice_t* vp )
  897. {
  898. assert( (pp==NULL && vp==*listPtrPtr) || pp->link==vp);
  899. // store the duration of the note into the track msg
  900. // assoc'd with the note-on msg
  901. cmMidiChMsg_t* cmp = (cmMidiChMsg_t*)vp->mp->u.chMsgPtr; // cast away const
  902. cmp->durMicros = vp->durMicros;
  903. _cmMidiVoice_t* np = vp->link;
  904. // release the voice msg
  905. cmMemFree(vp);
  906. // unlink the active voice msg
  907. if( pp == NULL )
  908. *listPtrPtr = np;
  909. else
  910. pp->link = np;
  911. }
  912. void _cmMidiFileCalcNoteDurationsAllocVoice( _cmMidiVoice_t** listPtrPtr, cmMidiTrackMsg_t* mp, bool sustainFl )
  913. {
  914. _cmMidiVoice_t* vp = cmMemAllocZ(_cmMidiVoice_t,1);
  915. vp->mp = mp;
  916. vp->sustainFl = sustainFl;
  917. vp->link = *listPtrPtr;
  918. *listPtrPtr = vp;
  919. }
  920. void cmMidiFileCalcNoteDurations2( cmMidiFileH_t h )
  921. {
  922. _cmMidiFile_t* p;
  923. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  924. return;
  925. if( p->msgN == 0 )
  926. return;
  927. unsigned mi = cmInvalidId;
  928. _cmMidiVoice_t* list = NULL; // list of active voices
  929. _cmMidiVoice_t* vp = NULL;
  930. cmMidiTrackMsg_t* sustainPedalDownMsg = NULL;
  931. bool sustainFlagV[ kMidiChCnt ];
  932. // clear the sustain pedal flag
  933. for(mi=0; mi<kMidiChCnt; ++mi)
  934. sustainFlagV[mi]=false;
  935. for(mi=0; mi<p->msgN; ++mi)
  936. {
  937. cmMidiTrackMsg_t* mp = p->msgV[mi];
  938. unsigned d_amicro = 0;
  939. if( mi > 0 )
  940. {
  941. assert( mp->amicro >= p->msgV[mi-1]->amicro );
  942. d_amicro = mp->amicro - p->msgV[mi-1]->amicro;
  943. }
  944. // update the duration of the sounding notes
  945. for(vp = list; vp!=NULL; vp=vp->link)
  946. vp->durMicros += d_amicro;
  947. // update the sustain pedal duration
  948. if( sustainPedalDownMsg != NULL )
  949. ((cmMidiChMsg_t*)(sustainPedalDownMsg->u.chMsgPtr))->durMicros += d_amicro; // cast away const
  950. //
  951. // If this is sustain pedal msg
  952. //
  953. if( mp->status==kCtlMdId && mp->u.chMsgPtr->d0 == kSustainCtlMdId )
  954. {
  955. unsigned chIdx = mp->u.chMsgPtr->ch;
  956. assert( chIdx < kMidiChCnt );
  957. // set the state of the sustain pedal flags
  958. sustainFlagV[chIdx] = mp->u.chMsgPtr->d1 >= 64;
  959. // if the pedal went down ...
  960. if( sustainFlagV[chIdx] )
  961. {
  962. if( sustainPedalDownMsg != NULL )
  963. {
  964. // TODO: the correct way to handle this is to maintain multiple sustain pedals
  965. //cmErrMsg(&p->err,kSustainPedalMfRC,"Sustain pedal down with no intervening sustain pedal up.");
  966. }
  967. else
  968. {
  969. sustainPedalDownMsg = mp;
  970. ((cmMidiChMsg_t*)(sustainPedalDownMsg->u.chMsgPtr))->durMicros = 0; // cast away const
  971. }
  972. _cmMidiFileCalcNoteDurationsAllocVoice( &list, mp, true );
  973. }
  974. else // ... if the pedal went up
  975. {
  976. sustainPedalDownMsg = NULL;
  977. // ... then release sustaining notes
  978. _cmMidiVoice_t* pp = NULL;
  979. for(vp=list; vp != NULL; )
  980. {
  981. _cmMidiVoice_t* np = vp->link;
  982. if( vp->sustainFl && (vp->mp->u.chMsgPtr->ch == chIdx) )
  983. _cmMidFileCalcNoteDurationReleaseNote(&list,pp,vp);
  984. else
  985. pp = vp;
  986. vp = np;
  987. }
  988. }
  989. }
  990. //
  991. // if this is a note-on msg
  992. //
  993. if( mp->status==kNoteOnMdId && mp->u.chMsgPtr->d1>0 )
  994. {
  995. _cmMidiFileCalcNoteDurationsAllocVoice( &list, mp, false );
  996. }
  997. else
  998. //
  999. // if this is a note-off msg
  1000. //
  1001. if( (mp->status==kNoteOnMdId && mp->u.chMsgPtr->d1==0) || (mp->status==kNoteOffMdId) )
  1002. {
  1003. _cmMidiVoice_t* pp = NULL;
  1004. unsigned chIdx = mp->u.chMsgPtr->ch;
  1005. assert( chIdx < kMidiChCnt );
  1006. // for each active voice
  1007. for(vp=list; vp!=NULL; vp=vp->link )
  1008. {
  1009. // if this active voice ch/pitch matches the note-off msg ch pitch
  1010. if( (vp->mp->u.chMsgPtr->d0==mp->u.chMsgPtr->d0) && (vp->mp->u.chMsgPtr->ch==chIdx) )
  1011. {
  1012. // if the sustain pedal is down for this channel - then defer turning the note off
  1013. if( sustainFlagV[chIdx] )
  1014. vp->sustainFl = true;
  1015. else
  1016. _cmMidFileCalcNoteDurationReleaseNote(&list,pp,vp);
  1017. break;
  1018. }
  1019. pp = vp;
  1020. } // end for
  1021. } // end if
  1022. } // end-for
  1023. // check for hung notes
  1024. _cmMidiVoice_t* np = NULL;
  1025. vp = list;
  1026. while( vp != NULL )
  1027. {
  1028. np = vp->link;
  1029. if( cmMidiIsNoteOn(vp->mp->status) == false )
  1030. cmErrMsg(&p->err,kMissingNoteOffMfRC,"Missing note-off for note-on:%s",cmMidiToSciPitch(vp->mp->u.chMsgPtr->d0,NULL,0));
  1031. cmMemFree(vp);
  1032. vp = np;
  1033. }
  1034. }
  1035. void _cmMidiFileSetDur( cmMidiTrackMsg_t* m0, cmMidiTrackMsg_t* m1 )
  1036. {
  1037. // calculate the duration of the sounding note
  1038. ((cmMidiChMsg_t*)m0->u.chMsgPtr)->durMicros = m1->amicro - m0->amicro;
  1039. }
  1040. bool _cmMidiFileCalcNoteDur( cmMidiTrackMsg_t* m0, cmMidiTrackMsg_t* m1, int noteGateFl, int sustainGateFl, bool sostGateFl )
  1041. {
  1042. // if the note is being kept sounding because the key is still depressed,
  1043. // the sustain pedal is down or it is being held by the sostenuto pedal ....
  1044. if( noteGateFl>0 || sustainGateFl>0 || sostGateFl )
  1045. return false; // ... do nothing
  1046. _cmMidiFileSetDur(m0,m1);
  1047. return true;
  1048. }
  1049. void cmMidiFileCalcNoteDurations( cmMidiFileH_t h )
  1050. {
  1051. _cmMidiFile_t* p;
  1052. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  1053. return;
  1054. if( p->msgN == 0 )
  1055. return;
  1056. unsigned mi = cmInvalidId;
  1057. cmMidiTrackMsg_t* noteM[ kMidiNoteCnt * kMidiChCnt ]; // ptr to note-on or NULL if the note is not sounding
  1058. cmMidiTrackMsg_t* sustV[ kMidiChCnt ];
  1059. cmMidiTrackMsg_t* sostV[ kMidiChCnt ];
  1060. int noteGateM[ kMidiNoteCnt * kMidiChCnt ]; // true if the associated note key is depressed
  1061. bool sostGateM[ kMidiNoteCnt * kMidiChCnt ]; // true if the associated note was active when the sost. pedal went down
  1062. int sustGateV[ kMidiChCnt]; // true if the associated sustain pedal is down
  1063. int sostGateV[ kMidiChCnt]; // true if the associated sostenuto pedal is down
  1064. unsigned i,j;
  1065. // initialize the state tracking variables
  1066. for(i=0; i<kMidiChCnt; ++i)
  1067. {
  1068. sustV[i] = NULL;
  1069. sustGateV[i] = 0;
  1070. sostV[i] = NULL;
  1071. sostGateV[i] = 0;
  1072. for(j=0; j<kMidiNoteCnt; ++j)
  1073. {
  1074. noteM[ i*kMidiNoteCnt + j ] = NULL;
  1075. noteGateM[ i*kMidiNoteCnt + j ] = 0;
  1076. sostGateM[ i*kMidiNoteCnt + j ] = false;
  1077. }
  1078. }
  1079. // for each midi event
  1080. for(mi=0; mi<p->msgN; ++mi)
  1081. {
  1082. cmMidiTrackMsg_t* m = p->msgV[mi];
  1083. // verify that time is also incrementing
  1084. assert( mi==0 || (mi>0 && m->amicro >= p->msgV[mi-1]->amicro) );
  1085. // ignore all non-channel messages
  1086. if( !cmMidiIsChStatus( m->status ) )
  1087. continue;
  1088. cmMidiByte_t ch = m->u.chMsgPtr->ch; // get the midi msg channel
  1089. cmMidiByte_t d0 = m->u.chMsgPtr->d0; // get the midi msg data value
  1090. // if this is a note-on msg
  1091. if( cmMidiFileIsNoteOn(m) )
  1092. {
  1093. unsigned k = ch*kMidiNoteCnt + d0;
  1094. // there should be no existing sounding note instance for this pitch
  1095. //if( noteGateM[k] == 0 && noteM[k] != NULL )
  1096. // cmErrWarnMsg(&p->err,kMissingNoteOffMfRC,"%i : Missing note-off instance for note on:%s",m->uid,cmMidiToSciPitch(d0,NULL,0));
  1097. if( noteM[k] != NULL )
  1098. noteGateM[k] += 1;
  1099. else
  1100. {
  1101. noteM[k] = m;
  1102. noteGateM[k] = 1;
  1103. }
  1104. }
  1105. else
  1106. // if this is a note-off msg
  1107. if( cmMidiFileIsNoteOff(m) )
  1108. {
  1109. unsigned k = ch*kMidiNoteCnt + d0;
  1110. cmMidiTrackMsg_t* m0 = noteM[k];
  1111. if( m0 == NULL )
  1112. cmErrWarnMsg(&p->err,kMissingNoteOffMfRC,"%i : Missing note-on instance for note-off:%s",m->uid,cmMidiToSciPitch(d0,NULL,0));
  1113. else
  1114. {
  1115. // a key was released - so it should not already be up
  1116. if( noteGateM[k]==0 )
  1117. cmErrWarnMsg(&p->err,kMissingNoteOffMfRC,"%i : Missing note-on for note-off:%s",m->uid,cmMidiToSciPitch(d0,NULL,0));
  1118. else
  1119. {
  1120. noteGateM[k] -= 1; // update the note gate state
  1121. // update the sounding note status
  1122. if( _cmMidiFileCalcNoteDur(m0, m, noteGateM[k], sustGateV[ch], sostGateM[k]) )
  1123. noteM[k] = NULL;
  1124. }
  1125. }
  1126. }
  1127. else
  1128. // This is a sustain-pedal down msg
  1129. if( cmMidiFileIsSustainPedalDown(m) )
  1130. {
  1131. // if the sustain channel is already down
  1132. //if( sustGateV[ch] )
  1133. // cmErrWarnMsg(&p->err,kSustainPedalMfRC,"%i : The sustain pedal went down twice with no intervening release.",m->uid);
  1134. sustGateV[ch] += 1;
  1135. if( sustV[ch] == NULL )
  1136. sustV[ch] = m;
  1137. }
  1138. else
  1139. // This is a sustain-pedal up msg
  1140. if( cmMidiFileIsSustainPedalUp(m) )
  1141. {
  1142. // if the sustain channel is already up
  1143. if( sustGateV[ch]==0 )
  1144. cmErrWarnMsg(&p->err,kSustainPedalMfRC,"%i : The sustain pedal release message was received with no previous pedal down.",m->uid);
  1145. if( sustGateV[ch] >= 1 )
  1146. {
  1147. sustGateV[ch] -= 1;
  1148. if( sustGateV[ch] == 0 )
  1149. {
  1150. unsigned k = ch*kMidiNoteCnt;
  1151. // for each sounding note on this channel
  1152. for(; k<ch*kMidiNoteCnt+kMidiNoteCnt; ++k)
  1153. if( noteM[k]!=NULL && _cmMidiFileCalcNoteDur(noteM[k], m, noteGateM[k], sustGateV[ch], sostGateM[k]) )
  1154. noteM[k] = NULL;
  1155. if( sustV[ch] != NULL )
  1156. {
  1157. _cmMidiFileSetDur(sustV[ch],m);
  1158. sustV[ch] = NULL;
  1159. }
  1160. }
  1161. }
  1162. }
  1163. else
  1164. // This is a sostenuto-pedal down msg
  1165. if( cmMidiFileIsSostenutoPedalDown(m) )
  1166. {
  1167. // if the sustain channel is already down
  1168. //if( sostGateV[ch] )
  1169. // cmErrWarnMsg(&p->err,kSostenutoPedalMfRC,"%i : The sostenuto pedal went down twice with no intervening release.",m->uid);
  1170. // record the notes that are active when the sostenuto pedal went down
  1171. unsigned k = ch * kMidiNoteCnt;
  1172. for(i=0; i<kMidiNoteCnt; ++i)
  1173. sostGateM[k+i] = noteGateM[k+i] > 0;
  1174. sostGateV[ch] += 1;
  1175. }
  1176. else
  1177. // This is a sostenuto-pedal up msg
  1178. if( cmMidiFileIsSostenutoPedalUp(m) )
  1179. {
  1180. // if the sustain channel is already up
  1181. if( sostGateV[ch]==0 )
  1182. cmErrWarnMsg(&p->err,kSostenutoPedalMfRC,"%i : The sostenuto pedal release message was received with no previous pedal down.",m->uid);
  1183. if( sostGateV[ch] >= 1 )
  1184. {
  1185. sostGateV[ch] -= 1;
  1186. if( sostGateV[ch] == 0 )
  1187. {
  1188. unsigned k = ch*kMidiNoteCnt;
  1189. // for each note on this channel
  1190. for(; k<ch*kMidiNoteCnt+kMidiNoteCnt; ++k)
  1191. {
  1192. sostGateM[k] = false;
  1193. if( noteM[k]!=NULL && _cmMidiFileCalcNoteDur(noteM[k], m, noteGateM[k], sustGateV[ch], sostGateM[k]) )
  1194. noteM[k] = NULL;
  1195. }
  1196. if( sostV[ch] != NULL )
  1197. {
  1198. _cmMidiFileSetDur(sostV[ch],m);
  1199. sostV[ch] = NULL;
  1200. }
  1201. }
  1202. }
  1203. }
  1204. } // for each midi file event
  1205. }
  1206. void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks )
  1207. {
  1208. _cmMidiFile_t* p;
  1209. unsigned mi;
  1210. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  1211. return;
  1212. if( p->msgN == 0 )
  1213. return;
  1214. for(mi=0; mi<p->msgN; ++mi)
  1215. {
  1216. cmMidiTrackMsg_t* mp = p->msgV[mi];
  1217. // locate the first msg which has a non-zero delta tick
  1218. if( mp->dtick > 0 )
  1219. {
  1220. mp->dtick = ticks;
  1221. break;
  1222. }
  1223. }
  1224. }
  1225. unsigned cmMidiFilePackTrackMsgBufByteCount( const cmMidiTrackMsg_t* m )
  1226. { return sizeof(cmMidiTrackMsg_t) + m->byteCnt; }
  1227. cmMidiTrackMsg_t* cmMidiFilePackTrackMsg( const cmMidiTrackMsg_t* m, void* buf, unsigned bufByteCnt )
  1228. {
  1229. unsigned n = sizeof(cmMidiTrackMsg_t) + m->byteCnt;
  1230. if( n < bufByteCnt )
  1231. {
  1232. assert(0);
  1233. return NULL;
  1234. }
  1235. // copy the cmMidiTrackMsg_t into the buffer
  1236. memcpy(buf, m, sizeof(cmMidiTrackMsg_t));
  1237. if( m->byteCnt > 0 )
  1238. {
  1239. // copy any linked data into the buffer
  1240. memcpy(buf + sizeof(cmMidiTrackMsg_t), m->u.voidPtr, m->byteCnt );
  1241. // fixup the linked data ptr
  1242. cmMidiTrackMsg_t* mp = (cmMidiTrackMsg_t*)buf;
  1243. mp->u.voidPtr = buf + sizeof(cmMidiTrackMsg_t);
  1244. }
  1245. return (cmMidiTrackMsg_t*)buf;
  1246. }
  1247. void _cmMidiFilePrintHdr( const _cmMidiFile_t* mfp, cmRpt_t* rpt )
  1248. {
  1249. if( mfp->fn != NULL )
  1250. cmRptPrintf(rpt,"%s ",mfp->fn);
  1251. cmRptPrintf(rpt,"fmt:%i ticksPerQN:%i tracks:%i\n",mfp->fmtId,mfp->ticksPerQN,mfp->trkN);
  1252. cmRptPrintf(rpt," UID dtick atick amicro type ch D0 D1\n");
  1253. cmRptPrintf(rpt,"----- ---------- ---------- ---------- : ---- --- --- ---\n");
  1254. }
  1255. void _cmMidiFilePrintMsg( cmRpt_t* rpt, const cmMidiTrackMsg_t* tmp )
  1256. {
  1257. cmRptPrintf(rpt,"%5i %10u %10llu %10llu : ",
  1258. tmp->uid,
  1259. tmp->dtick,
  1260. tmp->atick,
  1261. tmp->amicro );
  1262. if( tmp->status == kMetaStId )
  1263. {
  1264. cmRptPrintf(rpt,"%s ", cmMidiMetaStatusToLabel(tmp->metaId));
  1265. }
  1266. else
  1267. {
  1268. cmRptPrintf(rpt,"%4s %3i %3i %3i",
  1269. cmMidiStatusToLabel(tmp->status),
  1270. tmp->u.chMsgPtr->ch,
  1271. tmp->u.chMsgPtr->d0,
  1272. tmp->u.chMsgPtr->d1);
  1273. }
  1274. if( cmMidiIsChStatus(tmp->status) && cmMidiIsNoteOn(tmp->status) && (tmp->u.chMsgPtr->d1>0) )
  1275. cmRptPrintf(rpt," %4s ",cmMidiToSciPitch(tmp->u.chMsgPtr->d0,NULL,0));
  1276. cmRptPrintf(rpt,"\n");
  1277. }
  1278. void cmMidiFilePrintMsgs( cmMidiFileH_t h, cmRpt_t* rpt )
  1279. {
  1280. const _cmMidiFile_t* p = _cmMidiFileHandleToPtr(h);
  1281. unsigned mi;
  1282. _cmMidiFilePrintHdr(p,rpt);
  1283. for(mi=0; mi<p->msgN; ++mi)
  1284. {
  1285. cmMidiTrackMsg_t* mp = p->msgV[mi];
  1286. if( mp != NULL )
  1287. _cmMidiFilePrintMsg(rpt,mp);
  1288. }
  1289. }
  1290. void cmMidiFilePrintTracks( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt )
  1291. {
  1292. const _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  1293. _cmMidiFilePrintHdr(mfp,rpt);
  1294. int i = trkIdx == cmInvalidIdx ? 0 : trkIdx;
  1295. int n = trkIdx == cmInvalidIdx ? mfp->trkN : trkIdx+1;
  1296. for(; i<n; ++i)
  1297. {
  1298. cmRptPrintf(rpt,"Track:%i\n",i);
  1299. cmMidiTrackMsg_t* tmp = mfp->trkV[i].base;
  1300. while( tmp != NULL )
  1301. {
  1302. _cmMidiFilePrintMsg(rpt,tmp);
  1303. tmp = tmp->link;
  1304. }
  1305. }
  1306. }
  1307. bool cmMidiFileIsNull( cmMidiFileH_t h )
  1308. { return (_cmMidiFile_t*)h.h == NULL; }
  1309. void cmMidiFileTestPrint( void* printDataPtr, const char* fmt, va_list vl )
  1310. { vprintf(fmt,vl); }
  1311. cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outFn )
  1312. {
  1313. cmMfRC_t rc = kOkMfRC;
  1314. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  1315. cmFileH_t fH = cmFileNullHandle;
  1316. unsigned i = 0;
  1317. const cmMidiTrackMsg_t** m = NULL;
  1318. unsigned mN = 0;
  1319. if((rc = cmMidiFileOpen(ctx, &mfH, midiFn )) != kOkMfRC )
  1320. return cmErrMsg(&ctx->err,rc,"The MIDI file object could not be opened from '%s'.",cmStringNullGuard(midiFn));
  1321. _cmMidiFile_t* p = _cmMidiFileHandleToPtr(mfH);
  1322. if( (m = cmMidiFileMsgArray(mfH)) == NULL || (mN = cmMidiFileMsgCount(mfH)) == 0 )
  1323. {
  1324. rc = cmErrMsg(&p->err,kFileFailMfRC,"The MIDI file object appears to be empty.");
  1325. goto errLabel;
  1326. }
  1327. cmMidiFileCalcNoteDurations( mfH );
  1328. if( cmFileOpen(&fH,outFn,kWriteFileFl,p->err.rpt) != kOkFileRC )
  1329. return cmErrMsg(&p->err,kFileFailMfRC,"Unable to create the file '%s'.",cmStringNullGuard(outFn));
  1330. for(i=0; i<mN; ++i)
  1331. if( (m[i]!=NULL) && cmMidiIsChStatus(m[i]->status) && cmMidiIsNoteOn(m[i]->status) && (m[i]->u.chMsgPtr->d1>0) )
  1332. cmFilePrintf(fH,"n %f %f %i %s\n",m[i]->amicro/1000000.0,m[i]->u.chMsgPtr->durMicros/1000000.0,m[i]->uid,cmMidiToSciPitch(m[i]->u.chMsgPtr->d0,NULL,0));
  1333. errLabel:
  1334. cmMidiFileClose(&mfH);
  1335. cmFileClose(&fH);
  1336. return rc;
  1337. }
  1338. void cmMidiFilePrintControlNumbers( cmCtx_t* ctx, const char* fn )
  1339. {
  1340. cmMidiFileH_t h = cmMidiFileNullHandle;
  1341. cmMfRC_t rc;
  1342. if((rc = cmMidiFileOpen(ctx, &h, fn )) != kOkMfRC )
  1343. {
  1344. cmErrMsg(&ctx->err,rc,"MIDI file open failed on '%s'.",fn);
  1345. goto errLabel;
  1346. }
  1347. const cmMidiTrackMsg_t** mm;
  1348. unsigned n = cmMidiFileMsgCount(h);
  1349. if((mm = cmMidiFileMsgArray(h)) != NULL )
  1350. {
  1351. unsigned j;
  1352. for(j=0; j<n; ++j)
  1353. {
  1354. const cmMidiTrackMsg_t* m = mm[j];
  1355. if( m->status == kCtlMdId && m->u.chMsgPtr->d0==66 )
  1356. printf("%i %i\n",m->u.chMsgPtr->d0,m->u.chMsgPtr->d1);
  1357. }
  1358. }
  1359. errLabel:
  1360. cmMidiFileClose(&h);
  1361. }
  1362. void cmMidiFileTest( const char* fn, cmCtx_t* ctx )
  1363. {
  1364. cmMfRC_t rc;
  1365. cmMidiFileH_t h = cmMidiFileNullHandle;
  1366. if((rc = cmMidiFileOpen(ctx,&h,fn)) != kOkMfRC )
  1367. {
  1368. printf("Error:%i Unable to open the cmMidi file: %s\n",rc,fn);
  1369. return;
  1370. }
  1371. cmMidiFileCalcNoteDurations( h );
  1372. if( 1 )
  1373. {
  1374. //cmMidiFileTickToMicros( h );
  1375. //cmMidiFileTickToSamples(h,96000,false);
  1376. cmMidiFilePrintMsgs(h,&ctx->rpt);
  1377. }
  1378. if( 0 )
  1379. {
  1380. //cmMidiFilePrint(h,cmMidiFileTrackCount(h)-1,&ctx->rpt);
  1381. //cmMidiFilePrint(h,cmInvalidIdx,&ctx->rpt);
  1382. cmMidiFilePrintControlNumbers(ctx, fn );
  1383. }
  1384. if( 0 )
  1385. {
  1386. printf("Tracks:%i\n",cmMidiFileTrackCount(h));
  1387. unsigned i = 0;
  1388. for(i=0; i<cmMidiFileMsgCount(h); ++i)
  1389. {
  1390. cmMidiTrackMsg_t* tmp = (cmMidiTrackMsg_t*)cmMidiFileMsgArray(h)[i];
  1391. if( tmp->status==kMetaStId && tmp->metaId == kTempoMdId )
  1392. {
  1393. double bpm = 60000000.0/tmp->u.iVal;
  1394. printf("Tempo:%i %f\n",tmp->u.iVal,bpm);
  1395. tmp->u.iVal = floor( 60000000.0/69.0 );
  1396. break;
  1397. }
  1398. }
  1399. cmMidiFileWrite(h,"/home/kevin/temp/test0.mid");
  1400. }
  1401. cmMidiFileClose(&h);
  1402. }