fix choice of preset via plugin host (safely updates table from non-message thread)

This commit is contained in:
Alex Birch 2019-07-30 21:36:54 +01:00
parent ba6bec2d9d
commit 7dd9bb4c22
No known key found for this signature in database
GPG Key ID: 305EB1F98D44ACBA
3 changed files with 37 additions and 254 deletions

View File

@ -417,44 +417,39 @@ void FluidSynthModel::setCurrentProgram(int index)
RangedAudioParameter *param{valueTreeState.getParameter("preset")}; RangedAudioParameter *param{valueTreeState.getParameter("preset")};
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr); jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)}; AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
// setCurrentProgram() gets invoked from non-message thread.
// AudioParameterInt#operator= will activate any listeners of audio parameter "preset".
// This includes TableComponent, who will update its UI.
// we need to lock the message thread whilst it does that UI update.
const MessageManagerLock mmLock;
*castParam = index; *castParam = index;
} }
const String FluidSynthModel::getProgramName(int index) const String FluidSynthModel::getProgramName(int index)
{ {
// fluid_sfont_t* sfont{ fluid_sfont_t* sfont{
// sfont_id == -1 sfont_id == -1
// ? nullptr ? nullptr
// : fluid_synth_get_sfont_by_id(synth.get(), sfont_id) : fluid_synth_get_sfont_by_id(synth.get(), sfont_id)
// }; };
// if (!sfont) { if (!sfont) {
// return {}; String presetName{"Preset "};
// } return presetName << index;
// int bank, presetNum; }
// { RangedAudioParameter *param{valueTreeState.getParameter("bank")};
// RangedAudioParameter *param {valueTreeState.getParameter("bank")}; jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
// jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr); AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
// AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)}; int bank{castParam->get()};
// bank = castParam->get();
// }
// {
// RangedAudioParameter *param {valueTreeState.getParameter("preset")};
// jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
// AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
// presetNum = castParam->get();
// }
// fluid_preset_t *preset{fluid_sfont_get_preset(
// sfont,
// bank,
// presetNum)};
// if (!preset) {
// return {};
// }
// return {fluid_preset_get_name(preset)};
// I think the presets' names will be collected only at synth startup, so we won't yet have loaded the soundfont. fluid_preset_t *preset{fluid_sfont_get_preset(
String presetName{"Preset "}; sfont,
return presetName << index; bank,
index)};
if (!preset) {
String presetName{"Preset "};
return presetName << index;
}
return {fluid_preset_get_name(preset)};
} }
void FluidSynthModel::changeProgramName(int index, const String& newName) void FluidSynthModel::changeProgramName(int index, const String& newName)

View File

