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.

cmMidiScoreFollow.c 9.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmLinkedHeap.h"
  9. #include "cmFloatTypes.h"
  10. #include "cmComplexTypes.h"
  11. #include "cmFile.h"
  12. #include "cmFileSys.h"
  13. #include "cmJson.h"
  14. #include "cmSymTbl.h"
  15. #include "cmAudioFile.h"
  16. #include "cmText.h"
  17. #include "cmProcObj.h"
  18. #include "cmProcTemplate.h"
  19. #include "cmMath.h"
  20. #include "cmTime.h"
  21. #include "cmMidi.h"
  22. #include "cmMidiFile.h"
  23. #include "cmProc.h"
  24. #include "cmProc2.h"
  25. #include "cmVectOps.h"
  26. #include "cmTimeLine.h"
  27. #include "cmScore.h"
  28. #include "cmProc4.h"
  29. #include "cmMidiScoreFollow.h"
  30. #include "cmScoreMatchGraphic.h"
  31. typedef struct
  32. {
  33. cmScMatcherResult_t* rV; // rV[rN] - array of stored cmScMatcher callback records.
  34. unsigned rAllocN; //
  35. unsigned rN; //
  36. } _cmMsf_ScoreFollow_t;
  37. void _cmMsf_ReportScoreErrors( const _cmMsf_ScoreFollow_t* f, cmScH_t scH )
  38. {
  39. unsigned scoreEvtN = cmScoreEvtCount(scH);
  40. unsigned i,j;
  41. for(i=0; i<scoreEvtN; ++i)
  42. {
  43. const cmScoreEvt_t* e = cmScoreEvt(scH,i);
  44. assert(e != NULL);
  45. if( e->type == kNonEvtScId && cmIsNotFlag(e->flags,kSkipScFl) )
  46. {
  47. unsigned matchN = 0;
  48. for(j=0; j<f->rN; ++j)
  49. if( f->rV[j].scEvtIdx == i )
  50. matchN += 1;
  51. if( matchN != 1 )
  52. {
  53. const cmScoreLoc_t* l = cmScoreEvtLoc(scH,e);
  54. assert(l != NULL);
  55. printf("bar:%3i evtIdx:%5i pitch:%4s match:%i ",l->barNumb,e->index,cmMidiToSciPitch(e->pitch,NULL,0),matchN);
  56. // print the midi event associated with multiple matches.
  57. if( matchN > 1 )
  58. for(j=0; j<f->rN; ++j)
  59. if( f->rV[j].scEvtIdx == i )
  60. printf("(%i %s) ",f->rV[j].muid, cmMidiToSciPitch(f->rV[j].pitch,NULL,0) );
  61. printf("\n");
  62. }
  63. }
  64. }
  65. }
  66. void _cmMsf_ReportMidiErrors( const _cmMsf_ScoreFollow_t* f, cmScH_t scH, const cmMidiTrackMsg_t** m, unsigned mN)
  67. {
  68. unsigned i,j;
  69. unsigned lastBar = 0;
  70. // for each midi note-on msg
  71. for(i=0; i<mN; ++i)
  72. {
  73. if( (m[i]!=NULL) && cmMidiIsChStatus(m[i]->status) && cmMidiIsNoteOn(m[i]->status) && (m[i]->u.chMsgPtr->d1>0) )
  74. {
  75. unsigned matchN = 0;
  76. // find the note-on msg in the score-match result array
  77. for(j=0; j<f->rN; ++j)
  78. if( f->rV[j].muid == m[i]->uid )
  79. {
  80. if( f->rV[j].scEvtIdx != -1 )
  81. {
  82. const cmScoreEvt_t* e = cmScoreEvt(scH,f->rV[j].scEvtIdx);
  83. if( e != NULL )
  84. {
  85. const cmScoreLoc_t* l = cmScoreEvtLoc(scH,e);
  86. assert(l != NULL );
  87. lastBar = l->barNumb;
  88. }
  89. }
  90. matchN += 1;
  91. break;
  92. }
  93. if( matchN==0 )
  94. {
  95. printf("bar:%3i muid:%4i %s\n", lastBar, m[i]->uid, cmMidiToSciPitch(m[i]->u.chMsgPtr->d0,NULL,0));
  96. }
  97. }
  98. }
  99. }
  100. void _cmMsf_WriteMatchFileHeader( cmFileH_t fH )
  101. {
  102. cmFilePrintf(fH," Score Score Score MIDI MIDI MIDI\n");
  103. cmFilePrintf(fH," Bar UUID Pitch UUID Ptch Vel.\n");
  104. cmFilePrintf(fH,"- ----- ----- ----- ----- ---- ----\n");
  105. }
  106. // Write one scScoreMatcherResult_t record to the file fH.
  107. unsigned _cmMsf_WriteMatchFileLine( cmFileH_t fH, cmScH_t scH, const cmScMatcherResult_t* r )
  108. {
  109. unsigned scUid = -1;
  110. cmChar_t buf[6];
  111. buf[0] = 0;
  112. buf[5] = 0;
  113. cmScoreLoc_t* loc = NULL;
  114. if( r->scEvtIdx > 0 && r->scEvtIdx < cmScoreEvtCount(scH))
  115. {
  116. cmScoreEvt_t* e = cmScoreEvt(scH,r->scEvtIdx);
  117. loc = cmScoreEvtLoc(scH,e);
  118. scUid = e->csvEventId;
  119. cmMidiToSciPitch(e->pitch,buf,5);
  120. }
  121. cmFilePrintf(fH,"m %5i %5i %5s %5i %4s %3i\n",
  122. loc==NULL ? 0 : loc->barNumb, // score evt bar
  123. scUid, // score event uuid
  124. buf, // score event pitch
  125. r->muid, // midi event uuid
  126. cmMidiToSciPitch(r->pitch,NULL,0), // midi event pitch
  127. r->vel); // midi event velocity
  128. return scUid;
  129. }
  130. void _cmMsf_ScoreFollowCb( struct cmScMatcher_str* p, void* arg, cmScMatcherResult_t* rp )
  131. {
  132. _cmMsf_ScoreFollow_t* r = (_cmMsf_ScoreFollow_t*)arg;
  133. r->rV[r->rN++] = *rp;
  134. }
  135. cmMsfRC_t cmMidiScoreFollowMain(
  136. cmCtx_t* ctx,
  137. const cmChar_t* scoreCsvFn, // score CSV file as generated from cmXScoreTest().
  138. const cmChar_t* midiFn, // MIDI file to track
  139. const cmChar_t* matchRptOutFn, // Score follow status report
  140. const cmChar_t* matchSvgOutFn, // Score follow graphic report
  141. const cmChar_t* midiOutFn, // (optional) midiFn with apply sostenuto and velocities from the score to the MIDI file
  142. const cmChar_t* tlBarOutFn // (optional) bar positions sutiable for use in a cmTimeLine description file.
  143. )
  144. {
  145. cmMsfRC_t rc = kOkMsfRC;
  146. double srate = 96000.0;
  147. cmScMatcher* smp = NULL;
  148. cmScH_t scH = cmScNullHandle;
  149. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  150. unsigned scWndN = 10;
  151. unsigned midiWndN = 7;
  152. const cmMidiTrackMsg_t** m = NULL;
  153. unsigned mN = 0;
  154. unsigned scLocIdx = 0;
  155. cmFileH_t fH = cmFileNullHandle;
  156. cmSmgH_t smgH = cmSmgNullHandle;
  157. unsigned i;
  158. cmErr_t err;
  159. _cmMsf_ScoreFollow_t sfr;
  160. memset(&sfr,0,sizeof(sfr));
  161. cmErrSetup(&err,&ctx->rpt,"cmMidiScoreFollow");
  162. cmCtx* prCtx = cmCtxAlloc(NULL, err.rpt, cmLHeapNullHandle, cmSymTblNullHandle );
  163. // initialize the score
  164. if( cmScoreInitialize( ctx, &scH, scoreCsvFn, srate, NULL, 0, NULL, NULL, cmSymTblNullHandle) != kOkScRC )
  165. {
  166. rc = cmErrMsg(&err,kFailMsfRC,"cmScoreInitialize() failed on %s",cmStringNullGuard(scoreCsvFn));
  167. goto errLabel;
  168. }
  169. // setup the callback record
  170. if((sfr.rAllocN = cmScoreEvtCount( scH )*2) == 0)
  171. {
  172. rc = cmErrMsg(&err,kFailMsfRC,"The score %s appears to be empty.",cmStringNullGuard(scoreCsvFn));
  173. goto errLabel;
  174. }
  175. sfr.rV = cmMemAllocZ(cmScMatcherResult_t,sfr.rAllocN);
  176. sfr.rN = 0;
  177. // create a matcher
  178. if((smp = cmScMatcherAlloc(prCtx, NULL, srate, scH, scWndN, midiWndN, _cmMsf_ScoreFollowCb, &sfr)) == NULL )
  179. {
  180. rc = cmErrMsg(&err,kFailMsfRC,"cmScMatcherAlloc() failed.");
  181. goto errLabel;
  182. }
  183. // open the MIDI file
  184. if( cmMidiFileOpen(ctx, &mfH, midiFn ) != kOkMfRC )
  185. {
  186. rc = cmErrMsg(&err,kFailMsfRC,"The MIDI file object could not be opened from '%s'.",cmStringNullGuard(midiFn));
  187. goto errLabel;
  188. }
  189. // get a pointer to the MIDI msg array
  190. if( (m = cmMidiFileMsgArray(mfH)) == NULL || (mN = cmMidiFileMsgCount(mfH)) == 0 )
  191. {
  192. rc = cmErrMsg(&err,kFailMsfRC,"The MIDI file object appears to be empty.");
  193. goto errLabel;
  194. }
  195. // feed each MIDI note-on to the score follower
  196. for(i=0; i<mN; ++i)
  197. if( (m[i]!=NULL) && cmMidiIsChStatus(m[i]->status) && cmMidiIsNoteOn(m[i]->status) && (m[i]->u.chMsgPtr->d1>0) )
  198. if( cmScMatcherExec( smp, m[i]->amicro * srate / 1000000.0, m[i]->uid, m[i]->status, m[i]->u.chMsgPtr->d0, m[i]->u.chMsgPtr->d1, &scLocIdx ) != cmOkRC )
  199. {
  200. rc = cmErrMsg(&err,kFailMsfRC,"The score matcher exec failed.");
  201. goto errLabel;
  202. }
  203. printf("MIDI notes:%i Score Events:%i\n",mN,cmScoreEvtCount(scH));
  204. // create the output file
  205. if( cmFileOpen(&fH,matchRptOutFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
  206. {
  207. rc = cmErrMsg(&err,kFailMsfRC,"Unable to create the file '%s'.",cmStringNullGuard(matchRptOutFn));
  208. goto errLabel;
  209. }
  210. // allocate the graphics object
  211. if( cmScoreMatchGraphicAlloc( ctx, &smgH, scoreCsvFn, midiFn ) != kOkSmgRC )
  212. {
  213. rc = cmErrMsg(&err,kFailMsfRC,"Score Match Graphics allocation failed..");
  214. goto errLabel;
  215. }
  216. // write the match report output file header
  217. _cmMsf_WriteMatchFileHeader(fH);
  218. // for each score follower callback record
  219. for(i=0; i<sfr.rN; ++i)
  220. {
  221. // write the record to the output file
  222. unsigned scUid = _cmMsf_WriteMatchFileLine( fH, scH, sfr.rV + i );
  223. // insert the event->score match in the score match graphics object
  224. if( cmScoreMatchGraphicInsertMidi( smgH, sfr.rV[i].muid, sfr.rV[i].pitch, sfr.rV[i].vel, scUid ) != kOkSmgRC )
  225. {
  226. rc = cmErrMsg(&err,kFailMsfRC,"Score Match Graphics MIDI event insertion failed.");
  227. goto errLabel;
  228. }
  229. }
  230. _cmMsf_ReportScoreErrors(&sfr, scH );
  231. //_cmMsf_ReportMidiErrors(&sfr, scH, m, mN);
  232. //cmScorePrint(scH,&ctx->rpt);
  233. //cmMidiFilePrintMsgs( mfH, &ctx->rpt );
  234. // write the tracking match file as an SVG file.
  235. cmScoreMatchGraphicWrite( smgH, matchSvgOutFn );
  236. // write a cmTimeLine file which contains markers at each bar position
  237. if( tlBarOutFn != NULL )
  238. cmScoreMatchGraphicGenTimeLineBars(smgH, tlBarOutFn, srate );
  239. if( midiOutFn != NULL )
  240. cmScoreMatchGraphicUpdateMidiFromScore( ctx, smgH, midiOutFn );
  241. errLabel:
  242. cmFileClose(&fH);
  243. cmMemFree(sfr.rV);
  244. cmMidiFileClose(&mfH);
  245. cmScMatcherFree(&smp);
  246. cmScoreFinalize(&scH);
  247. cmScoreMatchGraphicFree(&smgH);
  248. cmCtxFree(&prCtx);
  249. //cmFsFreeFn(scoreCsvFn);
  250. //cmFsFreeFn(midiFn);
  251. //cmFsFreeFn(matchRptOutFn);
  252. //cmFsFreeFn(matchSvgOutFn);
  253. //cmFsFreeFn(outMidiFn);
  254. //cmFsFreeFn(tlBarFn);
  255. return rc;
  256. }