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

cmMidiFile.c 35KB

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