cmtools is a collection of utilities for generating and processing machine readable musical scores based on MusicXML, MIDI and other data files.
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.

cmtools.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  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 "cmFileSys.h"
  10. #include "cmText.h"
  11. #include "cmPgmOpts.h"
  12. #include "cmXScore.h"
  13. #include "cmMidiScoreFollow.h"
  14. #include "cmScoreProc.h"
  15. #include "cmSymTbl.h"
  16. #include "cmTime.h"
  17. #include "cmMidi.h"
  18. #include "cmScore.h"
  19. #include "cmMidiFile.h"
  20. #include "cmFloatTypes.h"
  21. #include "cmAudioFile.h"
  22. #include "cmTimeLine.h"
  23. enum
  24. {
  25. kOkCtRC = cmOkRC,
  26. kNoActionIdSelectedCtRC,
  27. kMissingRequiredFileNameCtRC,
  28. kScoreGenFailedCtRC,
  29. kScoreFollowFailedCtRC,
  30. kMidiFileRptFailedCtRC,
  31. kTimeLineRptFailedCtRC,
  32. kAudioFileRptFailedCtRC
  33. };
  34. const cmChar_t poEndHelpStr[] = "";
  35. const cmChar_t poBegHelpStr[] =
  36. "xscore_proc Music XML to electronic score generator\n"
  37. "\n"
  38. "USAGE:\n"
  39. "\n"
  40. "Parse an XML score file and decoration file to produce a score file in CSV format.\n"
  41. "\n"
  42. "cmtool --score_gen -x <xml_file> -d <dec_fn> {-c <csvScoreOutFn} {-m <midiOutFn>} {-s <svgOutFn>} {-r report} {-b begMeasNumb} {t begTempoBPM}\n"
  43. "\n"
  44. "Notes:\n"
  45. "1. If <dec_fn> does not exist then a decoration template file will be generated based on the MusicXML file. \n"
  46. "2. Along with the CSV score file MIDI and HTML/SVG files will also be produced based on the contents of the MusicXML and decoration file.\n"
  47. "3. See README.md for a detailed description of the how to edit the decoration file.\n"
  48. "\n"
  49. "\n"
  50. "Use the score follower to generate a timeline configuration file.\n"
  51. "\n"
  52. "cmtool --timeline_gen -c <csvScoreFn> -i <midiInFn> -r <matchRptFn> -s <matchSvgFn> {-m <midiOutFn>} {-t timelineOutFn} \n"
  53. "\n"
  54. "Measure some perforamance attributes:\n"
  55. "\n"
  56. "cmtool --meas_gen -g <pgmRsrcFn> -r <measRptFn>\n"
  57. "\n"
  58. "Generate a score file report\n"
  59. "\n"
  60. "cmtool --score_report -c <csvScoreFn> -r <scoreRptFn>\n"
  61. "\n"
  62. "Generate a MIDI file report and optional SVG piano roll image\n"
  63. "\n"
  64. "cmtool --midi_report -i <midiInFn> -r <midiRptFn> {-s <svgOutFn>}\n"
  65. "\n"
  66. "Generate a timeline report\n"
  67. "\n"
  68. "cmtool --timeline_report -t <timelineFn> -r <timelineRptFn>\n"
  69. "\n"
  70. "Generate an audio file report\n"
  71. "\n"
  72. "cmtool --audiofile_report -a <audioFn> -r <rptFn>\n"
  73. "\n";
  74. void print( void* arg, const char* text )
  75. {
  76. printf("%s",text);
  77. }
  78. bool verify_file_exists( cmCtx_t* ctx, const cmChar_t* fn, const cmChar_t* msg )
  79. {
  80. if( fn == NULL || cmFsIsFile(fn)==false )
  81. return cmErrMsg(&ctx->err,kMissingRequiredFileNameCtRC,"The required file <%s> does not exist.",msg);
  82. return kOkCtRC;
  83. }
  84. bool verify_non_null_filename( cmCtx_t* ctx, const cmChar_t* fn, const cmChar_t* msg )
  85. {
  86. if( fn == NULL )
  87. return cmErrMsg(&ctx->err,kMissingRequiredFileNameCtRC,"The required file name <%s> is blank.",msg);
  88. return kOkCtRC;
  89. }
  90. cmRC_t score_gen( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* decFn, const cmChar_t* csvOutFn, const cmChar_t* midiOutFn, const cmChar_t* svgOutFn, unsigned reportFl, int begMeasNumb, int begTempoBPM, bool svgStandAloneFl, bool svgPanZoomFl )
  91. {
  92. cmRC_t rc;
  93. if((rc = verify_file_exists(ctx,xmlFn,"XML file")) != kOkCtRC )
  94. return rc;
  95. if( cmXScoreTest( ctx, xmlFn, decFn, csvOutFn, midiOutFn, svgOutFn, reportFl, begMeasNumb, begTempoBPM, svgStandAloneFl, svgPanZoomFl ) != kOkXsRC )
  96. return cmErrMsg(&ctx->err,kScoreGenFailedCtRC,"score_gen failed.");
  97. return kOkCtRC;
  98. }
  99. cmRC_t score_follow( cmCtx_t* ctx, const cmChar_t* csvScoreFn, const cmChar_t* midiInFn, const cmChar_t* matchRptOutFn, const cmChar_t* matchSvgOutFn, const cmChar_t* midiOutFn, const cmChar_t* timelineFn )
  100. {
  101. cmRC_t rc;
  102. if((rc = verify_file_exists(ctx,csvScoreFn,"Score CSV file")) != kOkCtRC )
  103. return rc;
  104. if((rc = verify_file_exists(ctx,midiInFn,"MIDI input file")) != kOkCtRC )
  105. return rc;
  106. //if((rc = verify_file_exists(ctx,matchRptOutFn,"Match report file")) != kOkCtRC )
  107. // return rc;
  108. //if((rc = verify_file_exists(ctx,matchSvgOutFn,"Match HTML/SVG file")) != kOkCtRC )
  109. // return rc;
  110. if(cmMidiScoreFollowMain(ctx, csvScoreFn, midiInFn, matchRptOutFn, matchSvgOutFn, midiOutFn, timelineFn) != kOkMsfRC )
  111. return cmErrMsg(&ctx->err,kScoreFollowFailedCtRC,"score_follow failed.");
  112. return kOkCtRC;
  113. }
  114. cmRC_t meas_gen( cmCtx_t* ctx, const cmChar_t* pgmRsrcFn, const cmChar_t* outFn )
  115. {
  116. cmRC_t rc;
  117. if((rc = verify_file_exists(ctx,pgmRsrcFn,"Program resource file")) != kOkCtRC )
  118. return rc;
  119. if((rc = verify_non_null_filename( ctx,outFn,"Measurements output file.")) != kOkCtRC )
  120. return rc;
  121. return cmScoreProc(ctx, "meas", pgmRsrcFn, outFn );
  122. }
  123. cmRC_t score_report( cmCtx_t* ctx, const cmChar_t* csvScoreFn, const cmChar_t* rptFn )
  124. {
  125. cmRC_t rc;
  126. if((rc = verify_file_exists(ctx,csvScoreFn,"Score CSV file")) != kOkCtRC )
  127. return rc;
  128. cmScoreReport(ctx,csvScoreFn,rptFn);
  129. return rc;
  130. }
  131. cmRC_t midi_file_report( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* rptFn, const cmChar_t* svgFn, bool standAloneFl, bool panZoomFl )
  132. {
  133. cmRC_t rc ;
  134. if((rc = verify_file_exists(ctx,midiFn,"MIDI file")) != kOkCtRC )
  135. return rc;
  136. if((rc = cmMidiFileReport(ctx, midiFn, rptFn )) != kOkMfRC )
  137. return cmErrMsg(&ctx->err,kMidiFileRptFailedCtRC,"MIDI file report generation failed.");
  138. if( svgFn != NULL )
  139. if((rc = cmMidiFileGenSvgFile(ctx, midiFn, svgFn, "midi_file_svg.css", standAloneFl, panZoomFl )) != kOkMfRC )
  140. return cmErrMsg(&ctx->err,kMidiFileRptFailedCtRC,"MIDI file SVG output generation failed.");
  141. return kOkCtRC;
  142. }
  143. cmRC_t timeline_report( cmCtx_t* ctx, const cmChar_t* timelineFn, const cmChar_t* tlPrefixPath, const cmChar_t* rptFn )
  144. {
  145. cmRC_t rc ;
  146. if((rc = verify_file_exists(ctx,timelineFn,"Timeline file")) != kOkCtRC )
  147. return rc;
  148. if((rc = cmTimeLineReport( ctx, timelineFn, tlPrefixPath, rptFn )) != kOkTlRC )
  149. return cmErrMsg(&ctx->err,kTimeLineRptFailedCtRC,"The timeline file report failed.");
  150. return rc;
  151. }
  152. cmRC_t audio_file_report( cmCtx_t* ctx, const cmChar_t* audioFn, const cmChar_t* rptFn )
  153. {
  154. cmRC_t rc;
  155. if((rc = verify_file_exists(ctx,audioFn,"Audio file")) != kOkCtRC )
  156. return rc;
  157. if((rc = cmAudioFileReportInfo( ctx, audioFn, rptFn )) != kOkTlRC )
  158. return cmErrMsg(&ctx->err,kAudioFileRptFailedCtRC,"The audio file report failed.");
  159. return rc;
  160. }
  161. cmRC_t midi_trim(cmCtx_t* ctx, const cmChar_t* midiInFn, unsigned begMidiUId, unsigned endMidiUId, const cmChar_t* midiOutFn)
  162. {
  163. cmRC_t rc;
  164. if((rc = verify_file_exists(ctx,midiInFn,"MIDI file")) != kOkCtRC )
  165. return rc;
  166. // kNoteTerminateFl | kPedalTerminateFl
  167. return cmMidiFileTrimFn(ctx, midiInFn, begMidiUId, endMidiUId, 0, midiOutFn );
  168. }
  169. int main( int argc, char* argv[] )
  170. {
  171. cmRC_t rc = cmOkRC;
  172. enum
  173. {
  174. kInvalidPoId = kBasePoId,
  175. kActionPoId,
  176. kXmlFileNamePoId,
  177. kDecorateFileNamePoId,
  178. kCsvOutFileNamePoId,
  179. kPgmRsrcFileNamePoId,
  180. kMidiOutFileNamePoId,
  181. kMidiInFileNamePoId,
  182. kSvgOutFileNamePoId,
  183. kStatusOutFileNamePoId,
  184. kTimelineFileNamePoId,
  185. kTimelinePrefixPoId,
  186. kAudioFileNamePoId,
  187. kReportFlagPoId,
  188. kSvgStandAloneFlPoId,
  189. kSvgPanZoomFlPoId,
  190. kBegMeasPoId,
  191. kBegBpmPoId,
  192. kBegMidiUidPoId,
  193. kEndMidiUidPoId
  194. };
  195. enum {
  196. kNoSelId,
  197. kScoreGenSelId,
  198. kScoreFollowSelId,
  199. kMeasGenSelId,
  200. kScoreReportSelId,
  201. kMidiReportSelId,
  202. kTimelineReportSelId,
  203. kAudioReportSelId,
  204. kMidiTrimSelId
  205. };
  206. // initialize the heap check library
  207. bool memDebugFl = 0; //cmDEBUG_FL;
  208. unsigned memGuardByteCnt = memDebugFl ? 8 : 0;
  209. unsigned memAlignByteCnt = 16;
  210. unsigned memFlags = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl : 0;
  211. cmPgmOptH_t poH = cmPgmOptNullHandle;
  212. const cmChar_t* appTitle = "cmtools";
  213. cmCtx_t ctx;
  214. const cmChar_t* xmlFn = NULL;
  215. const cmChar_t* decFn = NULL;
  216. const cmChar_t* pgmRsrcFn = NULL;
  217. const cmChar_t* csvScoreFn = NULL;
  218. const cmChar_t* midiOutFn = NULL;
  219. const cmChar_t* midiInFn = NULL;
  220. const cmChar_t* audioFn = NULL;
  221. const cmChar_t* svgOutFn = NULL;
  222. const cmChar_t* timelineFn = NULL;
  223. const cmChar_t* timelinePrefix = NULL;
  224. const cmChar_t* rptFn = NULL;
  225. unsigned reportFl = 0;
  226. unsigned svgStandAloneFl = 1;
  227. unsigned svgPanZoomFl = 1;
  228. int begMeasNumb = 0;
  229. int begTempoBPM = 60;
  230. unsigned begMidiUId = cmInvalidId;
  231. unsigned endMidiUId = cmInvalidId;
  232. unsigned actionSelId = kNoSelId;
  233. cmCtxSetup(&ctx,appTitle,print,print,NULL,memGuardByteCnt,memAlignByteCnt,memFlags);
  234. cmMdInitialize( memGuardByteCnt, memAlignByteCnt, memFlags, &ctx.rpt );
  235. cmFsInitialize( &ctx, appTitle);
  236. cmTsInitialize(&ctx );
  237. cmPgmOptInitialize(&ctx, &poH, poBegHelpStr, poEndHelpStr );
  238. cmPgmOptInstallEnum( poH, kActionPoId, 'S', "score_gen", 0, kScoreGenSelId, kNoSelId, &actionSelId, 1,
  239. "Run the score generation tool.","Action selector");
  240. cmPgmOptInstallEnum( poH, kActionPoId, 'F', "score_follow", 0, kScoreFollowSelId, kNoSelId, &actionSelId, 1,
  241. "Run the time line marker generation tool.",NULL);
  242. cmPgmOptInstallEnum( poH, kActionPoId, 'M', "meas_gen", 0, kMeasGenSelId, kNoSelId, &actionSelId, 1,
  243. "Generate perfomance measurements.",NULL);
  244. cmPgmOptInstallEnum( poH, kActionPoId, 'R', "score_report", 0, kScoreReportSelId, kNoSelId, &actionSelId, 1,
  245. "Generate a score file report.",NULL);
  246. cmPgmOptInstallEnum( poH, kActionPoId, 'I', "midi_report", 0, kMidiReportSelId, kNoSelId, &actionSelId, 1,
  247. "Generate a MIDI file report and optional SVG piano roll output.",NULL);
  248. cmPgmOptInstallEnum( poH, kActionPoId, 'E', "timeline_report", 0, kTimelineReportSelId, kNoSelId, &actionSelId, 1,
  249. "Generate a timeline report.",NULL);
  250. cmPgmOptInstallEnum( poH, kActionPoId, 'A', "audio_report", 0, kAudioReportSelId, kNoSelId, &actionSelId, 1,
  251. "Generate an audio file report.",NULL);
  252. cmPgmOptInstallEnum( poH, kActionPoId, 'T', "midi_trim", 0, kMidiTrimSelId, kNoSelId, &actionSelId, 1,
  253. "Trim a MIDI file to create a shortened version.",NULL);
  254. cmPgmOptInstallStr( poH, kXmlFileNamePoId, 'x', "muisic_xml_fn",0, NULL, &xmlFn, 1,
  255. "Name of the input MusicXML file.");
  256. cmPgmOptInstallStr( poH, kDecorateFileNamePoId, 'd', "dec_fn", 0, NULL, &decFn, 1,
  257. "Name of a score decoration file.");
  258. cmPgmOptInstallStr( poH, kCsvOutFileNamePoId, 'c', "score_csv_fn",0, NULL, &csvScoreFn, 1,
  259. "Name of a CSV score file.");
  260. cmPgmOptInstallStr( poH, kPgmRsrcFileNamePoId, 'g', "pgm_rsrc_fn", 0, NULL, &pgmRsrcFn, 1,
  261. "Name of program resource file.");
  262. cmPgmOptInstallStr( poH, kMidiOutFileNamePoId, 'm', "midi_out_fn", 0, NULL, &midiOutFn, 1,
  263. "Name of a MIDI file to generate as output.");
  264. cmPgmOptInstallStr( poH, kMidiInFileNamePoId, 'i', "midi_in_fn", 0, NULL, &midiInFn, 1,
  265. "Name of a MIDI file to generate as output.");
  266. cmPgmOptInstallStr( poH, kSvgOutFileNamePoId, 's', "svg_fn", 0, NULL, &svgOutFn, 1,
  267. "Name of a HTML/SVG file to generate as output.");
  268. cmPgmOptInstallStr( poH, kTimelineFileNamePoId, 't', "timeline_fn", 0, NULL, &timelineFn, 1,
  269. "Name of a timeline to generate as output.");
  270. cmPgmOptInstallStr( poH, kTimelinePrefixPoId, 'l', "tl_prefix", 0, NULL, &timelinePrefix,1,
  271. "Timeline data path prefix.");
  272. cmPgmOptInstallStr( poH, kAudioFileNamePoId, 'a', "audio_fn", 0, NULL, &audioFn, 1,
  273. "Audio file name.");
  274. cmPgmOptInstallStr( poH, kStatusOutFileNamePoId,'r', "report_fn", 0, NULL, &rptFn, 1,
  275. "Name of a status file to generate as output.");
  276. cmPgmOptInstallFlag( poH, kReportFlagPoId, 'f', "debug_fl", 0, 1, &reportFl, 1,
  277. "Print a report of the score following processing." );
  278. cmPgmOptInstallInt( poH, kBegMeasPoId, 'b', "beg_meas", 0, 1, &begMeasNumb, 1,
  279. "The first measure the to be written to the output CSV, MIDI and SVG files." );
  280. cmPgmOptInstallInt( poH, kBegBpmPoId, 'e', "beg_bpm", 0, 0, &begTempoBPM, 1,
  281. "Set to 0 to use the tempo from the score otherwise set to use the tempo at begMeasNumb." );
  282. cmPgmOptInstallFlag( poH, kSvgStandAloneFlPoId, 'n', "svg_stand_alone_fl",0, 1, &svgStandAloneFl, 1,
  283. "Write the SVG file as a stand alone HTML file. Enabled by default." );
  284. cmPgmOptInstallFlag( poH, kSvgPanZoomFlPoId, 'z', "svg_pan_zoom_fl", 0, 1, &svgPanZoomFl, 1,
  285. "Include the pan-zoom control. Enabled by default." );
  286. cmPgmOptInstallUInt( poH, kBegMidiUidPoId, 'w', "beg_midi_uid", 0, 1, &begMidiUId, 1,
  287. "Begin MIDI msg. uuid." );
  288. cmPgmOptInstallUInt( poH, kEndMidiUidPoId, 'y', "end_midi_uid", 0, 1, &endMidiUId, 1,
  289. "End MIDI msg. uuid." );
  290. // parse the command line arguments
  291. if( cmPgmOptParse(poH, argc, argv ) == kOkPoRC )
  292. {
  293. // handle the built-in arg's (e.g. -v,-p,-h)
  294. // (returns false if only built-in options were selected)
  295. if( cmPgmOptHandleBuiltInActions(poH, &ctx.rpt ) == false )
  296. goto errLabel;
  297. switch( actionSelId )
  298. {
  299. case kScoreGenSelId:
  300. rc = score_gen( &ctx, xmlFn, decFn, csvScoreFn, midiOutFn, svgOutFn, reportFl, begMeasNumb, begTempoBPM, svgStandAloneFl, svgPanZoomFl );
  301. break;
  302. case kScoreFollowSelId:
  303. rc = score_follow( &ctx, csvScoreFn, midiInFn, rptFn, svgOutFn, midiOutFn, timelineFn );
  304. break;
  305. case kMeasGenSelId:
  306. rc = meas_gen(&ctx, pgmRsrcFn, rptFn);
  307. break;
  308. case kScoreReportSelId:
  309. rc = score_report(&ctx, csvScoreFn, rptFn );
  310. break;
  311. case kMidiReportSelId:
  312. rc = midi_file_report(&ctx, midiInFn, rptFn, svgOutFn, svgStandAloneFl, svgPanZoomFl );
  313. break;
  314. case kTimelineReportSelId:
  315. rc = timeline_report(&ctx, timelineFn, timelinePrefix, rptFn );
  316. break;
  317. case kAudioReportSelId:
  318. rc = audio_file_report(&ctx, audioFn, rptFn );
  319. break;
  320. case kMidiTrimSelId:
  321. rc = midi_trim(&ctx, midiInFn, begMidiUId, endMidiUId, midiOutFn);
  322. break;
  323. default:
  324. rc = cmErrMsg(&ctx.err, kNoActionIdSelectedCtRC,"No action selector was selected.");
  325. }
  326. }
  327. errLabel:
  328. cmPgmOptFinalize(&poH);
  329. cmTsFinalize();
  330. cmFsFinalize();
  331. cmMdReport( kIgnoreNormalMmFl );
  332. cmMdFinalize();
  333. return rc;
  334. }