Skip to content

Commit

Permalink
Add KBM and MPE PitchBend DAW streaming (surge-synthesizer#1482)
Browse files Browse the repository at this point in the history
Stream both the KBM and the mpePitchBend into the "DAW Extra" chunk which
we persist away from parameters. Test streaming both directions in the unit
tests. MPE needs some menu item fixing to be fully done but is streamed internally.

Along the way, refactor the unit tests into separate files so as not to
be as unwieldy

Addresses surge-synthesizer#1355
Addresses surge-synthesizer#1041
  • Loading branch information
baconpaul authored Jan 15, 2020
1 parent 4f1e6a0 commit 454c455
Show file tree
Hide file tree
Showing 19 changed files with 1,796 additions and 1,612 deletions.
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,13 @@ add_executable(surge-headless
src/headless/LinkFixesHeadless.cpp
src/headless/HeadlessUtils.cpp
src/headless/Player.cpp
src/headless/Stress.cpp
src/headless/UnitTests.cpp
src/headless/UnitTestUtilities.cpp
src/headless/UnitTestsDSP.cpp
src/headless/UnitTestsIO.cpp
src/headless/UnitTestsMIDI.cpp
src/headless/UnitTestsMOD.cpp
src/headless/UnitTestsTUN.cpp
)

target_compile_features(surge-headless
Expand Down
42 changes: 42 additions & 0 deletions src/common/SurgePatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1437,6 +1437,12 @@ void SurgePatch::load_xml(const void* data, int datasize, bool is_preset)
p->QueryIntAttribute("v",&ival) == TIXML_SUCCESS)
dawExtraState.mpeEnabled = ival;

p = TINYXML_SAFE_TO_ELEMENT(de->FirstChild("mpePitchBendRange"));
if( p &&
p->QueryIntAttribute("v",&ival) == TIXML_SUCCESS)
dawExtraState.mpePitchBendRange = ival;


p = TINYXML_SAFE_TO_ELEMENT(de->FirstChild("hasTuning"));
if( p &&
p->QueryIntAttribute("v",&ival) == TIXML_SUCCESS)
Expand All @@ -1456,6 +1462,24 @@ void SurgePatch::load_xml(const void* data, int datasize, bool is_preset)
}
}

p = TINYXML_SAFE_TO_ELEMENT(de->FirstChild("hasMapping"));
if( p &&
p->QueryIntAttribute("v",&ival) == TIXML_SUCCESS)
{
dawExtraState.hasMapping = (ival != 0);
}

if( dawExtraState.hasMapping )
{
p = TINYXML_SAFE_TO_ELEMENT(de->FirstChild("mappingContents"));
if( p &&
(td = p->Attribute("v") ))
{
auto tc = base64_decode(td);
dawExtraState.mappingContents = tc;
}
}

}
}

Expand Down Expand Up @@ -1672,6 +1696,10 @@ unsigned int SurgePatch::save_xml(void** data) // allocates mem, must be freed b
mpe.SetAttribute("v", dawExtraState.mpeEnabled ? 1 : 0 );
dawExtraXML.InsertEndChild(mpe);

TiXmlElement mppb("mpePitchBendRange");
mppb.SetAttribute("v", dawExtraState.mpePitchBendRange );
dawExtraXML.InsertEndChild(mppb);

TiXmlElement tun("hasTuning");
tun.SetAttribute("v", dawExtraState.hasTuning ? 1 : 0 );
dawExtraXML.InsertEndChild(tun);
Expand All @@ -1685,6 +1713,20 @@ unsigned int SurgePatch::save_xml(void** data) // allocates mem, must be freed b
tnc.SetAttribute("v", base64_encode( (unsigned const char *)dawExtraState.tuningContents.c_str(),
dawExtraState.tuningContents.size() ).c_str() );
dawExtraXML.InsertEndChild(tnc);

TiXmlElement hmp("hasMapping");
hmp.SetAttribute("v", dawExtraState.hasMapping ? 1 : 0 );
dawExtraXML.InsertEndChild(hmp);

