//*********************************************************************************************** //MidiFile module for VCV Rack by Marc Boulé // //Based on code from the Fundamental and AudibleInstruments plugins by Andrew Belt //and graphics from the Component Library by Wes Milholen //Also based on Midifile, a C++ MIDI file parsing library by Craig Stuart Sapp //See ./LICENSE.txt for all licenses //See ./res/fonts/ for font licenses // //Module concept by Marc Boulé //*********************************************************************************************** /* temporary notes https://github.com/craigsapp/midifile Dekstop (callback mechanism and file opening): https://github.com/dekstop/vcvrackplugins_dekstop/blob/master/src/Recorder.cpp VCVRack-Simple (file opening): https://github.com/IohannRabeson/VCVRack-Simple/commit/2d33e97d2e344d2926548a0b9f11f1c15ee4ca3c */ #include "ImpromptuModular.hpp" #include "midifile/MidiFile.h" #include "osdialog.h" #include using namespace std; using namespace smf; //***************************************************************************** namespace rack_plugin_ImpromptuModular { struct MidiFileModule : Module { enum ParamIds { LOADMIDI_PARAM, NUM_PARAMS }; enum InputIds { NUM_INPUTS }; enum OutputIds { NUM_OUTPUTS }; enum LightIds { ENUMS(LOADMIDI_LIGHT, 2), NUM_LIGHTS }; // Need to save, with reset // none // Need to save, no reset int panelTheme; string lastPath;// TODO: save also the filename so that it can automatically be reloaded when Rack starts? // No need to save, with reset // none // No need to save, no reset MidiFile midifile; bool fileLoaded; MidiFileModule() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { // Need to save, no reset panelTheme = 0; lastPath = ""; // No need to save, no reset fileLoaded = false; onReset(); } // widgets are not yet created when module is created (and when onReset() is called by constructor) // onReset() is also called when right-click initialization of module void onReset() override { } // widgets randomized before onRandomize() is called void onRandomize() override { } json_t *toJson() override { json_t *rootJ = json_object(); // TODO // Need to save (reset or not) return rootJ; } // widgets loaded before this fromJson() is called void fromJson(json_t *rootJ) override { // TODO // Need to save (reset or not) // No need to save, with reset // none } // Advances the module by 1 audio frame with duration 1.0 / engineGetSampleRate() void step() override { lights[LOADMIDI_LIGHT + 0].value = fileLoaded ? 1.0f : 0.0f; lights[LOADMIDI_LIGHT + 1].value = !fileLoaded ? 1.0f : 0.0f; }// step() void loadMidiFile() { osdialog_filters *filters = osdialog_filters_parse("Midi File (.mid):mid;Text File (.txt):txt"); string dir = lastPath.empty() ? assetLocal("") : stringDirectory(lastPath); char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters); if (path) { lastPath = path; //lastFilename = stringFilename(path); if (midifile.read(path)) { fileLoaded = true; midifile.doTimeAnalysis(); midifile.linkNotePairs(); int tracks = midifile.getTrackCount(); cout << "TPQ: " << midifile.getTicksPerQuarterNote() << endl; if (tracks > 1) cout << "TRACKS: " << tracks << endl; for (int track=0; track 1) cout << "\nTrack " << track << endl; cout << "Tick\tSeconds\tDur\tMessage" << endl; for (int event=0; event 0.0 && moduleL != nullptr) { moduleL->loadMidiFile(); } IMBigPushButton::onChange(e); } }; MidiFileWidget(MidiFileModule *module) : ModuleWidget(module) { // Main panel from Inkscape DynamicSVGPanel* panel = new DynamicSVGPanel(); panel->mode = &module->panelTheme; panel->addPanel(SVG::load(assetPlugin(plugin, "res/light/MidiFile.svg"))); //panel->addPanel(SVG::load(assetPlugin(plugin, "res/dark/MidiFile_dark.svg"))); box.size = panel->box.size; addChild(panel); // Screws addChild(createDynamicScrew(Vec(15, 0), &module->panelTheme)); addChild(createDynamicScrew(Vec(15, 365), &module->panelTheme)); addChild(createDynamicScrew(Vec(panel->box.size.x-30, 0), &module->panelTheme)); addChild(createDynamicScrew(Vec(panel->box.size.x-30, 365), &module->panelTheme)); // main load button LoadMidiPushButton* midiButton = createDynamicParam(Vec(100, 100), module, MidiFileModule::LOADMIDI_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme); midiButton->moduleL = module; addParam(midiButton); // load light addChild(ModuleLightWidget::create>(Vec(100, 200), module, MidiFileModule::LOADMIDI_LIGHT + 0)); } }; } // namespace rack_plugin_ImpromptuModular using namespace rack_plugin_ImpromptuModular; RACK_PLUGIN_MODEL_INIT(ImpromptuModular, MidiFile) { Model *modelMidiFile = Model::create("Impromptu Modular", "Midi-File", "UTIL - Midi-File", MIDI_TAG); return modelMidiFile; } /*CHANGE LOG 0.6.10: created */