@ -215,30 +215,17 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData)
// Create an outer XML element.. // Create an outer XML element..
XmlElement xml{"MYPLUGINSETTINGS"}; XmlElement xml{"MYPLUGINSETTINGS"};
// sharedParams->setAttributesOnXml(xml);
// auto state{valueTreeState.copyState()};
// unique_ptr<XmlElement> xml{state.createXml()};
// sharedParams.setAttributesOnXml(xml);
// list<StateChangeSubscriber*>::iterator p;
// for(p = stateChangeSubscribers.begin(); p != stateChangeSubscribers.end(); p++) {
// (*p)->getStateInformation(xml);
// }
// Store the values of all our parameters, using their param ID as the XML attribute // Store the values of all our parameters, using their param ID as the XML attribute
XmlElement* params{xml.createNewChildElement("params")}; XmlElement* params{xml.createNewChildElement("params")};
for (auto* param : getParameters()) { for (auto* param : getParameters()) {
if (auto* p = dynamic_cast<AudioProcessorParameterWithID*> (param)) { if (auto* p = dynamic_cast<AudioProcessorParameterWithID*> (param)) {
// xml.setAttribute(p->paramID, p->getValue());
// XmlElement* param{params->createNewChildElement("PARAM")};
// param->setAttribute(p->paramID, p->getValue());
params->setAttribute(p->paramID, p->getValue()); params->setAttribute(p->paramID, p->getValue());
} }
} }
{ {
ValueTree tree{valueTreeState.state.getChildWithName("uiState")}; ValueTree tree{valueTreeState.state.getChildWithName("uiState")};
XmlElement* newElement{xml.createNewChildElement("uiState")}; XmlElement* newElement{xml.createNewChildElement("uiState")};
// Value value{tree.getPropertyAsValue("width", nullptr)};
{ {
double value{tree.getProperty("width", GuiConstants::minWidth)}; double value{tree.getProperty("width", GuiConstants::minWidth)};
newElement->setAttribute("width", value); newElement->setAttribute("width", value);
@ -259,10 +246,6 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData)
DEBUG_PRINT(xml.createDocument("",false,false)); DEBUG_PRINT(xml.createDocument("",false,false));
// then use this helper function to stuff it into the binary blob and return it..
// if (xml.get() != nullptr) {
// copyXmlToBinary(*xml, destData);
// }
copyXmlToBinary(xml, destData); copyXmlToBinary(xml, destData);
} }
@ -272,54 +255,24 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt
// whose contents will have been created by the getStateInformation() call. // whose contents will have been created by the getStateInformation() call.
// This getXmlFromBinary() helper function retrieves our XML from the binary blob.. // This getXmlFromBinary() helper function retrieves our XML from the binary blob..
shared_ptr<XmlElement> xmlState{getXmlFromBinary(data, sizeInBytes)}; shared_ptr<XmlElement> xmlState{getXmlFromBinary(data, sizeInBytes)};
// unique_ptr<XmlElement> xmlState{getXmlFromBinary(data, sizeInBytes)};
DEBUG_PRINT(xmlState->createDocument("",false,false)); DEBUG_PRINT(xmlState->createDocument("",false,false));
/*
<MYPLUGINSETTINGS soundFontPath="">
<PARAM id="attack" value="0.0"/>
<PARAM id="bank" value="0.0"/>
<PARAM id="decay" value="0.0"/>
<PARAM id="filterCutOff" value="0.0"/>
<PARAM id="filterResonance" value="0.0"/>
<PARAM id="preset" value="0.0"/>
<PARAM id="release" value="0.0"/>
<PARAM id="sustain" value="0.0"/>
<uiState width="722" height="300"/>
</MYPLUGINSETTINGS>
*/
if (xmlState.get() != nullptr) { if (xmlState.get() != nullptr) {
// make sure that it's actually our type of XML object.. // make sure that it's actually our type of XML object..
// if (xmlState->hasTagName ("MYPLUGINSETTINGS")) {
if (xmlState->hasTagName(valueTreeState.state.getType())) { if (xmlState->hasTagName(valueTreeState.state.getType())) {
// valueTreeState.replaceState(ValueTree::fromXml(*xmlState));
// for (auto* param : getParameters())
// if (auto* p = dynamic_cast<AudioProcessorParameterWithID*>(param))
// p->setValue(static_cast<float>(xmlState->getDoubleAttribute(p->paramID, p->getValue())));
XmlElement* params{xmlState->getChildByName("params")}; XmlElement* params{xmlState->getChildByName("params")};
if (params) { if (params)
for (auto* param : getParameters()) for (auto* param : getParameters())
if (auto* p = dynamic_cast<AudioProcessorParameterWithID*>(param)) if (auto* p = dynamic_cast<AudioProcessorParameterWithID*>(param))
// XmlElement* xmlParam{params->getChildByAttribute("id", p->paramID)};
// p->setValue(static_cast<float>(xmlState->getDoubleAttribute(p->paramID, p->getValue())));
p->setValue(static_cast<float>(params->getDoubleAttribute(p->paramID, p->getValue()))); p->setValue(static_cast<float>(params->getDoubleAttribute(p->paramID, p->getValue())));
}
{ {
// Value value{valueTreeState.state.getPropertyAsValue("soundFontPath", nullptr)};
// value = xmlState->getStringAttribute("soundFontPath", value.getValue());
XmlElement* xmlElement{xmlState->getChildByName("soundFont")}; XmlElement* xmlElement{xmlState->getChildByName("soundFont")};
if (xmlElement) { if (xmlElement) {
ValueTree tree{valueTreeState.state.getChildWithName("soundFont")}; ValueTree tree{valueTreeState.state.getChildWithName("soundFont")};
Value value{tree.getPropertyAsValue("path", nullptr)}; Value value{tree.getPropertyAsValue("path", nullptr)};
value = xmlElement->getStringAttribute("path", value.getValue()); value = xmlElement->getStringAttribute("path", value.getValue());
} }
// valueTreeState.getParameter("soundFontPath")->getValue()
// valueTreeState.getParameter("soundFontPath")->getValue();
// RangedAudioParameter *param {valueTreeState.getParameter("release")};
// jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
// AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
// *castParam = m.getControllerValue();
} }
{ {
ValueTree tree{valueTreeState.state.getChildWithName("uiState")}; ValueTree tree{valueTreeState.state.getChildWithName("uiState")};
@ -334,47 +287,7 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt
value = xmlElement->getIntAttribute("height", value.getValue()); value = xmlElement->getIntAttribute("height", value.getValue());
} }
} }
// tree.getPropertyAsValue("width", nullptr)
// tree.
// valueTreeState.replaceState(ValueTree::fromXml(*xmlState))
// value = xmlState->getStringAttribute("soundFontPath", value.getValue());
} }
// list<StateChangeSubscriber*>::iterator p;
// for(p = stateChangeSubscribers.begin(); p != stateChangeSubscribers.end(); p++) {
// (*p)->setStateInformation(xmlState);
// }
// ok, now pull out our last window size..
// sharedParams.loadAttributesFromXml(xmlState);
// Now reload our parameters..
// for (auto* param : getParameters())
// if (auto* p = dynamic_cast<AudioProcessorParameterWithID*> (param))
// p->setValue ((float) xmlState->getDoubleAttribute (p->paramID, p->getValue()));
//
// fluidSynthModel.onFileNameChanged(
// sharedParams->getSoundFontPath(),
// sharedParams->getBank(),
// sharedParams->getPreset());
//
// AudioProcessorEditor* editor{getActiveEditor()};
// if (editor != nullptr) {
// editor->setSize(
// sharedParams->getUiWidth(),
// sharedParams->getUiHeight());
//
// jassert(dynamic_cast<ExposesComponents*> (editor) != nullptr);
// ExposesComponents* exposesComponents = dynamic_cast<ExposesComponents*> (editor);
// exposesComponents->getFilePicker().setDisplayedFilePath(sharedParams->getSoundFontPath());
// }
// const String& currentSoundFontAbsPath = fluidSynthModel->getCurrentSoundFontAbsPath();
// if (currentSoundFontAbsPath.isNotEmpty()) {
// fileChooser.setCurrentFile(File(currentSoundFontAbsPath), true, dontSendNotification);
// }
} }
} }
} }

