diff --git a/src/common/SurgePatch.cpp b/src/common/SurgePatch.cpp index cf816fc9ea9..33a5c9eb4a5 100644 --- a/src/common/SurgePatch.cpp +++ b/src/common/SurgePatch.cpp @@ -1651,6 +1651,36 @@ void SurgePatch::load_xml(const void* data, int datasize, bool is_preset) } } + p = TINYXML_SAFE_TO_ELEMENT(de->FirstChild("midictrl_map")); + if( p ) + { + auto c = TINYXML_SAFE_TO_ELEMENT(p->FirstChild( "c" ) ); + while( c ) + { + int p, v; + if( c->QueryIntAttribute( "p", &p ) == TIXML_SUCCESS && + c->QueryIntAttribute( "v", &v ) == TIXML_SUCCESS ) + { + dawExtraState.midictrl_map[p] = v; + } + c = TINYXML_SAFE_TO_ELEMENT(c->NextSibling( "c" ) ); + } + } + + p = TINYXML_SAFE_TO_ELEMENT(de->FirstChild("customcontrol_map")); + if( p ) + { + auto c = TINYXML_SAFE_TO_ELEMENT(p->FirstChild( "c" ) ); + while( c ) + { + int p, v; + if( c->QueryIntAttribute( "p", &p ) == TIXML_SUCCESS && + c->QueryIntAttribute( "v", &v ) == TIXML_SUCCESS ) + dawExtraState.customcontrol_map[p] = v; + c = TINYXML_SAFE_TO_ELEMENT(c->NextSibling( "c" ) ); + } + } + } } @@ -1922,6 +1952,29 @@ unsigned int SurgePatch::save_xml(void** data) // allocates mem, must be freed b mpc.SetAttribute("v", base64_encode( (unsigned const char *)dawExtraState.mappingContents.c_str(), dawExtraState.mappingContents.size() ).c_str() ); dawExtraXML.InsertEndChild(mpc); + + /* + ** Add the midi controls + */ + TiXmlElement mcm("midictrl_map"); + for( auto &p : dawExtraState.midictrl_map ) + { + TiXmlElement c("c"); + c.SetAttribute( "p", p.first ); + c.SetAttribute( "v", p.second ); + mcm.InsertEndChild(c); + } + dawExtraXML.InsertEndChild(mcm); + + TiXmlElement ccm("customcontrol_map"); + for( auto &p : dawExtraState.customcontrol_map ) + { + TiXmlElement c("c"); + c.SetAttribute( "p", p.first ); + c.SetAttribute( "v", p.second ); + ccm.InsertEndChild(c); + } + dawExtraXML.InsertEndChild(ccm); } patch.InsertEndChild(dawExtraXML); diff --git a/src/common/SurgeStorage.h b/src/common/SurgeStorage.h index 374c7a41456..190ac23d593 100644 --- a/src/common/SurgeStorage.h +++ b/src/common/SurgeStorage.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "Tunings.h" @@ -406,6 +407,9 @@ struct DAWExtraStateStorage bool hasMapping = false; std::string mappingContents = ""; + + std::unordered_map midictrl_map; // param -> midictrl + std::unordered_map customcontrol_map; // custom controller number -> midicontrol }; diff --git a/src/common/SurgeSynthesizer.cpp b/src/common/SurgeSynthesizer.cpp index 3222a4cd3c1..f5a427342ad 100644 --- a/src/common/SurgeSynthesizer.cpp +++ b/src/common/SurgeSynthesizer.cpp @@ -2859,3 +2859,102 @@ PluginLayer* SurgeSynthesizer::getParent() assert(_parent != nullptr); return _parent; } + +void SurgeSynthesizer::populateDawExtraState() { + storage.getPatch().dawExtraState.isPopulated = true; + storage.getPatch().dawExtraState.mpeEnabled = mpeEnabled; + storage.getPatch().dawExtraState.mpePitchBendRange = mpePitchBendRange; + + storage.getPatch().dawExtraState.hasTuning = !storage.isStandardTuning; + if( ! storage.isStandardTuning ) + storage.getPatch().dawExtraState.tuningContents = storage.currentScale.rawText; + else + storage.getPatch().dawExtraState.tuningContents = ""; + + storage.getPatch().dawExtraState.hasMapping = !storage.isStandardMapping; + if( ! storage.isStandardMapping ) + storage.getPatch().dawExtraState.mappingContents = storage.currentMapping.rawText; + else + storage.getPatch().dawExtraState.mappingContents = ""; + + int n = n_global_params + n_scene_params; // only store midictrl's for scene A (scene A -> scene + // B will be duplicated on load) + for (int i = 0; i < n; i++) + { + if (storage.getPatch().param_ptr[i]->midictrl >= 0) + { + storage.getPatch().dawExtraState.midictrl_map[i] = storage.getPatch().param_ptr[i]->midictrl; + } + } + + for (int i=0; i 0 ) + mpePitchBendRange = storage.getPatch().dawExtraState.mpePitchBendRange; + + if( storage.getPatch().dawExtraState.hasTuning ) + { + try { + auto sc = Tunings::parseSCLData(storage.getPatch().dawExtraState.tuningContents ); + storage.retuneToScale(sc); + } + catch( Tunings::TuningError &e ) + { + Surge::UserInteractions::promptError( e.what(), "Unable to restore tuning" ); + storage.retuneToStandardTuning(); + } + } + else + { + storage.retuneToStandardTuning(); + } + + if( storage.getPatch().dawExtraState.hasMapping ) + { + try + { + auto kb = Tunings::parseKBMData(storage.getPatch().dawExtraState.mappingContents ); + storage.remapToKeyboard(kb); + } + catch( Tunings::TuningError &e ) + { + Surge::UserInteractions::promptError( e.what(), "Unable to restore mapping" ); + storage.retuneToStandardTuning(); + } + + } + else + { + storage.remapToStandardKeyboard(); + } + + int n = n_global_params + n_scene_params; // only store midictrl's for scene A (scene A -> scene + // B will be duplicated on load) + for (int i = 0; i < n; i++) + { + if (storage.getPatch().dawExtraState.midictrl_map.find(i) != storage.getPatch().dawExtraState.midictrl_map.end() ) + { + storage.getPatch().param_ptr[i]->midictrl = storage.getPatch().dawExtraState.midictrl_map[i]; + if( i >= n_global_params ) + { + storage.getPatch().param_ptr[i + n_scene_params]->midictrl = storage.getPatch().dawExtraState.midictrl_map[i]; + } + } + } + + for (int i=0; i 0 ) - mpePitchBendRange = storage.getPatch().dawExtraState.mpePitchBendRange; - - if( storage.getPatch().dawExtraState.hasTuning ) - { - try { - auto sc = Tunings::parseSCLData(storage.getPatch().dawExtraState.tuningContents ); - storage.retuneToScale(sc); - } - catch( Tunings::TuningError &e ) - { - Surge::UserInteractions::promptError( e.what(), "Unable to restore tuning" ); - storage.retuneToStandardTuning(); - } - } - else - { - storage.retuneToStandardTuning(); - } - - if( storage.getPatch().dawExtraState.hasMapping ) - { - try - { - auto kb = Tunings::parseKBMData(storage.getPatch().dawExtraState.mappingContents ); - storage.remapToKeyboard(kb); - } - catch( Tunings::TuningError &e ) - { - Surge::UserInteractions::promptError( e.what(), "Unable to restore mapping" ); - storage.retuneToStandardTuning(); - } - - } - else - { - storage.remapToStandardKeyboard(); - } - } + void loadFromDawExtraState(); public: int CC0, CC32, PCH, patchid; diff --git a/src/headless/UnitTestsIO.cpp b/src/headless/UnitTestsIO.cpp index 78c287d7497..f6fe6d65b86 100644 --- a/src/headless/UnitTestsIO.cpp +++ b/src/headless/UnitTestsIO.cpp @@ -232,6 +232,82 @@ TEST_CASE( "DAW Streaming and Unstreaming", "[io][mpe][tun]" ) } + SECTION( "Save and Restore Param Midi Controls - Simple" ) + { + auto surgeSrc = Surge::Headless::createSurge(44100); + auto surgeDest = Surge::Headless::createSurge(44100); + + // Simplest case + surgeSrc->storage.getPatch().param_ptr[118]->midictrl = 57; + REQUIRE( surgeSrc->storage.getPatch().param_ptr[118]->midictrl == 57 ); + REQUIRE( surgeDest->storage.getPatch().param_ptr[118]->midictrl != 57 ); + + fromto( surgeSrc, surgeDest ); + REQUIRE( surgeSrc->storage.getPatch().param_ptr[118]->midictrl == 57 ); + REQUIRE( surgeDest->storage.getPatch().param_ptr[118]->midictrl == 57 ); + } + + SECTION( "Save and Restore Param Midi Controls - Empty" ) + { + auto surgeSrc = Surge::Headless::createSurge(44100); + auto surgeDest = Surge::Headless::createSurge(44100); + + fromto( surgeSrc, surgeDest ); + for( int i=0; istorage.getPatch().param_ptr[i]->midictrl == + surgeDest->storage.getPatch().param_ptr[i]->midictrl ); + REQUIRE( surgeSrc->storage.getPatch().param_ptr[i]->midictrl == -1 ); + } + } + + SECTION( "Save and Restore Param Midi Controls - Multi" ) + { + auto surgeSrc = Surge::Headless::createSurge(44100); + auto surgeDest = Surge::Headless::createSurge(44100); + + // Bigger Case + surgeSrc->storage.getPatch().param_ptr[118]->midictrl = 57; + surgeSrc->storage.getPatch().param_ptr[123]->midictrl = 59; + surgeSrc->storage.getPatch().param_ptr[172]->midictrl = 82; + REQUIRE( surgeSrc->storage.getPatch().param_ptr[118]->midictrl == 57 ); + REQUIRE( surgeSrc->storage.getPatch().param_ptr[123]->midictrl == 59 ); + REQUIRE( surgeSrc->storage.getPatch().param_ptr[172]->midictrl == 82 ); + REQUIRE( surgeDest->storage.getPatch().param_ptr[118]->midictrl != 57 ); + REQUIRE( surgeDest->storage.getPatch().param_ptr[123]->midictrl != 59 ); + REQUIRE( surgeDest->storage.getPatch().param_ptr[172]->midictrl != 82 ); + + fromto( surgeSrc, surgeDest ); + REQUIRE( surgeSrc->storage.getPatch().param_ptr[118]->midictrl == 57 ); + REQUIRE( surgeSrc->storage.getPatch().param_ptr[123]->midictrl == 59 ); + REQUIRE( surgeSrc->storage.getPatch().param_ptr[172]->midictrl == 82 ); + REQUIRE( surgeDest->storage.getPatch().param_ptr[118]->midictrl == 57 ); + REQUIRE( surgeDest->storage.getPatch().param_ptr[123]->midictrl == 59 ); + REQUIRE( surgeDest->storage.getPatch().param_ptr[172]->midictrl == 82 ); + } + + SECTION( "Save and Restore Custom Controllers" ) + { + auto surgeSrc = Surge::Headless::createSurge(44100); + auto surgeDest = Surge::Headless::createSurge(44100); + + for( int i=0; istorage.controllers[i] == 41 + i ); + REQUIRE( surgeDest->storage.controllers[i] == 41 + i ); + } + + surgeSrc->storage.controllers[2] = 75; + surgeSrc->storage.controllers[4] = 79; + fromto(surgeSrc, surgeDest ); + for( int i=0; istorage.controllers[i] == surgeDest->storage.controllers[i] ); + } + REQUIRE( surgeDest->storage.controllers[2] == 75 ); + REQUIRE( surgeDest->storage.controllers[4] == 79 ); + } + } TEST_CASE( "Stream WaveTable Names", "[io]" )