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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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. // Write one scScoreMatcherResult_t record to the file fH.
  101. unsigned _cmMsf_WriteMatchFileLine( cmFileH_t fH, cmScH_t scH, const cmScMatcherResult_t* r )
  102. {
  103. unsigned scUid = -1;
  104. cmChar_t buf[6];
  105. buf[0] = 0;
  106. buf[5] = 0;
  107. cmScoreLoc_t* loc = NULL;
  108. if( r->scEvtIdx > 0 && r->scEvtIdx < cmScoreEvtCount(scH))
  109. {
  110. cmScoreEvt_t* e = cmScoreEvt(scH,r->scEvtIdx);
  111. loc = cmScoreEvtLoc(scH,e);
  112. scUid = e->csvEventId;
  113. cmMidiToSciPitch(e->pitch,buf,5);
  114. }
  115. cmFilePrintf(fH,"m %3i %5i %4s %5i %4s %3i\n",
  116. loc==NULL ? 0 : loc->barNumb, // score evt bar
  117. scUid, // score event uuid
  118. buf, // score event pitch
  119. r->muid, // midi event uuid
  120. cmMidiToSciPitch(r->pitch,NULL,0), // midi event pitch
  121. r->vel); // midi event velocity
  122. return scUid;
  123. }
  124. void _cmMsf_ScoreFollowCb( struct cmScMatcher_str* p, void* arg, cmScMatcherResult_t* rp )
  125. {
  126. _cmMsf_ScoreFollow_t* r = (_cmMsf_ScoreFollow_t*)arg;
  127. r->rV[r->rN++] = *rp;
  128. }
  129. cmMsfRC_t cmMidiScoreFollowMain( cmCtx_t* ctx )
  130. {
  131. cmMsfRC_t rc = kOkMsfRC;
  132. //const cmChar_t* scoreFn = cmFsMakeUserFn("src/kc/src/kc/data","mod2e","csv",NULL);
  133. const cmChar_t* scoreFn = cmFsMakeUserFn("temp","a7","csv",NULL);
  134. const cmChar_t* midiFn = cmFsMakeUserFn("media/projects/imag_themes/scores/gen","round1-utf8_11","mid",NULL);
  135. const cmChar_t* outFn = cmFsMakeUserFn("temp","match","txt",NULL);
  136. const cmChar_t* svgFn = cmFsMakeUserFn("temp","score0","html",NULL);
  137. const cmChar_t* newMidiFn= cmFsMakeUserFn("temp","a7","mid",NULL);
  138. const cmChar_t* tlBarFn = cmFsMakeUserFn("temp","time_line_temp","txt",NULL);
  139. double srate = 96000.0;
  140. cmScMatcher* smp = NULL;
  141. cmScH_t scH = cmScNullHandle;
  142. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  143. unsigned scWndN = 10;
  144. unsigned midiWndN = 7;
  145. const cmMidiTrackMsg_t** m = NULL;
  146. unsigned mN = 0;
  147. unsigned scLocIdx = 0;
  148. cmFileH_t fH = cmFileNullHandle;
  149. cmSmgH_t smgH = cmSmgNullHandle;
  150. unsigned i;
  151. cmErr_t err;
  152. _cmMsf_ScoreFollow_t sfr;
  153. memset(&sfr,0,sizeof(sfr));
  154. cmErrSetup(&err,&ctx->rpt,"cmMidiScoreFollow");
  155. cmCtx* prCtx = cmCtxAlloc(NULL, err.rpt, cmLHeapNullHandle, cmSymTblNullHandle );
  156. // initialize the score
  157. if( cmScoreInitialize( ctx, &scH, scoreFn, srate, NULL, 0, NULL, NULL, cmSymTblNullHandle) != kOkScRC )
  158. {
  159. rc = cmErrMsg(&err,kFailMsfRC,"cmScoreInitialize() failed on %s",cmStringNullGuard(scoreFn));
  160. goto errLabel;
  161. }
  162. // setup the callback record
  163. if((sfr.rAllocN = cmScoreEvtCount( scH )*2) == 0)
  164. {
  165. rc = cmErrMsg(&err,kFailMsfRC,"The score %s appears to be empty.",cmStringNullGuard(scoreFn));
  166. goto errLabel;
  167. }
  168. sfr.rV = cmMemAllocZ(cmScMatcherResult_t,sfr.rAllocN);
  169. sfr.rN = 0;
  170. // create a matcher
  171. if((smp = cmScMatcherAlloc(prCtx, NULL, srate, scH, scWndN, midiWndN, _cmMsf_ScoreFollowCb, &sfr)) == NULL )
  172. {
  173. rc = cmErrMsg(&err,kFailMsfRC,"cmScMatcherAlloc() failed.");
  174. goto errLabel;
  175. }
  176. // open the MIDI file
  177. if( cmMidiFileOpen(ctx, &mfH, midiFn ) != kOkMfRC )
  178. {
  179. rc = cmErrMsg(&err,kFailMsfRC,"The MIDI file object could not be opened from '%s'.",cmStringNullGuard(midiFn));
  180. goto errLabel;
  181. }
  182. // get a pointer to the MIDI msg array
  183. if( (m = cmMidiFileMsgArray(mfH)) == NULL || (mN = cmMidiFileMsgCount(mfH)) == 0 )
  184. {
  185. rc = cmErrMsg(&err,kFailMsfRC,"The MIDI file object appears to be empty.");
  186. goto errLabel;
  187. }
  188. // feed each MIDI note-on to the score follower
  189. for(i=0; i<mN; ++i)
  190. if( (m[i]!=NULL) && cmMidiIsChStatus(m[i]->status) && cmMidiIsNoteOn(m[i]->status) && (m[i]->u.chMsgPtr->d1>0) )
  191. 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 )
  192. {
  193. rc = cmErrMsg(&err,kFailMsfRC,"The score matcher exec failed.");
  194. goto errLabel;
  195. }
  196. printf("MIDI notes:%i Score Events:%i\n",mN,cmScoreEvtCount(scH));
  197. // create the output file
  198. if( cmFileOpen(&fH,outFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
  199. {
  200. rc = cmErrMsg(&err,kFailMsfRC,"Unable to create the file '%s'.",cmStringNullGuard(outFn));
  201. goto errLabel;
  202. }
  203. // allocate the graphics object
  204. if( cmScoreMatchGraphicAlloc( ctx, &smgH, scoreFn, midiFn ) != kOkSmgRC )
  205. {
  206. rc = cmErrMsg(&err,kFailMsfRC,"Score Match Graphics allocation failed..");
  207. goto errLabel;
  208. }
  209. // for each score follower callback record
  210. for(i=0; i<sfr.rN; ++i)
  211. {
  212. // write the record to the output file
  213. unsigned scUid = _cmMsf_WriteMatchFileLine( fH, scH, sfr.rV + i );
  214. // insert the event->score match in the score match graphics object
  215. if( cmScoreMatchGraphicInsertMidi( smgH, sfr.rV[i].muid, sfr.rV[i].pitch, sfr.rV[i].vel, scUid ) != kOkSmgRC )
  216. {
  217. rc = cmErrMsg(&err,kFailMsfRC,"Score Match Graphics MIDI event insertion failed.");
  218. goto errLabel;
  219. }
  220. }
  221. //_cmMsf_ReportScoreErrors(&sfr, scH );
  222. //_cmMsf_ReportMidiErrors(&sfr, scH, m, mN);
  223. //cmScorePrint(scH,&ctx->rpt);
  224. //cmMidiFilePrintMsgs( mfH, &ctx->rpt );
  225. // write the tracking match file as an SVG file.
  226. cmScoreMatchGraphicWrite( smgH, svgFn );
  227. // write a cmTimeLine file which contains markers at each bar position
  228. cmScoreMatchGraphicGenTimeLineBars(smgH, tlBarFn, srate );
  229. cmScoreMatchGraphicUpdateMidiFromScore( ctx, smgH, newMidiFn );
  230. errLabel:
  231. cmFileClose(&fH);
  232. cmMemFree(sfr.rV);
  233. cmMidiFileClose(&mfH);
  234. cmScMatcherFree(&smp);
  235. cmScoreFinalize(&scH);
  236. cmScoreMatchGraphicFree(&smgH);
  237. cmCtxFree(&prCtx);
  238. cmFsFreeFn(scoreFn);
  239. cmFsFreeFn(midiFn);
  240. cmFsFreeFn(outFn);
  241. cmFsFreeFn(svgFn);
  242. cmFsFreeFn(newMidiFn);
  243. cmFsFreeFn(tlBarFn);
  244. return rc;
  245. }