/*
** we really want a cdata here but TIXML is ambiguous whether
** it does the right thing when I read the code, and is kinda crufty
** so just protect ourselves with a base 64 encoding.
*/
TiXmlElement mpc("mappingContents");
mpc.SetAttribute("v", base64_encode( (unsigned const char *)dawExtraState.mappingContents.c_str(),
dawExtraState.mappingContents.size() ).c_str() );
dawExtraXML.InsertEndChild(mpc);
}
patch.InsertEndChild(dawExtraXML);

Expand Down
7 changes: 7 additions & 0 deletions src/common/SurgeStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,13 @@ struct DAWExtraStateStorage

int instanceZoomFactor = -1;
bool mpeEnabled = false;
int mpePitchBendRange = -1;

bool hasTuning = false;
std::string tuningContents = "";

bool hasMapping = false;
std::string mappingContents = "";
};


Expand Down Expand Up @@ -608,6 +613,8 @@ class alignas(16) SurgeStorage
void note_to_omega(float, float&, float&);

bool retuneToScale(const Surge::Storage::Scale& s);
bool retuneToStandardTuning() { init_tables(); return true; }

bool remapToKeyboard(const Surge::Storage::KeyboardMapping &k);
bool remapToStandardKeyboard();
inline int scaleConstantNote() { return currentMapping.tuningConstantNote; }
Expand Down
26 changes: 26 additions & 0 deletions src/common/SurgeSynthesizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,21 +176,47 @@ class alignas(16) SurgeSynthesizer
void 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 = "";
}

void loadFromDawExtraState() {
if( ! storage.getPatch().dawExtraState.isPopulated )
return;
mpeEnabled = storage.getPatch().dawExtraState.mpeEnabled;
if( storage.getPatch().dawExtraState.mpePitchBendRange > 0 )
mpePitchBendRange = storage.getPatch().dawExtraState.mpePitchBendRange;

if( storage.getPatch().dawExtraState.hasTuning )
{
auto sc = Surge::Storage::parseSCLData(storage.getPatch().dawExtraState.tuningContents );
storage.retuneToScale(sc);
}
else
{
storage.retuneToStandardTuning();
}

if( storage.getPatch().dawExtraState.hasMapping )
{
auto kb = Surge::Storage::parseKBMData(storage.getPatch().dawExtraState.mappingContents );
storage.remapToKeyboard(kb);
}
else
{
storage.remapToStandardKeyboard();
}
}

