Rewrite save/load state via JSON.
Remember to update GUI after state changed.
This commit is contained in:
parent
c75d2a60dd
commit
25dd5a5707
2 changed files with 44 additions and 37 deletions
|
@ -4,6 +4,8 @@
|
||||||
#include "IntFloatParameter.h"
|
#include "IntFloatParameter.h"
|
||||||
#include "SbiLoader.h"
|
#include "SbiLoader.h"
|
||||||
|
|
||||||
|
const char *JuceOplvstiAudioProcessor::PROGRAM_INDEX = "Program Index";
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
JuceOplvstiAudioProcessor::JuceOplvstiAudioProcessor()
|
JuceOplvstiAudioProcessor::JuceOplvstiAudioProcessor()
|
||||||
: i_program(-1)
|
: i_program(-1)
|
||||||
|
@ -739,62 +741,67 @@ AudioProcessorEditor* JuceOplvstiAudioProcessor::createEditor()
|
||||||
return gui;
|
return gui;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: these two funcs and get-/setStateInformation should be replaced by a proper cross-platform archiving API, e.g. eos portable archive or boost text archive
|
|
||||||
template<class T>
|
|
||||||
void push(char *&cursor, T value)
|
|
||||||
{
|
|
||||||
*reinterpret_cast<T *>(cursor)=value;
|
|
||||||
cursor+=sizeof(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void pop(const char *&cursor, T &value)
|
|
||||||
{
|
|
||||||
value=*reinterpret_cast<const T *>(cursor);
|
|
||||||
cursor+=sizeof(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void JuceOplvstiAudioProcessor::getStateInformation (MemoryBlock& destData)
|
|
||||||
|
// JUCE requires that JSON names are valid Identifiers (restricted character set) even though the JSON standard allows any valid string.
|
||||||
|
// Per http://www.juce.com/forum/topic/problem-spaces-json-identifier jules allowed Strings to bypass the assertion check
|
||||||
|
// but a regression occured in 4317f60 re-enabling this check, so we need to sanitize the names.
|
||||||
|
// Technically, the code still works without this, but it will trigger a gargantuan number of assertions hindering effective debugging.
|
||||||
|
Identifier stringToIdentifier(const String &s)
|
||||||
{
|
{
|
||||||
destData.ensureSize(sizeof(CURRENT_VERSION)+sizeof(i_program)+sizeof(float) * getNumParameters());
|
return s.replaceCharacters(" ", "_");
|
||||||
|
}
|
||||||
|
|
||||||
char *cursor = static_cast<char *>(destData.getData());
|
void JuceOplvstiAudioProcessor::getStateInformation(MemoryBlock& destData)
|
||||||
|
{
|
||||||
|
ReferenceCountedObjectPtr<DynamicObject> v(new DynamicObject);
|
||||||
|
|
||||||
push(cursor, CURRENT_VERSION);
|
v->setProperty(stringToIdentifier(PROGRAM_INDEX), i_program);
|
||||||
push(cursor, i_program);
|
|
||||||
|
|
||||||
for (int i = 0; i < getNumParameters(); i++) {
|
for (int i = 0; i < getNumParameters(); i++) {
|
||||||
float p = getParameter(i);
|
double p = getParameter(i);
|
||||||
push(cursor, p);
|
|
||||||
|
v->setProperty(stringToIdentifier(getParameterName(i)), p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String s = JSON::toString(v.get());
|
||||||
|
|
||||||
|
destData.setSize(s.length());
|
||||||
|
destData.copyFrom(s.getCharPointer(), 0, destData.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
void JuceOplvstiAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
|
void JuceOplvstiAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
|
||||||
{
|
{
|
||||||
if (sizeInBytes < sizeof(CURRENT_VERSION))
|
if (sizeInBytes < 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const char *cursor = static_cast<const char *>(data);
|
const char *first = static_cast<const char *>(data);
|
||||||
const char *end = cursor+sizeInBytes;
|
const char *last = first + sizeInBytes - 1;
|
||||||
int version;
|
|
||||||
|
|
||||||
pop(cursor, version);
|
|
||||||
|
|
||||||
if (version == CURRENT_VERSION)
|
// simple check for JSON data - assume always object
|
||||||
|
if (*first=='{' && *last=='}')
|
||||||
{
|
{
|
||||||
pop(cursor, i_program);
|
// json format
|
||||||
|
String s(first, sizeInBytes);
|
||||||
|
var v = JSON::fromString(s);
|
||||||
|
|
||||||
const int parametersToLoad = std::min<int>(static_cast<int>(std::distance(cursor, end)) / sizeof(float), getNumParameters());
|
|
||||||
|
|
||||||
for (int i = 0; i < parametersToLoad; i++)
|
|
||||||
{
|
{
|
||||||
float p;
|
var program = v[stringToIdentifier(PROGRAM_INDEX)];
|
||||||
|
|
||||||
pop(cursor, p);
|
if (!program.isVoid())
|
||||||
setParameter(i, p);
|
i_program = program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<getNumParameters(); ++i)
|
||||||
|
{
|
||||||
|
var param = v[stringToIdentifier(getParameterName(i))];
|
||||||
|
|
||||||
|
if (!param.isVoid())
|
||||||
|
setParameter(i, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGuiIfPresent();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ private:
|
||||||
int i_program;
|
int i_program;
|
||||||
bool velocity;
|
bool velocity;
|
||||||
static const int NO_NOTE=-1;
|
static const int NO_NOTE=-1;
|
||||||
static const int CURRENT_VERSION = 0x09720100; // OPL2 v01.00 magic
|
static const char *PROGRAM_INDEX;
|
||||||
int active_notes[Hiopl::CHANNELS+1]; // keyed by channel
|
int active_notes[Hiopl::CHANNELS+1]; // keyed by channel
|
||||||
std::deque<int> available_channels; // most recently freed at end
|
std::deque<int> available_channels; // most recently freed at end
|
||||||
std::deque<int> used_channels; // most recently used at end
|
std::deque<int> used_channels; // most recently used at end
|
||||||
|
|
Loading…
Reference in a new issue