cmtools is a collection of utilities for generating and processing machine readable musical scores based on MusicXML, MIDI and other data files.
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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. }