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.

cmMidiFilePlay.c 15KB

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