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

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