public:
Expand Down
4 changes: 2 additions & 2 deletions src/headless/HeadlessUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ namespace Surge
namespace Headless
{
static std::unique_ptr<HeadlessPluginLayerProxy> parent = nullptr;
SurgeSynthesizer* createSurge(int sr)
std::shared_ptr<SurgeSynthesizer> createSurge(int sr)
{
if (parent.get()==nullptr)
parent.reset(new HeadlessPluginLayerProxy());
SurgeSynthesizer* surge = new SurgeSynthesizer(parent.get());
auto surge = std::shared_ptr<SurgeSynthesizer>(new SurgeSynthesizer( parent.get() ) );
surge->setSamplerate(sr);
surge->time_data.tempo = 120;
surge->time_data.ppqPos = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/headless/HeadlessUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Surge
namespace Headless
{

SurgeSynthesizer* createSurge(int sr);
std::shared_ptr<SurgeSynthesizer> createSurge(int sr);

void writeToStream(const float* data, int nSamples, int nChannels, std::ostream& str);
void writeToWav(const float* data, int nSamples, int nChannels, float sampleRate, std::string wavFileName);
Expand Down
12 changes: 6 additions & 6 deletions src/headless/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ playerEvents_t make120BPMCMajorQuarterNoteScale(long s0, int sr)
return result;
}

void playAsConfigured(SurgeSynthesizer* surge,
void playAsConfigured(std::shared_ptr<SurgeSynthesizer> surge,
const playerEvents_t& events,
float** data,
int* nSamples,
Expand Down Expand Up @@ -122,7 +122,7 @@ void playAsConfigured(SurgeSynthesizer* surge,
}
}

void playOnPatch(SurgeSynthesizer* surge,
void playOnPatch(std::shared_ptr<SurgeSynthesizer> surge,
int patch,
const playerEvents_t& events,
float** data,
Expand All @@ -134,7 +134,7 @@ void playOnPatch(SurgeSynthesizer* surge,
}

void playOnEveryPatch(
SurgeSynthesizer* surge,
std::shared_ptr<SurgeSynthesizer> surge,
const playerEvents_t& events,
std::function<void(
const Patch& p, const PatchCategory& c, const float* data, int nSamples, int nChannels)> cb)
Expand Down Expand Up @@ -166,7 +166,7 @@ void playOnEveryPatch(
}

void playOnNRandomPatches(
SurgeSynthesizer* surge,
std::shared_ptr<SurgeSynthesizer> surge,
const playerEvents_t& events,
int nPlays,
std::function<void(
Expand All @@ -192,7 +192,7 @@ void playOnNRandomPatches(
}
}

void playMidiFile(SurgeSynthesizer* synth,
void playMidiFile(std::shared_ptr<SurgeSynthesizer> synth,
std::string midiFileName,
long callBackEvery,
std::function<void(float* data, int nSamples, int nChannels)> dataCB)
Expand Down Expand Up @@ -262,7 +262,7 @@ void playMidiFile(SurgeSynthesizer* synth,
#endif
}

void renderMidiFileToWav(SurgeSynthesizer* surge,
void renderMidiFileToWav(std::shared_ptr<SurgeSynthesizer> surge,
std::string midiFileName,
std::string outputWavFile)
{
Expand Down
12 changes: 6 additions & 6 deletions src/headless/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ playerEvents_t make120BPMCMajorQuarterNoteScale(long sample0 = 0, int sr = 44100
*
* given a surge, play the events from first to last accumulating the result in the audiodata
*/
void playAsConfigured(SurgeSynthesizer* synth,
void playAsConfigured(std::shared_ptr<SurgeSynthesizer> synth,
const playerEvents_t& events,
float** resultData,
int* nSamples,
Expand All @@ -69,7 +69,7 @@ void playAsConfigured(SurgeSynthesizer* synth,
* given a surge and a patch, play the events accumulating the data. This is a convenience
* for loadpatch / playAsConfigured
*/
void playOnPatch(SurgeSynthesizer* synth,
void playOnPatch(std::shared_ptr<SurgeSynthesizer> synth,
int patch,
const playerEvents_t& events,
float** resultData,
Expand All @@ -83,7 +83,7 @@ void playOnPatch(SurgeSynthesizer* synth,
* the result.
*/
void playOnEveryPatch(
SurgeSynthesizer* synth,
std::shared_ptr<SurgeSynthesizer> synth,
const playerEvents_t& events,
std::function<void(
const Patch& p, const PatchCategory& c, const float* data, int nSamples, int nChannels)>
Expand All @@ -96,7 +96,7 @@ void playOnEveryPatch(
* the result.
*/
void playOnNRandomPatches(
SurgeSynthesizer* synth,
std::shared_ptr<SurgeSynthesizer> synth,
const playerEvents_t& events,
int nPlays,
std::function<void(
Expand All @@ -110,7 +110,7 @@ void playOnNRandomPatches(
* configuration of the synth. Rather than generate a mass of data, this calls you
* back with a pointer to the data every (n) samples
*/
void playMidiFile(SurgeSynthesizer* synth,
void playMidiFile(std::shared_ptr<SurgeSynthesizer> synth,
std::string midiFileName,
long callBackEvery,
std::function<void(float* data, int nSamples, int nChannels)> dataCB);
Expand All @@ -121,7 +121,7 @@ void playMidiFile(SurgeSynthesizer* synth,
* Given a surge synthesizer and MidiFile name, create a Wav file which results
* from playing that midi file.
*/
void renderMidiFileToWav(SurgeSynthesizer* synth,
void renderMidiFileToWav(std::shared_ptr<SurgeSynthesizer> synth,
std::string midiFileName,
std::string outputWavFile);

Expand Down
96 changes: 0 additions & 96 deletions src/headless/Stress.cpp

This file was deleted.

Loading

0 comments on commit 454c455

Please sign in to comment.