// // squanty.cpp // qparse // test new architecture Runey // // Created by Florent Jacquemard on 29/03/2019. // Copyright © 2019 Florent Jacquemard. All rights reserved. // /// command line for quantization of input segment according to /// weighted Symbolic Tree Automaton. /// parsing/enumeration scenari and demos /// each scenario is defined in a Parser /// workflow inputless: /// enumeration of language of a WTA #define QUANT_INPUTLESS 1 /// input segment of musical duration 1 bar, 1 best /// construction of a table indexed by SIP keys /// (state, interval, pre/post values) #define QUANT_1BAR_1BEST 2 /// input segment of musical duration 1 bar, K best /// construction of a table indexed by SIP keys /// (state, interval, pre/post values) #define QUANT_1BAR_KBEST 3 /// input segment of musical duration several bars, 1 best /// construction of a table indexed by SI keys (state, interval) /// runs for sequences of bars constructed as binary trees #define QUANT_MULTIBAR_1BEST_SI 7 /// input segment of musical duration several bars, 1 best /// construction of a table indexed by SIP keys /// (state, interval, pre/post values) /// runs for sequences of bars constructed as binary trees #define QUANT_MULTIBAR_1BEST_SIP 8 /// input segment of musical duration several bars, 1 best /// construction of a table indexed by SIR keys (rests) /// (state, interval, pre/post values) /// runs for sequences of bars constructed as binary trees #define QUANT_MULTIBAR_1BEST_SIR 9 /// idem with SIO keys (refactoring or SIR) #define QUANT_MULTIBAR_1BEST_SIO 10 /// input segment of musical duration several bars, 1 best /// construction of a table indexed by SIP keys /// (state, interval, pre/post values) /// runs for sequences of bars constructed as lists (flat) #define QUANT_MULTIBAR_1BEST_SIPFLAT 11 /// current scenario #define QUANT_SCENARIO QUANT_MULTIBAR_1BEST_SIO #include #include #include #include #include #include #include #include #include // handling commandline options #include #include "config.hpp" // check compile flags #include "trace.hpp" #include "Rational.hpp" #include "util.hpp" // input segment #include "RTU.hpp" #include "MTU.hpp" #include "InputSegment.hpp" #include "InputSegmentSerial.hpp" #include "InputSegmentMIDI.hpp" #include "InputSegmentMono.hpp" #include "Voicing.hpp" #include "VoicingMono.hpp" // automata and parsing // switch options for WCFG weight #include "Weight.hpp" #include "WeightDom.hpp" #include "FloatWeight.hpp" #include "TropicalWeight.hpp" #include "ViterbiWeight.hpp" #include "Label.hpp" #include "SWTAFileIn.hpp" //#include "Key.hpp" #include "KeyS.hpp" #include "KeySI.hpp" #include "KeySIP.hpp" //#include "KeySIR.hpp" #include "KeySIO.hpp" #include "Runey.hpp" #include "RunInner.hpp" #include "RunTerm.hpp" #include "RunCompare.hpp" #include "RecordOne.hpp" #include "RecordQueue.hpp" #include "TableA.hpp" #include "TableParse.hpp" #include "Quantizer.hpp" // output #include "LRT.hpp" //#include "ScoringEnv.hpp"; #include "TableMonoImporter.hpp" #include "MIDIOutput.hpp" #include "LRTTableIn.hpp" //#include "MEI.hpp" #include "Score.hpp" #include "Event.hpp" #include "Measure.hpp" #include "Note.hpp" #include "PS0.hpp" #include "TableMonoImporter.hpp" #include "SMPrinter.hpp" #include "SMMEI.hpp" namespace ScoreModel {}; namespace sm = ScoreModel; // ========================================================================== // Option handling with getop_long (stdlib) static int verbosity = 0; /// number of K-best to compute //was #define TEST_K 1 static size_t _nbest = 1; /// whether the best is the max (true) or the min (false) weight static int f_max = true; /// input file option is present static bool f_input = false; /// input file is text static bool f_in_text = false; /// input file is midi static bool f_in_midi = false; /// file name for reading input segment static string _input_file; /// input segment static InputSegment* _iseg; /// transform the input midi file into monophonic static int f_mono = false; /// ignore rests in input midi file static int f_norest = false; /// start date option is present static int f_start = false; /// argument of start date option static rtu_t d_start; /// end date option is present static int f_end = false; /// argument of end date option static rtu_t d_end; /// output file option is present static bool f_output = false; /// output file is mei static bool f_out_mei = false; /// output file is midi static bool f_out_midi = false; /// output file name for writing static string _output_file; /// config file (aka ini) option is present static bool f_config = false; /// text file name for writing static string _config_file; /// file name for reading schema static string _schema_file; /// schema file is present static bool f_schema = false; /// input schema static SWTAFileIn* _schema; /// flag: clean WCFG static int f_clean = false; /// flag: number of bars specified (otherwise default = 1) static int f_bars = false; /// number of bars in input static size_t _bars = 1; /// flag: bar real-time dur specified (otherwise default = 1) static int f_barsec = false; /// real-time duration of bar static rtu_t _barsec = 1; /// flag: bar musical-time dur specified (otherwise default = 1) static int f_barbeat = false; /// musical-time duration of bar static size_t _barbeat = 1; /// flag: time signature specified static int f_timesig = false; /// time signature static sm::MeterSig _ts(4,4); //static int _ts_num; //static int _ts_den; /// flag: pre value /// @todo TBR? static int f_pre = false; /// argument of pre option /// @todo TBR? static size_t _pre = 0; /// flag: post value static int f_post = false; /// argument of post option static size_t _post = 0; /// forced weight type for WCFG (given in command line options) static int f_penalty = false; static int f_count = false; static int f_proba = false; static WeightDom _weight_type = WeightDom::UNDEF; static struct option long_options[] = { // Options with an abbreviation // we dont use flags here, we set them after calling getopt instead /* 0 */ { "help", no_argument, NULL, 'h' }, /* 1 */ { "version", no_argument, NULL, 'V' }, /* 2 */ { "verbosity", required_argument, NULL, 'v' }, /* 3 */ { "debug", no_argument, NULL, 'd' }, /* 4 */ { "trace", no_argument, NULL, 't' }, /* 5 */ { "quiet", no_argument, NULL, 'q' }, /* 6 */ { "kbest", required_argument, NULL, 'k' }, /* 7 */ { "input", required_argument, NULL, 'i' }, /* 8 */ { "midi", required_argument, NULL, 'm' }, /* 9 */ { "schema", required_argument, NULL, 'a' }, /* 10 */ { "output", required_argument, NULL, 'o' }, /* 11 */ { "config", required_argument, NULL, 'c' }, // Options with no abbreviation /* 12 */ { "penalty", no_argument, &f_penalty, true}, /* 13 */ { "counting", no_argument, &f_count, true }, /* 14 */ { "proba", no_argument, &f_proba, true }, /* 15 */ { "probability", no_argument, &f_proba, true }, /* 16 */ { "stochastic", no_argument, &f_proba, true }, /* 17 */ { "clean", no_argument, &f_clean, true }, /* 18 */ { "norest", no_argument, &f_norest, true }, /* 19 */ { "start", required_argument, &f_start, true }, /* 20 */ { "end", required_argument, &f_end, true }, /* 21 */ { "bars", required_argument, &f_bars, true }, /* 22 */ { "pre", required_argument, &f_pre, true }, /* 23 */ { "post", required_argument, &f_post, true }, /* 24 */ { "barsec", required_argument, &f_barsec, true }, /* 25 */ { "barbeat", required_argument, &f_barbeat, true }, /* 26 */ { "ts", required_argument, &f_timesig, true }, /* 27 */ { "min", no_argument, &f_max, false }, /* 28 */ { "max", no_argument, &f_max, true }, /* 29 */ { "mono", no_argument, &f_mono, true }, /* last */ { NULL, 0, NULL, 0 } }; void usage() { std::cout << "Usage: squant2 [options...]"; std::cout << std::endl; std::cout << " -help -h"; std::cout << std::endl; std::cout << " -version -V"; std::cout << std::endl; std::cout << " -verbosity level -v level : level=0..6 (default is 0)"; std::cout << std::endl; std::cout << " levels: 0=off, 1=critical, 2=err, 3=warn"; std::cout << std::endl; std::cout << " 4=info, 5=debug, 6=trace"; std::cout << std::endl; std::cout << " -trace -t : same as -verbosity 6"; std::cout << std::endl; std::cout << " -debug -d : same as -verbosity 5"; std::cout << std::endl; std::cout << " -quiet -q : same as -verbosity 0"; std::cout << std::endl; std::cout << " --kbest N -k N : compute the N best trees"; std::cout << std::endl; std::cout << " -max : the best is the max weight"; std::cout << std::endl; std::cout << " -min : the best is the min weight"; std::cout << std::endl; std::cout << " -input filename -i filename : \ filename is either a text file or midifile \ (characterized by extension .mid or .midi)"; std::cout << std::endl; std::cout << " -midi midifilename -m midifilename : filename is a midi file"; std::cout << std::endl; std::cout << " -mono : convert input midifile to monophonic"; std::cout << std::endl; std::cout << " -norest : ignore rests in input midifile and convert to mono"; std::cout << std::endl; std::cout << " -start D : set start date for input segment"; std::cout << std::endl; std::cout << " -end D : set end date for input segment"; std::cout << std::endl; std::cout << " -schema filename -a filename : filename contains \ the text description of a WCFG"; std::cout << std::endl; std::cout << " -clean : clean input schema"; std::cout << std::endl; std::cout << " -output filename -o filename : output file \ .mei for a MEI score output,\ .midi or .mid for a MIDI file"; std::cout << std::endl; std::cout << " -config file.ini -c file.ini : configuration file"; std::cout << std::endl; std::cout << "schema weight models (mutually exclusive options):"; std::cout << std::endl; std::cout << " -counting"; std::cout << std::endl; std::cout << " -penalty"; std::cout << std::endl; std::cout << " -probability -stochastic (equivalent)"; std::cout << std::endl; std::cout << " -pre I : number of notes added (as grace notes)\ at the begining of the segment to quantize,\ e.g. originating from another precedent segment"; std::cout << std::endl; std::cout << " -post I : number of notes that will be aligned to right bound\ in quantization solutions"; std::cout << std::endl; std::cout << " -bar I : number of bars in the input segment (default 1)"; std::cout << std::endl; std::cout << " -barsec D : assumed real-time duration of 1 bar, in seconds (default 1)"; std::cout << std::endl; std::cout << " -barbeat I : musical-ime duration of 1 bar, in beats (default 1)"; std::cout << std::endl; std::cout << " -ts \"n/d\" : time signature (default 4/4)"; std::cout << std::endl; std::cout << std::endl; std::cout << "-input and -midi are mutualy exclusive"; std::cout << std::endl; std::cout << "-bar and -barsec are mutualy exclusive"; std::cout << std::endl; std::cout << "-probability -stochastic are equivalent"; std::cout << std::endl; std::cout << "-counting -penalty -probability -stochastic are mutually exclusive"; std::cout << std::endl; std::cout << "-trace has priority over -verbosity"; std::cout << std::endl; } void version() { INFO("qparse: version 2"); } // ========================================================================== // Option handling with getop_long (stdlib) #define check_index_is(NAME) \ assert(0 == strcmp(long_options[option_index].name, NAME)) static void setOptionArgs(int option_index) { TRACE("options: setOptionArgs: {}", option_index); string _arg; size_t sepindex; // long options without abbreviation and with argument switch(option_index) { case 19: check_index_is("start"); assert (f_start); d_start = strtod(optarg, NULL); TRACE("options: setOptionArgs {}: {}", long_options[option_index].name, d_start); break; case 20: check_index_is("end"); assert (f_end); d_end = strtod(optarg, NULL); TRACE("options: setOptionArgs {}: {}", long_options[option_index].name, d_end); break; case 21: check_index_is("bars"); assert (f_bars); _bars = strtol(optarg, NULL, 10); TRACE("options: setOptionArgs {}: {}", long_options[option_index].name, _bars); break; case 22: check_index_is("pre"); assert (f_pre); _pre = strtol(optarg, NULL, 10); TRACE("options: setOptionArgs {}: {}", long_options[option_index].name, _pre); break; case 23: check_index_is("post"); assert (f_post); _post = strtol(optarg, NULL, 10); TRACE("options: setOptionArgs {}: {}", long_options[option_index].name, _post); break; case 24: check_index_is("barsec"); assert (f_barsec); _barsec = strtod(optarg, NULL); TRACE("options: setOptionArgs {}: {}", long_options[option_index].name, _barsec); break; case 25: check_index_is("barbeat"); assert (f_barbeat); _barbeat = strtol(optarg, NULL, 10); TRACE("options: setOptionArgs {}: {}", long_options[option_index].name, _barsec); break; case 26: check_index_is("ts"); assert (f_timesig); _arg = string(optarg); sepindex = _arg.find_last_of("/"); // prefix // prefix, suffix _ts = sm::MeterSig(std::stoi(_arg.substr(0, sepindex)), std::stoi(_arg.substr(sepindex + 1, _arg.size() - sepindex))); TRACE("options: setOptionArgs {}: {}", long_options[option_index].name, optarg); break; default: ; // ignore } } /// @param c character of option /// @return 0 : continue without error /// 1 : stop without error /// > 1 : stop with error static int getOption(int c, int option_index) { // end of the options assert(c != -1); switch (c) { // long option with flag pointer case 0: setOptionArgs(option_index); break; case 'h': usage(); return 1; case 'V': version(); return 1; case 'v': if (optarg) //verbosity = strtol(optarg, NULL, 10); verbosity = atoi(optarg); else verbosity = 4; TRACE("option {}: {}", long_options[option_index].name, verbosity); break; case 'd': verbosity = 5; TRACE("option {}: verbosity={}", long_options[option_index].name, verbosity); break; case 't': verbosity = 6; TRACE("option {}: verbosity={}", long_options[option_index].name, verbosity); break; case 'q': verbosity = 0; TRACE("option {}: verbosity={}", long_options[option_index].name, verbosity); break; case 'k': //k = strtoul(optarg, NULL, 10); _nbest = atoi(optarg); TRACE("Option {} : {}", long_options[option_index].name, _nbest); if (_nbest == 0) { ERROR("option error: k={} must be > 0", _nbest); return 2; } break; case 'i': f_input = true; _input_file = std::string(optarg); TRACE("Option {}: {}", long_options[option_index].name, _input_file); // distinguish between MIDI and text input if (_input_file.size() > 0) { string suffix = util::suffix(_input_file); // export only if output file has midi suffix if (suffix == "MID" || suffix == "MIDI") { INFO("option input: MIDI file import"); f_in_midi = true; } else if (suffix == "TXT") { INFO("option input: plaintext file import"); f_in_text = true; } else { WARN("option -i input file {}, \ no suffix .txt or .midi or .mid found, assume text", _input_file); f_in_text = true; } } else { ERROR("option -i error: no output file name"); return 2; } break; case 'm': f_input = true; f_in_midi = true; _input_file = std::string(optarg); TRACE("option {}: {}", long_options[option_index].name, _input_file); break; case 'o': f_output = true; _output_file = std::string(optarg); TRACE("option {}: {}", long_options[option_index].name, _output_file); if (_output_file.size() > 0) // empty string : no output { string suffix = util::suffix(_output_file); // export only if output file has midi suffix if (suffix == "MID" || suffix == "MIDI") { INFO("option output: MIDI file export"); f_out_midi = true; } else if (suffix == "MEI") { INFO("option output: MEI file export"); f_out_mei = true; } else { ERROR("option -o error: ouput file {}, \ suffix must be .mei or .midi or .mid", _output_file); return 2; } } else { ERROR("option -o error: no output file name"); return 2; } break; case 'c': f_config = true; _config_file = std::string(optarg); TRACE("Option {}: {}", long_options[option_index].name, _config_file); break; case 'a': f_schema = true; _schema_file = std::string(optarg); TRACE("Option {}: {}", long_options[option_index].name, _schema_file); break; // missing option argument. Error already signaled by getopt case ':': ERROR("option error: missing option argument (ignore)"); break; // unknown or ambiguous option Error already signaled by getopt case '?': ERROR("option error: unkown or ambiguous option argument (ignore)"); break; // Do nothing for the options that are not flag-managed but without arguments default: ERROR("Unknown option {} ignored, use -h for help.", c); break; } return 0; } bool checkOptions() { bool error = false; if (f_penalty || f_count || f_proba) { if ((f_penalty && f_count) || ((f_penalty || f_count) == f_proba)) { ERROR("options conflict: more than one schema file type"); error = true; } } else { TRACE("options: no schema file type"); } // set forced weight type value if (f_penalty) { _weight_type = WeightDom::PENALTY; } else if (f_count) { _weight_type = WeightDom::COUNTING; } else if (f_proba) { _weight_type = WeightDom::STOCHASTIC; } else { _weight_type = WeightDom::UNDEF; } if (f_in_text == f_in_midi) { TRACE("options: f_input={}, f_in_midi={}", f_input, f_in_midi); if (f_in_midi) { ERROR("options conflict: 2 input files"); error = true; } else { TRACE("options: no input file"); } } if (! f_schema) { ERROR("missing option: schema file"); error = true; } if (f_start && f_end) { if (d_end < d_start) { ERROR("start date must be before end date"); error = true; } if (! f_input) { ERROR("options: set start or end date without input"); error = true; } } if (f_bars && f_barsec) { ERROR("options bars and barsec are mutually exclusive"); error = true; } if (f_out_midi && (! f_barbeat)) { /// @todo adjust according to grammar optional content /// SchemaFileIn.readTimesignature or /// SchemaFileIn.read_option ERROR("options: barbeat must be given for MIDI output"); error = true; } return error; } void setVerbosityLevel() { switch (verbosity) { case 0: spd::set_level(spd::level::off); break; case 1: spd::set_level(spd::level::critical); break; case 2: spd::set_level(spd::level::err); break; case 3: spd::set_level(spd::level::warn); break; case 4: spd::set_level(spd::level::info); break; case 5: spd::set_level(spd::level::debug); break; case 6: spd::set_level(spd::level::trace); break; default: { WARN("verbosity level {} too high, set to 6 (trace).", verbosity); spd::set_level(spd::level::trace); } } } /// @param init initial key to start parsing. /// @param n number of best trees to compute. /// @param f_max whether the best is the max (true) or the min (false) weight /// @param env parsing environment (input and automaton). /// @return the parse table computed, containing the k best trees.. /// - K = concrete key class (index rows in the table) /// - H = KeyHasher (for keys) template parsing::TableA* parse(const K& init, size_t n, bool f_max, parsing::ParsingEnv& env) { assert(env.segment); parsing::RunCompare comp = (f_max)?parsing::weightMax:parsing::weightMin; parsing::TableA* tab = nullptr; // initialize table (empty) if (n == 1) // 1-best { tab = new parsing::TableParse, H>(env, comp); } else // k-best { assert(n > 1); // cannot be 0 tab = new parsing::TableParse, H>(env, comp); } // fill table up to n-best assert(tab); tab->best(init, n); // const parsing::Run* (not used) return tab; } /// extract a ScoreModel with 1 unique part from the 1-best tree in given table. /// @param tab a parse table. /// @param init initial key at top of the 1-best tree. /// @param ts metre signature /// @param scorename name given to the score in the build Score Model /// @param partname name given to the unique part in the build Score Model /// @param env scoring environment (input and automaton and voices). template sm::Score* buildSM(parsing::TableA& tab, const K& init, const sm::MeterSig& ts, const string& scorename, const string& partname, sm::ScoringEnv& env) { sm::TableMonoImporter importer(std::string(scorename), ts); importer.readPart(std::string("part1"), env, tab, init, 1); sm::Score* score = importer.getScore(); assert(score); return score; } void spellPitch(sm::Score& score) { for (sm::PartSeq::iterator i = score.begin(); i != score.end(); ++i) { sm::Part& part = **i; INFO("Pitch Spelling part: {}", part.id()); sm::PS0 ps = sm::PS0(part); ps.spell1(); } } void printSM(const sm::Score& score) { sm::Printer sm_pp = sm::Printer(std::cout, false); std::cout << std::endl; sm_pp.visitScore(&score); std::cout << std::endl; } /// @param tab a parse table. /// @param init initial key to start parsing. /// @param n number of best trees to compute. template void printTrees(parsing::TableA& tab, const K& init, size_t n) { for (size_t i = 1; i <= _nbest; i++) { const parsing::Run* best_run = tab.best(init, i); if (best_run == NULL) // should no more happen (changed to FAIL) { //assert(best_tree == NULL); INFO("{}-best: NULL", i); } else if (_nbest == 1) { //LabeledRhythmTree* best_tree = tab->bestTree(best_run); LabeledRhythmTree* best_tree = LRTFactory::projection(tab, best_run, 1); assert(best_tree); INFO("{}-best = {} weight = {}", i, best_tree->to_string(), best_run->weight()); delete best_tree; } else { const parsing::RunRanked* best_runr = dynamic_cast*>(best_run); assert(best_runr); //LabeledRhythmTree* best_tree = tab->bestTree(best_runr); LabeledRhythmTree* best_tree = LRTFactory::projection(tab, best_runr); assert(best_tree); INFO("{}-best = {} weight = {}", i, best_tree->to_string(), best_run->weight()); delete best_tree; } } } size_t writeMEI(const sm::Score& score, const string& filename) { string prefix = util::prefix(filename); if (score.nbParts() == 0) { ERROR("empty score (0 parts)"); return 1; } INFO("Export part 1/{} of the score model into MEI.", score.nbParts()); sm::MEIExporter sm_export = sm::MEIExporter(); sm::PartSeq::const_iterator pi = score.cbegin(); assert(pi != score.cend()); assert(*pi); sm::Part& part1 = *(*pi); sm_export.addScore(part1); INFO("write to {}", filename); sm_export.writeInFile(filename); return 0; } template size_t exportMIDI(parsing::TableA& tab, const K& init, Rational barbeat, const string& input_filename, const string& output_filename, parsing::ParsingEnv& env) { assert(env.segment); INFO("quantize segment ({} points) wrt parse tree", env.segment->size()-1); // quantize wrt the 1-best for k0 //_iseg->quantize(tab, k0, 1); parsing::Quantizer quantizer(tab); quantizer.quantize(*(env.segment), init, 1); INFO("quantized input segment:"); env.segment->print(std::cout); // qseg->respell(); // not for MIDI out! INFO("export to MIDI file {}", output_filename); if (output_filename.empty()) return 2; assert(barbeat > Rational(0)); MIDIExporter midiexport(output_filename, barbeat); // track nb = 1 assert(! input_filename.empty()); midiexport.export_midifile(*(env.segment), input_filename, 1); return 0; } int main(int argc, char** argv) { //clock_t time_start; // chrono //clock_t time_end; // read command line options int c; // character of option while(1) { /* getopt_long stores the option index here. */ int option_index = 0; c = getopt_long_only(argc, argv, "hVv:dtk:i:m:a:", long_options, &option_index); // end of the options if (c == -1) break; int ret = getOption(c, option_index); // if ret ==0 : no problem, continue // exit silently if (ret == 1) return 0; // exit with error message else if (ret > 1) { ERROR("error in options (code {}), exit", ret); return ret; } } if (checkOptions()) { ERROR("option error, exit"); std::cerr << "option error. exit"; return(1); } // set verbosity level. log levels: // trace = 0, // debug = 1, // info = 2, // warn = 3, // err = 4, // critical = 5, // off = 6 INFO("verbosity level = {}", verbosity); spd::set_level(spd::level::debug); // default setVerbosityLevel(); // display input/output options if (f_input) { INFO("input file: {}", _input_file); } INFO("schema file: {} ({} weight model option)", _schema_file, (f_count?"counting":(f_proba?"stochastic":(f_penalty?"penalty":"???")))); if (f_output) { INFO("output file: {}", _output_file); } if (f_config) { INFO("config file: {}", _config_file); } // initialize running environment from INI file if (f_config) { INFO("loading config. parameters from ini file {}", _config_file); int res = read_config(_config_file); if (res == 0) { INFO("reading config from {} OK", _config_file); } else if (res == -1) { ERROR("error opening config file {}", _config_file); return 1; } else { ERROR("parse error in config file {} line {}", _config_file, res); return 2; } } // adjust options after reading config file // (possible redundancies between config.ini file and options) if ((! f_norest) && OPT_NOREST) { f_norest = true; } if (f_norest && (! f_mono)) { TRACE("option norest: input midi file implicitly assumed mono"); f_mono = true; } // read base schema (global) from file INFO("Reading schema from {}", _schema_file); _schema = new SWTAFileIn(_schema_file, _weight_type); if (_schema == NULL || _schema->empty()) { ERROR("error reading schema {}, abort", _schema_file); return(2); } if (f_timesig) { INFO("Time Signature (from command line option) {}/{}", _ts.getCount(), _ts.getUnit()); } else { if (SchemaFileIn::readTimesignature(_schema_file, _ts)) { INFO("Time Signature (from file {}) {}/{}", _schema_file, _ts.getCount(), _ts.getUnit()); } else { INFO("could not find time signature in {}, default to 4/4", _schema_file); } } // set domain of weights (cost values) if (CST_WEIGHT_TYPE == WeightDom::UNDEF) { ERROR("no weight type found for {}, abort", _schema_file); return(3); } switch(CST_WEIGHT_TYPE) { case WeightDom::UNDEF: ERROR("weight type undef after importing automaton. abort."); return(4); break; case WeightDom::PENALTY: INFO("weight model: penalty (alpha = {})", Weight::CST_ALPHA); break; case WeightDom::STOCHASTIC: INFO("weight type: stochastic (sigma2 = {})", Weight::CST_SIGMA2); break; case WeightDom::COUNTING: ERROR("weight type counting not supported for quantization. abort."); return(4); } // cleaning of base schema [optional] // TBC : remove 0 weighted transitions if (f_clean) { INFO("Cleaning schema"); _schema->clean(); } // TBC normalization flag ? if (! _schema->isClean()) { ERROR("schema {} is not clean, abort.", _schema_file); set empty = _schema->emptyStates(); for (set::iterator i = empty.begin(); i != empty.end(); i++) { ERROR("empty state : {}", *i); } return 2; } // display base schema INFO("SWTAFileIn (after casting and cleaning):"); if (verbosity >= 4) // info { std::cout << *(_schema); _schema->print(cout); } // read input segment (global) from file _iseg = NULL; // the input file is parsed as a midifile // if one of the following condition holds: // - the option -m was used (flag f_in_midi is set) // - the file has suffix .mid or .ID or .midi or .MIDI // otherwise is it parsed as a plain text file // describing monophonic segment. if (f_input) { //assert(f_in_midi || f_in_text); if (f_in_midi) { // TBC (uses stubs) INFO("main: read input segment from MIDI file {}", _input_file); // track nb = 1 InputSegmentMIDI* _iseg0 = new InputSegmentMIDI(_input_file, 1); if (f_mono) { INFO("main: make input segment mono, {} rests", (f_norest == true)?"without":"with"); _iseg = new InputSegmentMono(*_iseg0, f_norest); delete _iseg0; } else { _iseg = _iseg0; } } else if (f_in_text) { INFO("main: build input segment from text file {}", _input_file); _iseg = new InputSegmentSerial(_input_file, true); } else { ERROR("input file {} of unknown type", _input_file); } } if (f_start || f_end) // change start/end dates of input segment { assert(_iseg); rtu_t begin = f_start?d_start:_iseg->rbegin(); rtu_t end = f_end?d_end:_iseg->rend(); INFO("reset input segment start to {}, end to {}", begin, end); InputSegment* save = _iseg; // copy and resize _iseg = new InputSegment((*save), begin, end); //delete save; } if (f_bars) { assert(! f_barsec); assert(_iseg); assert(_bars > 0); _barsec = (_iseg->Interval::rduration()) / _bars; } if (f_barsec) { assert(! f_bars); assert(_iseg); assert(_barsec > 0); _bars = std::ceil(_iseg->Interval::rduration() / _barsec); } if (_iseg && _iseg->size() == 0) { ERROR("empty segment, stopping"); return(2); } else if (_iseg && verbosity >= 4) // info { INFO("segment : {} events", _iseg->size()); _iseg->print(std::cout); } #if QUANT_SCENARIO == QUANT_INPUTLESS typedef parsing::KeyS KeyS; INFO("{}-best computation", _nbest); // no input segment assert(_schema); parsing::ParsingEnv env = parsing::ParsingEnv(_schema, NULL); state_t init = _schema->initial(); const KeyS k0 = KeyS(init, env); // flag f_max: ordering for enumeration (user option) parsing::TableA* tab = parse(k0, _nbest, f_max, env); tab->best(k0, _nbest); // fill table up to n-best for (size_t i = 1; i <= _nbest; i++) { const parsing::Run* best_run = tab->best(k0, i); if (best_run == NULL) INFO("{}-BEST RUN: none", i); else if (_nbest == 1) { INFO("{}-BEST RUN = {}", i, *(best_run)); } else { const parsing::RunRanked* best_runr = dynamic_cast*>(best_run); assert(best_runr); INFO("{}-BEST RUN = {}", i, *(best_runr)); } } delete tab; #elif QUANT_SCENARIO == QUANT_1BAR_1BESTSIP ERROR("missing scenario QUANT_1BAR_1BESTSIP"); #elif QUANT_SCENARIO == QUANT_1BAR_KBESTSKIP ERROR("missing scenario QUANT_1BAR_KBESTSKIP"); #elif QUANT_SCENARIO == QUANT_MULTIBAR_1BEST_SI INFO("parser multibar with Key_SI"); INFO("compute best tree sequence for {} and input in {}", _schema_file, _input_file); // check options if (!(f_barsec || f_bars)) { ERROR("at least one of the options -bars or -barsec mandatory"); exit(2); } // prepare parsing environment assert(_schema); assert(_iseg); parsing::ParsingEnv env = parsing::ParsingEnv(_schema, _iseg); assert(env.segment); INFO("parsing segment [{}-{}], rdur={}", env.segment->rbegin(), env.segment->rend(), env.segment->Interval::rduration()); INFO("fixed bar duration = {}s ({} bars)", _barsec, _bars); INFO("start parsing"); clock_t time_start = clock(); // initial augmented state const parsing::KeySI k0 = parsing::KeySI(_barsec, env, false); // flag f_max: ordering for enumeration (user option) parsing::TableA* tab = parse(k0, _nbest, f_max, env); // fill table up to n-best const parsing::Run* r0 = tab->best(k0, _nbest); INFO("time to parse : {}ms", util::duration(time_start)); LabeledRhythmTree* t0 = LRTFactory::best(tab, k0); // was: tab->bestTree(r0); assert(t0); if (r0 == NULL) { assert(t0 == NULL); INFO("FAILED (no output)"); } else { INFO("output = {}", t0->to_string()); INFO("weight = {}", r0->weight()); } delete t0; if (tab) delete tab; #elif QUANT_SCENARIO == QUANT_MULTIBAR_1BEST_SIP INFO("parser multibar with Key_SIP"); INFO("compute best tree sequence for {} and input in {}", _schema_file, _input_file); // check options if (!(f_barsec || f_bars)) { ERROR("at least one of the options -bars or -barsec mandatory"); exit(2); } if (! f_timesig) { WARN("no time signature in commandline, default to {}", _ts); } assert(_schema); assert(_iseg); parsing::ParsingEnv penv = parsing::ParsingEnv(_schema, _iseg); assert(penv.segment); INFO("parsing segment [{}-{}], rdur={}", penv.segment->rbegin(), penv.segment->rend(), penv.segment->Interval::rduration()); INFO("fixed bar duration = {}s ({} bars)", _barsec, _bars); INFO("start parsing"); clock_t time_start = clock(); // initial augmented state // state_t init = _schema->initial(); // pre = 0 // post = Unknown (partial key) const parsing::KeySIP k0 = parsing::KeySIP(_barsec, penv, true, 0); // or post = 0 (complete key) // const parsing::KeySIP k0 = parsing::KeySIP(_barsec, penv, true, 0, 0); // flag f_max: ordering for enumeration (user option) parsing::TableA* tab = parse(k0, _nbest, f_max, penv); assert(tab); // fill table up to n-best tab->best(k0, _nbest); // const parsing::Run* r0 INFO("time to parse : {}ms", util::duration(time_start)); if (f_output && (_output_file.size() == 0)) { ERROR("empty output file name"); return 1; } /// @todo revise with score builder else if (f_output && f_out_mei) { INFO("Construct the symbolic score model from from the parse table"); VoicingMono* voicing = NULL; //= new VoicingMono(*(penv.segment)); // voicing->revoice_all(); // quantize first! //assert(voicing->voiced()); // pack Scoring Environment sm::ScoringEnv senv = sm::ScoringEnv(penv, voicing); string prefix = util::prefix(_output_file); assert(tab); sm::Score* score = buildSM(*tab, k0, _ts, prefix, "part1", senv); assert(score); INFO("Pitch Spelling in score: {}", score->id()); spellPitch(*score); INFO("Print the score model"); printSM(*score); INFO("export to MEI file {}", _output_file); writeMEI(*score, _output_file); if (score) delete score; if (voicing) delete voicing; } else if (f_output && f_out_midi) { INFO("export to MIDI file"); /// @todo MIDI export } else { INFO("DEBUG output:"); printTrees(*tab, k0, _nbest); } if (tab) delete tab; #elif QUANT_SCENARIO == QUANT_MULTIBAR_1BEST_SIR INFO("parser multibar with Key_SIR"); INFO("compute best tree sequence for {} and input in {}", _schema_file, _input_file); // check options if (!(f_barsec || f_bars)) { ERROR("at least one of the options -bars or -barsec mandatory"); exit(2); } if (f_norest) { ERROR("--norest option should not be used for rest processing with SIR."); } if (! f_timesig) { WARN("no time signature in commandline, default to {}", _ts); } INFO("{}-best computation", _nbest); // prepare parsing environment assert(_schema); assert(_iseg); parsing::ParsingEnv penv = parsing::ParsingEnv(_schema, _iseg); assert(penv.segment); INFO("parsing segment [{}-{}].rdur={}", penv.segment->rbegin(), penv.segment->rend(), penv.segment->Interval::rduration()); INFO("fixed bar duration = {}s ({} bars)", _barsec, _bars); INFO("start parsing"); clock_t time_start = clock(); // state_t init = _schema->initial(); // initial augmented state // pre = 0 (default) // post = Unknown (partial key) (default) const parsing::KeySIR k0 = parsing::KeySIR(_barsec, penv, true); // flag f_max: ordering for enumeration (user option) parsing::TableA* tab = parse(k0, _nbest, f_max, penv); tab->best(k0, _nbest); // fill table up to n-best INFO("time to parse : {}ms", util::duration(time_start)); if (f_output && (_output_file.size() == 0)) { ERROR("empty output file name"); return 1; } else if (f_output && f_out_mei) { INFO("Construct the symbolic score model from the parse table"); // no need of a voicing in monophonic case VoicingMono* voicing = NULL; //= new VoicingMono(*(penv.segment)); // voicing->revoice_all(); // quantize first! // assert(voicing->voiced()); // pack Scoring Environment sm::ScoringEnv senv = sm::ScoringEnv(penv, voicing); // construct score model string prefix = util::prefix(_output_file); // score name assert(tab); sm::Score* score = buildSM(*tab, k0, _ts, prefix, "part1", senv); assert(score); INFO("Pitch Spelling in score: {}", score->id()); spellPitch(*score); INFO("Print the score model"); printSM(*score); INFO("export to MEI file {}", _output_file); writeMEI(*score, _output_file); if (score) delete score; if (voicing) delete voicing; } else if (f_output && f_out_midi) { assert(tab); exportMIDI(*tab, k0, _barbeat, _input_file, _output_file, penv); } else // default output { INFO("DEBUG output:"); printTrees(*tab, k0, _nbest); } if (tab) delete tab; #elif QUANT_SCENARIO == QUANT_MULTIBAR_1BEST_SIO INFO("parser multibar with Key_SIO"); INFO("compute best tree sequence for {} and input in {}", _schema_file, _input_file); // check options if (!(f_barsec || f_bars)) { ERROR("at least one of the options -bars or -barsec mandatory"); exit(2); } if (f_norest) { ERROR("--norest option should not be used for rest processing with SIO."); } if (! f_timesig) { WARN("no time signature in commandline, default to {}", _ts); } INFO("{}-best computation", _nbest); // prepare parsing environment assert(_schema); assert(_iseg); parsing::ParsingEnv penv = parsing::ParsingEnv(_schema, _iseg); assert(penv.segment); INFO("parsing segment [{}-{}].rdur={}", penv.segment->rbegin(), penv.segment->rend(), penv.segment->Interval::rduration()); INFO("fixed bar duration = {}s ({} bars)", _barsec, _bars); INFO("start parsing"); clock_t time_start = clock(); // state_t init = _schema->initial(); // initial augmented state // pre = 0 (default) // post = Unknown (partial key) (default) const parsing::KeySIO k0 = parsing::KeySIO(_barsec, penv, true); // flag f_max: ordering for enumeration (user option) parsing::TableA* tab = parse(k0, _nbest, f_max, penv); tab->best(k0, _nbest); // fill table up to n-best INFO("time to parse : {}ms", util::duration(time_start)); if (f_output && (_output_file.size() == 0)) { ERROR("empty output file name"); return 1; } else if (f_output && f_out_mei) { INFO("Construct the symbolic score model from the parse table"); // no need of a voicing in monophonic case VoicingMono* voicing = NULL; //= new VoicingMono(*(penv.segment)); // voicing->revoice_all(); // quantize first! // assert(voicing->voiced()); // pack Scoring Environment sm::ScoringEnv senv = sm::ScoringEnv(penv, voicing); // construct score model string prefix = util::prefix(_output_file); // score name assert(tab); sm::Score* score = buildSM(*tab, k0, _ts, prefix, "part1", senv); assert(score); INFO("Pitch Spelling in score: {}", score->id()); spellPitch(*score); INFO("Print the score model"); printSM(*score); INFO("export to MEI file {}", _output_file); writeMEI(*score, _output_file); if (score) delete score; if (voicing) delete voicing; } else if (f_output && f_out_midi) { assert(tab); exportMIDI(*tab, k0, _barbeat, _input_file, _output_file, penv); } else // default output { INFO("DEBUG output:"); printTrees(*tab, k0, _nbest); } if (tab) delete tab; #else #error ERROR squant.cpp : invalid QUANT_SCENARIO #endif if (_schema) delete _schema; if (_iseg) delete _iseg; return(0); }