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

cmMidiFilePlay.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. #include <sys/time.h> // gettimeofday()
  2. #include "cmPrefix.h"
  3. #include "cmGlobal.h"
  4. #include "cmRpt.h"
  5. #include "cmErr.h"
  6. #include "cmCtx.h"
  7. #include "cmMem.h"
  8. #include "cmMallocDebug.h"
  9. #include "cmFile.h"
  10. #include "cmTime.h"
  11. #include "cmMidi.h"
  12. #include "cmMidiPort.h"
  13. #include "cmMidiFile.h"
  14. #include "cmMidiFilePlay.h"
  15. #include "cmThread.h" // cmSleepUs()
  16. #include "cmTime.h"
  17. typedef struct
  18. {
  19. cmErr_t err;
  20. cmCtx_t ctx;
  21. cmMfpCallback_t cbFunc;
  22. void* userCbPtr;
  23. void* printDataPtr;
  24. unsigned memBlockByteCnt;
  25. cmMidiFileH_t mfH; // midi file handle
  26. bool closeFileFl; // true mfH should be closed when this midi file player is closed
  27. unsigned ticksPerQN; // global for file
  28. unsigned microsPerTick; // set via tempo
  29. unsigned etime; // usecs elapsed since transmitting prev msg
  30. unsigned mtime; // usecs to wait before transmitting next msg
  31. unsigned msgN; // count of pointers in msgV[]
  32. unsigned msgIdx; // index into msgV[] of next msg to transmit
  33. const cmMidiTrackMsg_t** msgV; // array of msg pointers
  34. } cmMfp_t;
  35. cmMfpH_t cmMfpNullHandle = cmSTATIC_NULL_HANDLE;
  36. #define _cmMfpError( mfp, rc ) _cmMfpOnError(mfp, rc, __LINE__,__FILE__,__FUNCTION__ )
  37. // note: mfp may be NULL
  38. cmMfpRC_t _cmMfpOnError( cmMfp_t* mfp, cmMfpRC_t rc, int line, const char* fn, const char* func )
  39. {
  40. return cmErrMsg(&mfp->err,rc,"rc:%i %i %s %s\n",rc,line,func,fn);
  41. }
  42. cmMfp_t* _cmMfpHandleToPtr( cmMfpH_t h )
  43. {
  44. cmMfp_t* p = (cmMfp_t*)h.h;
  45. assert(p != NULL);
  46. return p;
  47. }
  48. void _cmMfpUpdateMicrosPerTick( cmMfp_t* mfp, unsigned microsPerQN )
  49. {
  50. mfp->microsPerTick = microsPerQN / mfp->ticksPerQN;
  51. printf("microsPerTick: %i bpm:%i ticksPerQN:%i\n", mfp->microsPerTick,microsPerQN,mfp->ticksPerQN);
  52. }
  53. cmMfpRC_t cmMfpCreate( cmMfpH_t* hp, cmMfpCallback_t cbFunc, void* userCbPtr, cmCtx_t* ctx )
  54. {
  55. cmMfp_t* p = cmMemAllocZ( cmMfp_t, 1 );
  56. cmErrSetup(&p->err,&ctx->rpt,"MIDI File Player");
  57. p->ctx = *ctx;
  58. p->cbFunc = cbFunc;
  59. p->userCbPtr = userCbPtr;
  60. p->mfH.h = NULL;
  61. p->closeFileFl = false;
  62. p->ticksPerQN = 0;
  63. p->microsPerTick = 0;
  64. p->etime = 0;
  65. p->msgN = 0;
  66. p->msgV = NULL;
  67. p->msgIdx = 0;
  68. hp->h = p;
  69. return kOkMfpRC;
  70. }
  71. cmMfpRC_t cmMfpDestroy( cmMfpH_t* hp )
  72. {
  73. if( hp == NULL )
  74. return kOkMfpRC;
  75. if( cmMfpIsValid(*hp) )
  76. {
  77. cmMfp_t* p = _cmMfpHandleToPtr(*hp);
  78. if( cmMidiFileIsNull(p->mfH)==false && p->closeFileFl==true )
  79. cmMidiFileClose(&p->mfH);
  80. cmMemFree(p);
  81. hp->h = NULL;
  82. }
  83. return kOkMfpRC;
  84. }
  85. bool cmMfpIsValid( cmMfpH_t h )
  86. { return h.h != NULL; }
  87. cmMfpRC_t cmMfpLoadFile( cmMfpH_t h, const char* fn )
  88. {
  89. cmMfpRC_t rc = kOkMfpRC;
  90. cmMfp_t* p = _cmMfpHandleToPtr(h);
  91. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  92. if((rc = cmMidiFileOpen( fn, &mfH, &p->ctx )) != kOkMfRC )
  93. return _cmMfpError(p,kFileOpenFailMfpRC);
  94. if((rc= cmMfpLoadHandle( h, mfH )) == kOkMfpRC )
  95. p->closeFileFl = true;
  96. return rc;
  97. }
  98. cmMfpRC_t cmMfpLoadHandle( cmMfpH_t h, cmMidiFileH_t mfH )
  99. {
  100. cmMfp_t* p = _cmMfpHandleToPtr(h);
  101. // if a file has already been assigned to this player
  102. if( (cmMidiFileIsNull(p->mfH) == false) && p->closeFileFl)
  103. {
  104. // close the existing file
  105. cmMidiFileClose(&p->mfH);
  106. }
  107. // get the count of msg's in the new midi file
  108. if((p->msgN = cmMidiFileMsgCount(mfH)) == cmInvalidCnt )
  109. return _cmMfpError(p,kInvalidFileMfpRC);
  110. // get a pointer to the first mesage
  111. if((p->msgV = cmMidiFileMsgArray(mfH)) == NULL )
  112. return _cmMfpError(p,kInvalidFileMfpRC);
  113. // get the count of ticks per qn
  114. if((p->ticksPerQN = cmMidiFileTicksPerQN( mfH )) == 0 )
  115. return _cmMfpError(p,kSmpteTickNotImplMfpRC);
  116. // set the initial tempo to 120
  117. _cmMfpUpdateMicrosPerTick(p,60000000/120);
  118. p->msgIdx = 0;
  119. p->mfH = mfH;
  120. p->etime = 0;
  121. p->mtime = 0;
  122. p->closeFileFl= false;
  123. //if( p->msgIdx > 0 )
  124. // p->mtime = p->msgV[0]->tick * p->microsPerTick;
  125. return kOkMfpRC;
  126. }
  127. cmMfpRC_t cmMfpSeek( cmMfpH_t h, unsigned offsUsecs )
  128. {
  129. cmMfp_t* p = _cmMfpHandleToPtr(h);
  130. unsigned msgOffsUsecs = 0;
  131. unsigned msgIdx;
  132. unsigned newMicrosPerTick;
  133. // if the requested offset is past the end of the file then return EOF
  134. if((msgIdx = cmMidiFileSeekUsecs( p->mfH, offsUsecs, &msgOffsUsecs, &newMicrosPerTick )) == cmInvalidIdx )
  135. {
  136. p->msgIdx = p->msgN;
  137. return _cmMfpError(p,kEndOfFileMfpRC);
  138. }
  139. if( msgIdx < p->msgIdx )
  140. p->msgIdx = 0;
  141. p->mtime = msgOffsUsecs;
  142. p->etime = 0;
  143. p->microsPerTick = newMicrosPerTick;
  144. p->msgIdx = msgIdx;
  145. assert(p->mtime >= 0);
  146. return kOkMfpRC;
  147. }
  148. // p 0 1 n 2
  149. // v v v v v
  150. // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  151. // 012345678901234567890123456780
  152. // 0 1 2
  153. //
  154. // p = 3 = prev msg sent
  155. // n = 19 = next msg to send
  156. // 0 = 6 = call to cmMfpClock()
  157. // 1 = 12 = call to cmMfpClock()
  158. // 2 = 22 = call to cmMfpClock()
  159. //
  160. // dusecs etime mtime
  161. // 0 n/a 3 13
  162. // 1 6 9 7
  163. // 2 10 19 -3
  164. //
  165. cmMfpRC_t cmMfpClock( cmMfpH_t h, unsigned dusecs )
  166. {
  167. cmMfp_t* p = _cmMfpHandleToPtr(h);
  168. if( p->msgIdx >= p->msgN )
  169. return kEndOfFileMfpRC;
  170. // get a pointer to the next msg to send
  171. const cmMidiTrackMsg_t* mp = p->msgV[p->msgIdx];
  172. // p->etime is the interval of time between when the last msg was
  173. // sent and the end of the time window for this mfpClock() cycle
  174. p->etime += dusecs;
  175. //printf("init e:%i d:%i\n",p->etime, p->mtime);
  176. // if the elapsed time (etime) since the last msg is greater or equal
  177. // to the delta time to the next msg (mtime)
  178. while( p->etime >= p->mtime )
  179. {
  180. //printf("e:%i d:%i\n",p->etime, p->mtime);
  181. if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
  182. _cmMfpUpdateMicrosPerTick(p,mp->u.iVal );
  183. p->cbFunc( p->userCbPtr, p->mtime, mp );
  184. ++(p->msgIdx);
  185. if( p->msgIdx >= p->msgN )
  186. break;
  187. // get the next msg to send
  188. mp = p->msgV[p->msgIdx];
  189. // we probably went past the actual mtime - so update etime
  190. // with the delta usecs from the msg just sent and the current time
  191. p->etime -= p->mtime;
  192. // calc the delta usecs from the message just sent to the next msg to send
  193. //p->mtime = (mp->tick - p->msgV[p->msgIdx-1]->tick) * p->microsPerTick;
  194. p->mtime = mp->dtick * p->microsPerTick;
  195. }
  196. return p->msgIdx >= p->msgN ? kEndOfFileMfpRC : kOkMfpRC;
  197. }
  198. void mfpPrint( void* userDataPtr, const char* fmt, va_list vl )
  199. {
  200. vprintf(fmt,vl);
  201. }
  202. // this assumes that the seconds have been normalized to a recent start time
  203. // so as to avoid overflow
  204. unsigned _cmMfpElapsedMicroSecs( const struct timespec* t0, const struct timespec* t1 )
  205. {
  206. // convert seconds to usecs
  207. long u0 = t0->tv_sec * 1000000;
  208. long u1 = t1->tv_sec * 1000000;
  209. // convert nanoseconds to usec
  210. u0 += t0->tv_nsec / 1000;
  211. u1 += t1->tv_nsec / 1000;
  212. // take diff between t1 and t0
  213. return u1 - u0;
  214. }
  215. void _cmMfpTestTimer()
  216. {
  217. useconds_t suspendUsecs = 15 * 1000;
  218. struct timespec t0,t1,t2;
  219. unsigned accum = 0;
  220. unsigned i;
  221. unsigned n = 4000;
  222. // t0 will be the base time which all other times will be
  223. // set relative to.
  224. cmTimeGet(&t0);
  225. t2 = t0;
  226. t2.tv_sec = 0;
  227. for(i=0; i<n; ++i)
  228. {
  229. cmSleepUs(suspendUsecs);
  230. cmTimeGet(&t1);
  231. t1.tv_sec -= t0.tv_sec;
  232. unsigned d0usec = _cmMfpElapsedMicroSecs(&t0,&t1);
  233. unsigned d1usec = _cmMfpElapsedMicroSecs(&t2,&t1);
  234. accum += d1usec;
  235. if( i == n-1 )
  236. printf("%i %i %i\n",d0usec,d1usec,accum);
  237. t2 = t1;
  238. }
  239. }
  240. // midi file player callback test function
  241. void _cmMfpCallbackTest( void* userCbPtr, unsigned dmicros, const cmMidiTrackMsg_t* msgPtr )
  242. {
  243. if( kNoteOffMdId <= msgPtr->status && msgPtr->status <= kPbendMdId )
  244. cmMpDeviceSend( 0, 0, msgPtr->status+msgPtr->u.chMsgPtr->ch, msgPtr->u.chMsgPtr->d0,msgPtr->u.chMsgPtr->d1);
  245. //printf("%i 0x%x 0x%x %i\n",msgPtr->tick,msgPtr->status,msgPtr->metaId,msgPtr->trkIdx);
  246. }
  247. // midi port callback test function
  248. void _cmMpCallbackTest( const cmMidiPacket_t* pktArray, unsigned pktCnt )
  249. {}
  250. cmMfpRC_t cmMfpTest( const char* fn, cmCtx_t* ctx )
  251. {
  252. cmMfpH_t mfpH = cmMfpNullHandle;
  253. cmMfpRC_t rc;
  254. useconds_t suspendUsecs = 15 * 1000;
  255. struct timespec t0,t1,base;
  256. //unsigned i;
  257. //unsigned n = 4000;
  258. unsigned mdParserBufByteCnt = 1024;
  259. printf("Initializing MIDI Devices...\n");
  260. cmMpInitialize( ctx, _cmMpCallbackTest, NULL, mdParserBufByteCnt,"app" );
  261. //mdReport();
  262. printf("Creating Player...\n");
  263. if((rc = cmMfpCreate( &mfpH, _cmMfpCallbackTest, NULL, ctx )) != kOkMfpRC )
  264. return rc;
  265. printf("Loading MIDI file...\n");
  266. if((rc = cmMfpLoadFile( mfpH, fn )) != kOkMfpRC )
  267. goto errLabel;
  268. if((rc = cmMfpSeek( mfpH, 60 * 1000000 )) != kOkMfpRC )
  269. goto errLabel;
  270. cmTimeGet(&base);
  271. t0 = base;
  272. t0.tv_sec = 0;
  273. //for(i=0; i<n; ++i)
  274. while(rc != kEndOfFileMfpRC)
  275. {
  276. cmSleepUs(suspendUsecs);
  277. cmTimeGet(&t1);
  278. t1.tv_sec -= base.tv_sec;
  279. unsigned dusecs = _cmMfpElapsedMicroSecs(&t0,&t1);
  280. rc = cmMfpClock( mfpH, dusecs );
  281. //printf("%i %i\n",dusecs,rc);
  282. t0 = t1;
  283. }
  284. errLabel:
  285. cmMfpDestroy(&mfpH);
  286. cmMpFinalize();
  287. return rc;
  288. }
  289. //------------------------------------------------------------------------------------------------------------
  290. #include "cmFloatTypes.h"
  291. #include "cmComplexTypes.h"
  292. #include "cmLinkedHeap.h"
  293. #include "cmSymTbl.h"
  294. #include "cmAudioFile.h"
  295. #include "cmProcObj.h"
  296. #include "cmProcTemplateMain.h"
  297. #include "cmVectOps.h"
  298. #include "cmProc.h"
  299. #include "cmProc2.h"
  300. enum
  301. {
  302. kOkMfptRC = cmOkRC,
  303. kMfpFailMfptRC,
  304. kAudioFileFailMfptRC,
  305. kProcObjFailMfptRC
  306. };
  307. typedef struct
  308. {
  309. cmErr_t* err;
  310. cmMidiSynth* msp;
  311. } _cmMfpTest2CbData_t;
  312. // Called by the MIDI file player to send a msg to the MIDI synth.
  313. void _cmMfpCb( void* userCbPtr, unsigned dmicros, const cmMidiTrackMsg_t* msgPtr )
  314. {
  315. if( kNoteOffMdId <= msgPtr->status && msgPtr->status <= kPbendMdId )
  316. {
  317. cmMidiPacket_t pkt;
  318. cmMidiMsg msg;
  319. _cmMfpTest2CbData_t* d = (_cmMfpTest2CbData_t*)userCbPtr;
  320. msg.timeStamp.tv_sec = 0;
  321. msg.timeStamp.tv_nsec = 0;
  322. msg.status = msgPtr->status + msgPtr->u.chMsgPtr->ch;
  323. msg.d0 = msgPtr->u.chMsgPtr->d0;
  324. msg.d1 = msgPtr->u.chMsgPtr->d1;
  325. pkt.cbDataPtr = NULL;
  326. pkt.devIdx = cmInvalidIdx;
  327. pkt.portIdx = cmInvalidIdx;
  328. pkt.msgArray = &msg;
  329. pkt.sysExMsg = NULL;
  330. pkt.msgCnt = 1;
  331. if( cmMidiSynthOnMidi( d->msp, &pkt, 1 ) != cmOkRC )
  332. cmErrMsg(d->err,kProcObjFailMfptRC,"Synth. MIDI receive failed.");
  333. }
  334. }
  335. // Called by the MIDI synth to send a msg to the voice bank.
  336. int _cmMidiSynthCb( struct cmMidiVoice_str* voicePtr, unsigned sel, cmSample_t* outChArray[], unsigned outChCnt )
  337. {
  338. return cmWtVoiceBankExec( ((cmWtVoiceBank*)voicePtr->pgm.cbDataPtr), voicePtr, sel, outChArray, outChCnt );
  339. }
  340. // BUG BUG BUG: THIS FUNCTION IS NOT TESTED!!!!!
  341. cmRC_t cmMfpTest2( const char* midiFn, const char* audioFn, cmCtx_t* ctx )
  342. {
  343. cmRC_t rc = kOkMfptRC;
  344. cmMfpH_t mfpH = cmMfpNullHandle;
  345. _cmMfpTest2CbData_t cbData;
  346. cmErr_t err;
  347. cmAudioFileH_t afH = cmNullAudioFileH;
  348. cmRC_t afRC = kOkAfRC;
  349. double afSrate = 44100;
  350. unsigned afBits = 16;
  351. unsigned afChCnt = 1;
  352. cmCtx* cctx;
  353. cmMidiSynth* msp;
  354. cmWtVoiceBank* vbp;
  355. unsigned msPgmCnt = 127;
  356. cmMidiSynthPgm msPgmArray[ msPgmCnt ];
  357. unsigned msVoiceCnt = 36;
  358. unsigned procSmpCnt = 64;
  359. unsigned i;
  360. cmErrSetup(&err,&ctx->rpt,"MFP Test 2");
  361. // create the MIDI file player
  362. if( cmMfpCreate(&mfpH, _cmMfpCb, &cbData, ctx ) != kOkMfpRC )
  363. return cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player create failed.");
  364. // create an output audio file
  365. if( cmAudioFileIsValid( afH = cmAudioFileNewCreate(audioFn, afSrate, afBits, afChCnt, &afRC, &ctx->rpt))==false)
  366. {
  367. rc = cmErrMsg(&err,kAudioFileFailMfptRC,"The audio file create failed.");
  368. goto errLabel;
  369. }
  370. // load the midi file into the player
  371. if( cmMfpLoadFile( mfpH, midiFn ) != kOkMfpRC )
  372. {
  373. rc = cmErrMsg(&err,kMfpFailMfptRC,"MIDI file load failed.");
  374. goto errLabel;
  375. }
  376. // create the proc obj context
  377. if((cctx = cmCtxAlloc(NULL, &ctx->rpt, cmLHeapNullHandle, cmSymTblNullHandle )) == NULL)
  378. {
  379. rc = cmErrMsg(&err,kProcObjFailMfptRC,"cmCtx allocate failed.");
  380. goto errLabel;
  381. }
  382. // create the voice bank
  383. if((vbp = cmWtVoiceBankAlloc(cctx, NULL, afSrate, procSmpCnt, msVoiceCnt, afChCnt )) == NULL)
  384. {
  385. rc = cmErrMsg(&err,kProcObjFailMfptRC,"WT voice bank allocate failed.");
  386. goto errLabel;
  387. }
  388. // a MIDI synth
  389. if((msp = cmMidiSynthAlloc(cctx, NULL, msPgmArray, msPgmCnt, msVoiceCnt, procSmpCnt, afChCnt, afSrate )) == NULL )
  390. {
  391. rc = cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth allocate failed.");
  392. goto errLabel;
  393. }
  394. cbData.msp = msp;
  395. cbData.err = &err;
  396. // load all of the the MIDI pgm recds with the same settings
  397. for(i=0; i<msPgmCnt; ++i)
  398. {
  399. msPgmArray[i].pgm = i;
  400. msPgmArray[i].cbPtr = _cmMidiSynthCb; // Call this function to update voices using this pgm
  401. msPgmArray[i].cbDataPtr = vbp; // Voice bank containing the voice states.
  402. }
  403. unsigned dusecs = floor((double)procSmpCnt * 1000000. / afSrate);
  404. while(rc != kEndOfFileMfpRC)
  405. {
  406. // update the MFP's current time and call _cmMfpCb() for MIDI msgs whose time has elapsed
  407. rc = cmMfpClock( mfpH, dusecs );
  408. // check for MFP errors
  409. if(rc!=kOkMfpRC && rc!=kEndOfFileMfpRC)
  410. {
  411. cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player exec failed.");
  412. goto errLabel;
  413. }
  414. // generate audio based on the current state of the synth voices
  415. if( cmMidiSynthExec(msp, NULL, 0 ) != cmOkRC )
  416. {
  417. cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth exec. failed.");
  418. goto errLabel;
  419. }
  420. // write the last frame of synth. generated audio to the output file
  421. if( cmAudioFileWriteSample(afH, procSmpCnt, msp->outChCnt, msp->outChArray ) != kOkAfRC )
  422. {
  423. cmErrMsg(&err,kProcObjFailMfptRC,"Audio file write failed.");
  424. goto errLabel;
  425. }
  426. }
  427. errLabel:
  428. if( cmMidiSynthFree(&msp) != cmOkRC )
  429. cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth. free failed.");
  430. if( cmWtVoiceBankFree(&vbp) != cmOkRC )
  431. cmErrMsg(&err,kProcObjFailMfptRC,"WT voice free failed.");
  432. if( cmCtxFree(&cctx) != cmOkRC )
  433. cmErrMsg(&err,kProcObjFailMfptRC,"cmCtx free failed.");
  434. if( cmAudioFileDelete(&afH) )
  435. cmErrMsg(&err,kAudioFileFailMfptRC,"The audio file close failed.");
  436. if( cmMfpDestroy(&mfpH) != kOkMfpRC )
  437. cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player destroy failed.");
  438. return rc;
  439. }