View File

@ -21,18 +21,9 @@ using namespace Util;
*/ */
TableComponent::TableComponent( TableComponent::TableComponent(
AudioProcessorValueTreeState& valueTreeState AudioProcessorValueTreeState& valueTreeState
// const vector<string> &columns,
// const vector<TableRow> &rows,
// const function<void (int)> &onRowSelected,
// const function<int (const vector<string>&)> &rowToIDMapper,
// int initiallySelectedRow
) )
: valueTreeState{valueTreeState} : valueTreeState{valueTreeState}
, font{14.0f} , font{14.0f}
//, columns{columns}
//, rows{rows}
// , onRowSelected{onRowSelected}
// rowToIDMapper(rowToIDMapper)
{ {
// Create our table component and add it to this component.. // Create our table component and add it to this component..
addAndMakeVisible (table); addAndMakeVisible (table);
@ -44,19 +35,6 @@ TableComponent::TableComponent(
int columnIx = 1; int columnIx = 1;
// Add some columns to the table header, based on the column list in our database..
// for (auto &column : columns) // access by reference to avoid copying
// {
// const int colWidth{ columnIx == 1 ? 30 : 200 };
// table.getHeader().addColumn (
// String(column),
// columnIx++,
// colWidth, // column width
// 30, // min width
// 400, // max width
// TableHeaderComponent::defaultFlags
// );
// }
table.getHeader().addColumn ( table.getHeader().addColumn (
String("#"), String("#"),
columnIx++, columnIx++,
@ -76,20 +54,11 @@ TableComponent::TableComponent(
table.setWantsKeyboardFocus(false); table.setWantsKeyboardFocus(false);
// table.selectRow();
// ValueTree presets{valueTreeState.state.getChildWithName("presets")};
ValueTree banks{valueTreeState.state.getChildWithName("banks")}; ValueTree banks{valueTreeState.state.getChildWithName("banks")};
loadModelFrom(banks); loadModelFrom(banks);
// selectCurrentPreset();
// we could now change some initial settings.. // we could now change some initial settings..
table.getHeader().setSortColumnId(1, false); // sort ascending by ID column table.getHeader().setSortColumnId(1, false); // sort ascending by ID column
// table.getHeader().setColumnVisible (7, false); // hide the "length" column until the user shows it
// un-comment this line to have a go of stretch-to-fit mode
// table.getHeader().setStretchToFitActive (true);
// table.setMultipleSelectionEnabled (false);
valueTreeState.state.addListener(this); valueTreeState.state.addListener(this);
valueTreeState.addParameterListener("bank", this); valueTreeState.addParameterListener("bank", this);
valueTreeState.addParameterListener("preset", this); valueTreeState.addParameterListener("preset", this);
@ -101,27 +70,10 @@ TableComponent::~TableComponent() {
valueTreeState.state.removeListener(this); valueTreeState.state.removeListener(this);
} }
// void TableComponent::loadModelFrom(ValueTree& presets) {
// rows.clear();
// int numChildren{presets.getNumChildren()};
// for(int i{0}; i<numChildren; i++) {
// ValueTree child{presets.getChild(i)};
// int num{child.getProperty("num")};
// String name{child.getProperty("name")};
// rows.emplace_back(num, name);
// }
// table.deselectAllRows();
// table.updateContent();
// table.getHeader().setSortColumnId(0, true);
// selectCurrentPreset();
// table.repaint();
// }
void TableComponent::loadModelFrom(ValueTree& banks) { void TableComponent::loadModelFrom(ValueTree& banks) {
banksToPresets.clear(); banksToPresets.clear();
int banksChildren{banks.getNumChildren()}; int banksChildren{banks.getNumChildren()};
for(int bankIx{0}; bankIx<banksChildren; bankIx++) { for(int bankIx{0}; bankIx<banksChildren; bankIx++) {
// vector<TableRow> presets;
ValueTree bank{banks.getChild(bankIx)}; ValueTree bank{banks.getChild(bankIx)};
int bankNum{bank.getProperty("num")}; int bankNum{bank.getProperty("num")};
int bankChildren{bank.getNumChildren()}; int bankChildren{bank.getNumChildren()};
@ -129,9 +81,7 @@ void TableComponent::loadModelFrom(ValueTree& banks) {
ValueTree preset{bank.getChild(presetIx)}; ValueTree preset{bank.getChild(presetIx)};
int presetNum{preset.getProperty("num")}; int presetNum{preset.getProperty("num")};
String presetName{preset.getProperty("name")}; String presetName{preset.getProperty("name")};
// presets.emplace_back(presetNum, presetName);
TableRow row{presetNum, move(presetName)}; TableRow row{presetNum, move(presetName)};
// banksToPresets.insert(BanksToPresets::value_type(bankNum, move(row)));
banksToPresets.emplace(bankNum, move(row)); banksToPresets.emplace(bankNum, move(row));
} }
} }
@ -139,15 +89,10 @@ void TableComponent::loadModelFrom(ValueTree& banks) {
} }
void TableComponent::parameterChanged(const String& parameterID, float newValue) { void TableComponent::parameterChanged(const String& parameterID, float newValue) {
// valueTreeState.getParameter
if (parameterID == "bank") { if (parameterID == "bank") {
repopulateTable(); repopulateTable();
} else if (parameterID == "preset") { } else if (parameterID == "preset") {
selectCurrentPreset(); selectCurrentPreset();
// RangedAudioParameter *param {valueTreeState.getParameter("preset")};
// jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
// AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
// int value{castParam->get()};
} }
} }
@ -170,7 +115,6 @@ void TableComponent::repopulateTable() {
upperBound, upperBound,
back_inserter(rows), back_inserter(rows),
mem_fn(&BanksToPresets::value_type::second) mem_fn(&BanksToPresets::value_type::second)
// [](BanksToPresets::value_type element){return element.second;}
); );
table.deselectAllRows(); table.deselectAllRows();
table.updateContent(); table.updateContent();
@ -182,11 +126,6 @@ void TableComponent::repopulateTable() {
void TableComponent::valueTreePropertyChanged( void TableComponent::valueTreePropertyChanged(
ValueTree& treeWhosePropertyHasChanged, ValueTree& treeWhosePropertyHasChanged,
const Identifier& property) { const Identifier& property) {
// if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) {
// if (property == StringRef("synthetic")) {
// loadModelFrom(treeWhosePropertyHasChanged);
// }
// }
if (treeWhosePropertyHasChanged.getType() == StringRef("banks")) { if (treeWhosePropertyHasChanged.getType() == StringRef("banks")) {
if (property == StringRef("synthetic")) { if (property == StringRef("synthetic")) {
loadModelFrom(treeWhosePropertyHasChanged); loadModelFrom(treeWhosePropertyHasChanged);
@ -194,52 +133,6 @@ void TableComponent::valueTreePropertyChanged(
} }
} }
// void TableComponent::valueTreeParentChanged(ValueTree& treeWhoseParentHasChanged) {
// if (treeWhoseParentHasChanged.getType() == StringRef("presets")) {
// loadModelFrom(treeWhoseParentHasChanged);
// }
// }
// void TableComponent::valueTreePropertyChanged(
// ValueTree& treeWhosePropertyHasChanged,
// const Identifier& property) {
// DEBUG_PRINT(treeWhosePropertyHasChanged.getType().toString());
// }
// void TableComponent::valueTreeChildAdded(
// ValueTree& parentTree,
// ValueTree& childWhichHasBeenAdded) {
// DEBUG_PRINT(parentTree.getType().toString());
// }
// void TableComponent::valueTreeChildRemoved(
// ValueTree& parentTree,
// ValueTree& childWhichHasBeenRemoved,
// int indexFromWhichChildWasRemoved) {
// DEBUG_PRINT(parentTree.getType().toString());
// }
// void TableComponent::valueTreeChildOrderChanged(
// ValueTree& parentTreeWhoseChildrenHaveMoved,
// int oldIndex,
// int newIndex) {
// DEBUG_PRINT(parentTreeWhoseChildrenHaveMoved.getType().toString());
// }
// void TableComponent::valueTreeParentChanged(
// ValueTree& treeWhoseParentHasChanged) {
// DEBUG_PRINT(treeWhoseParentHasChanged.getType().toString());
// }
// void TableComponent::valueTreeRedirected(
// ValueTree& treeWhichHasBeenChanged) {
// DEBUG_PRINT(treeWhichHasBeenChanged.getType().toString());
// }
// void TableComponent::setRows(const vector<vector<string>>& rows, int initiallySelectedRow) {
// this->rows = rows;
// table.deselectAllRows();
// table.updateContent();
// table.getHeader().setSortColumnId(0, true);
// table.selectRow(initiallySelectedRow);
// table.repaint();
// }
// This is overloaded from TableListBoxModel, and must return the total number of rows in our table // This is overloaded from TableListBoxModel, and must return the total number of rows in our table
int TableComponent::getNumRows() int TableComponent::getNumRows()
{ {
@ -297,40 +190,24 @@ void TableComponent::sortOrderChanged (
bool isForwards bool isForwards
) { ) {
if (newSortColumnId != 0) { if (newSortColumnId != 0) {
// int selectedRowIx = table.getSelectedRow();
// TableRow* selectedRow;
// if (selectedRowIx >= 0) {
// selectedRow = &rows[selectedRowIx];
// }
TableComponent::DataSorter sorter (newSortColumnId, isForwards); TableComponent::DataSorter sorter (newSortColumnId, isForwards);
sort(rows.begin(), rows.end(), sorter); sort(rows.begin(), rows.end(), sorter);
table.updateContent(); table.updateContent();
selectCurrentPreset(); selectCurrentPreset();
// if (selectedRowIx >= 0) {
// for (auto it = rows.begin(); it != rows.end(); ++it) {
// if(it->preset == value) {
// int index {static_cast<int>(std::distance(rows.begin(), it))};
// table.selectRow(index);
// break;
// }
// }
// }
} }
} }
void TableComponent::selectCurrentPreset() { void TableComponent::selectCurrentPreset() {
table.deselectAllRows(); table.deselectAllRows();
RangedAudioParameter *param {valueTreeState.getParameter("preset")}; RangedAudioParameter *param{valueTreeState.getParameter("preset")};
jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr); jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)}; AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
int value{castParam->get()}; int value{castParam->get()};
for (auto it = rows.begin(); it != rows.end(); ++it) { for (auto it{rows.begin()}; it != rows.end(); ++it) {
if(it->preset == value) { if(it->preset == value) {
int index {static_cast<int>(distance(rows.begin(), it))}; int index{static_cast<int>(distance(rows.begin(), it))};
table.selectRow(index); table.selectRow(index);
break; break;
} }
@ -397,12 +274,10 @@ void TableComponent::selectedRowsChanged (int row) {
if (row < 0) { if (row < 0) {
return; return;
} }
// onRowSelected(rowToIDMapper(rows[row]));
// onRowSelected(stoi(rows[row][0]));
int newPreset{rows[row].preset}; int newPreset{rows[row].preset};
RangedAudioParameter *param {valueTreeState.getParameter("preset")}; RangedAudioParameter *param{valueTreeState.getParameter("preset")};
jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr); jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)}; AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
*castParam = newPreset; *castParam = newPreset;
} }