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

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