From 218beb00e7de6c7368bf49e7f3bcd205865dfe52 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Mon, 1 Jul 2019 23:55:14 +0100 Subject: [PATCH 01/33] encapsulate responsibility of params. add params for ADSR and filter. attempt to sync Slider. not obviously working yet. --- .../juicysfplugin.xcodeproj/project.pbxproj | 12 ++ Source/ExposesComponents.h | 2 + Source/FluidSynthModel.cpp | 30 ++-- Source/FluidSynthModel.h | 4 +- Source/GuiConstants.h | 8 + Source/MidiConstants.h | 5 + Source/Params.cpp | 152 ++++++++++++++++++ Source/Params.h | 59 +++++++ Source/PluginEditor.cpp | 33 ++-- Source/PluginEditor.h | 3 + Source/PluginProcessor.cpp | 82 ++++------ Source/PluginProcessor.h | 16 +- Source/SharesParams.h | 30 +++- Source/SlidersComponent.cpp | 52 +++++- Source/SlidersComponent.h | 20 ++- Source/SlidersFragment.h | 18 +++ 16 files changed, 430 insertions(+), 96 deletions(-) create mode 100644 Source/GuiConstants.h create mode 100644 Source/Params.cpp create mode 100644 Source/Params.h create mode 100644 Source/SlidersFragment.h diff --git a/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj b/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj index 02c0a02..54de6c0 100644 --- a/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ 2918F46AFD2AB89F9FA847DC /* include_juce_events.mm in Sources */ = {isa = PBXBuildFile; fileRef = 373EF982A53046CE00BECE68 /* include_juce_events.mm */; }; 2E77C6FAF1BCDB9EB29D20B9 /* PluginProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53CAB963D5051C786D3A52D /* PluginProcessor.cpp */; }; 305606C42BB0F2A12D382D34 /* SoundfontSynthVoice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5A057FEC371053E83A73E47 /* SoundfontSynthVoice.cpp */; }; + 35099D9322CAA87D00CD4523 /* Params.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 35099D9122CAA87D00CD4523 /* Params.cpp */; }; 358E458C22BEE5090087ED8D /* RecentFilesMenuTemplate.nib in Resources */ = {isa = PBXBuildFile; fileRef = 78CC5234CCFE3B170585DDAD /* RecentFilesMenuTemplate.nib */; }; 358E458D22BEE5090087ED8D /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1616112041466F7324D7E19 /* Accelerate.framework */; }; 358E458E22BEE5090087ED8D /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CA077CDD21D0FEC66FC290 /* AudioToolbox.framework */; }; @@ -285,6 +286,11 @@ 2C66D01D1DD9006E77E2E260 /* include_juce_data_structures.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_data_structures.mm; path = ../../JuceLibraryCode/include_juce_data_structures.mm; sourceTree = SOURCE_ROOT; }; 307CB49DF900DE4A612FF98E /* FluidSynthModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FluidSynthModel.h; path = ../../Source/FluidSynthModel.h; sourceTree = SOURCE_ROOT; }; 35099D9022CA8EF500CD4523 /* Util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Util.h; path = ../../Source/Util.h; sourceTree = ""; }; + 35099D9122CAA87D00CD4523 /* Params.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Params.cpp; path = ../../Source/Params.cpp; sourceTree = ""; }; + 35099D9222CAA87D00CD4523 /* Params.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Params.h; path = ../../Source/Params.h; sourceTree = ""; }; + 35099D9422CAB0A400CD4523 /* GuiConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GuiConstants.h; path = ../../Source/GuiConstants.h; sourceTree = ""; }; + 35099D9522CAB7CD00CD4523 /* SlidersFragment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SlidersFragment.h; path = ../../Source/SlidersFragment.h; sourceTree = ""; }; + 35099D9622CAC3C800CD4523 /* SharesParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharesParams.h; path = ../../Source/SharesParams.h; sourceTree = ""; }; 35880F58CB540AD30D1B0ED3 /* TablesComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TablesComponent.h; path = ../../Source/TablesComponent.h; sourceTree = SOURCE_ROOT; }; 358E45B422BEE53A0087ED8D /* libpcre.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libpcre.1.dylib; sourceTree = ""; }; 358E45B522BEE53A0087ED8D /* libvorbisenc.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libvorbisenc.2.dylib; sourceTree = ""; }; @@ -521,6 +527,7 @@ 403EB0CF49CF1D62BF359002 /* Source */ = { isa = PBXGroup; children = ( + 35099D9622CAC3C800CD4523 /* SharesParams.h */, DECFA95359BC1DDDD1CC86C3 /* BankAndPreset.cpp */, B000E7A360C0C86ADD3C911D /* BankAndPreset.h */, F1EB35E262DC717222E2F93D /* ExposesComponents.h */, @@ -554,6 +561,10 @@ 358E45F922C80DCA0087ED8D /* SlidersComponent.cpp */, 358E45FA22C80DCA0087ED8D /* SlidersComponent.h */, 35099D9022CA8EF500CD4523 /* Util.h */, + 35099D9122CAA87D00CD4523 /* Params.cpp */, + 35099D9222CAA87D00CD4523 /* Params.h */, + 35099D9422CAB0A400CD4523 /* GuiConstants.h */, + 35099D9522CAB7CD00CD4523 /* SlidersFragment.h */, ); name = Source; sourceTree = ""; @@ -951,6 +962,7 @@ 7DF73014FFCCE46E228216DB /* PluginEditor.cpp in Sources */, E08B3A2AF85F9FCF991F1CA2 /* include_juce_audio_basics.mm in Sources */, C4D76C968347E2ACBAB5B6E7 /* include_juce_audio_devices.mm in Sources */, + 35099D9322CAA87D00CD4523 /* Params.cpp in Sources */, 51C9DBCA840E334DB1804133 /* include_juce_audio_formats.mm in Sources */, 358E45FB22C80DCA0087ED8D /* SlidersComponent.cpp in Sources */, 5E5B833BBDD65F0D4271CA52 /* include_juce_audio_plugin_client_utils.cpp in Sources */, diff --git a/Source/ExposesComponents.h b/Source/ExposesComponents.h index c42d193..b0dda84 100644 --- a/Source/ExposesComponents.h +++ b/Source/ExposesComponents.h @@ -6,11 +6,13 @@ #pragma once #include "FilePickerFragment.h" +#include "SlidersFragment.h" class ExposesComponents { public: virtual ~ExposesComponents() {} virtual FilePickerFragment& getFilePicker() = 0; + virtual SlidersFragment& getSliders() = 0; }; \ No newline at end of file diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 40bddf3..085323f 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -8,15 +8,15 @@ using namespace std; -FluidSynthModel::FluidSynthModel(SharesParams& p) - : sharesParams(p), - synth(nullptr), - settings(nullptr), - currentSoundFontAbsPath(), - currentSampleRate(44100), - initialised(false), - sfont_id(0), - channel(0)/*, +FluidSynthModel::FluidSynthModel(shared_ptr sharedParams) + : sharedParams{sharedParams}, + synth{nullptr}, + settings{nullptr}, + currentSoundFontAbsPath{}, + currentSampleRate{44100}, + initialised{false}, + sfont_id{0}, + channel{0}/*, mod(nullptr)*/ {} @@ -51,9 +51,9 @@ void FluidSynthModel::initialise() { synth = new_fluid_synth(settings); fluid_synth_set_sample_rate(synth, currentSampleRate); - if (sharesParams.getSoundFontPath().isNotEmpty()) { - loadFont(sharesParams.getSoundFontPath()); - changePreset(sharesParams.getBank(), sharesParams.getPreset()); + if (sharedParams->getSoundFontPath().isNotEmpty()) { + loadFont(sharedParams->getSoundFontPath()); + changePreset(sharedParams->getBank(), sharedParams->getPreset()); } fluid_synth_set_gain(synth, 2.0); @@ -189,8 +189,8 @@ void FluidSynthModel::changePreset(int bank, int preset) { preset = bankAndPreset->getPreset(); } changePresetImpl(bank, preset); - sharesParams.setPreset(preset); - sharesParams.setBank(bank); + sharedParams->setPreset(preset); + sharedParams->setBank(bank); } void FluidSynthModel::changePresetImpl(int bank, int preset) { @@ -272,7 +272,7 @@ void FluidSynthModel::onFileNameChanged(const String &absPath, int bank, int pre } unloadAndLoadFont(absPath); changePreset(bank, preset); - sharesParams.setSoundFontPath(absPath); + sharedParams->setSoundFontPath(absPath); eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath); } diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index a630cdf..7785306 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -19,7 +19,7 @@ using namespace std; class FluidSynthModel { public: - FluidSynthModel(SharesParams& p); + FluidSynthModel(shared_ptr sharedParams); ~FluidSynthModel(); fluid_synth_t* getSynth(); @@ -65,7 +65,7 @@ class FluidSynthModel { const String& getCurrentSoundFontAbsPath(); private: - SharesParams& sharesParams; + shared_ptr sharedParams; fluid_synth_t* synth; fluid_settings_t* settings; diff --git a/Source/GuiConstants.h b/Source/GuiConstants.h new file mode 100644 index 0000000..8223443 --- /dev/null +++ b/Source/GuiConstants.h @@ -0,0 +1,8 @@ +#pragma once + +struct GuiConstants { + inline static const int minWidth = 500; + inline static const int maxWidth = 1900; + inline static const int minHeight = 300; + inline static const int maxHeight = 1000; +}; diff --git a/Source/MidiConstants.h b/Source/MidiConstants.h index f31a69a..68617f2 100644 --- a/Source/MidiConstants.h +++ b/Source/MidiConstants.h @@ -162,3 +162,8 @@ enum fluid_midi_control_change - for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB). */ #define FLUID_PEAK_ATTENUATION 960.0f + +struct MidiConstants { + inline static const int midiMinValue = 0; + inline static const int midiMaxValue = 127; +}; diff --git a/Source/Params.cpp b/Source/Params.cpp new file mode 100644 index 0000000..a29b722 --- /dev/null +++ b/Source/Params.cpp @@ -0,0 +1,152 @@ +// +// Params.cpp +// juicysfplugin - Shared Code +// +// Created by Alex Birch on 01/07/2019. +// Copyright © 2019 Birchlabs. All rights reserved. +// + +#include "Params.h" +#include "../JuceLibraryCode/JuceHeader.h" +#include "MidiConstants.h" +#include "GuiConstants.h" +#include "SharesParams.h" + +using namespace std; + +Params::Params() noexcept : + uiWidth{GuiConstants::minWidth}, + uiHeight{GuiConstants::minHeight}, + soundFontPath{String()}, + preset{-1}, + bank{-1}, + attack{0}, + decay{0}, + sustain{0}, + release{0}, + filterCutOff{0}, + filterResonance{0} +{ +} + +void Params::setAttributesOnXml(XmlElement& xml) { + xml.setAttribute("uiWidth", uiWidth); + xml.setAttribute("uiHeight", uiHeight); + xml.setAttribute("soundFontPath", soundFontPath); + xml.setAttribute("preset", preset); + xml.setAttribute("bank", bank); + xml.setAttribute("attack", attack); + xml.setAttribute("decay", decay); + xml.setAttribute("sustain", sustain); + xml.setAttribute("release", release); + xml.setAttribute("filterCutOff", filterCutOff); + xml.setAttribute("filterResonance", filterResonance); +} + +void Params::loadAttributesFromXml(shared_ptr xmlState) { + uiWidth = jmin(jmax(xmlState->getIntAttribute("uiWidth", uiWidth), GuiConstants::minWidth), GuiConstants::maxWidth); + uiHeight = jmin(jmax(xmlState->getIntAttribute("uiHeight", uiHeight), GuiConstants::minHeight), GuiConstants::maxHeight); + soundFontPath = xmlState->getStringAttribute("soundFontPath", soundFontPath); + preset = xmlState->getIntAttribute("preset", preset); + bank = xmlState->getIntAttribute("bank", bank); + attack = jmin(jmax(xmlState->getIntAttribute("attack", attack), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); + decay = jmin(jmax(xmlState->getIntAttribute("decay", decay), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); + sustain = jmin(jmax(xmlState->getIntAttribute("sustain", sustain), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); + release = jmin(jmax(xmlState->getIntAttribute("release", release), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); + filterCutOff = jmin(jmax(xmlState->getIntAttribute("filterCutOff", filterCutOff), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); + filterResonance = jmin(jmax(xmlState->getIntAttribute("filterResonance", filterResonance), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); +} + +void Params::acceptMidiControlEvent(int controller, int value) { + switch(static_cast(controller)) { + case SOUND_CTRL2: // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) + filterResonance = value; + break; + case SOUND_CTRL3: // MIDI CC 72 Release time + release = value; + break; + case SOUND_CTRL4: // MIDI CC 73 Attack time + attack = value; + break; + case SOUND_CTRL5: // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) + filterCutOff = value; + break; + case SOUND_CTRL6: // MIDI CC 75 Decay Time + decay = value; + break; + case SOUND_CTRL10: // MIDI CC 79 undefined + sustain = value; + break; + default: + break; + } +} + +void Params::setSoundFontPath(const String& value) { + soundFontPath = value; +} + +String& Params::getSoundFontPath() { + return soundFontPath; +} +int Params::getPreset() { + return preset; +} +int Params::getBank() { + return bank; +} +int Params::getUiWidth() { + return uiWidth; +} +int Params::getUiHeight() { + return uiHeight; +} +int Params::getAttack() { + return attack; +} +int Params::getDecay() { + return decay; +} +int Params::getSustain() { + return sustain; +} +int Params::getRelease() { + return release; +} +int Params::getFilterCutOff() { + return filterCutOff; +} +int Params::getFilterResonance() { + return filterResonance; +} + +void Params::setPreset(int value) { + preset = value; +} +void Params::setBank(int value) { + bank = value; +} +void Params::setUiWidth(int value) { + uiWidth = value; +} +void Params::setUiHeight(int value) { + uiHeight = value; +} +void Params::setAttack(int value) { + attack = value; +} +void Params::setDecay(int value) { + decay = value; +} +void Params::setSustain(int value) { + sustain = value; +} +void Params::setRelease(int value) { + release = value; +} +void Params::setFilterCutOff(int value) { + filterCutOff = value; +} +void Params::setFilterResonance(int value) { + filterResonance = value; +} diff --git a/Source/Params.h b/Source/Params.h new file mode 100644 index 0000000..e6979f4 --- /dev/null +++ b/Source/Params.h @@ -0,0 +1,59 @@ +#pragma once + +#include "SharesParams.h" +#include "../JuceLibraryCode/JuceHeader.h" + +using namespace std; + +class Params: public SharesParams { +public: + Params() noexcept; + + virtual void setAttributesOnXml(XmlElement& xml) override; + virtual void loadAttributesFromXml(shared_ptr xmlState) override; + virtual void acceptMidiControlEvent(int controller, int value) override; + + virtual void setSoundFontPath(const String& value) override; + virtual String& getSoundFontPath() override; + virtual int getPreset() override; + virtual void setPreset(int value) override; + virtual int getBank() override; + virtual void setBank(int value) override; + + virtual int getUiWidth() override; + virtual void setUiWidth(int value) override; + virtual int getUiHeight() override; + virtual void setUiHeight(int value) override; + + virtual int getAttack() override; + virtual void setAttack(int value) override; + virtual int getDecay() override; + virtual void setDecay(int value) override; + virtual int getSustain() override; + virtual void setSustain(int value) override; + virtual int getRelease() override; + virtual void setRelease(int value) override; + + virtual int getFilterCutOff() override; + virtual void setFilterCutOff(int value) override; + virtual int getFilterResonance() override; + virtual void setFilterResonance(int value) override; + +private: + int uiWidth; + int uiHeight; + + String soundFontPath; + int preset; + int bank; + + int attack; + int decay; + int sustain; + int release; + + int filterCutOff; + int filterResonance; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Params) +}; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 8edc3c4..8283425 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -10,20 +10,26 @@ #include "PluginProcessor.h" #include "PluginEditor.h" +#include "GuiConstants.h" //============================================================================== -JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor (JuicySFAudioProcessor& p) - : AudioProcessorEditor (&p), - processor (p), - midiKeyboard (p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard), - tablesComponent(p.getFluidSynthModel()), - filePicker(p.getFluidSynthModel()), - slidersComponent{p.getFluidSynthModel()} +JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor(JuicySFAudioProcessor& p) + : AudioProcessorEditor{&p}, + processor{p}, + sharedParams{p.sharedParams}, + midiKeyboard{p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard}, + tablesComponent{p.getFluidSynthModel()}, + filePicker{p.getFluidSynthModel()}, + slidersComponent{p.sharedParams, p.getFluidSynthModel()} { // set resize limits for this plug-in - setResizeLimits (500, 300, 1900, 1000); + setResizeLimits( + GuiConstants::minWidth, + GuiConstants::minHeight, + GuiConstants::maxWidth, + GuiConstants::maxHeight); - setSize (p.lastUIWidth, p.lastUIHeight); + setSize(sharedParams->getUiWidth(), sharedParams->getUiHeight()); // processor.subscribeToStateChanges(this); @@ -97,9 +103,8 @@ void JuicySFAudioProcessorEditor::resized() tablesComponent.setBounds(rContent); - - processor.lastUIWidth = getWidth(); - processor.lastUIHeight = getHeight(); + sharedParams->setUiWidth(getWidth()); + sharedParams->setUiHeight(getHeight()); // Rectangle r2 (getLocalBounds()); // r2.reduce(0, padding); @@ -150,4 +155,8 @@ bool JuicySFAudioProcessorEditor::keyStateChanged (bool isKeyDown) { FilePickerFragment& JuicySFAudioProcessorEditor::getFilePicker() { return filePicker; +} + +SlidersFragment& JuicySFAudioProcessorEditor::getSliders() { + return slidersComponent; } \ No newline at end of file diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index d0da863..a242cb7 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -42,12 +42,15 @@ class JuicySFAudioProcessorEditor : public AudioProcessorEditor, // void setStateInformation (XmlElement* xmlState) override; virtual FilePickerFragment& getFilePicker() override; + virtual SlidersFragment& getSliders() override; private: // This reference is provided as a quick way for your editor to // access the processor object that created it. JuicySFAudioProcessor& processor; + shared_ptr sharedParams; + SurjectiveMidiKeyboardComponent midiKeyboard; TablesComponent tablesComponent; FilePicker filePicker; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 61f1736..ba3d357 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -15,19 +15,20 @@ #include "ExposesComponents.h" #include "MidiConstants.h" #include "Util.h" +#include "SharesParams.h" +#include "Params.h" + +using namespace std; AudioProcessor* JUCE_CALLTYPE createPluginFilter(); //============================================================================== JuicySFAudioProcessor::JuicySFAudioProcessor() - : AudioProcessor (getBusesProperties()), - lastUIWidth(400), - lastUIHeight(300), - soundFontPath(String()), - lastPreset(-1), - lastBank(-1), - fluidSynthModel(*this)/*, + : AudioProcessor{getBusesProperties()}, + sharedParams{static_pointer_cast(make_shared())}, + fluidSynthModel{sharedParams}/*, + fluidSynthModel{*this}, pluginEditor(nullptr)*/ { initialiseSynth(); @@ -47,10 +48,10 @@ void JuicySFAudioProcessor::initialiseSynth() { // Add some voices... for (int i = numVoices; --i >= 0;) - synth.addVoice (new SoundfontSynthVoice(fluidSynthModel.getSynth())); + synth.addVoice(new SoundfontSynthVoice(fluidSynthModel.getSynth())); // ..and give the synth a sound to play - synth.addSound (new SoundfontSynthSound()); + synth.addSound(new SoundfontSynthSound()); } //============================================================================== @@ -168,6 +169,11 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer // responsibilities of SoundfontSynthVoice. // well, by that logic maybe I should move program change onto Voice. but it doesn't feel like a per-voice concern. if (m.isController()) { + // shared_ptr midi_event{ + // new_fluid_midi_event(), + // [](fluid_midi_event_t *event) { + // delete_fluid_midi_event(midi_event); + // }}; fluid_midi_event_t *midi_event(new_fluid_midi_event()); fluid_midi_event_set_type(midi_event, static_cast(CONTROL_CHANGE)); fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel()); @@ -175,6 +181,13 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer fluid_midi_event_set_value(midi_event, m.getControllerValue()); fluid_synth_handle_midi_event(fluidSynth, midi_event); delete_fluid_midi_event(midi_event); + + sharedParams->acceptMidiControlEvent(m.getControllerNumber(), m.getControllerValue()); + + AudioProcessorEditor* editor{getActiveEditor()}; + jassert(dynamic_cast (editor) != nullptr); + ExposesComponents* exposesComponents{dynamic_cast(editor)}; + exposesComponents->getSliders().acceptMidiControlEvent(m.getControllerNumber(), m.getControllerValue()); } else if (m.isProgramChange()) { fluid_midi_event_t *midi_event(new_fluid_midi_event()); fluid_midi_event_set_type(midi_event, static_cast(PROGRAM_CHANGE)); @@ -266,14 +279,8 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData) // as intermediaries to make it easy to save and load complex data. // Create an outer XML element.. - XmlElement xml ("MYPLUGINSETTINGS"); - - // add some attributes to it.. - xml.setAttribute ("uiWidth", lastUIWidth); - xml.setAttribute ("uiHeight", lastUIHeight); - xml.setAttribute ("soundFontPath", soundFontPath); - xml.setAttribute ("preset", lastPreset); - xml.setAttribute ("bank", lastBank); + XmlElement xml{"MYPLUGINSETTINGS"}; + sharedParams->setAttributesOnXml(xml); // list::iterator p; // for(p = stateChangeSubscribers.begin(); p != stateChangeSubscribers.end(); p++) { @@ -294,7 +301,7 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt // You should use this method to restore your parameters from this memory block, // whose contents will have been created by the getStateInformation() call. // This getXmlFromBinary() helper function retrieves our XML from the binary blob.. - ScopedPointer xmlState (getXmlFromBinary (data, sizeInBytes)); + shared_ptr xmlState{getXmlFromBinary(data, sizeInBytes)}; if (xmlState != nullptr) { @@ -307,26 +314,27 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt // } // ok, now pull out our last window size.. - lastUIWidth = jmax (xmlState->getIntAttribute ("uiWidth", lastUIWidth), 400); - lastUIHeight = jmax (xmlState->getIntAttribute ("uiHeight", lastUIHeight), 300); - soundFontPath = xmlState->getStringAttribute ("soundFontPath", soundFontPath); - lastPreset = xmlState->getIntAttribute ("preset", lastPreset); - lastBank = xmlState->getIntAttribute ("bank", lastBank); + sharedParams->loadAttributesFromXml(xmlState); // Now reload our parameters.. for (auto* param : getParameters()) if (auto* p = dynamic_cast (param)) p->setValue ((float) xmlState->getDoubleAttribute (p->paramID, p->getValue())); - fluidSynthModel.onFileNameChanged(soundFontPath, lastBank, lastPreset); + fluidSynthModel.onFileNameChanged( + sharedParams->getSoundFontPath(), + sharedParams->getBank(), + sharedParams->getPreset()); - AudioProcessorEditor* editor = getActiveEditor(); + AudioProcessorEditor* editor{getActiveEditor()}; if (editor != nullptr) { - editor->setSize(lastUIWidth, lastUIHeight); + editor->setSize( + sharedParams->getUiWidth(), + sharedParams->getUiHeight()); jassert(dynamic_cast (editor) != nullptr); ExposesComponents* exposesComponents = dynamic_cast (editor); - exposesComponents->getFilePicker().setDisplayedFilePath(soundFontPath); + exposesComponents->getFilePicker().setDisplayedFilePath(sharedParams->getSoundFontPath()); } // const String& currentSoundFontAbsPath = fluidSynthModel->getCurrentSoundFontAbsPath(); @@ -354,26 +362,6 @@ FluidSynthModel* JuicySFAudioProcessor::getFluidSynthModel() { return &fluidSynthModel; } -void JuicySFAudioProcessor::setSoundFontPath(const String& value) { - soundFontPath = value; -} - -String& JuicySFAudioProcessor::getSoundFontPath() { - return soundFontPath; -} -int JuicySFAudioProcessor::getPreset() { - return lastPreset; -} -int JuicySFAudioProcessor::getBank() { - return lastBank; -} -void JuicySFAudioProcessor::setPreset(int preset) { - lastPreset = preset; -} -void JuicySFAudioProcessor::setBank(int bank) { - lastBank = bank; -} - //============================================================================== // This creates new instances of the plugin.. AudioProcessor* JUCE_CALLTYPE createPluginFilter() diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 8d60769..5aee38f 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -21,8 +21,7 @@ using namespace std; //============================================================================== /** */ -class JuicySFAudioProcessor : public AudioProcessor, - public SharesParams +class JuicySFAudioProcessor : public AudioProcessor { public: //============================================================================== @@ -67,25 +66,14 @@ class JuicySFAudioProcessor : public AudioProcessor, MidiKeyboardState keyboardState; - virtual void setSoundFontPath(const String& value) override; - virtual String& getSoundFontPath() override; - virtual int getPreset() override; - virtual void setPreset(int preset) override; - virtual int getBank() override; - virtual void setBank(int bank) override; + shared_ptr sharedParams; // void subscribeToStateChanges(StateChangeSubscriber* subscriber); // void unsubscribeFromStateChanges(StateChangeSubscriber* subscriber); - int lastUIWidth, lastUIHeight; - private: void initialiseSynth(); - String soundFontPath; - int lastPreset; - int lastBank; - FluidSynthModel fluidSynthModel; fluid_synth_t* fluidSynth; Synthesiser synth; diff --git a/Source/SharesParams.h b/Source/SharesParams.h index 7cae325..1f25d0b 100644 --- a/Source/SharesParams.h +++ b/Source/SharesParams.h @@ -7,16 +7,42 @@ #include "../JuceLibraryCode/JuceHeader.h" +using namespace std; + class SharesParams { public: virtual ~SharesParams() {} + virtual void setAttributesOnXml(XmlElement& xml) = 0; + virtual void loadAttributesFromXml(shared_ptr xmlState) = 0; + virtual void acceptMidiControlEvent(int controller, int value) = 0; + virtual void setSoundFontPath(const String& value) = 0; virtual String& getSoundFontPath() = 0; virtual int getPreset() = 0; - virtual void setPreset(int preset) = 0; + virtual void setPreset(int value) = 0; virtual int getBank() = 0; - virtual void setBank(int bank) = 0; + virtual void setBank(int value) = 0; + + virtual int getUiWidth() = 0; + virtual void setUiWidth(int value) = 0; + virtual int getUiHeight() = 0; + virtual void setUiHeight(int value) = 0; + + virtual int getAttack() = 0; + virtual void setAttack(int value) = 0; + virtual int getDecay() = 0; + virtual void setDecay(int value) = 0; + virtual int getSustain() = 0; + virtual void setSustain(int value) = 0; + virtual int getRelease() = 0; + virtual void setRelease(int value) = 0; + + virtual int getFilterCutOff() = 0; + virtual void setFilterCutOff(int value) = 0; + virtual int getFilterResonance() = 0; + virtual void setFilterResonance(int value) = 0; + }; diff --git a/Source/SlidersComponent.cpp b/Source/SlidersComponent.cpp index 4d008d5..1fa4325 100644 --- a/Source/SlidersComponent.cpp +++ b/Source/SlidersComponent.cpp @@ -10,10 +10,11 @@ #include "FluidSynthModel.h" #include "MidiConstants.h" -std::function SlidersComponent::makeSliderListener(Slider& slider, int controller) { +std::function SlidersComponent::makeSliderListener(Slider& slider, int controller/*, std::function callback*/) { return [this, controller, &slider]{ // slider.setValue(slider.getValue(), NotificationType::dontSendNotification); fluidSynthModel->setControllerValue(controller, slider.getValue()); + // callback(); }; } @@ -58,7 +59,54 @@ void SlidersComponent::resized() { filterResonanceSlider.setBounds(rFilter.removeFromLeft(sliderWidth + sliderXMargin).withTrimmedTop(labelHeight).withTrimmedLeft(sliderXMargin)); } -SlidersComponent::SlidersComponent(FluidSynthModel* fluidSynthModel) : +void SlidersComponent::acceptMidiControlEvent(int controller, int value) { + switch(static_cast(controller)) { + case SOUND_CTRL2: // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) + filterResonanceSlider.setValue(value, NotificationType::dontSendNotification); + break; + case SOUND_CTRL3: // MIDI CC 72 Release time + releaseSlider.setValue(value, NotificationType::dontSendNotification); + break; + case SOUND_CTRL4: // MIDI CC 73 Attack time + attackSlider.setValue(value, NotificationType::dontSendNotification); + break; + case SOUND_CTRL5: // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) + filterCutOffSlider.setValue(value, NotificationType::dontSendNotification); + break; + case SOUND_CTRL6: // MIDI CC 75 Decay Time + decaySlider.setValue(value, NotificationType::dontSendNotification); + break; + case SOUND_CTRL10: // MIDI CC 79 undefined + sustainSlider.setValue(value, NotificationType::dontSendNotification); + break; + default: + break; + } +} + +// void SlidersComponent::updateAttackSlider(int value) { +// attackSlider.setValue(value, NotificationType::dontSendNotification); +// } +// void SlidersComponent::updateDecaySlider(int value) { +// decaySlider.setValue(value, NotificationType::dontSendNotification); +// } +// void SlidersComponent::updateSustainSlider(int value) { +// sustainSlider.setValue(value, NotificationType::dontSendNotification); +// } +// void SlidersComponent::updateReleaseSlider(int value) { +// releaseSlider.setValue(value, NotificationType::dontSendNotification); +// } +// void SlidersComponent::updateFilterCutOffSlider(int value) { +// filterCutOffSlider.setValue(value, NotificationType::dontSendNotification); +// } +// void SlidersComponent::updateFilterResonanceSlider(int value) { +// filterResonanceSlider.setValue(value, NotificationType::dontSendNotification); +// } + +SlidersComponent::SlidersComponent( + shared_ptr sharedParams, + FluidSynthModel* fluidSynthModel) : +sharedParams{sharedParams}, fluidSynthModel{fluidSynthModel}, envelopeGroup{"envelopeGroup", "Envelope"}, filterGroup{"filterGroup", "Filter"} diff --git a/Source/SlidersComponent.h b/Source/SlidersComponent.h index ede78d3..97dfeeb 100644 --- a/Source/SlidersComponent.h +++ b/Source/SlidersComponent.h @@ -2,22 +2,38 @@ #include "../JuceLibraryCode/JuceHeader.h" #include "FluidSynthModel.h" +#include "SharesParams.h" +#include "SlidersFragment.h" using namespace std; -class SlidersComponent : public Component +class SlidersComponent : public Component, + public SlidersFragment { public: - SlidersComponent(FluidSynthModel* fluidSynthModel); + SlidersComponent( + shared_ptr sharedParams, + FluidSynthModel* fluidSynthModel); ~SlidersComponent(); void resized() override; const int getDesiredWidth(); + virtual void acceptMidiControlEvent(int controller, int value) override; + + // virtual void updateAttackSlider(int value) override; + // virtual void updateDecaySlider(int value) override; + // virtual void updateSustainSlider(int value) override; + // virtual void updateReleaseSlider(int value) override; + + // virtual void updateFilterCutOffSlider(int value) override; + // virtual void updateFilterResonanceSlider(int value) override; + private: std::function makeSliderListener(Slider& slider, int controller); + shared_ptr sharedParams; FluidSynthModel* fluidSynthModel; GroupComponent envelopeGroup; diff --git a/Source/SlidersFragment.h b/Source/SlidersFragment.h new file mode 100644 index 0000000..255dbe1 --- /dev/null +++ b/Source/SlidersFragment.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../JuceLibraryCode/JuceHeader.h" + +class SlidersFragment { +public: + virtual ~SlidersFragment() {} + + virtual void acceptMidiControlEvent(int controller, int value) = 0; + + // virtual void updateAttackSlider(int value) = 0; + // virtual void updateDecaySlider(int value) = 0; + // virtual void updateSustainSlider(int value) = 0; + // virtual void updateReleaseSlider(int value) = 0; + + // virtual void updateFilterCutOffSlider(int value) = 0; + // virtual void updateFilterResonanceSlider(int value) = 0; +}; From 1fe64e31f741f7c613c1d3f43259d240cccb1855 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Tue, 2 Jul 2019 23:27:56 +0100 Subject: [PATCH 02/33] start setting up a global shared state and listeners --- Source/PluginEditor.cpp | 7 ++++--- Source/PluginEditor.h | 3 ++- Source/PluginProcessor.cpp | 35 +++++++++++++++++++++++++++++------ Source/PluginProcessor.h | 4 ++++ Source/SlidersComponent.cpp | 19 ++++++++++++++----- Source/SlidersComponent.h | 9 +++++++++ 6 files changed, 62 insertions(+), 15 deletions(-) diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 8283425..5531372 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -13,14 +13,15 @@ #include "GuiConstants.h" //============================================================================== -JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor(JuicySFAudioProcessor& p) +JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor(JuicySFAudioProcessor& p, AudioProcessorValueTreeState& state) : AudioProcessorEditor{&p}, processor{p}, + state{state}, sharedParams{p.sharedParams}, midiKeyboard{p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard}, tablesComponent{p.getFluidSynthModel()}, filePicker{p.getFluidSynthModel()}, - slidersComponent{p.sharedParams, p.getFluidSynthModel()} + slidersComponent{p.sharedParams, state, p.getFluidSynthModel()} { // set resize limits for this plug-in setResizeLimits( @@ -159,4 +160,4 @@ FilePickerFragment& JuicySFAudioProcessorEditor::getFilePicker() { SlidersFragment& JuicySFAudioProcessorEditor::getSliders() { return slidersComponent; -} \ No newline at end of file +} diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index a242cb7..6c2fd07 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -28,7 +28,7 @@ class JuicySFAudioProcessorEditor : public AudioProcessorEditor, public StateChangeSubscriber*/ { public: - JuicySFAudioProcessorEditor (JuicySFAudioProcessor&); + JuicySFAudioProcessorEditor (JuicySFAudioProcessor&, AudioProcessorValueTreeState& state); ~JuicySFAudioProcessorEditor(); //============================================================================== @@ -49,6 +49,7 @@ class JuicySFAudioProcessorEditor : public AudioProcessorEditor, // access the processor object that created it. JuicySFAudioProcessor& processor; + AudioProcessorValueTreeState& state; shared_ptr sharedParams; SurjectiveMidiKeyboardComponent midiKeyboard; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index ba3d357..fb9e166 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -19,21 +19,44 @@ #include "Params.h" using namespace std; +using Parameter = AudioProcessorValueTreeState::Parameter; AudioProcessor* JUCE_CALLTYPE createPluginFilter(); //============================================================================== JuicySFAudioProcessor::JuicySFAudioProcessor() - : AudioProcessor{getBusesProperties()}, - sharedParams{static_pointer_cast(make_shared())}, - fluidSynthModel{sharedParams}/*, - fluidSynthModel{*this}, - pluginEditor(nullptr)*/ +: AudioProcessor{getBusesProperties()} +, sharedParams{static_pointer_cast(make_shared())} +, state{*this, nullptr, + { "PARAMETERS" /* MYPLUGINSETTINGS */ }, + createParameterLayout() + } +, fluidSynthModel{sharedParams} +//, fluidSynthModel{*this} +//, pluginEditor(nullptr) { initialiseSynth(); } +AudioProcessorValueTreeState::ParameterLayout JuicySFAudioProcessor::createParameterLayout() { + // std::vector> params; + + // for (int i = 1; i < 9; ++i) + // params.push_back (std::make_unique (String (i), String (i), 0, i, 0)); + + vector> params{ + make_unique("attack", "volume envelope attack time", 0, 127, 0, "A" ), + make_unique("decay", "volume envelope sustain attentuation", 0, 127, 0, "D" ), + make_unique("sustain", "volume envelope decay time", 0, 127, 0, "S" ), + make_unique("release", "volume envelope release time", 0, 127, 0, "R" ), + make_unique("filterCutOff", "low-pass filter cut-off frequency", 0, 127, 0, "Cut" ), + make_unique("filterResonance", "low-pass filter resonance attentuation", 0, 127, 0, "Res" ), + }; + + return { params.begin(), params.end() }; +} + JuicySFAudioProcessor::~JuicySFAudioProcessor() { // delete fluidSynthModel; @@ -268,7 +291,7 @@ bool JuicySFAudioProcessor::hasEditor() const AudioProcessorEditor* JuicySFAudioProcessor::createEditor() { // grab a raw pointer to it for our own use - return /*pluginEditor = */new JuicySFAudioProcessorEditor (*this); + return /*pluginEditor = */new JuicySFAudioProcessorEditor (*this, state); } //============================================================================== diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 5aee38f..93554f2 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -74,6 +74,8 @@ class JuicySFAudioProcessor : public AudioProcessor private: void initialiseSynth(); + AudioProcessorValueTreeState state; + FluidSynthModel fluidSynthModel; fluid_synth_t* fluidSynth; Synthesiser synth; @@ -83,6 +85,8 @@ class JuicySFAudioProcessor : public AudioProcessor // list stateChangeSubscribers; + AudioProcessorValueTreeState::ParameterLayout createParameterLayout(); + static BusesProperties getBusesProperties(); // Model* model; diff --git a/Source/SlidersComponent.cpp b/Source/SlidersComponent.cpp index 1fa4325..608a7fd 100644 --- a/Source/SlidersComponent.cpp +++ b/Source/SlidersComponent.cpp @@ -9,6 +9,7 @@ #include "SlidersComponent.h" #include "FluidSynthModel.h" #include "MidiConstants.h" +using SliderAttachment = AudioProcessorValueTreeState::SliderAttachment; std::function SlidersComponent::makeSliderListener(Slider& slider, int controller/*, std::function callback*/) { return [this, controller, &slider]{ @@ -105,11 +106,13 @@ void SlidersComponent::acceptMidiControlEvent(int controller, int value) { SlidersComponent::SlidersComponent( shared_ptr sharedParams, - FluidSynthModel* fluidSynthModel) : -sharedParams{sharedParams}, -fluidSynthModel{fluidSynthModel}, -envelopeGroup{"envelopeGroup", "Envelope"}, -filterGroup{"filterGroup", "Filter"} + AudioProcessorValueTreeState& state, + FluidSynthModel* fluidSynthModel) +: sharedParams{sharedParams} +, state{state} +, fluidSynthModel{fluidSynthModel} +, envelopeGroup{"envelopeGroup", "Envelope"} +, filterGroup{"filterGroup", "Filter"} { const Slider::SliderStyle style{Slider::SliderStyle::LinearVertical}; const double rangeMin(0); @@ -120,31 +123,37 @@ filterGroup{"filterGroup", "Filter"} attackSlider.setRange(rangeMin, rangeMax, rangeStep); attackSlider.onValueChange = makeSliderListener(attackSlider, static_cast(SOUND_CTRL4)); attackSlider.setTextBoxStyle(Slider::TextBoxBelow, true, attackSlider.getTextBoxWidth(), attackSlider.getTextBoxHeight()); + attackSliderAttachment = make_unique(state, "attack", attackSlider); decaySlider.setSliderStyle(style); decaySlider.setRange(rangeMin, rangeMax, rangeStep); decaySlider.onValueChange = makeSliderListener(decaySlider, static_cast(SOUND_CTRL6)); decaySlider.setTextBoxStyle(Slider::TextBoxBelow, true, decaySlider.getTextBoxWidth(), decaySlider.getTextBoxHeight()); + decaySliderAttachment = make_unique(state, "decay", decaySlider); sustainSlider.setSliderStyle(style); sustainSlider.setRange(rangeMin, rangeMax, rangeStep); sustainSlider.onValueChange = makeSliderListener(sustainSlider, static_cast(SOUND_CTRL10)); sustainSlider.setTextBoxStyle(Slider::TextBoxBelow, true, sustainSlider.getTextBoxWidth(), sustainSlider.getTextBoxHeight()); + sustainSliderAttachment = make_unique(state, "sustain", sustainSlider); releaseSlider.setSliderStyle(style); releaseSlider.setRange(rangeMin, rangeMax, rangeStep); releaseSlider.onValueChange = makeSliderListener(releaseSlider, static_cast(SOUND_CTRL3)); releaseSlider.setTextBoxStyle(Slider::TextBoxBelow, true, releaseSlider.getTextBoxWidth(), releaseSlider.getTextBoxHeight()); + releaseSliderAttachment = make_unique(state, "release", releaseSlider); filterCutOffSlider.setSliderStyle(style); filterCutOffSlider.setRange(rangeMin, rangeMax, rangeStep); filterCutOffSlider.onValueChange = makeSliderListener(filterCutOffSlider, static_cast(SOUND_CTRL5)); filterCutOffSlider.setTextBoxStyle(Slider::TextBoxBelow, true, filterCutOffSlider.getTextBoxWidth(), filterCutOffSlider.getTextBoxHeight()); + filterCutOffSliderAttachment = make_unique(state, "filterCutOff", filterCutOffSlider); filterResonanceSlider.setSliderStyle(style); filterResonanceSlider.setRange(rangeMin, rangeMax, rangeStep); filterResonanceSlider.onValueChange = makeSliderListener(filterResonanceSlider, static_cast(SOUND_CTRL2)); filterResonanceSlider.setTextBoxStyle(Slider::TextBoxBelow, true, filterResonanceSlider.getTextBoxWidth(), filterResonanceSlider.getTextBoxHeight()); + filterResonanceSliderAttachment = make_unique(state, "filterResonance", filterResonanceSlider); addAndMakeVisible(attackSlider); addAndMakeVisible(decaySlider); diff --git a/Source/SlidersComponent.h b/Source/SlidersComponent.h index 97dfeeb..cc8d1dc 100644 --- a/Source/SlidersComponent.h +++ b/Source/SlidersComponent.h @@ -6,6 +6,7 @@ #include "SlidersFragment.h" using namespace std; +using SliderAttachment = AudioProcessorValueTreeState::SliderAttachment; class SlidersComponent : public Component, public SlidersFragment @@ -13,6 +14,7 @@ class SlidersComponent : public Component, public: SlidersComponent( shared_ptr sharedParams, + AudioProcessorValueTreeState& state, FluidSynthModel* fluidSynthModel); ~SlidersComponent(); @@ -34,29 +36,36 @@ class SlidersComponent : public Component, std::function makeSliderListener(Slider& slider, int controller); shared_ptr sharedParams; + AudioProcessorValueTreeState& state; FluidSynthModel* fluidSynthModel; GroupComponent envelopeGroup; Slider attackSlider; Label attackLabel; + unique_ptr attackSliderAttachment; Slider decaySlider; Label decayLabel; + unique_ptr decaySliderAttachment; Slider sustainSlider; Label sustainLabel; + unique_ptr sustainSliderAttachment; Slider releaseSlider; Label releaseLabel; + unique_ptr releaseSliderAttachment; GroupComponent filterGroup; Slider filterCutOffSlider; Label filterCutOffLabel; + unique_ptr filterCutOffSliderAttachment; Slider filterResonanceSlider; Label filterResonanceLabel; + unique_ptr filterResonanceSliderAttachment; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SlidersComponent) }; From 82f18ed4a0731f2184de8d1cd3002d7e1453f4a7 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Wed, 3 Jul 2019 23:38:56 +0100 Subject: [PATCH 03/33] successfully notified release slider UI to move --- Source/PluginEditor.cpp | 6 ++-- Source/PluginEditor.h | 2 +- Source/PluginProcessor.cpp | 64 +++++++++++++++++++++++++++++++------ Source/PluginProcessor.h | 2 +- Source/SlidersComponent.cpp | 28 ++++++++-------- Source/SlidersComponent.h | 4 +-- 6 files changed, 76 insertions(+), 30 deletions(-) diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 5531372..42b0f63 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -13,15 +13,15 @@ #include "GuiConstants.h" //============================================================================== -JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor(JuicySFAudioProcessor& p, AudioProcessorValueTreeState& state) +JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor(JuicySFAudioProcessor& p, AudioProcessorValueTreeState& valueTreeState) : AudioProcessorEditor{&p}, processor{p}, - state{state}, + valueTreeState{valueTreeState}, sharedParams{p.sharedParams}, midiKeyboard{p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard}, tablesComponent{p.getFluidSynthModel()}, filePicker{p.getFluidSynthModel()}, - slidersComponent{p.sharedParams, state, p.getFluidSynthModel()} + slidersComponent{p.sharedParams, valueTreeState, p.getFluidSynthModel()} { // set resize limits for this plug-in setResizeLimits( diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 6c2fd07..10ada3a 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -49,7 +49,7 @@ class JuicySFAudioProcessorEditor : public AudioProcessorEditor, // access the processor object that created it. JuicySFAudioProcessor& processor; - AudioProcessorValueTreeState& state; + AudioProcessorValueTreeState& valueTreeState; shared_ptr sharedParams; SurjectiveMidiKeyboardComponent midiKeyboard; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index fb9e166..2090560 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -17,6 +17,7 @@ #include "Util.h" #include "SharesParams.h" #include "Params.h" +#include "MidiConstants.h" using namespace std; using Parameter = AudioProcessorValueTreeState::Parameter; @@ -28,7 +29,7 @@ AudioProcessor* JUCE_CALLTYPE createPluginFilter(); JuicySFAudioProcessor::JuicySFAudioProcessor() : AudioProcessor{getBusesProperties()} , sharedParams{static_pointer_cast(make_shared())} -, state{*this, nullptr, +, valueTreeState{*this, nullptr, { "PARAMETERS" /* MYPLUGINSETTINGS */ }, createParameterLayout() } @@ -45,7 +46,8 @@ AudioProcessorValueTreeState::ParameterLayout JuicySFAudioProcessor::createParam // for (int i = 1; i < 9; ++i) // params.push_back (std::make_unique (String (i), String (i), 0, i, 0)); - vector> params{ + // https://stackoverflow.com/a/8469002/5257399 + unique_ptr params[] = { make_unique("attack", "volume envelope attack time", 0, 127, 0, "A" ), make_unique("decay", "volume envelope sustain attentuation", 0, 127, 0, "D" ), make_unique("sustain", "volume envelope decay time", 0, 127, 0, "S" ), @@ -54,7 +56,10 @@ AudioProcessorValueTreeState::ParameterLayout JuicySFAudioProcessor::createParam make_unique("filterResonance", "low-pass filter resonance attentuation", 0, 127, 0, "Res" ), }; - return { params.begin(), params.end() }; + return { + make_move_iterator(begin(params)), + make_move_iterator(end(params)) + }; } JuicySFAudioProcessor::~JuicySFAudioProcessor() @@ -205,12 +210,53 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer fluid_synth_handle_midi_event(fluidSynth, midi_event); delete_fluid_midi_event(midi_event); - sharedParams->acceptMidiControlEvent(m.getControllerNumber(), m.getControllerValue()); + switch(static_cast(m.getControllerNumber())) { + case SOUND_CTRL2: { // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) + valueTreeState.state.setProperty({"filterResonance"}, m.getControllerValue(), nullptr); + break; + } + case SOUND_CTRL3: { // MIDI CC 72 Release time +// valueTreeState.state.setProperty({"release"}, m.getControllerValue(), nullptr); +// valueTreeState.state.flushParameterValuesToValueTree(); +// jassert(dynamic_cast (editor) != nullptr); + RangedAudioParameter *param {valueTreeState.getParameter("release")}; +// dynamic_cast(*param) + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; +// castParam->setValue(m.getControllerValue()); +// castParam-> +// param->setValue(m.getControllerValue()); +// param->setValueNotifyingHost(m.getControllerValue()); + *castParam = m.getControllerValue(); + break; + } + case SOUND_CTRL4: { // MIDI CC 73 Attack time + valueTreeState.state.setProperty({"attack"}, m.getControllerValue(), nullptr); + break; + } + case SOUND_CTRL5: { // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) + valueTreeState.state.setProperty({"filterCutOff"}, m.getControllerValue(), nullptr); + break; + } + case SOUND_CTRL6: { // MIDI CC 75 Decay Time + valueTreeState.state.setProperty({"decay"}, m.getControllerValue(), nullptr); + break; + } + case SOUND_CTRL10: { // MIDI CC 79 undefined + valueTreeState.state.setProperty({"sustain"}, m.getControllerValue(), nullptr); + break; + } + default: { + break; + } + } - AudioProcessorEditor* editor{getActiveEditor()}; - jassert(dynamic_cast (editor) != nullptr); - ExposesComponents* exposesComponents{dynamic_cast(editor)}; - exposesComponents->getSliders().acceptMidiControlEvent(m.getControllerNumber(), m.getControllerValue()); + // sharedParams->acceptMidiControlEvent(m.getControllerNumber(), m.getControllerValue()); + + // AudioProcessorEditor* editor{getActiveEditor()}; + // jassert(dynamic_cast (editor) != nullptr); + // ExposesComponents* exposesComponents{dynamic_cast(editor)}; + // exposesComponents->getSliders().acceptMidiControlEvent(m.getControllerNumber(), m.getControllerValue()); } else if (m.isProgramChange()) { fluid_midi_event_t *midi_event(new_fluid_midi_event()); fluid_midi_event_set_type(midi_event, static_cast(PROGRAM_CHANGE)); @@ -291,7 +337,7 @@ bool JuicySFAudioProcessor::hasEditor() const AudioProcessorEditor* JuicySFAudioProcessor::createEditor() { // grab a raw pointer to it for our own use - return /*pluginEditor = */new JuicySFAudioProcessorEditor (*this, state); + return /*pluginEditor = */new JuicySFAudioProcessorEditor (*this, valueTreeState); } //============================================================================== diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 93554f2..a0599f8 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -74,7 +74,7 @@ class JuicySFAudioProcessor : public AudioProcessor private: void initialiseSynth(); - AudioProcessorValueTreeState state; + AudioProcessorValueTreeState valueTreeState; FluidSynthModel fluidSynthModel; fluid_synth_t* fluidSynth; diff --git a/Source/SlidersComponent.cpp b/Source/SlidersComponent.cpp index 608a7fd..edf88a5 100644 --- a/Source/SlidersComponent.cpp +++ b/Source/SlidersComponent.cpp @@ -106,10 +106,10 @@ void SlidersComponent::acceptMidiControlEvent(int controller, int value) { SlidersComponent::SlidersComponent( shared_ptr sharedParams, - AudioProcessorValueTreeState& state, + AudioProcessorValueTreeState& valueTreeState, FluidSynthModel* fluidSynthModel) : sharedParams{sharedParams} -, state{state} +, valueTreeState{valueTreeState} , fluidSynthModel{fluidSynthModel} , envelopeGroup{"envelopeGroup", "Envelope"} , filterGroup{"filterGroup", "Filter"} @@ -121,39 +121,39 @@ SlidersComponent::SlidersComponent( attackSlider.setSliderStyle(style); attackSlider.setRange(rangeMin, rangeMax, rangeStep); - attackSlider.onValueChange = makeSliderListener(attackSlider, static_cast(SOUND_CTRL4)); + // attackSlider.onValueChange = makeSliderListener(attackSlider, static_cast(SOUND_CTRL4)); attackSlider.setTextBoxStyle(Slider::TextBoxBelow, true, attackSlider.getTextBoxWidth(), attackSlider.getTextBoxHeight()); - attackSliderAttachment = make_unique(state, "attack", attackSlider); + attackSliderAttachment = make_unique(valueTreeState, "attack", attackSlider); decaySlider.setSliderStyle(style); decaySlider.setRange(rangeMin, rangeMax, rangeStep); - decaySlider.onValueChange = makeSliderListener(decaySlider, static_cast(SOUND_CTRL6)); + // decaySlider.onValueChange = makeSliderListener(decaySlider, static_cast(SOUND_CTRL6)); decaySlider.setTextBoxStyle(Slider::TextBoxBelow, true, decaySlider.getTextBoxWidth(), decaySlider.getTextBoxHeight()); - decaySliderAttachment = make_unique(state, "decay", decaySlider); + decaySliderAttachment = make_unique(valueTreeState, "decay", decaySlider); sustainSlider.setSliderStyle(style); sustainSlider.setRange(rangeMin, rangeMax, rangeStep); - sustainSlider.onValueChange = makeSliderListener(sustainSlider, static_cast(SOUND_CTRL10)); + // sustainSlider.onValueChange = makeSliderListener(sustainSlider, static_cast(SOUND_CTRL10)); sustainSlider.setTextBoxStyle(Slider::TextBoxBelow, true, sustainSlider.getTextBoxWidth(), sustainSlider.getTextBoxHeight()); - sustainSliderAttachment = make_unique(state, "sustain", sustainSlider); + sustainSliderAttachment = make_unique(valueTreeState, "sustain", sustainSlider); releaseSlider.setSliderStyle(style); releaseSlider.setRange(rangeMin, rangeMax, rangeStep); - releaseSlider.onValueChange = makeSliderListener(releaseSlider, static_cast(SOUND_CTRL3)); + // releaseSlider.onValueChange = makeSliderListener(releaseSlider, static_cast(SOUND_CTRL3)); releaseSlider.setTextBoxStyle(Slider::TextBoxBelow, true, releaseSlider.getTextBoxWidth(), releaseSlider.getTextBoxHeight()); - releaseSliderAttachment = make_unique(state, "release", releaseSlider); + releaseSliderAttachment = make_unique(valueTreeState, "release", releaseSlider); filterCutOffSlider.setSliderStyle(style); filterCutOffSlider.setRange(rangeMin, rangeMax, rangeStep); - filterCutOffSlider.onValueChange = makeSliderListener(filterCutOffSlider, static_cast(SOUND_CTRL5)); + // filterCutOffSlider.onValueChange = makeSliderListener(filterCutOffSlider, static_cast(SOUND_CTRL5)); filterCutOffSlider.setTextBoxStyle(Slider::TextBoxBelow, true, filterCutOffSlider.getTextBoxWidth(), filterCutOffSlider.getTextBoxHeight()); - filterCutOffSliderAttachment = make_unique(state, "filterCutOff", filterCutOffSlider); + filterCutOffSliderAttachment = make_unique(valueTreeState, "filterCutOff", filterCutOffSlider); filterResonanceSlider.setSliderStyle(style); filterResonanceSlider.setRange(rangeMin, rangeMax, rangeStep); - filterResonanceSlider.onValueChange = makeSliderListener(filterResonanceSlider, static_cast(SOUND_CTRL2)); + // filterResonanceSlider.onValueChange = makeSliderListener(filterResonanceSlider, static_cast(SOUND_CTRL2)); filterResonanceSlider.setTextBoxStyle(Slider::TextBoxBelow, true, filterResonanceSlider.getTextBoxWidth(), filterResonanceSlider.getTextBoxHeight()); - filterResonanceSliderAttachment = make_unique(state, "filterResonance", filterResonanceSlider); + filterResonanceSliderAttachment = make_unique(valueTreeState, "filterResonance", filterResonanceSlider); addAndMakeVisible(attackSlider); addAndMakeVisible(decaySlider); diff --git a/Source/SlidersComponent.h b/Source/SlidersComponent.h index cc8d1dc..613182c 100644 --- a/Source/SlidersComponent.h +++ b/Source/SlidersComponent.h @@ -14,7 +14,7 @@ class SlidersComponent : public Component, public: SlidersComponent( shared_ptr sharedParams, - AudioProcessorValueTreeState& state, + AudioProcessorValueTreeState& valueTreeState, FluidSynthModel* fluidSynthModel); ~SlidersComponent(); @@ -36,7 +36,7 @@ class SlidersComponent : public Component, std::function makeSliderListener(Slider& slider, int controller); shared_ptr sharedParams; - AudioProcessorValueTreeState& state; + AudioProcessorValueTreeState& valueTreeState; FluidSynthModel* fluidSynthModel; GroupComponent envelopeGroup; From 7582fbcc9ef031db94941ef69552c3b953fb31d9 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Thu, 4 Jul 2019 23:48:20 +0100 Subject: [PATCH 04/33] sliders control fluidsynth when user interacts with them, but also update their position to reflect incoming MIDI messages, without attempting to control fluidsynth (since audio processor will have already done so) --- Source/PluginProcessor.cpp | 30 +++++++++++++++++------------- Source/SlidersComponent.cpp | 22 ++++++++++++++++------ 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 2090560..e2ddf59 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -47,7 +47,7 @@ AudioProcessorValueTreeState::ParameterLayout JuicySFAudioProcessor::createParam // params.push_back (std::make_unique (String (i), String (i), 0, i, 0)); // https://stackoverflow.com/a/8469002/5257399 - unique_ptr params[] = { + unique_ptr params[] { make_unique("attack", "volume envelope attack time", 0, 127, 0, "A" ), make_unique("decay", "volume envelope sustain attentuation", 0, 127, 0, "D" ), make_unique("sustain", "volume envelope decay time", 0, 127, 0, "S" ), @@ -216,34 +216,38 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer break; } case SOUND_CTRL3: { // MIDI CC 72 Release time -// valueTreeState.state.setProperty({"release"}, m.getControllerValue(), nullptr); -// valueTreeState.state.flushParameterValuesToValueTree(); -// jassert(dynamic_cast (editor) != nullptr); RangedAudioParameter *param {valueTreeState.getParameter("release")}; -// dynamic_cast(*param) jassert(dynamic_cast (param) != nullptr); AudioParameterInt* castParam {dynamic_cast (param)}; -// castParam->setValue(m.getControllerValue()); -// castParam-> -// param->setValue(m.getControllerValue()); -// param->setValueNotifyingHost(m.getControllerValue()); *castParam = m.getControllerValue(); break; } case SOUND_CTRL4: { // MIDI CC 73 Attack time - valueTreeState.state.setProperty({"attack"}, m.getControllerValue(), nullptr); + RangedAudioParameter *param {valueTreeState.getParameter("release")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + *castParam = m.getControllerValue(); break; } case SOUND_CTRL5: { // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) - valueTreeState.state.setProperty({"filterCutOff"}, m.getControllerValue(), nullptr); + RangedAudioParameter *param {valueTreeState.getParameter("filterCutOff")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + *castParam = m.getControllerValue(); break; } case SOUND_CTRL6: { // MIDI CC 75 Decay Time - valueTreeState.state.setProperty({"decay"}, m.getControllerValue(), nullptr); + RangedAudioParameter *param {valueTreeState.getParameter("decay")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + *castParam = m.getControllerValue(); break; } case SOUND_CTRL10: { // MIDI CC 79 undefined - valueTreeState.state.setProperty({"sustain"}, m.getControllerValue(), nullptr); + RangedAudioParameter *param {valueTreeState.getParameter("sustain")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + *castParam = m.getControllerValue(); break; } default: { diff --git a/Source/SlidersComponent.cpp b/Source/SlidersComponent.cpp index edf88a5..789e696 100644 --- a/Source/SlidersComponent.cpp +++ b/Source/SlidersComponent.cpp @@ -9,10 +9,20 @@ #include "SlidersComponent.h" #include "FluidSynthModel.h" #include "MidiConstants.h" +#include "Util.h" using SliderAttachment = AudioProcessorValueTreeState::SliderAttachment; std::function SlidersComponent::makeSliderListener(Slider& slider, int controller/*, std::function callback*/) { return [this, controller, &slider]{ + + RangedAudioParameter *param {valueTreeState.getParameter("release")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + int value {castParam->get()}; + + String s{"slider "}; + s << slider.getComponentID() << ", controller " << controller << ", value " << slider.getValue() << ", xmlReleaseValue " << value; + DEBUG_PRINT(s); // slider.setValue(slider.getValue(), NotificationType::dontSendNotification); fluidSynthModel->setControllerValue(controller, slider.getValue()); // callback(); @@ -121,37 +131,37 @@ SlidersComponent::SlidersComponent( attackSlider.setSliderStyle(style); attackSlider.setRange(rangeMin, rangeMax, rangeStep); - // attackSlider.onValueChange = makeSliderListener(attackSlider, static_cast(SOUND_CTRL4)); + attackSlider.onDragEnd = makeSliderListener(attackSlider, static_cast(SOUND_CTRL4)); attackSlider.setTextBoxStyle(Slider::TextBoxBelow, true, attackSlider.getTextBoxWidth(), attackSlider.getTextBoxHeight()); attackSliderAttachment = make_unique(valueTreeState, "attack", attackSlider); decaySlider.setSliderStyle(style); decaySlider.setRange(rangeMin, rangeMax, rangeStep); - // decaySlider.onValueChange = makeSliderListener(decaySlider, static_cast(SOUND_CTRL6)); + decaySlider.onDragEnd = makeSliderListener(decaySlider, static_cast(SOUND_CTRL6)); decaySlider.setTextBoxStyle(Slider::TextBoxBelow, true, decaySlider.getTextBoxWidth(), decaySlider.getTextBoxHeight()); decaySliderAttachment = make_unique(valueTreeState, "decay", decaySlider); sustainSlider.setSliderStyle(style); sustainSlider.setRange(rangeMin, rangeMax, rangeStep); - // sustainSlider.onValueChange = makeSliderListener(sustainSlider, static_cast(SOUND_CTRL10)); + sustainSlider.onDragEnd = makeSliderListener(sustainSlider, static_cast(SOUND_CTRL10)); sustainSlider.setTextBoxStyle(Slider::TextBoxBelow, true, sustainSlider.getTextBoxWidth(), sustainSlider.getTextBoxHeight()); sustainSliderAttachment = make_unique(valueTreeState, "sustain", sustainSlider); releaseSlider.setSliderStyle(style); releaseSlider.setRange(rangeMin, rangeMax, rangeStep); - // releaseSlider.onValueChange = makeSliderListener(releaseSlider, static_cast(SOUND_CTRL3)); + releaseSlider.onDragEnd = makeSliderListener(releaseSlider, static_cast(SOUND_CTRL3)); releaseSlider.setTextBoxStyle(Slider::TextBoxBelow, true, releaseSlider.getTextBoxWidth(), releaseSlider.getTextBoxHeight()); releaseSliderAttachment = make_unique(valueTreeState, "release", releaseSlider); filterCutOffSlider.setSliderStyle(style); filterCutOffSlider.setRange(rangeMin, rangeMax, rangeStep); - // filterCutOffSlider.onValueChange = makeSliderListener(filterCutOffSlider, static_cast(SOUND_CTRL5)); + filterCutOffSlider.onDragEnd = makeSliderListener(filterCutOffSlider, static_cast(SOUND_CTRL5)); filterCutOffSlider.setTextBoxStyle(Slider::TextBoxBelow, true, filterCutOffSlider.getTextBoxWidth(), filterCutOffSlider.getTextBoxHeight()); filterCutOffSliderAttachment = make_unique(valueTreeState, "filterCutOff", filterCutOffSlider); filterResonanceSlider.setSliderStyle(style); filterResonanceSlider.setRange(rangeMin, rangeMax, rangeStep); - // filterResonanceSlider.onValueChange = makeSliderListener(filterResonanceSlider, static_cast(SOUND_CTRL2)); + filterResonanceSlider.onDragEnd = makeSliderListener(filterResonanceSlider, static_cast(SOUND_CTRL2)); filterResonanceSlider.setTextBoxStyle(Slider::TextBoxBelow, true, filterResonanceSlider.getTextBoxWidth(), filterResonanceSlider.getTextBoxHeight()); filterResonanceSliderAttachment = make_unique(valueTreeState, "filterResonance", filterResonanceSlider); From 6d2267e23aac14c1ce049456058d060847c198f8 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 7 Jul 2019 00:22:47 +0100 Subject: [PATCH 05/33] prefer references where owner will definitely outlive recipient. replace some fluidsynth raw pointers with smart pointers. --- Source/FilePicker.cpp | 32 ++--- Source/FilePicker.h | 6 +- Source/FluidSynthModel.cpp | 200 +++++++++++++++++-------------- Source/FluidSynthModel.h | 20 ++-- Source/Params.cpp | 208 ++++++++++++++++----------------- Source/Params.h | 52 ++++----- Source/PluginEditor.cpp | 14 +-- Source/PluginEditor.h | 7 +- Source/PluginProcessor.cpp | 95 ++++++++------- Source/PluginProcessor.h | 7 +- Source/PresetsToBanks.h | 6 +- Source/SharesParams.h | 38 +++--- Source/SlidersComponent.cpp | 6 +- Source/SlidersComponent.h | 8 +- Source/SoundfontSynthVoice.cpp | 20 ++-- Source/SoundfontSynthVoice.h | 6 +- Source/TablesComponent.cpp | 23 ++-- Source/TablesComponent.h | 6 +- 18 files changed, 411 insertions(+), 343 deletions(-) diff --git a/Source/FilePicker.cpp b/Source/FilePicker.cpp index 38517c0..6ddf8ab 100644 --- a/Source/FilePicker.cpp +++ b/Source/FilePicker.cpp @@ -6,24 +6,26 @@ #include "MyColours.h" FilePicker::FilePicker( - FluidSynthModel* fluidSynthModel + AudioProcessorValueTreeState& valueTreeState, + FluidSynthModel& fluidSynthModel ) -: fileChooser( - "File", - File(), - true, - false, - false, - "*.sf2;*.sf3", - String(), - "Choose a Soundfont file to load into the synthesizer" -), - fluidSynthModel(fluidSynthModel), - currentPath() { +: fileChooser{ + "File", + File(), + true, + false, + false, + "*.sf2;*.sf3", + String(), + "Choose a Soundfont file to load into the synthesizer"} +, valueTreeState{valueTreeState} +, fluidSynthModel{fluidSynthModel} +// , currentPath{} +{ // faster (rounded edges introduce transparency) setOpaque (true); - setDisplayedFilePath(fluidSynthModel->getCurrentSoundFontAbsPath()); + setDisplayedFilePath(fluidSynthModel.getCurrentSoundFontAbsPath()); addAndMakeVisible (fileChooser); fileChooser.addListener (this); @@ -47,7 +49,7 @@ void FilePicker::paint(Graphics& g) void FilePicker::filenameComponentChanged (FilenameComponent*) { currentPath = fileChooser.getCurrentFile().getFullPathName(); - fluidSynthModel->onFileNameChanged(fileChooser.getCurrentFile().getFullPathName(), -1, -1); + fluidSynthModel.onFileNameChanged(fileChooser.getCurrentFile().getFullPathName(), -1, -1); } void FilePicker::setDisplayedFilePath(const String& path) { diff --git a/Source/FilePicker.h b/Source/FilePicker.h index 8a1c3bc..c187724 100644 --- a/Source/FilePicker.h +++ b/Source/FilePicker.h @@ -14,7 +14,8 @@ class FilePicker: public Component, { public: FilePicker( - FluidSynthModel* fluidSynthModel + AudioProcessorValueTreeState& valueTreeState, + FluidSynthModel& fluidSynthModel ); ~FilePicker(); @@ -25,7 +26,8 @@ class FilePicker: public Component, private: FilenameComponent fileChooser; - FluidSynthModel* fluidSynthModel; + AudioProcessorValueTreeState& valueTreeState; + FluidSynthModel& fluidSynthModel; String currentPath; diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 085323f..27ab5fd 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -3,33 +3,46 @@ // #include +#include #include "FluidSynthModel.h" #include "MidiConstants.h" +#include "Util.h" using namespace std; -FluidSynthModel::FluidSynthModel(shared_ptr sharedParams) - : sharedParams{sharedParams}, - synth{nullptr}, - settings{nullptr}, - currentSoundFontAbsPath{}, - currentSampleRate{44100}, - initialised{false}, - sfont_id{0}, - channel{0}/*, - mod(nullptr)*/ +FluidSynthModel::FluidSynthModel( + AudioProcessorValueTreeState& valueTreeState, + SharesParams& sharedParams + ) +: valueTreeState{valueTreeState} +, sharedParams{sharedParams} +//, synth{nullptr} +, settings{nullptr, nullptr} +, currentSoundFontAbsPath{} +, currentSampleRate{44100} +, initialised{false} +, sfont_id{0} +, channel{0}/*, +mod(nullptr)*/ {} -FluidSynthModel::~FluidSynthModel() { - if (initialised) { +// FluidSynthModel::~FluidSynthModel() { + // if (initialised) { // delete_fluid_audio_driver(driver); - delete_fluid_synth(synth); - delete_fluid_settings(settings); + // delete_fluid_synth(synth); +// delete_fluid_settings(settings); // delete driver; // delete settings; // delete_fluid_mod(mod); - } + // } +// } + +int FluidSynthModel::handleMidiEvent(void* data, fluid_midi_event_t* event) +{ +// DEBUG_PRINT(fluid_midi_event_get_type(event)); + // printf("event type: %d\n", fluid_midi_event_get_type(event)); + return 0; } void FluidSynthModel::initialise() { @@ -37,26 +50,47 @@ void FluidSynthModel::initialise() { // delete_fluid_synth(synth); // delete_fluid_settings(settings); // } + settings = { new_fluid_settings(), delete_fluid_settings }; + // deactivate all audio drivers in fluidsynth to avoid FL Studio deadlock when initialising CoreAudio // after all: we only use fluidsynth to render blocks of audio. it doesn't output to audio driver. - const char *DRV[] = {NULL}; + const char *DRV[] {NULL}; fluid_audio_driver_register(DRV); + +// handle_midi_event_func_t handler = [](void* data, fluid_midi_event_t* event) -> int { +// +// }; + +// midiDriver = unique_ptr( +// new_fluid_midi_driver( +// settings.get(), +// [](void* data, fluid_midi_event_t* event) -> int { +// +// }, +// nullptr), +// delete_fluid_midi_driver); - settings = new_fluid_settings(); + // https://sourceforge.net/p/fluidsynth/wiki/FluidSettings/ #if JUCE_DEBUG - fluid_settings_setint(settings, "synth.verbose", 1); + fluid_settings_setint(settings.get(), "synth.verbose", 1); #endif - synth = new_fluid_synth(settings); - fluid_synth_set_sample_rate(synth, currentSampleRate); + synth = { new_fluid_synth(settings.get()), delete_fluid_synth }; + fluid_synth_set_sample_rate(synth.get(), currentSampleRate); + + valueTreeState.getParameter("soundFontPath")->getValue(); + // RangedAudioParameter *param {valueTreeState.getParameter("release")}; + // jassert(dynamic_cast (param) != nullptr); + // AudioParameterInt* castParam {dynamic_cast (param)}; + // *castParam = m.getControllerValue(); - if (sharedParams->getSoundFontPath().isNotEmpty()) { - loadFont(sharedParams->getSoundFontPath()); - changePreset(sharedParams->getBank(), sharedParams->getPreset()); + if (sharedParams.getSoundFontPath().isNotEmpty()) { + loadFont(sharedParams.getSoundFontPath()); +// changePreset(sharedParams->getBank(), sharedParams->getPreset()); } - fluid_synth_set_gain(synth, 2.0); + fluid_synth_set_gain(synth.get(), 2.0); for(int i{SOUND_CTRL1}; i <= SOUND_CTRL10; i++) { @@ -81,90 +115,84 @@ void FluidSynthModel::initialise() { // all SOUND_CTRL are inited with value of 64, not zero. // "Just like panning, a value of 64 indicates no change for sound ctrls" - fluid_mod_t *mod(new_fluid_mod()); + unique_ptr mod{new_fluid_mod(), delete_fluid_mod}; // - fluid_mod_set_source1(mod, + fluid_mod_set_source1(mod.get(), static_cast(SOUND_CTRL2), // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) FLUID_MOD_CC | FLUID_MOD_UNIPOLAR | FLUID_MOD_CONCAVE | FLUID_MOD_POSITIVE); - fluid_mod_set_source2(mod, 0, 0); - fluid_mod_set_dest(mod, GEN_FILTERQ); - fluid_mod_set_amount(mod, FLUID_PEAK_ATTENUATION); - fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD); - delete_fluid_mod(mod); + fluid_mod_set_source2(mod.get(), 0, 0); + fluid_mod_set_dest(mod.get(), GEN_FILTERQ); + fluid_mod_set_amount(mod.get(), FLUID_PEAK_ATTENUATION); + fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); - mod = new_fluid_mod(); - fluid_mod_set_source1(mod, + mod = {new_fluid_mod(), delete_fluid_mod}; + fluid_mod_set_source1(mod.get(), static_cast(SOUND_CTRL3), // MIDI CC 72 Release time FLUID_MOD_CC | FLUID_MOD_UNIPOLAR | FLUID_MOD_LINEAR | FLUID_MOD_POSITIVE); - fluid_mod_set_source2(mod, 0, 0); - fluid_mod_set_dest(mod, GEN_VOLENVRELEASE); -// fluid_mod_set_amount(mod, 15200.0f); - fluid_mod_set_amount(mod, env_amount); - fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD); - delete_fluid_mod(mod); + fluid_mod_set_source2(mod.get(), 0, 0); + fluid_mod_set_dest(mod.get(), GEN_VOLENVRELEASE); +// fluid_mod_set_amount(mod.get(), 15200.0f); + fluid_mod_set_amount(mod.get(), env_amount); + fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); - mod = new_fluid_mod(); - fluid_mod_set_source1(mod, + mod = {new_fluid_mod(), delete_fluid_mod}; + fluid_mod_set_source1(mod.get(), static_cast(SOUND_CTRL4), // MIDI CC 73 Attack time FLUID_MOD_CC | FLUID_MOD_UNIPOLAR | FLUID_MOD_LINEAR | FLUID_MOD_POSITIVE); - fluid_mod_set_source2(mod, 0, 0); - fluid_mod_set_dest(mod, GEN_VOLENVATTACK); - fluid_mod_set_amount(mod, env_amount); - fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD); - delete_fluid_mod(mod); + fluid_mod_set_source2(mod.get(), 0, 0); + fluid_mod_set_dest(mod.get(), GEN_VOLENVATTACK); + fluid_mod_set_amount(mod.get(), env_amount); + fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); // soundfont spec says that if cutoff is >20kHz and resonance Q is 0, then no filtering occurs - mod = new_fluid_mod(); - fluid_mod_set_source1(mod, + mod = {new_fluid_mod(), delete_fluid_mod}; + fluid_mod_set_source1(mod.get(), static_cast(SOUND_CTRL5), // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) FLUID_MOD_CC | FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE); - fluid_mod_set_source2(mod, 0, 0); - fluid_mod_set_dest(mod, GEN_FILTERFC); - fluid_mod_set_amount(mod, -2400.0f); - fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD); - delete_fluid_mod(mod); + fluid_mod_set_source2(mod.get(), 0, 0); + fluid_mod_set_dest(mod.get(), GEN_FILTERFC); + fluid_mod_set_amount(mod.get(), -2400.0f); + fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); - mod = new_fluid_mod(); - fluid_mod_set_source1(mod, + mod = {new_fluid_mod(), delete_fluid_mod}; + fluid_mod_set_source1(mod.get(), static_cast(SOUND_CTRL6), // MIDI CC 75 Decay Time FLUID_MOD_CC | FLUID_MOD_UNIPOLAR | FLUID_MOD_LINEAR | FLUID_MOD_POSITIVE); - fluid_mod_set_source2(mod, 0, 0); - fluid_mod_set_dest(mod, GEN_VOLENVDECAY); - fluid_mod_set_amount(mod, env_amount); - fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD); - delete_fluid_mod(mod); + fluid_mod_set_source2(mod.get(), 0, 0); + fluid_mod_set_dest(mod.get(), GEN_VOLENVDECAY); + fluid_mod_set_amount(mod.get(), env_amount); + fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); - mod = new_fluid_mod(); - fluid_mod_set_source1(mod, + mod = {new_fluid_mod(), delete_fluid_mod}; + fluid_mod_set_source1(mod.get(), static_cast(SOUND_CTRL10), // MIDI CC 79 undefined FLUID_MOD_CC | FLUID_MOD_UNIPOLAR | FLUID_MOD_CONCAVE | FLUID_MOD_POSITIVE); - fluid_mod_set_source2(mod, 0, 0); - fluid_mod_set_dest(mod, GEN_VOLENVSUSTAIN); + fluid_mod_set_source2(mod.get(), 0, 0); + fluid_mod_set_dest(mod.get(), GEN_VOLENVSUSTAIN); // fluice_voice.c#fluid_voice_update_param() // clamps the range to between 0 and 1000, so we'll copy that - fluid_mod_set_amount(mod, 1000.0f); - fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD); - delete_fluid_mod(mod); + fluid_mod_set_amount(mod.get(), 1000.0f); + fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); - initialised = true; + // initialised = true; } void FluidSynthModel::setControllerValue(int controller, int value) { @@ -173,7 +201,7 @@ void FluidSynthModel::setControllerValue(int controller, int value) { fluid_midi_event_set_channel(midi_event, channel); fluid_midi_event_set_control(midi_event, controller); fluid_midi_event_set_value(midi_event, value); - fluid_synth_handle_midi_event(synth, midi_event); + fluid_synth_handle_midi_event(synth.get(), midi_event); delete_fluid_midi_event(midi_event); // fluid_channel_set_cc(channel, i, 0); } @@ -189,16 +217,16 @@ void FluidSynthModel::changePreset(int bank, int preset) { preset = bankAndPreset->getPreset(); } changePresetImpl(bank, preset); - sharedParams->setPreset(preset); - sharedParams->setBank(bank); +// sharedParams->setPreset(preset); +// sharedParams->setBank(bank); } void FluidSynthModel::changePresetImpl(int bank, int preset) { - fluid_synth_program_select(synth, channel, sfont_id, static_cast(bank), static_cast(preset)); + fluid_synth_program_select(synth.get(), channel, sfont_id, static_cast(bank), static_cast(preset)); } fluid_preset_t* FluidSynthModel::getFirstPreset() { - fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, sfont_id); + fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth.get(), sfont_id); jassert(sfont != nullptr); fluid_sfont_iteration_start(sfont); @@ -209,7 +237,7 @@ fluid_preset_t* FluidSynthModel::getFirstPreset() { unique_ptr FluidSynthModel::getFirstBankAndPreset() { fluid_preset_t* preset = getFirstPreset(); - int offset = fluid_synth_get_bank_offset(synth, sfont_id); + int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); return make_unique(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset)); }; @@ -217,7 +245,7 @@ unique_ptr FluidSynthModel::getFirstBankAndPreset() { void FluidSynthModel::selectFirstPreset() { fluid_preset_t* preset = getFirstPreset(); - int offset = fluid_synth_get_bank_offset(synth, sfont_id); + int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); changePreset(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset)); } @@ -225,14 +253,14 @@ void FluidSynthModel::selectFirstPreset() { BanksToPresets FluidSynthModel::getBanks() { BanksToPresets banksToPresets; - int soundfontCount = fluid_synth_sfcount(synth); + int soundfontCount = fluid_synth_sfcount(synth.get()); if (soundfontCount == 0) { // no soundfont selected return banksToPresets; } - fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, sfont_id); + fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth.get(), sfont_id); if(sfont == nullptr) { // no soundfont found by that ID // the above guard (soundfontCount) protects us for the @@ -240,7 +268,7 @@ BanksToPresets FluidSynthModel::getBanks() { return banksToPresets; } - int offset = fluid_synth_get_bank_offset(synth, sfont_id); + int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); fluid_sfont_iteration_start(sfont); @@ -259,7 +287,7 @@ BanksToPresets FluidSynthModel::getBanks() { return banksToPresets; } -fluid_synth_t* FluidSynthModel::getSynth() { +shared_ptr FluidSynthModel::getSynth() { // https://msdn.microsoft.com/en-us/library/hh279669.aspx // You can pass a shared_ptr to another function in the following ways: // Pass the shared_ptr by value. This invokes the copy constructor, increments the reference count, and makes the callee an owner. @@ -272,14 +300,14 @@ void FluidSynthModel::onFileNameChanged(const String &absPath, int bank, int pre } unloadAndLoadFont(absPath); changePreset(bank, preset); - sharedParams->setSoundFontPath(absPath); + sharedParams.setSoundFontPath(absPath); eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath); } void FluidSynthModel::unloadAndLoadFont(const String &absPath) { // in the base case, there is no font loaded - if (fluid_synth_sfcount(synth) > 0) { - fluid_synth_sfunload(synth, sfont_id, 1); + if (fluid_synth_sfcount(synth.get()) > 0) { + fluid_synth_sfunload(synth.get(), sfont_id, 1); } loadFont(absPath); } @@ -287,7 +315,7 @@ void FluidSynthModel::unloadAndLoadFont(const String &absPath) { void FluidSynthModel::loadFont(const String &absPath) { currentSoundFontAbsPath = absPath; sfont_id++; - fluid_synth_sfload(synth, absPath.toStdString().c_str(), 1); + fluid_synth_sfload(synth.get(), absPath.toStdString().c_str(), 1); } FluidSynthModel::Listener::~Listener() { @@ -323,9 +351,11 @@ void FluidSynthModel::removeListener (FluidSynthModel::Listener* const listener) void FluidSynthModel::setSampleRate(float sampleRate) { currentSampleRate = sampleRate; - if (!initialised) { + // https://stackoverflow.com/a/40856043/5257399 + // test if a smart pointer is null + if (!synth) { // don't worry; we'll do this in initialise phase regardless return; } - fluid_synth_set_sample_rate(synth, sampleRate); + fluid_synth_set_sample_rate(synth.get(), sampleRate); } diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index 7785306..85a0b94 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -19,10 +19,13 @@ using namespace std; class FluidSynthModel { public: - FluidSynthModel(shared_ptr sharedParams); - ~FluidSynthModel(); + FluidSynthModel( + AudioProcessorValueTreeState& valueTreeState, + SharesParams& sharedParams + ); + // ~FluidSynthModel(); - fluid_synth_t* getSynth(); + shared_ptr getSynth(); void initialise(); BanksToPresets getBanks(); @@ -65,11 +68,14 @@ class FluidSynthModel { const String& getCurrentSoundFontAbsPath(); private: - shared_ptr sharedParams; + int handleMidiEvent(void* data, fluid_midi_event_t* event); - fluid_synth_t* synth; - fluid_settings_t* settings; -// fluid_audio_driver_t* driver; + AudioProcessorValueTreeState& valueTreeState; + SharesParams& sharedParams; + + shared_ptr synth; + unique_ptr settings; + // unique_ptr midiDriver; String currentSoundFontAbsPath; diff --git a/Source/Params.cpp b/Source/Params.cpp index a29b722..88c0046 100644 --- a/Source/Params.cpp +++ b/Source/Params.cpp @@ -14,73 +14,73 @@ using namespace std; -Params::Params() noexcept : - uiWidth{GuiConstants::minWidth}, - uiHeight{GuiConstants::minHeight}, - soundFontPath{String()}, - preset{-1}, - bank{-1}, - attack{0}, - decay{0}, - sustain{0}, - release{0}, - filterCutOff{0}, - filterResonance{0} +Params::Params() noexcept +: uiWidth{GuiConstants::minWidth} +, uiHeight{GuiConstants::minHeight} +, soundFontPath{String()} +// , preset{-1} +// , bank{-1} +// , attack{0} +// , decay{0} +// , sustain{0} +// , release{0} +// , filterCutOff{0} +// .filterResonance{0} { } -void Params::setAttributesOnXml(XmlElement& xml) { - xml.setAttribute("uiWidth", uiWidth); - xml.setAttribute("uiHeight", uiHeight); - xml.setAttribute("soundFontPath", soundFontPath); - xml.setAttribute("preset", preset); - xml.setAttribute("bank", bank); - xml.setAttribute("attack", attack); - xml.setAttribute("decay", decay); - xml.setAttribute("sustain", sustain); - xml.setAttribute("release", release); - xml.setAttribute("filterCutOff", filterCutOff); - xml.setAttribute("filterResonance", filterResonance); +void Params::setAttributesOnXml(shared_ptr xml) { + xml->setAttribute("uiWidth", uiWidth); + xml->setAttribute("uiHeight", uiHeight); + xml->setAttribute("soundFontPath", soundFontPath); + // xml.setAttribute("preset", preset); + // xml.setAttribute("bank", bank); + // xml.setAttribute("attack", attack); + // xml.setAttribute("decay", decay); + // xml.setAttribute("sustain", sustain); + // xml.setAttribute("release", release); + // xml.setAttribute("filterCutOff", filterCutOff); + // xml.setAttribute("filterResonance", filterResonance); } void Params::loadAttributesFromXml(shared_ptr xmlState) { uiWidth = jmin(jmax(xmlState->getIntAttribute("uiWidth", uiWidth), GuiConstants::minWidth), GuiConstants::maxWidth); uiHeight = jmin(jmax(xmlState->getIntAttribute("uiHeight", uiHeight), GuiConstants::minHeight), GuiConstants::maxHeight); soundFontPath = xmlState->getStringAttribute("soundFontPath", soundFontPath); - preset = xmlState->getIntAttribute("preset", preset); - bank = xmlState->getIntAttribute("bank", bank); - attack = jmin(jmax(xmlState->getIntAttribute("attack", attack), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); - decay = jmin(jmax(xmlState->getIntAttribute("decay", decay), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); - sustain = jmin(jmax(xmlState->getIntAttribute("sustain", sustain), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); - release = jmin(jmax(xmlState->getIntAttribute("release", release), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); - filterCutOff = jmin(jmax(xmlState->getIntAttribute("filterCutOff", filterCutOff), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); - filterResonance = jmin(jmax(xmlState->getIntAttribute("filterResonance", filterResonance), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); + // preset = xmlState->getIntAttribute("preset", preset); + // bank = xmlState->getIntAttribute("bank", bank); + // attack = jmin(jmax(xmlState->getIntAttribute("attack", attack), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); + // decay = jmin(jmax(xmlState->getIntAttribute("decay", decay), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); + // sustain = jmin(jmax(xmlState->getIntAttribute("sustain", sustain), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); + // release = jmin(jmax(xmlState->getIntAttribute("release", release), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); + // filterCutOff = jmin(jmax(xmlState->getIntAttribute("filterCutOff", filterCutOff), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); + // filterResonance = jmin(jmax(xmlState->getIntAttribute("filterResonance", filterResonance), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); } -void Params::acceptMidiControlEvent(int controller, int value) { - switch(static_cast(controller)) { - case SOUND_CTRL2: // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) - filterResonance = value; - break; - case SOUND_CTRL3: // MIDI CC 72 Release time - release = value; - break; - case SOUND_CTRL4: // MIDI CC 73 Attack time - attack = value; - break; - case SOUND_CTRL5: // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) - filterCutOff = value; - break; - case SOUND_CTRL6: // MIDI CC 75 Decay Time - decay = value; - break; - case SOUND_CTRL10: // MIDI CC 79 undefined - sustain = value; - break; - default: - break; - } -} +//void Params::acceptMidiControlEvent(int controller, int value) { +// switch(static_cast(controller)) { +// case SOUND_CTRL2: // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) +// filterResonance = value; +// break; +// case SOUND_CTRL3: // MIDI CC 72 Release time +// release = value; +// break; +// case SOUND_CTRL4: // MIDI CC 73 Attack time +// attack = value; +// break; +// case SOUND_CTRL5: // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) +// filterCutOff = value; +// break; +// case SOUND_CTRL6: // MIDI CC 75 Decay Time +// decay = value; +// break; +// case SOUND_CTRL10: // MIDI CC 79 undefined +// sustain = value; +// break; +// default: +// break; +// } +//} void Params::setSoundFontPath(const String& value) { soundFontPath = value; @@ -89,64 +89,64 @@ void Params::setSoundFontPath(const String& value) { String& Params::getSoundFontPath() { return soundFontPath; } -int Params::getPreset() { - return preset; -} -int Params::getBank() { - return bank; -} +//int Params::getPreset() { +// return preset; +//} +//int Params::getBank() { +// return bank; +//} int Params::getUiWidth() { return uiWidth; } int Params::getUiHeight() { return uiHeight; } -int Params::getAttack() { - return attack; -} -int Params::getDecay() { - return decay; -} -int Params::getSustain() { - return sustain; -} -int Params::getRelease() { - return release; -} -int Params::getFilterCutOff() { - return filterCutOff; -} -int Params::getFilterResonance() { - return filterResonance; -} +//int Params::getAttack() { +// return attack; +//} +//int Params::getDecay() { +// return decay; +//} +//int Params::getSustain() { +// return sustain; +//} +//int Params::getRelease() { +// return release; +//} +//int Params::getFilterCutOff() { +// return filterCutOff; +//} +//int Params::getFilterResonance() { +// return filterResonance; +//} -void Params::setPreset(int value) { - preset = value; -} -void Params::setBank(int value) { - bank = value; -} +//void Params::setPreset(int value) { +// preset = value; +//} +//void Params::setBank(int value) { +// bank = value; +//} void Params::setUiWidth(int value) { uiWidth = value; } void Params::setUiHeight(int value) { uiHeight = value; } -void Params::setAttack(int value) { - attack = value; -} -void Params::setDecay(int value) { - decay = value; -} -void Params::setSustain(int value) { - sustain = value; -} -void Params::setRelease(int value) { - release = value; -} -void Params::setFilterCutOff(int value) { - filterCutOff = value; -} -void Params::setFilterResonance(int value) { - filterResonance = value; -} +//void Params::setAttack(int value) { +// attack = value; +//} +//void Params::setDecay(int value) { +// decay = value; +//} +//void Params::setSustain(int value) { +// sustain = value; +//} +//void Params::setRelease(int value) { +// release = value; +//} +//void Params::setFilterCutOff(int value) { +// filterCutOff = value; +//} +//void Params::setFilterResonance(int value) { +// filterResonance = value; +//} diff --git a/Source/Params.h b/Source/Params.h index e6979f4..7452da2 100644 --- a/Source/Params.h +++ b/Source/Params.h @@ -9,51 +9,51 @@ class Params: public SharesParams { public: Params() noexcept; - virtual void setAttributesOnXml(XmlElement& xml) override; + virtual void setAttributesOnXml(shared_ptr xml) override; virtual void loadAttributesFromXml(shared_ptr xmlState) override; - virtual void acceptMidiControlEvent(int controller, int value) override; +// virtual void acceptMidiControlEvent(int controller, int value) override; virtual void setSoundFontPath(const String& value) override; virtual String& getSoundFontPath() override; - virtual int getPreset() override; - virtual void setPreset(int value) override; - virtual int getBank() override; - virtual void setBank(int value) override; + // virtual int getPreset() override; + // virtual void setPreset(int value) override; + // virtual int getBank() override; + // virtual void setBank(int value) override; virtual int getUiWidth() override; virtual void setUiWidth(int value) override; virtual int getUiHeight() override; virtual void setUiHeight(int value) override; - virtual int getAttack() override; - virtual void setAttack(int value) override; - virtual int getDecay() override; - virtual void setDecay(int value) override; - virtual int getSustain() override; - virtual void setSustain(int value) override; - virtual int getRelease() override; - virtual void setRelease(int value) override; + // virtual int getAttack() override; + // virtual void setAttack(int value) override; + // virtual int getDecay() override; + // virtual void setDecay(int value) override; + // virtual int getSustain() override; + // virtual void setSustain(int value) override; + // virtual int getRelease() override; + // virtual void setRelease(int value) override; - virtual int getFilterCutOff() override; - virtual void setFilterCutOff(int value) override; - virtual int getFilterResonance() override; - virtual void setFilterResonance(int value) override; + // virtual int getFilterCutOff() override; + // virtual void setFilterCutOff(int value) override; + // virtual int getFilterResonance() override; + // virtual void setFilterResonance(int value) override; private: int uiWidth; int uiHeight; String soundFontPath; - int preset; - int bank; + // int preset; + // int bank; - int attack; - int decay; - int sustain; - int release; + // int attack; + // int decay; + // int sustain; + // int release; - int filterCutOff; - int filterResonance; + // int filterCutOff; + // int filterResonance; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Params) }; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 42b0f63..f6d5355 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -17,11 +17,11 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor(JuicySFAudioProcessor& : AudioProcessorEditor{&p}, processor{p}, valueTreeState{valueTreeState}, - sharedParams{p.sharedParams}, + sharedParams{p.getSharedParams()}, midiKeyboard{p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard}, - tablesComponent{p.getFluidSynthModel()}, - filePicker{p.getFluidSynthModel()}, - slidersComponent{p.sharedParams, valueTreeState, p.getFluidSynthModel()} + tablesComponent{valueTreeState, p.getFluidSynthModel()}, + filePicker{valueTreeState, p.getFluidSynthModel()}, + slidersComponent{p.getSharedParams(), valueTreeState, p.getFluidSynthModel()} { // set resize limits for this plug-in setResizeLimits( @@ -30,7 +30,7 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor(JuicySFAudioProcessor& GuiConstants::maxWidth, GuiConstants::maxHeight); - setSize(sharedParams->getUiWidth(), sharedParams->getUiHeight()); + setSize(sharedParams.getUiWidth(), sharedParams.getUiHeight()); // processor.subscribeToStateChanges(this); @@ -104,8 +104,8 @@ void JuicySFAudioProcessorEditor::resized() tablesComponent.setBounds(rContent); - sharedParams->setUiWidth(getWidth()); - sharedParams->setUiHeight(getHeight()); + sharedParams.setUiWidth(getWidth()); + sharedParams.setUiHeight(getHeight()); // Rectangle r2 (getLocalBounds()); // r2.reduce(0, padding); diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 10ada3a..51255ef 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -28,7 +28,10 @@ class JuicySFAudioProcessorEditor : public AudioProcessorEditor, public StateChangeSubscriber*/ { public: - JuicySFAudioProcessorEditor (JuicySFAudioProcessor&, AudioProcessorValueTreeState& state); + JuicySFAudioProcessorEditor( + JuicySFAudioProcessor&, + AudioProcessorValueTreeState& state + ); ~JuicySFAudioProcessorEditor(); //============================================================================== @@ -50,7 +53,7 @@ class JuicySFAudioProcessorEditor : public AudioProcessorEditor, JuicySFAudioProcessor& processor; AudioProcessorValueTreeState& valueTreeState; - shared_ptr sharedParams; + SharesParams& sharedParams; SurjectiveMidiKeyboardComponent midiKeyboard; TablesComponent tablesComponent; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index e2ddf59..0a3716f 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -26,14 +26,16 @@ AudioProcessor* JUCE_CALLTYPE createPluginFilter(); //============================================================================== +//, sharedParams{static_pointer_cast(make_shared())} JuicySFAudioProcessor::JuicySFAudioProcessor() : AudioProcessor{getBusesProperties()} -, sharedParams{static_pointer_cast(make_shared())} -, valueTreeState{*this, nullptr, - { "PARAMETERS" /* MYPLUGINSETTINGS */ }, - createParameterLayout() - } -, fluidSynthModel{sharedParams} +, sharedParams{} +, valueTreeState{ + *this, + nullptr, + { "MYPLUGINSETTINGS" }, + createParameterLayout()} +, fluidSynthModel{valueTreeState, sharedParams} //, fluidSynthModel{*this} //, pluginEditor(nullptr) { @@ -45,9 +47,15 @@ AudioProcessorValueTreeState::ParameterLayout JuicySFAudioProcessor::createParam // for (int i = 1; i < 9; ++i) // params.push_back (std::make_unique (String (i), String (i), 0, i, 0)); + + +// make_unique("soundfontPath", "filepath to soundfont", 0, 127, 0, "A" ), // https://stackoverflow.com/a/8469002/5257399 unique_ptr params[] { + make_unique("bank", "which bank is selected in the soundfont", 0, 127, 0, "Bank" ), + // note: banks may be sparse, and lack a 0th preset. so defend against this. + make_unique("preset", "which patch (aka patch, program, instrument) is selected in the soundfont", 0, 127, 0, "Preset" ), make_unique("attack", "volume envelope attack time", 0, 127, 0, "A" ), make_unique("decay", "volume envelope sustain attentuation", 0, 127, 0, "D" ), make_unique("sustain", "volume envelope decay time", 0, 127, 0, "S" ), @@ -70,7 +78,7 @@ JuicySFAudioProcessor::~JuicySFAudioProcessor() void JuicySFAudioProcessor::initialiseSynth() { fluidSynthModel.initialise(); - fluidSynth = fluidSynthModel.getSynth(); +// fluidSynth = fluidSynthModel.getSynth(); const int numVoices = 8; @@ -352,8 +360,11 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData) // as intermediaries to make it easy to save and load complex data. // Create an outer XML element.. - XmlElement xml{"MYPLUGINSETTINGS"}; - sharedParams->setAttributesOnXml(xml); +// XmlElement xml{"MYPLUGINSETTINGS"}; +// sharedParams->setAttributesOnXml(xml); + auto state{valueTreeState.copyState()}; + shared_ptr xml{state.createXml()}; + sharedParams.setAttributesOnXml(xml); // list::iterator p; // for(p = stateChangeSubscribers.begin(); p != stateChangeSubscribers.end(); p++) { @@ -361,12 +372,12 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData) // } // Store the values of all our parameters, using their param ID as the XML attribute - for (auto* param : getParameters()) - if (auto* p = dynamic_cast (param)) - xml.setAttribute (p->paramID, p->getValue()); + // for (auto* param : getParameters()) + // if (auto* p = dynamic_cast (param)) + // xml->setAttribute (p->paramID, p->getValue()); // then use this helper function to stuff it into the binary blob and return it.. - copyXmlToBinary (xml, destData); + copyXmlToBinary (*xml, destData); } void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInBytes) @@ -375,40 +386,42 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt // whose contents will have been created by the getStateInformation() call. // This getXmlFromBinary() helper function retrieves our XML from the binary blob.. shared_ptr xmlState{getXmlFromBinary(data, sizeInBytes)}; +// unique_ptr xmlState{getXmlFromBinary(data, sizeInBytes)}; - if (xmlState != nullptr) - { + if (xmlState.get() != nullptr) { // make sure that it's actually our type of XML object.. - if (xmlState->hasTagName ("MYPLUGINSETTINGS")) - { +// if (xmlState->hasTagName ("MYPLUGINSETTINGS")) { + if (xmlState->hasTagName(valueTreeState.state.getType())) { + valueTreeState.replaceState(ValueTree::fromXml(*xmlState)); // list::iterator p; // for(p = stateChangeSubscribers.begin(); p != stateChangeSubscribers.end(); p++) { // (*p)->setStateInformation(xmlState); // } // ok, now pull out our last window size.. - sharedParams->loadAttributesFromXml(xmlState); + sharedParams.loadAttributesFromXml(xmlState); // Now reload our parameters.. - for (auto* param : getParameters()) - if (auto* p = dynamic_cast (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 (editor) != nullptr); - ExposesComponents* exposesComponents = dynamic_cast (editor); - exposesComponents->getFilePicker().setDisplayedFilePath(sharedParams->getSoundFontPath()); - } + +// for (auto* param : getParameters()) +// if (auto* p = dynamic_cast (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 (editor) != nullptr); +// ExposesComponents* exposesComponents = dynamic_cast (editor); +// exposesComponents->getFilePicker().setDisplayedFilePath(sharedParams->getSoundFontPath()); +// } // const String& currentSoundFontAbsPath = fluidSynthModel->getCurrentSoundFontAbsPath(); // if (currentSoundFontAbsPath.isNotEmpty()) { @@ -431,8 +444,12 @@ bool JuicySFAudioProcessor::supportsDoublePrecisionProcessing() const { return false; } -FluidSynthModel* JuicySFAudioProcessor::getFluidSynthModel() { - return &fluidSynthModel; +FluidSynthModel& JuicySFAudioProcessor::getFluidSynthModel() { + return fluidSynthModel; +} + +SharesParams& JuicySFAudioProcessor::getSharedParams() { + return sharedParams; } //============================================================================== diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index a0599f8..33b3a37 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -14,6 +14,7 @@ #include "FluidSynthModel.h" #include "StateChangeSubscriber.h" #include "SharesParams.h" +#include "Params.h" #include using namespace std; @@ -62,18 +63,18 @@ class JuicySFAudioProcessor : public AudioProcessor bool supportsDoublePrecisionProcessing() const override; - FluidSynthModel* getFluidSynthModel(); + FluidSynthModel& getFluidSynthModel(); + SharesParams& getSharedParams(); MidiKeyboardState keyboardState; - shared_ptr sharedParams; - // void subscribeToStateChanges(StateChangeSubscriber* subscriber); // void unsubscribeFromStateChanges(StateChangeSubscriber* subscriber); private: void initialiseSynth(); + Params sharedParams; AudioProcessorValueTreeState valueTreeState; FluidSynthModel fluidSynthModel; diff --git a/Source/PresetsToBanks.h b/Source/PresetsToBanks.h index ea70353..bbfcdba 100644 --- a/Source/PresetsToBanks.h +++ b/Source/PresetsToBanks.h @@ -1,6 +1,6 @@ -// -// Created by Alex Birch on 17/09/2017. -// +// http://www.synthfont.com/Tutorial6.html +// a bank can hold many (128) presets +// bank 128 is reserved for percussion #pragma once diff --git a/Source/SharesParams.h b/Source/SharesParams.h index 1f25d0b..5b9621c 100644 --- a/Source/SharesParams.h +++ b/Source/SharesParams.h @@ -13,35 +13,35 @@ class SharesParams { public: virtual ~SharesParams() {} - virtual void setAttributesOnXml(XmlElement& xml) = 0; + virtual void setAttributesOnXml(shared_ptr xml) = 0; virtual void loadAttributesFromXml(shared_ptr xmlState) = 0; - virtual void acceptMidiControlEvent(int controller, int value) = 0; +// virtual void acceptMidiControlEvent(int controller, int value) = 0; virtual void setSoundFontPath(const String& value) = 0; virtual String& getSoundFontPath() = 0; - virtual int getPreset() = 0; - virtual void setPreset(int value) = 0; - virtual int getBank() = 0; - virtual void setBank(int value) = 0; + // virtual int getPreset() = 0; + // virtual void setPreset(int value) = 0; + // virtual int getBank() = 0; + // virtual void setBank(int value) = 0; virtual int getUiWidth() = 0; virtual void setUiWidth(int value) = 0; virtual int getUiHeight() = 0; virtual void setUiHeight(int value) = 0; - virtual int getAttack() = 0; - virtual void setAttack(int value) = 0; - virtual int getDecay() = 0; - virtual void setDecay(int value) = 0; - virtual int getSustain() = 0; - virtual void setSustain(int value) = 0; - virtual int getRelease() = 0; - virtual void setRelease(int value) = 0; - - virtual int getFilterCutOff() = 0; - virtual void setFilterCutOff(int value) = 0; - virtual int getFilterResonance() = 0; - virtual void setFilterResonance(int value) = 0; + // virtual int getAttack() = 0; + // virtual void setAttack(int value) = 0; + // virtual int getDecay() = 0; + // virtual void setDecay(int value) = 0; + // virtual int getSustain() = 0; + // virtual void setSustain(int value) = 0; + // virtual int getRelease() = 0; + // virtual void setRelease(int value) = 0; + + // virtual int getFilterCutOff() = 0; + // virtual void setFilterCutOff(int value) = 0; + // virtual int getFilterResonance() = 0; + // virtual void setFilterResonance(int value) = 0; }; diff --git a/Source/SlidersComponent.cpp b/Source/SlidersComponent.cpp index 789e696..196ce1f 100644 --- a/Source/SlidersComponent.cpp +++ b/Source/SlidersComponent.cpp @@ -24,7 +24,7 @@ std::function SlidersComponent::makeSliderListener(Slider& slider, int c s << slider.getComponentID() << ", controller " << controller << ", value " << slider.getValue() << ", xmlReleaseValue " << value; DEBUG_PRINT(s); // slider.setValue(slider.getValue(), NotificationType::dontSendNotification); - fluidSynthModel->setControllerValue(controller, slider.getValue()); + fluidSynthModel.setControllerValue(controller, slider.getValue()); // callback(); }; } @@ -115,9 +115,9 @@ void SlidersComponent::acceptMidiControlEvent(int controller, int value) { // } SlidersComponent::SlidersComponent( - shared_ptr sharedParams, + SharesParams& sharedParams, AudioProcessorValueTreeState& valueTreeState, - FluidSynthModel* fluidSynthModel) + FluidSynthModel& fluidSynthModel) : sharedParams{sharedParams} , valueTreeState{valueTreeState} , fluidSynthModel{fluidSynthModel} diff --git a/Source/SlidersComponent.h b/Source/SlidersComponent.h index 613182c..c2c9d4e 100644 --- a/Source/SlidersComponent.h +++ b/Source/SlidersComponent.h @@ -13,9 +13,9 @@ class SlidersComponent : public Component, { public: SlidersComponent( - shared_ptr sharedParams, + SharesParams& sharedParams, AudioProcessorValueTreeState& valueTreeState, - FluidSynthModel* fluidSynthModel); + FluidSynthModel& fluidSynthModel); ~SlidersComponent(); void resized() override; @@ -35,9 +35,9 @@ class SlidersComponent : public Component, private: std::function makeSliderListener(Slider& slider, int controller); - shared_ptr sharedParams; + SharesParams& sharedParams; AudioProcessorValueTreeState& valueTreeState; - FluidSynthModel* fluidSynthModel; + FluidSynthModel& fluidSynthModel; GroupComponent envelopeGroup; diff --git a/Source/SoundfontSynthVoice.cpp b/Source/SoundfontSynthVoice.cpp index 652b995..60590c1 100644 --- a/Source/SoundfontSynthVoice.cpp +++ b/Source/SoundfontSynthVoice.cpp @@ -6,13 +6,15 @@ #include "SoundfontSynthSound.h" #include "Util.h" -SoundfontSynthVoice::SoundfontSynthVoice(fluid_synth_t* synth) -: tailOff (0.0), -level(0.0), -currentAngle(0.0), -angleDelta(0.0), -midiNoteNumber(0), -synth(synth) +using namespace std; + +SoundfontSynthVoice::SoundfontSynthVoice(shared_ptr synth) +: tailOff(0.0) +, level(0.0) +, currentAngle(0.0) +, angleDelta(0.0) +, midiNoteNumber(0) +, synth(synth) { } @@ -26,7 +28,7 @@ void SoundfontSynthVoice::startNote( int /*currentPitchWheelPosition*/) { this->midiNoteNumber = midiNoteNumber; DEBUG_PRINT ( juce::String::formatted("JUCE noteon: %d, %d\n", midiNoteNumber, velocity) ); - fluid_synth_noteon(synth, 0, midiNoteNumber, static_cast(velocity * 127)); + fluid_synth_noteon(synth.get(), 0, midiNoteNumber, static_cast(velocity * 127)); // currentAngle = 0.0; // level = velocity * 0.15; @@ -59,7 +61,7 @@ void SoundfontSynthVoice::stopNote (float /*velocity*/, bool allowTailOff) { // } DEBUG_PRINT ( juce::String("JUCE noteoff\n") ); clearCurrentNote(); - fluid_synth_noteoff(synth, 0, this->midiNoteNumber); + fluid_synth_noteoff(synth.get(), 0, this->midiNoteNumber); } // receives input as MIDI 0 to 16383, with 8192 being center diff --git a/Source/SoundfontSynthVoice.h b/Source/SoundfontSynthVoice.h index e920882..291ac3d 100644 --- a/Source/SoundfontSynthVoice.h +++ b/Source/SoundfontSynthVoice.h @@ -8,11 +8,11 @@ #include #include "../JuceLibraryCode/JuceHeader.h" -using std::shared_ptr; +using namespace std; class SoundfontSynthVoice : public SynthesiserVoice { public: - SoundfontSynthVoice(fluid_synth_t* synth); + SoundfontSynthVoice(shared_ptr synth); bool canPlaySound (SynthesiserSound* sound) override; void startNote ( @@ -35,5 +35,5 @@ class SoundfontSynthVoice : public SynthesiserVoice { double angleDelta; int midiNoteNumber; - fluid_synth_t* synth; + shared_ptr synth; }; diff --git a/Source/TablesComponent.cpp b/Source/TablesComponent.cpp index 67f40de..c64bddf 100644 --- a/Source/TablesComponent.cpp +++ b/Source/TablesComponent.cpp @@ -8,10 +8,13 @@ using namespace std; using namespace placeholders; TablesComponent::TablesComponent( - FluidSynthModel* fluidSynthModel -) : fluidSynthModel(fluidSynthModel), - banksToPresets(fluidSynthModel->getBanks()), - initialised(false) + AudioProcessorValueTreeState& valueTreeState, + FluidSynthModel& fluidSynthModel +) +: valueTreeState{valueTreeState} +, fluidSynthModel{fluidSynthModel} +, banksToPresets{fluidSynthModel.getBanks()} +, initialised{false} { fluid_preset_t* currentPreset = getCurrentPreset(); selectedBank = -1; @@ -59,13 +62,13 @@ TablesComponent::TablesComponent( initialised = true; - fluidSynthModel->addListener(this); + fluidSynthModel.addListener(this); } fluid_preset_t* TablesComponent::getCurrentPreset() { - fluid_synth_t* synth = fluidSynthModel->getSynth(); + shared_ptr synth {fluidSynthModel.getSynth()}; - return fluid_synth_get_channel_preset(synth, fluidSynthModel->getChannel()); + return fluid_synth_get_channel_preset(synth.get(), fluidSynthModel.getChannel()); } Preset TablesComponent::getFirstPresetInBank(int bank) { @@ -108,13 +111,13 @@ void TablesComponent::onPresetSelected(int preset) { } cout << "Preset " << preset << endl; // selectedPreset = preset; - fluidSynthModel->changePreset(selectedBank, preset); + fluidSynthModel.changePreset(selectedBank, preset); } TablesComponent::~TablesComponent() { delete presetTable; delete banks; - fluidSynthModel->removeListener(this); + fluidSynthModel.removeListener(this); } vector TablesComponent::mapBanks(const BanksToPresets &banksToPresets) { @@ -165,7 +168,7 @@ bool TablesComponent::keyPressed(const KeyPress &key) { } void TablesComponent::fontChanged(FluidSynthModel *, const String &) { - banksToPresets = fluidSynthModel->getBanks(); + banksToPresets = fluidSynthModel.getBanks(); fluid_preset_t* currentPreset = getCurrentPreset(); diff --git a/Source/TablesComponent.h b/Source/TablesComponent.h index d86e7d9..9f57815 100644 --- a/Source/TablesComponent.h +++ b/Source/TablesComponent.h @@ -20,7 +20,8 @@ class TablesComponent : public Component, { public: TablesComponent( - FluidSynthModel* fluidSynthModel + AudioProcessorValueTreeState& valueTreeState, + FluidSynthModel& fluidSynthModel ); ~TablesComponent(); @@ -30,7 +31,8 @@ class TablesComponent : public Component, void fontChanged(FluidSynthModel *, const String &) override; private: - FluidSynthModel* fluidSynthModel; + AudioProcessorValueTreeState& valueTreeState; + FluidSynthModel& fluidSynthModel; int selectedBank; Pills* banks; From a990072f1f735094e8006c9a01b031bf5cb18756 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 7 Jul 2019 17:35:31 +0100 Subject: [PATCH 06/33] progress moving uiWidth/Height into audio params, and moving soundFontPath out of SharesParams (for better listener support, and to generalize) --- Source/FluidSynthModel.cpp | 43 +++++++++---- Source/FluidSynthModel.h | 50 +++++++++++++-- Source/Params.cpp | 38 +++++------ Source/Params.h | 12 ++-- Source/PluginEditor.cpp | 123 ++++++++++++++++++++++++++++++------ Source/PluginEditor.h | 33 ++++++++-- Source/PluginProcessor.cpp | 55 +++++++++------- Source/PluginProcessor.h | 7 +- Source/SharesParams.h | 8 +-- Source/SlidersComponent.cpp | 6 +- Source/SlidersComponent.h | 4 +- 11 files changed, 275 insertions(+), 104 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 27ab5fd..53be85e 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -12,10 +12,12 @@ using namespace std; FluidSynthModel::FluidSynthModel( AudioProcessorValueTreeState& valueTreeState, - SharesParams& sharedParams + ValueTree& valueTree + // SharesParams& sharedParams ) : valueTreeState{valueTreeState} -, sharedParams{sharedParams} +, valueTree{valueTree} +// , sharedParams{sharedParams} //, synth{nullptr} , settings{nullptr, nullptr} , currentSoundFontAbsPath{} @@ -24,10 +26,12 @@ FluidSynthModel::FluidSynthModel( , sfont_id{0} , channel{0}/*, mod(nullptr)*/ +{ + valueTree.addListener(this); +} -{} - -// FluidSynthModel::~FluidSynthModel() { +FluidSynthModel::~FluidSynthModel() { + valueTree.removeListener(this); // if (initialised) { // delete_fluid_audio_driver(driver); // delete_fluid_synth(synth); @@ -36,7 +40,7 @@ mod(nullptr)*/ // delete settings; // delete_fluid_mod(mod); // } -// } + } int FluidSynthModel::handleMidiEvent(void* data, fluid_midi_event_t* event) { @@ -79,16 +83,16 @@ void FluidSynthModel::initialise() { synth = { new_fluid_synth(settings.get()), delete_fluid_synth }; fluid_synth_set_sample_rate(synth.get(), currentSampleRate); - valueTreeState.getParameter("soundFontPath")->getValue(); + // valueTreeState.getParameter("soundFontPath")->getValue(); // RangedAudioParameter *param {valueTreeState.getParameter("release")}; // jassert(dynamic_cast (param) != nullptr); // AudioParameterInt* castParam {dynamic_cast (param)}; // *castParam = m.getControllerValue(); - - if (sharedParams.getSoundFontPath().isNotEmpty()) { - loadFont(sharedParams.getSoundFontPath()); + +// if (sharedParams.getSoundFontPath().isNotEmpty()) { +// loadFont(sharedParams.getSoundFontPath()); // changePreset(sharedParams->getBank(), sharedParams->getPreset()); - } +// } fluid_synth_set_gain(synth.get(), 2.0); @@ -191,10 +195,24 @@ void FluidSynthModel::initialise() { // clamps the range to between 0 and 1000, so we'll copy that fluid_mod_set_amount(mod.get(), 1000.0f); fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); + + valueTree.sendPropertyChangeMessage("soundFontPath"); // initialised = true; } +void FluidSynthModel::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) { + if (&treeWhosePropertyHasChanged == &valueTree) { + if (property == Identifier("soundFontPath")) { + String soundFontPath{treeWhosePropertyHasChanged.getProperty("soundFontPath", "")}; + if (soundFontPath.isNotEmpty()) { + loadFont(soundFontPath); + } + } + } +} + void FluidSynthModel::setControllerValue(int controller, int value) { fluid_midi_event_t *midi_event(new_fluid_midi_event()); fluid_midi_event_set_type(midi_event, static_cast(CONTROL_CHANGE)); @@ -300,7 +318,8 @@ void FluidSynthModel::onFileNameChanged(const String &absPath, int bank, int pre } unloadAndLoadFont(absPath); changePreset(bank, preset); - sharedParams.setSoundFontPath(absPath); + valueTree.setPropertyExcludingListener(this, "soundFontPath", absPath, nullptr); +// sharedParams.setSoundFontPath(absPath); eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath); } diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index 85a0b94..a5f2c05 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -17,13 +17,14 @@ using namespace std; -class FluidSynthModel { +class FluidSynthModel: public ValueTree::Listener { public: FluidSynthModel( AudioProcessorValueTreeState& valueTreeState, - SharesParams& sharedParams + // SharesParams& sharedParams + ValueTree& valueTree ); - // ~FluidSynthModel(); + ~FluidSynthModel(); shared_ptr getSynth(); void initialise(); @@ -66,15 +67,54 @@ class FluidSynthModel { void setSampleRate(float sampleRate); const String& getCurrentSoundFontAbsPath(); + + + virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) override; + inline virtual void valueTreeChildAdded (ValueTree& parentTree, + ValueTree& childWhichHasBeenAdded) override {}; + inline virtual void valueTreeChildRemoved (ValueTree& parentTree, + ValueTree& childWhichHasBeenRemoved, + int indexFromWhichChildWasRemoved) override {}; + inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, + int oldIndex, int newIndex) override {}; + inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; + inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; private: +// class ValueTreeListener: public ValueTree::Listener { +// public: +//// ValueTreeListener(); +//// ~ValueTreeListener(); +// virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, +// const Identifier& property) override; +// inline virtual void valueTreeChildAdded (ValueTree& parentTree, +// ValueTree& childWhichHasBeenAdded) override {}; +// inline virtual void valueTreeChildRemoved (ValueTree& parentTree, +// ValueTree& childWhichHasBeenRemoved, +// int indexFromWhichChildWasRemoved) override {}; +// inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, +// int oldIndex, int newIndex) override {}; +// inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; +// inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; +// JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeListener) +// }; + int handleMidiEvent(void* data, fluid_midi_event_t* event); + +// ValueTreeListener valueTreeListener; AudioProcessorValueTreeState& valueTreeState; - SharesParams& sharedParams; + // SharesParams& sharedParams; + ValueTree& valueTree; - shared_ptr synth; + // https://stackoverflow.com/questions/38980315/is-stdunique-ptr-deletion-order-guaranteed + // members are destroyed in reverse of the order they're declared + // http://www.fluidsynth.org/api/ + // in their examples, they destroy the synth before destroying the settings unique_ptr settings; + // TODO: shared_ptr may ruin our guarantee of synth's being destroyed first, so consider changing the access we expose + shared_ptr synth; // unique_ptr midiDriver; String currentSoundFontAbsPath; diff --git a/Source/Params.cpp b/Source/Params.cpp index 88c0046..4f6c9cc 100644 --- a/Source/Params.cpp +++ b/Source/Params.cpp @@ -15,9 +15,9 @@ using namespace std; Params::Params() noexcept -: uiWidth{GuiConstants::minWidth} -, uiHeight{GuiConstants::minHeight} -, soundFontPath{String()} +// : uiWidth{GuiConstants::minWidth} +// , uiHeight{GuiConstants::minHeight} +: soundFontPath{String()} // , preset{-1} // , bank{-1} // , attack{0} @@ -30,8 +30,8 @@ Params::Params() noexcept } void Params::setAttributesOnXml(shared_ptr xml) { - xml->setAttribute("uiWidth", uiWidth); - xml->setAttribute("uiHeight", uiHeight); + // xml->setAttribute("uiWidth", uiWidth); + // xml->setAttribute("uiHeight", uiHeight); xml->setAttribute("soundFontPath", soundFontPath); // xml.setAttribute("preset", preset); // xml.setAttribute("bank", bank); @@ -44,8 +44,8 @@ void Params::setAttributesOnXml(shared_ptr xml) { } void Params::loadAttributesFromXml(shared_ptr xmlState) { - uiWidth = jmin(jmax(xmlState->getIntAttribute("uiWidth", uiWidth), GuiConstants::minWidth), GuiConstants::maxWidth); - uiHeight = jmin(jmax(xmlState->getIntAttribute("uiHeight", uiHeight), GuiConstants::minHeight), GuiConstants::maxHeight); + // uiWidth = jmin(jmax(xmlState->getIntAttribute("uiWidth", uiWidth), GuiConstants::minWidth), GuiConstants::maxWidth); + // uiHeight = jmin(jmax(xmlState->getIntAttribute("uiHeight", uiHeight), GuiConstants::minHeight), GuiConstants::maxHeight); soundFontPath = xmlState->getStringAttribute("soundFontPath", soundFontPath); // preset = xmlState->getIntAttribute("preset", preset); // bank = xmlState->getIntAttribute("bank", bank); @@ -95,12 +95,12 @@ String& Params::getSoundFontPath() { //int Params::getBank() { // return bank; //} -int Params::getUiWidth() { - return uiWidth; -} -int Params::getUiHeight() { - return uiHeight; -} +// int Params::getUiWidth() { +// return uiWidth; +// } +// int Params::getUiHeight() { +// return uiHeight; +// } //int Params::getAttack() { // return attack; //} @@ -126,12 +126,12 @@ int Params::getUiHeight() { //void Params::setBank(int value) { // bank = value; //} -void Params::setUiWidth(int value) { - uiWidth = value; -} -void Params::setUiHeight(int value) { - uiHeight = value; -} +// void Params::setUiWidth(int value) { +// uiWidth = value; +// } +// void Params::setUiHeight(int value) { +// uiHeight = value; +// } //void Params::setAttack(int value) { // attack = value; //} diff --git a/Source/Params.h b/Source/Params.h index 7452da2..678c16f 100644 --- a/Source/Params.h +++ b/Source/Params.h @@ -20,10 +20,10 @@ class Params: public SharesParams { // virtual int getBank() override; // virtual void setBank(int value) override; - virtual int getUiWidth() override; - virtual void setUiWidth(int value) override; - virtual int getUiHeight() override; - virtual void setUiHeight(int value) override; + // virtual int getUiWidth() override; + // virtual void setUiWidth(int value) override; + // virtual int getUiHeight() override; + // virtual void setUiHeight(int value) override; // virtual int getAttack() override; // virtual void setAttack(int value) override; @@ -40,8 +40,8 @@ class Params: public SharesParams { // virtual void setFilterResonance(int value) override; private: - int uiWidth; - int uiHeight; + // int uiWidth; + // int uiHeight; String soundFontPath; // int preset; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index f6d5355..6050aff 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -13,15 +13,17 @@ #include "GuiConstants.h" //============================================================================== -JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor(JuicySFAudioProcessor& p, AudioProcessorValueTreeState& valueTreeState) - : AudioProcessorEditor{&p}, - processor{p}, - valueTreeState{valueTreeState}, - sharedParams{p.getSharedParams()}, - midiKeyboard{p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard}, - tablesComponent{valueTreeState, p.getFluidSynthModel()}, - filePicker{valueTreeState, p.getFluidSynthModel()}, - slidersComponent{p.getSharedParams(), valueTreeState, p.getFluidSynthModel()} +JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor( + JuicySFAudioProcessor& p, + AudioProcessorValueTreeState& valueTreeState) +: AudioProcessorEditor{&p} +, processor{p} +, valueTreeState{valueTreeState} + // sharedParams{p.getSharedParams()}, +, midiKeyboard{p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard} +, tablesComponent{valueTreeState, p.getFluidSynthModel()} +, filePicker{valueTreeState, p.getFluidSynthModel()} +, slidersComponent{valueTreeState, p.getFluidSynthModel()} { // set resize limits for this plug-in setResizeLimits( @@ -29,8 +31,29 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor(JuicySFAudioProcessor& GuiConstants::minHeight, GuiConstants::maxWidth, GuiConstants::maxHeight); - - setSize(sharedParams.getUiWidth(), sharedParams.getUiHeight()); + + // int width, height; + // { + // RangedAudioParameter *param {valueTreeState.getParameter("uiWidth")}; + // jassert(dynamic_cast (param) != nullptr); + // AudioParameterInt* castParam {dynamic_cast (param)}; + // width = castParam->get(); + // } + // { + // RangedAudioParameter *param {valueTreeState.getParameter("uiHeight")}; + // jassert(dynamic_cast (param) != nullptr); + // AudioParameterInt* castParam {dynamic_cast (param)}; + // height = castParam->get(); + // } + + // valueTreeState.addParameterListener("uiWidthPersist", this); + // valueTreeState.addParameterListener("uiHeightPersist", this); + // valueTreeState.addParameterListener("uiWidth", this); + // valueTreeState.addParameterListener("uiHeight", this); + + valueTreeState.state.addListener(this); + + setSize(GuiConstants::minWidth, GuiConstants::minHeight); // processor.subscribeToStateChanges(this); @@ -50,9 +73,53 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor(JuicySFAudioProcessor& JuicySFAudioProcessorEditor::~JuicySFAudioProcessorEditor() { + // valueTreeState.removeParameterListener("uiWidthPersist", this); + // valueTreeState.removeParameterListener("uiHeightPersist", this); + // valueTreeState.removeParameterListener("uiWidth", this); + // valueTreeState.removeParameterListener("uiHeight", this); + valueTreeState.state.removeListener(this); // processor.unsubscribeFromStateChanges(this); } +void JuicySFAudioProcessorEditor::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) { + if (&treeWhosePropertyHasChanged == &valueTreeState.state) { + if (property == Identifier("uiWidth")) { + // String soundFontPath{treeWhosePropertyHasChanged.getProperty("soundFontPath", "")}; + // if (soundFontPath.isNotEmpty()) { + // loadFont(soundFontPath); + // } + int value{treeWhosePropertyHasChanged.getProperty("uiWidth", GuiConstants::minWidth)}; + setSize(value, getHeight()); + } else if (property == Identifier("uiHeight")) { + int value{treeWhosePropertyHasChanged.getProperty("uiHeight", GuiConstants::minHeight)}; + setSize(getWidth(), value); + } + } +} + +// void JuicySFAudioProcessorEditor::parameterChanged(const String& parameterID, float newValue) { +// // if (parameterID == "uiWidthPersist" +// // || parameterID == "uiHeightPersist") { +// if (parameterID == "uiWidth" +// || parameterID == "uiHeight") { +// RangedAudioParameter *param {valueTreeState.getParameter(parameterID)}; +// jassert(dynamic_cast (param) != nullptr); +// AudioParameterInt* castParam {dynamic_cast (param)}; +// int value{castParam->get()}; +// // if (parameterID == "uiWidthPersist") { +// // setSize(value, getHeight()); +// // } else if (parameterID == "uiHeightPersist") { +// // setSize(getWidth(), value); +// // } +// if (parameterID == "uiWidth") { +// setSize(value, getHeight()); +// } else if (parameterID == "uiHeight") { +// setSize(getWidth(), value); +// } +// } +// } + //void JuicySFAudioProcessorEditor::getStateInformation (XmlElement& xml) { // // save // xml.setAttribute ("uiWidth", getWidth()); @@ -104,8 +171,24 @@ void JuicySFAudioProcessorEditor::resized() tablesComponent.setBounds(rContent); - sharedParams.setUiWidth(getWidth()); - sharedParams.setUiHeight(getHeight()); + valueTreeState.state.setPropertyExcludingListener(this, "uiWidth", getWidth(), nullptr); + valueTreeState.state.setPropertyExcludingListener(this, "uiHeight", getHeight(), nullptr); + + // { + // RangedAudioParameter *param {valueTreeState.getParameter("uiWidth2")}; + // jassert(dynamic_cast (param) != nullptr); + // AudioParameterInt* castParam {dynamic_cast (param)}; + // *castParam = getWidth(); + // } + // { + // RangedAudioParameter *param {valueTreeState.getParameter("uiHeight2")}; + // jassert(dynamic_cast (param) != nullptr); + // AudioParameterInt* castParam {dynamic_cast (param)}; + // *castParam = getHeight(); + // } + +// sharedParams.setUiWidth(getWidth()); +// sharedParams.setUiHeight(getHeight()); // Rectangle r2 (getLocalBounds()); // r2.reduce(0, padding); @@ -154,10 +237,10 @@ bool JuicySFAudioProcessorEditor::keyStateChanged (bool isKeyDown) { // return false; } -FilePickerFragment& JuicySFAudioProcessorEditor::getFilePicker() { - return filePicker; -} - -SlidersFragment& JuicySFAudioProcessorEditor::getSliders() { - return slidersComponent; -} +//FilePickerFragment& JuicySFAudioProcessorEditor::getFilePicker() { +// return filePicker; +//} +// +//SlidersFragment& JuicySFAudioProcessorEditor::getSliders() { +// return slidersComponent; +//} diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 51255ef..68ed6f5 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -23,14 +23,18 @@ //============================================================================== /** */ -class JuicySFAudioProcessorEditor : public AudioProcessorEditor, - public ExposesComponents/*, +class JuicySFAudioProcessorEditor +: public AudioProcessorEditor +// , public AudioProcessorValueTreeState::Listener +, public ValueTree::Listener + /*, + , public ExposesComponents public StateChangeSubscriber*/ { public: JuicySFAudioProcessorEditor( JuicySFAudioProcessor&, - AudioProcessorValueTreeState& state + AudioProcessorValueTreeState& valueTreeState ); ~JuicySFAudioProcessorEditor(); @@ -44,16 +48,33 @@ class JuicySFAudioProcessorEditor : public AudioProcessorEditor, // void getStateInformation (XmlElement& xml) override; // void setStateInformation (XmlElement* xmlState) override; - virtual FilePickerFragment& getFilePicker() override; - virtual SlidersFragment& getSliders() override; + // virtual FilePickerFragment& getFilePicker() override; + // virtual SlidersFragment& getSliders() override; + // virtual void parameterChanged (const String& parameterID, float newValue) override; + + // int getWidth(); + // int getHeight(); + + virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) override; + inline virtual void valueTreeChildAdded (ValueTree& parentTree, + ValueTree& childWhichHasBeenAdded) override {}; + inline virtual void valueTreeChildRemoved (ValueTree& parentTree, + ValueTree& childWhichHasBeenRemoved, + int indexFromWhichChildWasRemoved) override {}; + inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, + int oldIndex, int newIndex) override {}; + inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; + inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; private: + // This reference is provided as a quick way for your editor to // access the processor object that created it. JuicySFAudioProcessor& processor; AudioProcessorValueTreeState& valueTreeState; - SharesParams& sharedParams; + // SharesParams& sharedParams; SurjectiveMidiKeyboardComponent midiKeyboard; TablesComponent tablesComponent; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 0a3716f..952cf2c 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -17,7 +17,7 @@ #include "Util.h" #include "SharesParams.h" #include "Params.h" -#include "MidiConstants.h" +#include "GuiConstants.h" using namespace std; using Parameter = AudioProcessorValueTreeState::Parameter; @@ -29,13 +29,13 @@ AudioProcessor* JUCE_CALLTYPE createPluginFilter(); //, sharedParams{static_pointer_cast(make_shared())} JuicySFAudioProcessor::JuicySFAudioProcessor() : AudioProcessor{getBusesProperties()} -, sharedParams{} +// , sharedParams{} , valueTreeState{ *this, nullptr, { "MYPLUGINSETTINGS" }, createParameterLayout()} -, fluidSynthModel{valueTreeState, sharedParams} +, fluidSynthModel{valueTreeState, valueTree} //, fluidSynthModel{*this} //, pluginEditor(nullptr) { @@ -53,15 +53,22 @@ AudioProcessorValueTreeState::ParameterLayout JuicySFAudioProcessor::createParam // https://stackoverflow.com/a/8469002/5257399 unique_ptr params[] { - make_unique("bank", "which bank is selected in the soundfont", 0, 127, 0, "Bank" ), + // make_unique("uiWidthPersist", "width of this plugin's GUI. Editor listens for changes (e.g. on load)", GuiConstants::minWidth, GuiConstants::maxWidth, GuiConstants::minWidth, "UI Width Persist" ), + // make_unique("uiHeightPersist", "height of this plugin's GUI. Editor listens for changes (e.g. on load)", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height Persist" ), + // make_unique("uiWidthTemp", "width of this plugin's GUI. Editor writes here on change (e.g. on window resize). Processor copies this into Persist before any save.", GuiConstants::minWidth, GuiConstants::maxWidth, GuiConstants::minWidth, "UI Width Temp" ), + // make_unique("uiHeightTemp", "height of this plugin's GUI. Editor writes here on change (e.g. on window resize). Processor copies this into Persist before any save.", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height Temp" ), + make_unique("uiWidth", "width of this plugin's GUI", GuiConstants::minWidth, GuiConstants::maxWidth, GuiConstants::minWidth, "UI Width" ), + make_unique("uiHeight", "height of this plugin's GUI", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height" ), + // todo: check whether bank really is 0-127 + make_unique("bank", "which bank is selected in the soundfont", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "Bank" ), // note: banks may be sparse, and lack a 0th preset. so defend against this. - make_unique("preset", "which patch (aka patch, program, instrument) is selected in the soundfont", 0, 127, 0, "Preset" ), - make_unique("attack", "volume envelope attack time", 0, 127, 0, "A" ), - make_unique("decay", "volume envelope sustain attentuation", 0, 127, 0, "D" ), - make_unique("sustain", "volume envelope decay time", 0, 127, 0, "S" ), - make_unique("release", "volume envelope release time", 0, 127, 0, "R" ), - make_unique("filterCutOff", "low-pass filter cut-off frequency", 0, 127, 0, "Cut" ), - make_unique("filterResonance", "low-pass filter resonance attentuation", 0, 127, 0, "Res" ), + make_unique("preset", "which patch (aka patch, program, instrument) is selected in the soundfont", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "Preset" ), + make_unique("attack", "volume envelope attack time", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "A" ), + make_unique("decay", "volume envelope sustain attentuation", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "D" ), + make_unique("sustain", "volume envelope decay time", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "S" ), + make_unique("release", "volume envelope release time", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "R" ), + make_unique("filterCutOff", "low-pass filter cut-off frequency", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "Cut" ), + make_unique("filterResonance", "low-pass filter resonance attentuation", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "Res" ), }; return { @@ -215,7 +222,7 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel()); fluid_midi_event_set_control(midi_event, m.getControllerNumber()); fluid_midi_event_set_value(midi_event, m.getControllerValue()); - fluid_synth_handle_midi_event(fluidSynth, midi_event); + fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); delete_fluid_midi_event(midi_event); switch(static_cast(m.getControllerNumber())) { @@ -274,21 +281,21 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer fluid_midi_event_set_type(midi_event, static_cast(PROGRAM_CHANGE)); fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel()); fluid_midi_event_set_program(midi_event, m.getProgramChangeNumber()); - fluid_synth_handle_midi_event(fluidSynth, midi_event); + fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); delete_fluid_midi_event(midi_event); } else if (m.isPitchWheel()) { fluid_midi_event_t *midi_event(new_fluid_midi_event()); fluid_midi_event_set_type(midi_event, static_cast(PITCH_BEND)); fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel()); fluid_midi_event_set_pitch(midi_event, m.getPitchWheelValue()); - fluid_synth_handle_midi_event(fluidSynth, midi_event); + fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); delete_fluid_midi_event(midi_event); } else if (m.isChannelPressure()) { fluid_midi_event_t *midi_event(new_fluid_midi_event()); fluid_midi_event_set_type(midi_event, static_cast(CHANNEL_PRESSURE)); fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel()); fluid_midi_event_set_program(midi_event, m.getChannelPressureValue()); - fluid_synth_handle_midi_event(fluidSynth, midi_event); + fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); delete_fluid_midi_event(midi_event); } else if (m.isAftertouch()) { fluid_midi_event_t *midi_event(new_fluid_midi_event()); @@ -296,12 +303,12 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel()); fluid_midi_event_set_key(midi_event, m.getNoteNumber()); fluid_midi_event_set_value(midi_event, m.getAfterTouchValue()); - fluid_synth_handle_midi_event(fluidSynth, midi_event); + fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); delete_fluid_midi_event(midi_event); // } else if (m.isMetaEvent()) { // fluid_midi_event_t *midi_event(new_fluid_midi_event()); // fluid_midi_event_set_type(midi_event, static_cast(MIDI_SYSTEM_RESET)); -// fluid_synth_handle_midi_event(fluidSynth, midi_event); +// fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); // delete_fluid_midi_event(midi_event); } else if (m.isSysEx()) { fluid_midi_event_t *midi_event(new_fluid_midi_event()); @@ -309,7 +316,7 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer // I assume that the MidiMessage's sysex buffer would be freed anyway when MidiMessage is destroyed, so set dynamic=false // to ensure that fluidsynth does not attempt to free the sysex buffer during delete_fluid_midi_event() fluid_midi_event_set_sysex(midi_event, const_cast(m.getSysExData()), m.getSysExDataSize(), static_cast(false)); - fluid_synth_handle_midi_event(fluidSynth, midi_event); + fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); delete_fluid_midi_event(midi_event); } } @@ -324,7 +331,7 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer // and now get our synth to process these midi events and generate its output. synth.renderNextBlock (buffer, midiMessages, 0, numSamples); - fluid_synth_process(fluidSynth, numSamples, 0, nullptr, buffer.getNumChannels(), buffer.getArrayOfWritePointers()); + fluid_synth_process(fluidSynthModel.getSynth().get(), numSamples, 0, nullptr, buffer.getNumChannels(), buffer.getArrayOfWritePointers()); // (see juce_VST3_Wrapper.cpp for the assertion this would trip otherwise) // we are !JucePlugin_ProducesMidiOutput, so clear remaining MIDI messages from our buffer @@ -364,7 +371,7 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData) // sharedParams->setAttributesOnXml(xml); auto state{valueTreeState.copyState()}; shared_ptr xml{state.createXml()}; - sharedParams.setAttributesOnXml(xml); +// sharedParams.setAttributesOnXml(xml); // list::iterator p; // for(p = stateChangeSubscribers.begin(); p != stateChangeSubscribers.end(); p++) { @@ -399,7 +406,7 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt // } // ok, now pull out our last window size.. - sharedParams.loadAttributesFromXml(xmlState); +// sharedParams.loadAttributesFromXml(xmlState); // Now reload our parameters.. @@ -448,9 +455,9 @@ FluidSynthModel& JuicySFAudioProcessor::getFluidSynthModel() { return fluidSynthModel; } -SharesParams& JuicySFAudioProcessor::getSharedParams() { - return sharedParams; -} +//SharesParams& JuicySFAudioProcessor::getSharedParams() { +// return sharedParams; +//} //============================================================================== // This creates new instances of the plugin.. diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 33b3a37..08a7c41 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -64,7 +64,7 @@ class JuicySFAudioProcessor : public AudioProcessor bool supportsDoublePrecisionProcessing() const override; FluidSynthModel& getFluidSynthModel(); - SharesParams& getSharedParams(); +// SharesParams& getSharedParams(); MidiKeyboardState keyboardState; @@ -74,11 +74,12 @@ class JuicySFAudioProcessor : public AudioProcessor private: void initialiseSynth(); - Params sharedParams; +// Params sharedParams; AudioProcessorValueTreeState valueTreeState; + ValueTree valueTree; FluidSynthModel fluidSynthModel; - fluid_synth_t* fluidSynth; + // fluid_synth_t* fluidSynth; Synthesiser synth; // // just a raw pointer; we do not own diff --git a/Source/SharesParams.h b/Source/SharesParams.h index 5b9621c..88740b3 100644 --- a/Source/SharesParams.h +++ b/Source/SharesParams.h @@ -24,10 +24,10 @@ class SharesParams { // virtual int getBank() = 0; // virtual void setBank(int value) = 0; - virtual int getUiWidth() = 0; - virtual void setUiWidth(int value) = 0; - virtual int getUiHeight() = 0; - virtual void setUiHeight(int value) = 0; + // virtual int getUiWidth() = 0; + // virtual void setUiWidth(int value) = 0; + // virtual int getUiHeight() = 0; + // virtual void setUiHeight(int value) = 0; // virtual int getAttack() = 0; // virtual void setAttack(int value) = 0; diff --git a/Source/SlidersComponent.cpp b/Source/SlidersComponent.cpp index 196ce1f..5a4f787 100644 --- a/Source/SlidersComponent.cpp +++ b/Source/SlidersComponent.cpp @@ -115,11 +115,11 @@ void SlidersComponent::acceptMidiControlEvent(int controller, int value) { // } SlidersComponent::SlidersComponent( - SharesParams& sharedParams, + // SharesParams& sharedParams, AudioProcessorValueTreeState& valueTreeState, FluidSynthModel& fluidSynthModel) -: sharedParams{sharedParams} -, valueTreeState{valueTreeState} +// : sharedParams{sharedParams} +: valueTreeState{valueTreeState} , fluidSynthModel{fluidSynthModel} , envelopeGroup{"envelopeGroup", "Envelope"} , filterGroup{"filterGroup", "Filter"} diff --git a/Source/SlidersComponent.h b/Source/SlidersComponent.h index c2c9d4e..47bdfa8 100644 --- a/Source/SlidersComponent.h +++ b/Source/SlidersComponent.h @@ -13,7 +13,7 @@ class SlidersComponent : public Component, { public: SlidersComponent( - SharesParams& sharedParams, + // SharesParams& sharedParams, AudioProcessorValueTreeState& valueTreeState, FluidSynthModel& fluidSynthModel); ~SlidersComponent(); @@ -35,7 +35,7 @@ class SlidersComponent : public Component, private: std::function makeSliderListener(Slider& slider, int controller); - SharesParams& sharedParams; + // SharesParams& sharedParams; AudioProcessorValueTreeState& valueTreeState; FluidSynthModel& fluidSynthModel; From 374394330f1b5a9641f221c1bb7c9855faee694d Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Mon, 8 Jul 2019 23:36:27 +0100 Subject: [PATCH 07/33] successfully save/load ui width/height --- .../xcschemes/juicysfplugin - AU.xcscheme | 84 ++++++++++++ .../xcschemes/juicysfplugin - All.xcscheme | 2 +- ...juicysfplugin - Standalone Plugin.xcscheme | 9 +- .../xcschemes/juicysfplugin - VST.xcscheme | 84 ++++++++++++ .../xcschemes/juicysfplugin - VST3.xcscheme | 4 + Source/PluginEditor.cpp | 127 +++++++++++------- Source/PluginEditor.h | 31 +++-- Source/PluginProcessor.cpp | 61 ++++++++- 8 files changed, 329 insertions(+), 73 deletions(-) create mode 100644 Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - AU.xcscheme create mode 100644 Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST.xcscheme diff --git a/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - AU.xcscheme b/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - AU.xcscheme new file mode 100644 index 0000000..6ec7b0e --- /dev/null +++ b/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - AU.xcscheme @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - All.xcscheme b/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - All.xcscheme index bee1ae6..f8aae0e 100644 --- a/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - All.xcscheme +++ b/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - All.xcscheme @@ -33,7 +33,7 @@ - + + + - + diff --git a/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST.xcscheme b/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST.xcscheme new file mode 100644 index 0000000..0d99cec --- /dev/null +++ b/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST.xcscheme @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST3.xcscheme b/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST3.xcscheme index f1ab27e..b6c7b9d 100644 --- a/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST3.xcscheme +++ b/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST3.xcscheme @@ -42,6 +42,10 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> + + (param) != nullptr); // AudioParameterInt* castParam {dynamic_cast (param)}; // width = castParam->get(); // } // { - // RangedAudioParameter *param {valueTreeState.getParameter("uiHeight")}; + // RangedAudioParameter *param {valueTreeState.getParameter("uiHeightPersist")}; // jassert(dynamic_cast (param) != nullptr); // AudioParameterInt* castParam {dynamic_cast (param)}; // height = castParam->get(); // } - // valueTreeState.addParameterListener("uiWidthPersist", this); - // valueTreeState.addParameterListener("uiHeightPersist", this); - // valueTreeState.addParameterListener("uiWidth", this); - // valueTreeState.addParameterListener("uiHeight", this); + // valueTreeState.addParameterListener("uiWidthPersist", this); + // valueTreeState.addParameterListener("uiHeightPersist", this); +// valueTreeState.addParameterListener("uiWidth", this); +// valueTreeState.addParameterListener("uiHeight", this); - valueTreeState.state.addListener(this); +// valueTreeState.state.addListener(this); - setSize(GuiConstants::minWidth, GuiConstants::minHeight); + // setSize(GuiConstants::minWidth, GuiConstants::minHeight); + // setSize(width, height); + + lastUIWidth.referTo(valueTreeState.state.getChildWithName("uiState").getPropertyAsValue("width", nullptr)); + lastUIHeight.referTo(valueTreeState.state.getChildWithName("uiState").getPropertyAsValue("height", nullptr)); + + // set our component's initial size to be the last one that was stored in the filter's settings + setSize(lastUIWidth.getValue(), lastUIHeight.getValue()); + + lastUIWidth.addListener(this); + lastUIHeight.addListener(this); // processor.subscribeToStateChanges(this); @@ -71,52 +81,60 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor( } +// called when the stored window size changes +void JuicySFAudioProcessorEditor::valueChanged(Value&) { + setSize(lastUIWidth.getValue(), lastUIHeight.getValue()); +} + JuicySFAudioProcessorEditor::~JuicySFAudioProcessorEditor() { - // valueTreeState.removeParameterListener("uiWidthPersist", this); - // valueTreeState.removeParameterListener("uiHeightPersist", this); + lastUIWidth.removeListener(this); + lastUIHeight.removeListener(this); + // valueTreeState.removeParameterListener("uiWidthPersist", this); + // valueTreeState.removeParameterListener("uiHeightPersist", this); // valueTreeState.removeParameterListener("uiWidth", this); // valueTreeState.removeParameterListener("uiHeight", this); - valueTreeState.state.removeListener(this); +// valueTreeState.state.removeListener(this); // processor.unsubscribeFromStateChanges(this); } -void JuicySFAudioProcessorEditor::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, - const Identifier& property) { - if (&treeWhosePropertyHasChanged == &valueTreeState.state) { - if (property == Identifier("uiWidth")) { - // String soundFontPath{treeWhosePropertyHasChanged.getProperty("soundFontPath", "")}; - // if (soundFontPath.isNotEmpty()) { - // loadFont(soundFontPath); - // } - int value{treeWhosePropertyHasChanged.getProperty("uiWidth", GuiConstants::minWidth)}; - setSize(value, getHeight()); - } else if (property == Identifier("uiHeight")) { - int value{treeWhosePropertyHasChanged.getProperty("uiHeight", GuiConstants::minHeight)}; - setSize(getWidth(), value); - } - } -} +// void JuicySFAudioProcessorEditor::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, +// const Identifier& property) { +// // if (treeWhosePropertyHasChanged.getType() == Identifier("PARAM")) { +// const String propertyString{property.toString()}; +// if (propertyString == "uiWidth") { +// // String soundFontPath{treeWhosePropertyHasChanged.getProperty("soundFontPath", "")}; +// // if (soundFontPath.isNotEmpty()) { +// // loadFont(soundFontPath); +// // } +// int value{treeWhosePropertyHasChanged.getProperty("uiWidth", GuiConstants::minWidth)}; +// setSize(value, getHeight()); +// } else if (propertyString == "uiHeight") { +// int value{treeWhosePropertyHasChanged.getProperty("uiHeight", GuiConstants::minHeight)}; +// setSize(getWidth(), value); +// } +// // } +// } // void JuicySFAudioProcessorEditor::parameterChanged(const String& parameterID, float newValue) { -// // if (parameterID == "uiWidthPersist" -// // || parameterID == "uiHeightPersist") { -// if (parameterID == "uiWidth" -// || parameterID == "uiHeight") { +// // if (parameterID == "uiWidthPersist" +// // || parameterID == "uiHeightPersist") { +// if (parameterID == "uiWidth" +// || parameterID == "uiHeight") { // RangedAudioParameter *param {valueTreeState.getParameter(parameterID)}; // jassert(dynamic_cast (param) != nullptr); // AudioParameterInt* castParam {dynamic_cast (param)}; // int value{castParam->get()}; -// // if (parameterID == "uiWidthPersist") { -// // setSize(value, getHeight()); -// // } else if (parameterID == "uiHeightPersist") { -// // setSize(getWidth(), value); -// // } // if (parameterID == "uiWidth") { // setSize(value, getHeight()); // } else if (parameterID == "uiHeight") { // setSize(getWidth(), value); // } +// // if (parameterID == "uiWidthPersist") { +// // setSize(value, getHeight()); +// // } else if (parameterID == "uiHeightPersist") { +// // setSize(getWidth(), value); +// // } // } // } @@ -171,21 +189,30 @@ void JuicySFAudioProcessorEditor::resized() tablesComponent.setBounds(rContent); - valueTreeState.state.setPropertyExcludingListener(this, "uiWidth", getWidth(), nullptr); - valueTreeState.state.setPropertyExcludingListener(this, "uiHeight", getHeight(), nullptr); + lastUIWidth = getWidth(); + lastUIHeight = getHeight(); + + // valueTreeState.state.setPropertyExcludingListener(this, "uiWidth", getWidth(), nullptr); + // valueTreeState.state.setPropertyExcludingListener(this, "uiHeight", getHeight(), nullptr); - // { - // RangedAudioParameter *param {valueTreeState.getParameter("uiWidth2")}; - // jassert(dynamic_cast (param) != nullptr); - // AudioParameterInt* castParam {dynamic_cast (param)}; - // *castParam = getWidth(); - // } - // { - // RangedAudioParameter *param {valueTreeState.getParameter("uiHeight2")}; - // jassert(dynamic_cast (param) != nullptr); - // AudioParameterInt* castParam {dynamic_cast (param)}; - // *castParam = getHeight(); - // } +// { +// // RangedAudioParameter *param {valueTreeState.getParameter("uiWidthTemp")}; +// RangedAudioParameter *param {valueTreeState.getParameter("uiWidth")}; +// // param->setValueNotifyingHost(param->convertTo0to1(getWidth())); +// jassert(dynamic_cast (param) != nullptr); +// AudioParameterInt* castParam {dynamic_cast (param)}; +// *castParam = getWidth(); +// // castParam->AudioProcessorParameter::setValue(castParam->convertTo0to1(static_cast(getWidth()))); +// } +// { +// // RangedAudioParameter *param {valueTreeState.getParameter("uiHeightTemp")}; +// RangedAudioParameter *param {valueTreeState.getParameter("uiHeight")}; +// // param->setValueNotifyingHost(param->convertTo0to1(getHeight())); +// jassert(dynamic_cast (param) != nullptr); +// AudioParameterInt* castParam {dynamic_cast (param)}; +// *castParam = getHeight(); +// // castParam->AudioProcessorParameter::setValue(castParam->convertTo0to1(static_cast(getHeight()))); +// } // sharedParams.setUiWidth(getWidth()); // sharedParams.setUiHeight(getHeight()); diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 68ed6f5..1e39902 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -26,7 +26,8 @@ class JuicySFAudioProcessorEditor : public AudioProcessorEditor // , public AudioProcessorValueTreeState::Listener -, public ValueTree::Listener +, private Value::Listener +// , public ValueTree::Listener /*, , public ExposesComponents public StateChangeSubscriber*/ @@ -55,19 +56,20 @@ class JuicySFAudioProcessorEditor // int getWidth(); // int getHeight(); - virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, - const Identifier& property) override; - inline virtual void valueTreeChildAdded (ValueTree& parentTree, - ValueTree& childWhichHasBeenAdded) override {}; - inline virtual void valueTreeChildRemoved (ValueTree& parentTree, - ValueTree& childWhichHasBeenRemoved, - int indexFromWhichChildWasRemoved) override {}; - inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, - int oldIndex, int newIndex) override {}; - inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; - inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; + // virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, + // const Identifier& property) override; + // inline virtual void valueTreeChildAdded (ValueTree& parentTree, + // ValueTree& childWhichHasBeenAdded) override {}; + // inline virtual void valueTreeChildRemoved (ValueTree& parentTree, + // ValueTree& childWhichHasBeenRemoved, + // int indexFromWhichChildWasRemoved) override {}; + // inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, + // int oldIndex, int newIndex) override {}; + // inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; + // inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; private: + void valueChanged (Value&) override; // This reference is provided as a quick way for your editor to // access the processor object that created it. @@ -76,6 +78,11 @@ class JuicySFAudioProcessorEditor AudioProcessorValueTreeState& valueTreeState; // SharesParams& sharedParams; + // these are used to persist the UI's size - the values are stored along with the + // filter's other parameters, and the UI component will update them when it gets + // resized. + Value lastUIWidth, lastUIHeight; + SurjectiveMidiKeyboardComponent midiKeyboard; TablesComponent tablesComponent; FilePicker filePicker; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 952cf2c..d683e70 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -33,12 +33,19 @@ JuicySFAudioProcessor::JuicySFAudioProcessor() , valueTreeState{ *this, nullptr, - { "MYPLUGINSETTINGS" }, + "MYPLUGINSETTINGS", createParameterLayout()} , fluidSynthModel{valueTreeState, valueTree} //, fluidSynthModel{*this} //, pluginEditor(nullptr) { + valueTreeState.state.appendChild({ "uiState", { + { "width", GuiConstants::minWidth }, + { "height", GuiConstants::minHeight } + }, {} }, nullptr); + valueTreeState.state.setProperty("soundFontPath", "", nullptr); +// valueTreeState.state.appendChild({ "soundFontPath", {} }, nullptr); + initialiseSynth(); } @@ -57,8 +64,8 @@ AudioProcessorValueTreeState::ParameterLayout JuicySFAudioProcessor::createParam // make_unique("uiHeightPersist", "height of this plugin's GUI. Editor listens for changes (e.g. on load)", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height Persist" ), // make_unique("uiWidthTemp", "width of this plugin's GUI. Editor writes here on change (e.g. on window resize). Processor copies this into Persist before any save.", GuiConstants::minWidth, GuiConstants::maxWidth, GuiConstants::minWidth, "UI Width Temp" ), // make_unique("uiHeightTemp", "height of this plugin's GUI. Editor writes here on change (e.g. on window resize). Processor copies this into Persist before any save.", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height Temp" ), - make_unique("uiWidth", "width of this plugin's GUI", GuiConstants::minWidth, GuiConstants::maxWidth, GuiConstants::minWidth, "UI Width" ), - make_unique("uiHeight", "height of this plugin's GUI", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height" ), + // make_unique("uiWidth", "width of this plugin's GUI", GuiConstants::minWidth, GuiConstants::maxWidth, GuiConstants::minWidth, "UI Width" ), + // make_unique("uiHeight", "height of this plugin's GUI", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height" ), // todo: check whether bank really is 0-127 make_unique("bank", "which bank is selected in the soundfont", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "Bank" ), // note: banks may be sparse, and lack a 0th preset. so defend against this. @@ -227,7 +234,11 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer switch(static_cast(m.getControllerNumber())) { case SOUND_CTRL2: { // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) - valueTreeState.state.setProperty({"filterResonance"}, m.getControllerValue(), nullptr); + // valueTreeState.state.setProperty({"filterResonance"}, m.getControllerValue(), nullptr); + RangedAudioParameter *param {valueTreeState.getParameter("filterResonance")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + *castParam = m.getControllerValue(); break; } case SOUND_CTRL3: { // MIDI CC 72 Release time @@ -382,9 +393,11 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData) // for (auto* param : getParameters()) // if (auto* p = dynamic_cast (param)) // xml->setAttribute (p->paramID, p->getValue()); - + // then use this helper function to stuff it into the binary blob and return it.. - copyXmlToBinary (*xml, destData); + if (xml.get() != nullptr) { + copyXmlToBinary(*xml, destData); + } } void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInBytes) @@ -399,7 +412,41 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt // make sure that it's actually our type of XML object.. // if (xmlState->hasTagName ("MYPLUGINSETTINGS")) { if (xmlState->hasTagName(valueTreeState.state.getType())) { - valueTreeState.replaceState(ValueTree::fromXml(*xmlState)); + // valueTreeState.replaceState(ValueTree::fromXml(*xmlState)); + for (auto* param : getParameters()) + if (auto* p = dynamic_cast(param)) + p->setValue(static_cast(xmlState->getDoubleAttribute(p->paramID, p->getValue()))); + + { + Value value{valueTreeState.state.getPropertyAsValue("soundFontPath", nullptr)}; + value = xmlState->getStringAttribute("soundFontPath", value.getValue()); + // valueTreeState.getParameter("soundFontPath")->getValue() + // valueTreeState.getParameter("soundFontPath")->getValue(); + // RangedAudioParameter *param {valueTreeState.getParameter("release")}; + // jassert(dynamic_cast (param) != nullptr); + // AudioParameterInt* castParam {dynamic_cast (param)}; + // *castParam = m.getControllerValue(); + } + { + ValueTree tree{valueTreeState.state.getChildWithName("uiState")}; + XmlElement* uiState{xmlState->getChildByName("uiState")}; + if (uiState) { + { + Value value{tree.getPropertyAsValue("width", nullptr)}; + value = uiState->getIntAttribute("width", value.getValue()); + } + { + Value value{tree.getPropertyAsValue("height", nullptr)}; + value = uiState->getIntAttribute("height", value.getValue()); + } + } + +// tree.getPropertyAsValue("width", nullptr) +// tree. +// valueTreeState.replaceState(ValueTree::fromXml(*xmlState)) +// value = xmlState->getStringAttribute("soundFontPath", value.getValue()); + } + // list::iterator p; // for(p = stateChangeSubscribers.begin(); p != stateChangeSubscribers.end(); p++) { // (*p)->setStateInformation(xmlState); From 568495ea74f0b06822fcfba1d103f1423dd8eab0 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Tue, 9 Jul 2019 20:44:26 +0100 Subject: [PATCH 08/33] picking soundfont no longer crashes synth --- Source/FluidSynthModel.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 53be85e..23f2794 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -196,21 +196,22 @@ void FluidSynthModel::initialise() { fluid_mod_set_amount(mod.get(), 1000.0f); fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); - valueTree.sendPropertyChangeMessage("soundFontPath"); + valueTreeState.state.sendPropertyChangeMessage("soundFontPath"); + // valueTree.sendPropertyChangeMessage("soundFontPath"); // initialised = true; } void FluidSynthModel::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { - if (&treeWhosePropertyHasChanged == &valueTree) { + // if (&treeWhosePropertyHasChanged == &valueTree) { if (property == Identifier("soundFontPath")) { String soundFontPath{treeWhosePropertyHasChanged.getProperty("soundFontPath", "")}; if (soundFontPath.isNotEmpty()) { loadFont(soundFontPath); } } - } + // } } void FluidSynthModel::setControllerValue(int controller, int value) { @@ -318,7 +319,8 @@ void FluidSynthModel::onFileNameChanged(const String &absPath, int bank, int pre } unloadAndLoadFont(absPath); changePreset(bank, preset); - valueTree.setPropertyExcludingListener(this, "soundFontPath", absPath, nullptr); + valueTreeState.state.setPropertyExcludingListener(this, "soundFontPath", absPath, nullptr); + // valueTree.setPropertyExcludingListener(this, "soundFontPath", absPath, nullptr); // sharedParams.setSoundFontPath(absPath); eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath); } From d972a23ce8fc55369605e85ef86b6f6cda0644a6 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Wed, 10 Jul 2019 23:13:58 +0100 Subject: [PATCH 09/33] move soundFont to its own tag. remove extraneous valueTree. listen directly to relevant part of tree. --- Source/FluidSynthModel.cpp | 22 ++++++++++---------- Source/FluidSynthModel.h | 6 +++--- Source/PluginProcessor.cpp | 41 +++++++++++++++++++++++++++++--------- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 23f2794..b9725b2 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -11,12 +11,12 @@ using namespace std; FluidSynthModel::FluidSynthModel( - AudioProcessorValueTreeState& valueTreeState, - ValueTree& valueTree + AudioProcessorValueTreeState& valueTreeState + // ValueTree& valueTree // SharesParams& sharedParams ) : valueTreeState{valueTreeState} -, valueTree{valueTree} +// , valueTree{valueTree} // , sharedParams{sharedParams} //, synth{nullptr} , settings{nullptr, nullptr} @@ -27,11 +27,11 @@ FluidSynthModel::FluidSynthModel( , channel{0}/*, mod(nullptr)*/ { - valueTree.addListener(this); + valueTreeState.state.getChildWithName("soundFont").addListener(this); } FluidSynthModel::~FluidSynthModel() { - valueTree.removeListener(this); + valueTreeState.state.getChildWithName("soundFont").removeListener(this); // if (initialised) { // delete_fluid_audio_driver(driver); // delete_fluid_synth(synth); @@ -196,7 +196,7 @@ void FluidSynthModel::initialise() { fluid_mod_set_amount(mod.get(), 1000.0f); fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); - valueTreeState.state.sendPropertyChangeMessage("soundFontPath"); + valueTreeState.state.sendPropertyChangeMessage("soundFont"); // valueTree.sendPropertyChangeMessage("soundFontPath"); // initialised = true; @@ -204,14 +204,15 @@ void FluidSynthModel::initialise() { void FluidSynthModel::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { + if (treeWhosePropertyHasChanged.getType() == StringRef("soundFont")) { // if (&treeWhosePropertyHasChanged == &valueTree) { - if (property == Identifier("soundFontPath")) { - String soundFontPath{treeWhosePropertyHasChanged.getProperty("soundFontPath", "")}; + if (property == StringRef("path")) { + String soundFontPath{treeWhosePropertyHasChanged.getProperty("path", "")}; if (soundFontPath.isNotEmpty()) { loadFont(soundFontPath); } } - // } + } } void FluidSynthModel::setControllerValue(int controller, int value) { @@ -319,7 +320,8 @@ void FluidSynthModel::onFileNameChanged(const String &absPath, int bank, int pre } unloadAndLoadFont(absPath); changePreset(bank, preset); - valueTreeState.state.setPropertyExcludingListener(this, "soundFontPath", absPath, nullptr); + ValueTree valueTree{valueTreeState.state.getChildWithName("soundFont")}; + valueTree.setPropertyExcludingListener(this, "path", absPath, nullptr); // valueTree.setPropertyExcludingListener(this, "soundFontPath", absPath, nullptr); // sharedParams.setSoundFontPath(absPath); eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath); diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index a5f2c05..9f08a4b 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -20,9 +20,9 @@ using namespace std; class FluidSynthModel: public ValueTree::Listener { public: FluidSynthModel( - AudioProcessorValueTreeState& valueTreeState, + AudioProcessorValueTreeState& valueTreeState // SharesParams& sharedParams - ValueTree& valueTree + // ValueTree& valueTree ); ~FluidSynthModel(); @@ -106,7 +106,7 @@ class FluidSynthModel: public ValueTree::Listener { AudioProcessorValueTreeState& valueTreeState; // SharesParams& sharedParams; - ValueTree& valueTree; + // ValueTree& valueTree; // https://stackoverflow.com/questions/38980315/is-stdunique-ptr-deletion-order-guaranteed // members are destroyed in reverse of the order they're declared diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index d683e70..8211184 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -35,7 +35,7 @@ JuicySFAudioProcessor::JuicySFAudioProcessor() nullptr, "MYPLUGINSETTINGS", createParameterLayout()} -, fluidSynthModel{valueTreeState, valueTree} +, fluidSynthModel{valueTreeState} //, fluidSynthModel{*this} //, pluginEditor(nullptr) { @@ -43,7 +43,10 @@ JuicySFAudioProcessor::JuicySFAudioProcessor() { "width", GuiConstants::minWidth }, { "height", GuiConstants::minHeight } }, {} }, nullptr); - valueTreeState.state.setProperty("soundFontPath", "", nullptr); + valueTreeState.state.appendChild({ "soundFont", { + { "path", "" }, + }, {} }, nullptr); + // valueTreeState.state.setProperty("soundFontPath", "", nullptr); // valueTreeState.state.appendChild({ "soundFontPath", {} }, nullptr); initialiseSynth(); @@ -407,7 +410,20 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt // This getXmlFromBinary() helper function retrieves our XML from the binary blob.. shared_ptr xmlState{getXmlFromBinary(data, sizeInBytes)}; // unique_ptr xmlState{getXmlFromBinary(data, sizeInBytes)}; - + DEBUG_PRINT(xmlState->createDocument("",false,false)); +/* + + + + + + + + + + + + */ if (xmlState.get() != nullptr) { // make sure that it's actually our type of XML object.. // if (xmlState->hasTagName ("MYPLUGINSETTINGS")) { @@ -418,8 +434,15 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt p->setValue(static_cast(xmlState->getDoubleAttribute(p->paramID, p->getValue()))); { - Value value{valueTreeState.state.getPropertyAsValue("soundFontPath", nullptr)}; - value = xmlState->getStringAttribute("soundFontPath", value.getValue()); + // Value value{valueTreeState.state.getPropertyAsValue("soundFontPath", nullptr)}; + // value = xmlState->getStringAttribute("soundFontPath", value.getValue()); + ValueTree tree{valueTreeState.state.getChildWithName("soundFont")}; + XmlElement* xmlElement{xmlState->getChildByName("soundFont")}; + if (xmlElement) { + Value value{tree.getPropertyAsValue("path", nullptr)}; + value = xmlState->getStringAttribute("path", value.getValue()); + } + // valueTreeState.getParameter("soundFontPath")->getValue() // valueTreeState.getParameter("soundFontPath")->getValue(); // RangedAudioParameter *param {valueTreeState.getParameter("release")}; @@ -429,15 +452,15 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt } { ValueTree tree{valueTreeState.state.getChildWithName("uiState")}; - XmlElement* uiState{xmlState->getChildByName("uiState")}; - if (uiState) { + XmlElement* xmlElement{xmlState->getChildByName("uiState")}; + if (xmlElement) { { Value value{tree.getPropertyAsValue("width", nullptr)}; - value = uiState->getIntAttribute("width", value.getValue()); + value = xmlElement->getIntAttribute("width", value.getValue()); } { Value value{tree.getPropertyAsValue("height", nullptr)}; - value = uiState->getIntAttribute("height", value.getValue()); + value = xmlElement->getIntAttribute("height", value.getValue()); } } From e02188b7f4e4e79a32e793467227048657c9ba75 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Wed, 10 Jul 2019 23:52:15 +0100 Subject: [PATCH 10/33] start hooking up fileChooser to use valueTree instead of coupling to fluidSynth model --- Source/FilePicker.cpp | 37 +++++++++++++++++++++++++++++-------- Source/FilePicker.h | 14 ++++++++++++++ Source/FluidSynthModel.cpp | 6 +++--- Source/PluginProcessor.h | 2 +- 4 files changed, 47 insertions(+), 12 deletions(-) diff --git a/Source/FilePicker.cpp b/Source/FilePicker.cpp index 6ddf8ab..46d1aa5 100644 --- a/Source/FilePicker.cpp +++ b/Source/FilePicker.cpp @@ -4,6 +4,7 @@ #include "FilePicker.h" #include "MyColours.h" +#include "Util.h" FilePicker::FilePicker( AudioProcessorValueTreeState& valueTreeState, @@ -25,13 +26,17 @@ FilePicker::FilePicker( // faster (rounded edges introduce transparency) setOpaque (true); - setDisplayedFilePath(fluidSynthModel.getCurrentSoundFontAbsPath()); + // setDisplayedFilePath(fluidSynthModel.getCurrentSoundFontAbsPath()); + setDisplayedFilePath(""); addAndMakeVisible (fileChooser); fileChooser.addListener (this); + valueTreeState.state.addListener(this); + valueTreeState.state.getChildWithName("soundFont").sendPropertyChangeMessage("path"); } FilePicker::~FilePicker() { fileChooser.removeListener (this); + valueTreeState.state.removeListener(this); } void FilePicker::resized() { @@ -48,15 +53,31 @@ void FilePicker::paint(Graphics& g) } void FilePicker::filenameComponentChanged (FilenameComponent*) { - currentPath = fileChooser.getCurrentFile().getFullPathName(); - fluidSynthModel.onFileNameChanged(fileChooser.getCurrentFile().getFullPathName(), -1, -1); + // currentPath = fileChooser.getCurrentFile().getFullPathName(); + // fluidSynthModel.onFileNameChanged(fileChooser.getCurrentFile().getFullPathName(), -1, -1); + Value value{valueTreeState.state.getChildWithName("soundFont").getPropertyAsValue("path", nullptr)}; + value = fileChooser.getCurrentFile().getFullPathName(); } -void FilePicker::setDisplayedFilePath(const String& path) { - if (!shouldChangeDisplayedFilePath(path)) { - return; +void FilePicker::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) { + if (treeWhosePropertyHasChanged.getType() == StringRef("soundFont")) { + // if (&treeWhosePropertyHasChanged == &valueTree) { + if (property == StringRef("path")) { + String soundFontPath{treeWhosePropertyHasChanged.getProperty("path", "")}; + DEBUG_PRINT(soundFontPath); + // if (soundFontPath.isNotEmpty()) { + // loadFont(soundFontPath); + // } + } } - currentPath = path; +} + +void FilePicker::setDisplayedFilePath(const String& path) { + // if (!shouldChangeDisplayedFilePath(path)) { + // return; + // } + // currentPath = path; fileChooser.setCurrentFile(File(path), true, dontSendNotification); } @@ -68,4 +89,4 @@ bool FilePicker::shouldChangeDisplayedFilePath(const String &path) { return false; } return true; -} \ No newline at end of file +} diff --git a/Source/FilePicker.h b/Source/FilePicker.h index c187724..1c3e39d 100644 --- a/Source/FilePicker.h +++ b/Source/FilePicker.h @@ -10,6 +10,7 @@ class FilePicker: public Component, public FilePickerFragment, + public ValueTree::Listener, private FilenameComponentListener { public: @@ -23,6 +24,19 @@ class FilePicker: public Component, void paint (Graphics& g) override; virtual void setDisplayedFilePath(const String&) override; + + + virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) override; + inline virtual void valueTreeChildAdded (ValueTree& parentTree, + ValueTree& childWhichHasBeenAdded) override {}; + inline virtual void valueTreeChildRemoved (ValueTree& parentTree, + ValueTree& childWhichHasBeenRemoved, + int indexFromWhichChildWasRemoved) override {}; + inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, + int oldIndex, int newIndex) override {}; + inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; + inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; private: FilenameComponent fileChooser; diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index b9725b2..976352e 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -27,11 +27,11 @@ FluidSynthModel::FluidSynthModel( , channel{0}/*, mod(nullptr)*/ { - valueTreeState.state.getChildWithName("soundFont").addListener(this); + valueTreeState.state.addListener(this); } FluidSynthModel::~FluidSynthModel() { - valueTreeState.state.getChildWithName("soundFont").removeListener(this); + valueTreeState.state.removeListener(this); // if (initialised) { // delete_fluid_audio_driver(driver); // delete_fluid_synth(synth); @@ -196,7 +196,7 @@ void FluidSynthModel::initialise() { fluid_mod_set_amount(mod.get(), 1000.0f); fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); - valueTreeState.state.sendPropertyChangeMessage("soundFont"); + valueTreeState.state.getChildWithName("soundFont").sendPropertyChangeMessage("path"); // valueTree.sendPropertyChangeMessage("soundFontPath"); // initialised = true; diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 08a7c41..d3cfdca 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -76,7 +76,7 @@ class JuicySFAudioProcessor : public AudioProcessor // Params sharedParams; AudioProcessorValueTreeState valueTreeState; - ValueTree valueTree; + // ValueTree valueTree; FluidSynthModel fluidSynthModel; // fluid_synth_t* fluidSynth; From e1a8df9e8f01c1dc537c1258f94f6b15e280cc91 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sat, 13 Jul 2019 00:16:35 +0100 Subject: [PATCH 11/33] progress moving table model to be managed by fluidsynth, de-generalizing and decoupling table component --- Source/FilePicker.cpp | 6 +- Source/FluidSynthModel.cpp | 36 ++++++++++++ Source/PluginProcessor.cpp | 4 ++ Source/TableComponent.cpp | 109 ++++++++++++++++++++++++++++--------- Source/TableComponent.h | 27 +++++++-- Source/TablesComponent.cpp | 12 ++-- 6 files changed, 155 insertions(+), 39 deletions(-) diff --git a/Source/FilePicker.cpp b/Source/FilePicker.cpp index 46d1aa5..dbd6930 100644 --- a/Source/FilePicker.cpp +++ b/Source/FilePicker.cpp @@ -27,12 +27,12 @@ FilePicker::FilePicker( setOpaque (true); // setDisplayedFilePath(fluidSynthModel.getCurrentSoundFontAbsPath()); - setDisplayedFilePath(""); + setDisplayedFilePath(valueTreeState.state.getChildWithName("soundFont").getProperty("path", "")); addAndMakeVisible (fileChooser); fileChooser.addListener (this); valueTreeState.state.addListener(this); - valueTreeState.state.getChildWithName("soundFont").sendPropertyChangeMessage("path"); +// valueTreeState.state.getChildWithName("soundFont").sendPropertyChangeMessage("path"); } FilePicker::~FilePicker() { fileChooser.removeListener (this); @@ -65,7 +65,7 @@ void FilePicker::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged // if (&treeWhosePropertyHasChanged == &valueTree) { if (property == StringRef("path")) { String soundFontPath{treeWhosePropertyHasChanged.getProperty("path", "")}; - DEBUG_PRINT(soundFontPath); + // DEBUG_PRINT(soundFontPath); // if (soundFontPath.isNotEmpty()) { // loadFont(soundFontPath); // } diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 976352e..2d33629 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -339,6 +339,42 @@ void FluidSynthModel::loadFont(const String &absPath) { currentSoundFontAbsPath = absPath; sfont_id++; fluid_synth_sfload(synth.get(), absPath.toStdString().c_str(), 1); + fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; + ValueTree banks{"banks"}; + ValueTree presets{"presets"}; + if (sfont != nullptr) { + int initialBankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; + banks.appendChild({ "bank", { + { "num", initialBankOffset }, + }, {} }, nullptr); + + fluid_sfont_iteration_start(sfont); + + for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; + preset != nullptr; + preset = fluid_sfont_iteration_next(sfont)) { + int bankOffset{fluid_preset_get_banknum(preset) + initialBankOffset}; + // ValueTree preset{"preset"}; + // banksToPresets.insert(BanksToPresets::value_type( + // fluid_preset_get_banknum(preset) + bankOffset, + // *new Preset( + // fluid_preset_get_num(preset), + // fluid_preset_get_name(preset) + // ) + // )); + if (bankOffset > initialBankOffset) { + banks.appendChild({ "bank", { + { "num", bankOffset }, + }, {} }, nullptr); + } + presets.appendChild({ "preset", { + { "num", fluid_preset_get_num(preset) }, + { "name", String{fluid_preset_get_name(preset)} } + }, {} }, nullptr); + } + } + valueTreeState.state.getChildWithName("banks") = banks; + valueTreeState.state.getChildWithName("presets") = presets; } FluidSynthModel::Listener::~Listener() { diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 8211184..e94fc05 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -46,6 +46,10 @@ JuicySFAudioProcessor::JuicySFAudioProcessor() valueTreeState.state.appendChild({ "soundFont", { { "path", "" }, }, {} }, nullptr); + // no properties, no subtrees (yet) + valueTreeState.state.appendChild({ "presets", {}, {} }, nullptr); + // no properties, no subtrees (yet) + valueTreeState.state.appendChild({ "banks", {}, {} }, nullptr); // valueTreeState.state.setProperty("soundFontPath", "", nullptr); // valueTreeState.state.appendChild({ "soundFontPath", {} }, nullptr); diff --git a/Source/TableComponent.cpp b/Source/TableComponent.cpp index 75487e5..219aad4 100644 --- a/Source/TableComponent.cpp +++ b/Source/TableComponent.cpp @@ -15,17 +15,18 @@ using namespace std; This class shows how to implement a TableListBoxModel to show in a TableListBox. */ TableComponent::TableComponent( - const vector &columns, - const vector> &rows, - const function &onRowSelected, - const function&)> &rowToIDMapper, - int initiallySelectedRow + AudioProcessorValueTreeState& valueTreeState, + // const vector &columns, + const vector> &rows, + const function &onRowSelected, + // const function&)> &rowToIDMapper, + int initiallySelectedRow ) - : font (14.0f), - columns(columns), - rows(rows), - onRowSelected(onRowSelected), - rowToIDMapper(rowToIDMapper) +: font (14.0f) +, columns(columns) +, rows(rows) +, onRowSelected(onRowSelected)/*, +rowToIDMapper(rowToIDMapper)*/ { // Create our table component and add it to this component.. addAndMakeVisible (table); @@ -38,31 +39,81 @@ TableComponent::TableComponent( 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 - ); - } + // 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 ( + String("#"), + columnIx++, + 30, // column width + 30, // min width + 400, // max width + TableHeaderComponent::defaultFlags + ); + table.getHeader().addColumn ( + String("Name"), + columnIx++, + 200, // column width + 30, // min width + 400, // max width + TableHeaderComponent::defaultFlags + ); table.setWantsKeyboardFocus(false); table.selectRow(initiallySelectedRow); // 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); +} + +TableComponent::~TableComponent() { + valueTreeState.state.removeListener(this); +} + +// void TableComponent::parameterChanged(const String& parameterID, float newValue) { +// // valueTreeState.getParameter +// RangedAudioParameter *param {valueTreeState.getParameter("bank")}; +// if (parameterID == "bank") { +// jassert(dynamic_cast (param) != nullptr); +// AudioParameterInt* castParam {dynamic_cast (param)}; +// int value{castParam->get()}; +// } else if (parameterID == "preset") { +// jassert(dynamic_cast (param) != nullptr); +// AudioParameterInt* castParam {dynamic_cast (param)}; +// int value{castParam->get()}; +// } +// } + +void TableComponent::valueTreePropertyChanged( + ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) { + if (treeWhosePropertyHasChanged.getType() == StringRef("soundFont")) { + // if (&treeWhosePropertyHasChanged == &valueTree) { + if (property == StringRef("path")) { + String soundFontPath{treeWhosePropertyHasChanged.getProperty("path", "")}; + // DEBUG_PRINT(soundFontPath); + // if (soundFontPath.isNotEmpty()) { + // loadFont(soundFontPath); + // } + } + } } void TableComponent::setRows(const vector>& rows, int initiallySelectedRow) { @@ -148,10 +199,8 @@ void TableComponent::sortOrderChanged ( // This is overloaded from TableListBoxModel, and should choose the best width for the specified // column. int TableComponent::getColumnAutoSizeWidth (int columnId) { -// if (columnId == 5) -// return 100; // (this is the ratings column, containing a custom combobox component) if (columnId == 1) - return 30; // (this is the ratings column, containing a custom combobox component) + return 30; int widest = 32; @@ -201,7 +250,13 @@ void TableComponent::selectedRowsChanged (int row) { if (row < 0) { return; } - onRowSelected(rowToIDMapper(rows[row])); + // onRowSelected(rowToIDMapper(rows[row])); + // onRowSelected(stoi(rows[row][0])); + int newPreset{stoi(rows[row][0])}; + RangedAudioParameter *param {valueTreeState.getParameter("preset")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + *castParam = newPreset; } bool TableComponent::keyPressed(const KeyPress &key) { diff --git a/Source/TableComponent.h b/Source/TableComponent.h index d436f4c..7445e44 100644 --- a/Source/TableComponent.h +++ b/Source/TableComponent.h @@ -16,15 +16,19 @@ using namespace std; class TableComponent : public Component, - public TableListBoxModel { + public TableListBoxModel, + public ValueTree::Listener/*, + public AudioProcessorValueTreeState::Listener */ { public: TableComponent( - const vector &columns, + AudioProcessorValueTreeState& valueTreeState, + // const vector &columns, const vector> &rows, const function &onRowSelected, - const function&)> &rowToIDMapper, + // const function&)> &rowToIDMapper, int initiallySelectedRow ); + ~TableComponent(); int getNumRows() override; @@ -56,7 +60,22 @@ class TableComponent : public Component, bool keyPressed(const KeyPress &key) override; +// virtual void parameterChanged (const String& parameterID, float newValue) override; + + virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) override; + inline virtual void valueTreeChildAdded (ValueTree& parentTree, + ValueTree& childWhichHasBeenAdded) override {}; + inline virtual void valueTreeChildRemoved (ValueTree& parentTree, + ValueTree& childWhichHasBeenRemoved, + int indexFromWhichChildWasRemoved) override {}; + inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, + int oldIndex, int newIndex) override {}; + inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; + inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; private: + AudioProcessorValueTreeState& valueTreeState; + TableListBox table; // the table component itself Font font; @@ -64,7 +83,7 @@ class TableComponent : public Component, vector> rows; function onRowSelected; - function&)> rowToIDMapper; +// function&)> rowToIDMapper; // A comparator used to sort our data when the user clicks a column header class DataSorter { diff --git a/Source/TablesComponent.cpp b/Source/TablesComponent.cpp index c64bddf..6e45cbb 100644 --- a/Source/TablesComponent.cpp +++ b/Source/TablesComponent.cpp @@ -20,20 +20,22 @@ TablesComponent::TablesComponent( selectedBank = -1; int selectedPreset = -1; + + if (currentPreset != nullptr) { selectedBank = fluid_preset_get_banknum(currentPreset); selectedPreset = fluid_preset_get_num(currentPreset); } - auto rowToPresetMapper = [this](const vector &row) { - return stoi(row[0]); - }; + // auto rowToPresetMapper = [this](const vector &row) { + // return stoi(row[0]); + // }; auto itemToBankMapper = [](const string &item) { return stoi(item); }; presetTable = new TableComponent( - {"#", "Name"}, + // {"#", "Name"}, mapPresets( banksToPresets, selectedBank @@ -41,7 +43,7 @@ TablesComponent::TablesComponent( [this](int preset){ this->onPresetSelected(preset); }, - rowToPresetMapper, + // rowToPresetMapper, presetToIndexMapper(selectedPreset) ); banks = new Pills( From 4140b3b85b4cbf6dee59e5ee69f053b6782dbdf1 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sat, 13 Jul 2019 22:37:26 +0100 Subject: [PATCH 12/33] progress integrating table with store, decoupling from fluidsynth model --- Source/FluidSynthModel.cpp | 28 ++++----- Source/FluidSynthModel.h | 22 +++---- Source/TableComponent.cpp | 116 +++++++++++++++++++++++++------------ Source/TableComponent.h | 29 +++++++--- Source/TablesComponent.cpp | 43 +++++++------- Source/TablesComponent.h | 6 +- Source/Util.h | 8 +++ 7 files changed, 159 insertions(+), 93 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 2d33629..c11ba06 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -324,7 +324,7 @@ void FluidSynthModel::onFileNameChanged(const String &absPath, int bank, int pre valueTree.setPropertyExcludingListener(this, "path", absPath, nullptr); // valueTree.setPropertyExcludingListener(this, "soundFontPath", absPath, nullptr); // sharedParams.setSoundFontPath(absPath); - eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath); + // eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath); } void FluidSynthModel::unloadAndLoadFont(const String &absPath) { @@ -377,8 +377,8 @@ void FluidSynthModel::loadFont(const String &absPath) { valueTreeState.state.getChildWithName("presets") = presets; } -FluidSynthModel::Listener::~Listener() { -} +// FluidSynthModel::Listener::~Listener() { +// } bool FluidSynthModel::shouldLoadFont(const String &absPath) { if (absPath.isEmpty()) { @@ -390,23 +390,23 @@ bool FluidSynthModel::shouldLoadFont(const String &absPath) { return true; } -void FluidSynthModel::Listener::fontChanged(FluidSynthModel * model, const String &absPath) { -} +// void FluidSynthModel::Listener::fontChanged(FluidSynthModel * model, const String &absPath) { +// } const String& FluidSynthModel::getCurrentSoundFontAbsPath() { return currentSoundFontAbsPath; } //============================================================================== -void FluidSynthModel::addListener (FluidSynthModel::Listener* const newListener) -{ - eventListeners.add(newListener); -} - -void FluidSynthModel::removeListener (FluidSynthModel::Listener* const listener) -{ - eventListeners.remove(listener); -} +// void FluidSynthModel::addListener (FluidSynthModel::Listener* const newListener) +// { +// eventListeners.add(newListener); +// } + +// void FluidSynthModel::removeListener (FluidSynthModel::Listener* const listener) +// { +// eventListeners.remove(listener); +// } void FluidSynthModel::setSampleRate(float sampleRate) { currentSampleRate = sampleRate; diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index 9f08a4b..89dd7ef 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -43,26 +43,26 @@ class FluidSynthModel: public ValueTree::Listener { @see Button::addListener, Button::removeListener */ - class Listener - { - public: - /** Destructor. */ - virtual ~Listener(); + // class Listener + // { + // public: + // /** Destructor. */ + // virtual ~Listener(); - /** Called when the button is clicked. */ - virtual void fontChanged (FluidSynthModel*, const String &absPath); - }; + // /** Called when the button is clicked. */ + // virtual void fontChanged (FluidSynthModel*, const String &absPath); + // }; /** Registers a listener to receive events when this button's state changes. If the listener is already registered, this will not register it again. @see removeListener */ - void addListener (Listener* newListener); + // void addListener (Listener* newListener); /** Removes a previously-registered button listener @see addListener */ - void removeListener (Listener* listener); + // void removeListener (Listener* listener); void setSampleRate(float sampleRate); @@ -137,7 +137,7 @@ class FluidSynthModel: public ValueTree::Listener { // fluid_mod_t* mod; - ListenerList eventListeners; + // ListenerList eventListeners; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FluidSynthModel) }; diff --git a/Source/TableComponent.cpp b/Source/TableComponent.cpp index 219aad4..8888c18 100644 --- a/Source/TableComponent.cpp +++ b/Source/TableComponent.cpp @@ -7,8 +7,10 @@ // #include "TableComponent.h" +#include "Util.h" using namespace std; +using namespace Util; //============================================================================== /** @@ -17,15 +19,16 @@ using namespace std; TableComponent::TableComponent( AudioProcessorValueTreeState& valueTreeState, // const vector &columns, - const vector> &rows, +// const vector &rows, const function &onRowSelected, // const function&)> &rowToIDMapper, int initiallySelectedRow ) -: font (14.0f) -, columns(columns) -, rows(rows) -, onRowSelected(onRowSelected)/*, +: valueTreeState{valueTreeState} +, font{14.0f} +//, columns{columns} +//, rows{rows} +, onRowSelected{onRowSelected}/*, rowToIDMapper(rowToIDMapper)*/ { // Create our table component and add it to this component.. @@ -104,26 +107,34 @@ TableComponent::~TableComponent() { void TableComponent::valueTreePropertyChanged( ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { - if (treeWhosePropertyHasChanged.getType() == StringRef("soundFont")) { + if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { + rows.clear(); + int numChildren{treeWhosePropertyHasChanged.getNumChildren()}; + for(int i{0}; i>& rows, int initiallySelectedRow) { - this->rows = rows; - table.deselectAllRows(); - table.updateContent(); - table.getHeader().setSortColumnId(0, true); - table.selectRow(initiallySelectedRow); - table.repaint(); -} +// void TableComponent::setRows(const vector>& 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 int TableComponent::getNumRows() @@ -160,7 +171,14 @@ void TableComponent::paintCell ( g.setColour (getLookAndFeel().findColour (ListBox::textColourId)); g.setFont (font); - g.drawText (rows[rowNumber][columnId-1], 2, 0, width - 4, height, Justification::centredLeft, true); + TableRow& row{rows[rowNumber]}; + String text; + if (columnId <= 1) { + text = String(row.preset); + } else { + text = row.name; + } + g.drawText (text, 2, 0, width - 4, height, Justification::centredLeft, true); g.setColour (getLookAndFeel().findColour (ListBox::backgroundColourId)); g.fillRect (width - 1, 0, 1, height); @@ -174,20 +192,25 @@ void TableComponent::sortOrderChanged ( ) { if (newSortColumnId != 0) { int selectedRowIx = table.getSelectedRow(); - vector selectedRow; - if (selectedRowIx >= 0) { - selectedRow = rows[selectedRowIx]; - } + // TableRow* selectedRow; + // if (selectedRowIx >= 0) { + // selectedRow = &rows[selectedRowIx]; + // } TableComponent::DataSorter sorter (newSortColumnId, isForwards); sort(rows.begin(), rows.end(), sorter); table.updateContent(); + RangedAudioParameter *param {valueTreeState.getParameter("preset")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + int value{castParam->get()}; + if (selectedRowIx >= 0) { for (auto it = rows.begin(); it != rows.end(); ++it) { - if(*it == selectedRow) { - int index = static_cast(std::distance(rows.begin(), it)); + if(it->preset == value) { + int index {static_cast(std::distance(rows.begin(), it))}; table.selectRow(index); break; } @@ -206,8 +229,15 @@ int TableComponent::getColumnAutoSizeWidth (int columnId) { int widest = 32; // find the widest bit of text in this column.. - for (int i = getNumRows(); --i >= 0;) { - widest = jmax (widest, font.getStringWidth (rows[i][columnId-1])); + for (int i{getNumRows()}; --i >= 0;) { + TableRow& row{rows[i]}; + String text; + if (columnId <= 1) { + text = String(row.preset); + } else { + text = row.name; + } + widest = jmax (widest, font.getStringWidth (text)); } return widest + 8; @@ -226,20 +256,24 @@ TableComponent::DataSorter::DataSorter ( int columnByWhichToSort, bool forwards ) - : columnByWhichToSort (columnByWhichToSort), - direction (forwards ? 1 : -1) +: columnByWhichToSort (columnByWhichToSort) +, direction (forwards ? 1 : -1) {} bool TableComponent::DataSorter::operator ()( - vector first, - vector second + TableRow first, + TableRow second ) { - int result = String(first[columnByWhichToSort-1]) - .compareNatural (String(second[columnByWhichToSort-1])); - - if (result == 0) - result = String(first[0]) - .compareNatural (String(second[0])); + int result; + if (columnByWhichToSort <= 1) { + result = compare(first.preset, second.preset); + } else { + result = first.name + .compareNatural (second.name); + if (result == 0) { + result = compare(first.preset, second.preset); + } + } result *= direction; @@ -252,7 +286,7 @@ void TableComponent::selectedRowsChanged (int row) { } // onRowSelected(rowToIDMapper(rows[row])); // onRowSelected(stoi(rows[row][0])); - int newPreset{stoi(rows[row][0])}; + int newPreset{rows[row].preset}; RangedAudioParameter *param {valueTreeState.getParameter("preset")}; jassert(dynamic_cast (param) != nullptr); AudioParameterInt* castParam {dynamic_cast (param)}; @@ -262,3 +296,11 @@ void TableComponent::selectedRowsChanged (int row) { bool TableComponent::keyPressed(const KeyPress &key) { return table.keyPressed(key); } + +TableRow::TableRow( + int preset, + String name +) +: preset{preset} +: name{name} +{} diff --git a/Source/TableComponent.h b/Source/TableComponent.h index 7445e44..7153923 100644 --- a/Source/TableComponent.h +++ b/Source/TableComponent.h @@ -15,6 +15,21 @@ using namespace std; +class TableRow { +public: + TableRow( + int preset, + String name + ); +private: + int preset; + String name; + + friend class TableComponent; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableRow) +}; + + class TableComponent : public Component, public TableListBoxModel, public ValueTree::Listener/*, @@ -23,7 +38,7 @@ class TableComponent : public Component, TableComponent( AudioProcessorValueTreeState& valueTreeState, // const vector &columns, - const vector> &rows, + // const vector &rows, const function &onRowSelected, // const function&)> &rowToIDMapper, int initiallySelectedRow @@ -56,7 +71,7 @@ class TableComponent : public Component, void resized() override; - void setRows(const vector>& rows, int initiallySelectedRow); +// void setRows(const vector& rows, int initiallySelectedRow); bool keyPressed(const KeyPress &key) override; @@ -79,8 +94,8 @@ class TableComponent : public Component, TableListBox table; // the table component itself Font font; - vector columns; - vector> rows; +// vector columns; + vector rows; function onRowSelected; // function&)> rowToIDMapper; @@ -94,8 +109,8 @@ class TableComponent : public Component, ); bool operator ()( - vector first, - vector second + TableRow first, + TableRow second ); private: @@ -104,4 +119,4 @@ class TableComponent : public Component, }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableComponent) -}; \ No newline at end of file +}; diff --git a/Source/TablesComponent.cpp b/Source/TablesComponent.cpp index 6e45cbb..1f89040 100644 --- a/Source/TablesComponent.cpp +++ b/Source/TablesComponent.cpp @@ -35,11 +35,12 @@ TablesComponent::TablesComponent( }; presetTable = new TableComponent( + valueTreeState, // {"#", "Name"}, - mapPresets( - banksToPresets, - selectedBank - ), + // mapPresets( + // banksToPresets, + // selectedBank + // ), [this](int preset){ this->onPresetSelected(preset); }, @@ -169,24 +170,24 @@ bool TablesComponent::keyPressed(const KeyPress &key) { return presetTable->keyPressed(key); } -void TablesComponent::fontChanged(FluidSynthModel *, const String &) { - banksToPresets = fluidSynthModel.getBanks(); +// void TablesComponent::fontChanged(FluidSynthModel *, const String &) { +// banksToPresets = fluidSynthModel.getBanks(); - fluid_preset_t* currentPreset = getCurrentPreset(); +// fluid_preset_t* currentPreset = getCurrentPreset(); - selectedBank = fluid_preset_get_banknum(currentPreset); - int selectedPreset = fluid_preset_get_num(currentPreset); +// selectedBank = fluid_preset_get_banknum(currentPreset); +// int selectedPreset = fluid_preset_get_num(currentPreset); - presetTable->setRows( - mapPresets( - banksToPresets, - selectedBank - ), - presetToIndexMapper(selectedPreset) - ); +// presetTable->setRows( +// mapPresets( +// banksToPresets, +// selectedBank +// ), +// presetToIndexMapper(selectedPreset) +// ); - banks->setItems( - mapBanks(banksToPresets), - selectedBank - ); -} +// banks->setItems( +// mapBanks(banksToPresets), +// selectedBank +// ); +// } diff --git a/Source/TablesComponent.h b/Source/TablesComponent.h index 9f57815..9156db6 100644 --- a/Source/TablesComponent.h +++ b/Source/TablesComponent.h @@ -15,8 +15,8 @@ using namespace std; -class TablesComponent : public Component, - public FluidSynthModel::Listener +class TablesComponent : public Component/*, + public FluidSynthModel::Listener */ { public: TablesComponent( @@ -28,7 +28,7 @@ class TablesComponent : public Component, void resized() override; bool keyPressed(const KeyPress &key) override; - void fontChanged(FluidSynthModel *, const String &) override; + // void fontChanged(FluidSynthModel *, const String &) override; private: AudioProcessorValueTreeState& valueTreeState; diff --git a/Source/Util.h b/Source/Util.h index c186f83..85de943 100644 --- a/Source/Util.h +++ b/Source/Util.h @@ -9,3 +9,11 @@ #define DEBUG_PRINT(str) #endif #endif + +namespace Util { + inline int compare(int a, int b) { + if (a > b) return 1; + if (a == b) return 0; + return -1; + } +} \ No newline at end of file From 58574425f350d6f1465dc26dfd0515ec261c479f Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 14 Jul 2019 14:19:27 +0100 Subject: [PATCH 13/33] further progress making TableComponent use valueTree for its model. begin doing the same for Pills. --- Source/Pills.cpp | 193 +++++++++++++++++++++++++++---------- Source/Pills.h | 82 ++++++++++++---- Source/TableComponent.cpp | 131 +++++++++++++------------ Source/TableComponent.h | 20 ++-- Source/TablesComponent.cpp | 50 +++++----- Source/TablesComponent.h | 6 +- 6 files changed, 316 insertions(+), 166 deletions(-) diff --git a/Source/Pills.cpp b/Source/Pills.cpp index 6e5afc5..438f7a6 100644 --- a/Source/Pills.cpp +++ b/Source/Pills.cpp @@ -7,74 +7,163 @@ using namespace std; +Pill::Pill( + AudioProcessorValueTreeState& valueTreeState, + int bank, + bool isFirst, + bool isLast +) +// : pills{pills} +: bank{bank} +, textButton{String(bank)} +{ + textButton.setConnectedEdges ( + (isFirst ? 0 : Button::ConnectedOnLeft) + | (isLast ? 0 : Button::ConnectedOnRight) + ); + textButton.setRadioGroupId(34567); + loadToggleState(); + textButton.setClickingTogglesState(true); + + valueTreeState.state.addListener(this); + textButton.addListener(this); +} + +Pill::~Pill() { + valueTreeState.state.removeListener(this); + textButton.removeListener(this); +} + +Pill::loadToggleState() { + RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + int value{castParam->get()}; + textButton.setToggleState(value == bank, dontSendNotification); +} + +void Pill::buttonClicked (Button* button) { + // selected = button; + // onItemSelected(itemToIDMapper(button->getName().toStdString())); + RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + *castParam = bank; +} + +void Pill::parameterChanged(const String& parameterID, float newValue) { + if (parameterID == "bank") { + loadToggleState(); + } +} + +void Pill::valueTreePropertyChanged( + ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) { + if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { + loadModelFrom(treeWhosePropertyHasChanged); + } +} + Pills::Pills( - string label, - const vector &items, - const function &onItemSelected, - const function &itemToIDMapper, - int initiallySelectedItem -) : label(label), - items(items), - onItemSelected(onItemSelected), - itemToIDMapper(itemToIDMapper) + AudioProcessorValueTreeState& valueTreeState + // string label, + // const vector &items, + // const function &onItemSelected, + // const function &itemToIDMapper, + // int initiallySelectedItem +) +: valueTreeState{valueTreeState} +// , label{label} + // items(items), + // onItemSelected(onItemSelected), + // itemToIDMapper(itemToIDMapper) { // faster (rounded edges introduce transparency) setOpaque (true); - populate(initiallySelectedItem); + // populate(initiallySelectedItem); + loadModelFrom(valueTreeState.state.getChildWithName("banks")); + + valueTreeState.state.addListener(this); } -void Pills::populate(int initiallySelectedItem) { - int index = 0; - for (string item : items) { - TextButton* pill = addToList(new TextButton( - item - )); -// pill->setColour (TextButton::buttonOnColourId, Colours::blueviolet.brighter()); -// pill->setBounds(20 + index * 55, 260, 55, 24); - pill->setConnectedEdges ( - (index == 0 ? 0 : Button::ConnectedOnLeft) - | (index == (items.size()-1) ? 0 : Button::ConnectedOnRight) - ); - pill->setRadioGroupId(34567); - if (index == initiallySelectedItem) { - pill->setToggleState(true, dontSendNotification); - selected = pill; - } - pill->setClickingTogglesState(true); - pill->addListener(this); - index++; - } +Pills::~Pills() { + valueTreeState.state.removeListener(this); } -void Pills::setItems( - const vector &items, - int initiallySelectedItem -) { - this->items = items; - for(TextButton* t : buttons) { - t->removeListener(this); +void Pills::valueTreePropertyChanged( + ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) { + if (treeWhosePropertyHasChanged.getType() == StringRef("banks")) { + loadModelFrom(treeWhosePropertyHasChanged); } - buttons.clear(true); - populate(initiallySelectedItem); - resized(); } -void Pills::buttonClicked (Button* button) { - selected = button; - onItemSelected(itemToIDMapper(button->getName().toStdString())); +void Pills::loadModelFrom(ValueTree& banks) { + pills.clear(); + int numChildren{banks.getNumChildren()}; + for(int i{0}; i < numChildren; i++) { + ValueTree child{banks.getChild(i)}; + int num{child.getProperty("num")}; + // rows.push_back(unique_ptr(new Pill(), [](Pill* pill) { + // pill->remo + // })); + pills.emplace_back( + this.valueTreeState, + num, + i == 0, + i == numChildren - 1); + } } -TextButton* Pills::addToList (TextButton* newButton) { - buttons.add (newButton); - addAndMakeVisible (newButton); - return newButton; -} +// void Pills::populate(int initiallySelectedItem) { +// int index = 0; +// for (string item : items) { +// TextButton* pill = addToList(new TextButton( +// item +// )); +// // pill->setColour (TextButton::buttonOnColourId, Colours::blueviolet.brighter()); +// // pill->setBounds(20 + index * 55, 260, 55, 24); +// pill->setConnectedEdges ( +// (index == 0 ? 0 : Button::ConnectedOnLeft) +// | (index == (items.size()-1) ? 0 : Button::ConnectedOnRight) +// ); +// pill->setRadioGroupId(34567); +// if (index == initiallySelectedItem) { +// pill->setToggleState(true, dontSendNotification); +// selected = pill; +// } +// pill->setClickingTogglesState(true); +// pill->addListener(this); +// index++; +// } +// } + +// void Pills::setItems( +// const vector &items, +// int initiallySelectedItem +// ) { +// this->items = items; +// for(TextButton* t : buttons) { +// t->removeListener(this); +// } +// buttons.clear(true); +// populate(initiallySelectedItem); +// resized(); +// } + +// TextButton* Pills::addToList (TextButton* newButton) { +// buttons.add (newButton); +// addAndMakeVisible (newButton); +// return newButton; +// } void Pills::cycle(bool right) { - int currentIx = static_cast(distance(buttons.begin(), find(buttons.begin(), buttons.end(), selected))); - currentIx += right ? 1 : buttons.size()-1; - buttons[currentIx % buttons.size()]->triggerClick(); + // TODO: base this on valueTree + int currentIx = static_cast(distance(pills.begin(), find(pills.begin(), pills.end(), selected))); + currentIx += right ? 1 : pills.size()-1; + pills[currentIx % pills.size()]->textButton.triggerClick(); } void Pills::resized() { diff --git a/Source/Pills.h b/Source/Pills.h index 530a0eb..1440ae1 100644 --- a/Source/Pills.h +++ b/Source/Pills.h @@ -8,35 +8,81 @@ using namespace std; -class Pills : public Component, - public Button::Listener { +class Pill +: public Button::Listener +, public AudioProcessorValueTreeState::Listener +{ public: - Pills( - string label, - const vector &items, - const function &onItemSelected, - const function &itemToIDMapper, - int initiallySelectedItem + Pill( + AudioProcessorValueTreeState& valueTreeState, + int bank, + bool isFirst, + bool isLast ); + ~Pill(); + + void buttonClicked (Button* button) override; + + virtual void parameterChanged (const String& parameterID, float newValue) override; +private: + void loadToggleState(); + + AudioProcessorValueTreeState& valueTreeState; + int bank; + TextButton textButton; - void setItems( - const vector &items, - int initiallySelectedItem + friend class Pills; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pill) +} + +class Pills +: public Component +, public ValueTree::Listener +{ +public: + Pills( + AudioProcessorValueTreeState& valueTreeState + // string label + // const vector &items, + // const function &onItemSelected, + // const function &itemToIDMapper, + // int initiallySelectedItem ); + ~Pills(); - void buttonClicked (Button* button) override; + // void setItems( + // const vector &items, + // int initiallySelectedItem + // ); + + // void buttonClicked (Button* button) override; void cycle(bool right); + virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) override; + inline virtual void valueTreeChildAdded (ValueTree& parentTree, + ValueTree& childWhichHasBeenAdded) override {}; + inline virtual void valueTreeChildRemoved (ValueTree& parentTree, + ValueTree& childWhichHasBeenRemoved, + int indexFromWhichChildWasRemoved) override {}; + inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, + int oldIndex, int newIndex) override {}; + inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; + inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; private: - string label; - vector items; - function onItemSelected; - function itemToIDMapper; + void loadModelFrom(ValueTree& banks); + + AudioProcessorValueTreeState& valueTreeState; + // string label; + // vector items; + // function onItemSelected; + // function itemToIDMapper; - OwnedArray buttons; + // OwnedArray buttons; + vector> pills; Button *selected; - TextButton* addToList (TextButton* newButton); + // Pill* addToList (Pill* newButton); void populate(int initiallySelectedItem); void resized() override; diff --git a/Source/TableComponent.cpp b/Source/TableComponent.cpp index 8888c18..0023c4b 100644 --- a/Source/TableComponent.cpp +++ b/Source/TableComponent.cpp @@ -20,16 +20,16 @@ TableComponent::TableComponent( AudioProcessorValueTreeState& valueTreeState, // const vector &columns, // const vector &rows, - const function &onRowSelected, + // const function &onRowSelected, // const function&)> &rowToIDMapper, - int initiallySelectedRow + // int initiallySelectedRow ) : valueTreeState{valueTreeState} , font{14.0f} //, columns{columns} //, rows{rows} -, onRowSelected{onRowSelected}/*, -rowToIDMapper(rowToIDMapper)*/ +// , onRowSelected{onRowSelected} +// rowToIDMapper(rowToIDMapper) { // Create our table component and add it to this component.. addAndMakeVisible (table); @@ -73,7 +73,9 @@ rowToIDMapper(rowToIDMapper)*/ table.setWantsKeyboardFocus(false); - table.selectRow(initiallySelectedRow); + // table.selectRow(); + loadModelFrom(valueTreeState.state.getChildWithName("presets")); + // selectCurrentPreset(); // we could now change some initial settings.. table.getHeader().setSortColumnId(1, false); // sort ascending by ID column @@ -90,40 +92,38 @@ TableComponent::~TableComponent() { valueTreeState.state.removeListener(this); } -// void TableComponent::parameterChanged(const String& parameterID, float newValue) { -// // valueTreeState.getParameter -// RangedAudioParameter *param {valueTreeState.getParameter("bank")}; -// if (parameterID == "bank") { -// jassert(dynamic_cast (param) != nullptr); -// AudioParameterInt* castParam {dynamic_cast (param)}; -// int value{castParam->get()}; -// } else if (parameterID == "preset") { -// jassert(dynamic_cast (param) != nullptr); -// AudioParameterInt* castParam {dynamic_cast (param)}; -// int value{castParam->get()}; -// } -// } +void TableComponent::loadModelFrom(ValueTree& presets) { + rows.clear(); + int numChildren{presets.getNumChildren()}; + for(int i{0}; i (param) != nullptr); + // AudioParameterInt* castParam {dynamic_cast (param)}; + // int value{castParam->get()}; + } +} void TableComponent::valueTreePropertyChanged( ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { - rows.clear(); - int numChildren{treeWhosePropertyHasChanged.getNumChildren()}; - for(int i{0}; i= 0) { // selectedRow = &rows[selectedRowIx]; @@ -201,20 +203,32 @@ void TableComponent::sortOrderChanged ( sort(rows.begin(), rows.end(), sorter); table.updateContent(); + selectCurrentPreset(); + + // if (selectedRowIx >= 0) { + // for (auto it = rows.begin(); it != rows.end(); ++it) { + // if(it->preset == value) { + // int index {static_cast(std::distance(rows.begin(), it))}; + // table.selectRow(index); + // break; + // } + // } + // } + } +} + +void TableComponent::selectCurrentPreset() { + table.deselectAllRows(); + RangedAudioParameter *param {valueTreeState.getParameter("preset")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + int value{castParam->get()}; - RangedAudioParameter *param {valueTreeState.getParameter("preset")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - int value{castParam->get()}; - - if (selectedRowIx >= 0) { - for (auto it = rows.begin(); it != rows.end(); ++it) { - if(it->preset == value) { - int index {static_cast(std::distance(rows.begin(), it))}; - table.selectRow(index); - break; - } - } + for (auto it = rows.begin(); it != rows.end(); ++it) { + if(it->preset == value) { + int index {static_cast(std::distance(rows.begin(), it))}; + table.selectRow(index); + break; } } } @@ -231,12 +245,7 @@ int TableComponent::getColumnAutoSizeWidth (int columnId) { // find the widest bit of text in this column.. for (int i{getNumRows()}; --i >= 0;) { TableRow& row{rows[i]}; - String text; - if (columnId <= 1) { - text = String(row.preset); - } else { - text = row.name; - } + String text{row.getStringContents(columnId)}; widest = jmax (widest, font.getStringWidth (text)); } @@ -302,5 +311,5 @@ TableRow::TableRow( String name ) : preset{preset} -: name{name} +, name{name} {} diff --git a/Source/TableComponent.h b/Source/TableComponent.h index 7153923..8292249 100644 --- a/Source/TableComponent.h +++ b/Source/TableComponent.h @@ -22,6 +22,9 @@ class TableRow { String name ); private: + /** 1-indexed */ + String getStringContents(int columnId); + int preset; String name; @@ -32,16 +35,16 @@ class TableRow { class TableComponent : public Component, public TableListBoxModel, - public ValueTree::Listener/*, - public AudioProcessorValueTreeState::Listener */ { + public ValueTree::Listener, + public AudioProcessorValueTreeState::Listener { public: TableComponent( - AudioProcessorValueTreeState& valueTreeState, + AudioProcessorValueTreeState& valueTreeState // const vector &columns, // const vector &rows, - const function &onRowSelected, + // const function &onRowSelected, // const function&)> &rowToIDMapper, - int initiallySelectedRow + // int initiallySelectedRow ); ~TableComponent(); @@ -75,7 +78,7 @@ class TableComponent : public Component, bool keyPressed(const KeyPress &key) override; -// virtual void parameterChanged (const String& parameterID, float newValue) override; + virtual void parameterChanged (const String& parameterID, float newValue) override; virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) override; @@ -89,6 +92,9 @@ class TableComponent : public Component, inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; private: + void loadModelFrom(ValueTree& presets); + void selectCurrentPreset(); + AudioProcessorValueTreeState& valueTreeState; TableListBox table; // the table component itself @@ -97,7 +103,7 @@ class TableComponent : public Component, // vector columns; vector rows; - function onRowSelected; + // function onRowSelected; // function&)> rowToIDMapper; // A comparator used to sort our data when the user clicks a column header diff --git a/Source/TablesComponent.cpp b/Source/TablesComponent.cpp index 1f89040..6549909 100644 --- a/Source/TablesComponent.cpp +++ b/Source/TablesComponent.cpp @@ -41,11 +41,11 @@ TablesComponent::TablesComponent( // banksToPresets, // selectedBank // ), - [this](int preset){ - this->onPresetSelected(preset); - }, + // [this](int preset){ + // this->onPresetSelected(preset); + // }, // rowToPresetMapper, - presetToIndexMapper(selectedPreset) + // presetToIndexMapper(selectedPreset) ); banks = new Pills( "Banks", @@ -108,19 +108,19 @@ int TablesComponent::presetToIndexMapper(int preset) { return 0; } -void TablesComponent::onPresetSelected(int preset) { - if (!initialised || preset == -1) { - return; - } - cout << "Preset " << preset << endl; -// selectedPreset = preset; - fluidSynthModel.changePreset(selectedBank, preset); -} +// void TablesComponent::onPresetSelected(int preset) { +// if (!initialised || preset == -1) { +// return; +// } +// cout << "Preset " << preset << endl; +// // selectedPreset = preset; +// fluidSynthModel.changePreset(selectedBank, preset); +// } TablesComponent::~TablesComponent() { delete presetTable; delete banks; - fluidSynthModel.removeListener(this); + // fluidSynthModel.removeListener(this); } vector TablesComponent::mapBanks(const BanksToPresets &banksToPresets) { @@ -138,21 +138,21 @@ vector TablesComponent::mapBanks(const BanksToPresets &banksToPresets) { } -vector> TablesComponent::mapPresets(const BanksToPresets &banksToPresets, int bank) { - vector> rows; +// vector> TablesComponent::mapPresets(const BanksToPresets &banksToPresets, int bank) { +// vector> rows; - pair iterators = banksToPresets.equal_range(bank); - for (auto it = iterators.first; it != iterators.second; ++it) { - Preset b = it->second; - vector row; - row.push_back(to_string(b.getPreset())); - row.push_back(b.getName()); +// pair iterators = banksToPresets.equal_range(bank); +// for (auto it = iterators.first; it != iterators.second; ++it) { +// Preset b = it->second; +// vector row; +// row.push_back(to_string(b.getPreset())); +// row.push_back(b.getName()); - rows.push_back(row); - } +// rows.push_back(row); +// } - return rows; -} +// return rows; +// } void TablesComponent::resized() { Rectangle r (getLocalBounds()); diff --git a/Source/TablesComponent.h b/Source/TablesComponent.h index 9156db6..69191c9 100644 --- a/Source/TablesComponent.h +++ b/Source/TablesComponent.h @@ -35,12 +35,12 @@ class TablesComponent : public Component/*, FluidSynthModel& fluidSynthModel; int selectedBank; - Pills* banks; - TableComponent* presetTable; + Pills banks; + TableComponent presetTable; BanksToPresets banksToPresets; - static vector> mapPresets(const BanksToPresets &banksToPresets, int bank); + // static vector> mapPresets(const BanksToPresets &banksToPresets, int bank); static vector mapBanks(const BanksToPresets &banksToPresets); void onBankSelected(int bank); From 8c1be957fee69c987d06d77c2460604180776f21 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 14 Jul 2019 17:22:36 +0100 Subject: [PATCH 14/33] decoupling achieved. compiles. doesn't immediately explode. but neither banks nor presets visible. --- Source/FilePicker.cpp | 6 +- Source/FilePicker.h | 6 +- Source/Pills.cpp | 82 +++++++++++----- Source/Pills.h | 5 +- Source/PluginEditor.cpp | 4 +- Source/TableComponent.cpp | 9 +- Source/TableComponent.h | 1 - Source/TablesComponent.cpp | 194 +++++++++++++++++++------------------ Source/TablesComponent.h | 26 ++--- 9 files changed, 185 insertions(+), 148 deletions(-) diff --git a/Source/FilePicker.cpp b/Source/FilePicker.cpp index dbd6930..7a74eef 100644 --- a/Source/FilePicker.cpp +++ b/Source/FilePicker.cpp @@ -7,8 +7,8 @@ #include "Util.h" FilePicker::FilePicker( - AudioProcessorValueTreeState& valueTreeState, - FluidSynthModel& fluidSynthModel + AudioProcessorValueTreeState& valueTreeState + // FluidSynthModel& fluidSynthModel ) : fileChooser{ "File", @@ -20,7 +20,7 @@ FilePicker::FilePicker( String(), "Choose a Soundfont file to load into the synthesizer"} , valueTreeState{valueTreeState} -, fluidSynthModel{fluidSynthModel} +// , fluidSynthModel{fluidSynthModel} // , currentPath{} { // faster (rounded edges introduce transparency) diff --git a/Source/FilePicker.h b/Source/FilePicker.h index 1c3e39d..f590604 100644 --- a/Source/FilePicker.h +++ b/Source/FilePicker.h @@ -15,8 +15,8 @@ class FilePicker: public Component, { public: FilePicker( - AudioProcessorValueTreeState& valueTreeState, - FluidSynthModel& fluidSynthModel + AudioProcessorValueTreeState& valueTreeState + // FluidSynthModel& fluidSynthModel ); ~FilePicker(); @@ -41,7 +41,7 @@ class FilePicker: public Component, FilenameComponent fileChooser; AudioProcessorValueTreeState& valueTreeState; - FluidSynthModel& fluidSynthModel; + // FluidSynthModel& fluidSynthModel; String currentPath; diff --git a/Source/Pills.cpp b/Source/Pills.cpp index 438f7a6..cb03df7 100644 --- a/Source/Pills.cpp +++ b/Source/Pills.cpp @@ -4,6 +4,7 @@ #include "Pills.h" #include "MyColours.h" +#include using namespace std; @@ -14,7 +15,8 @@ Pill::Pill( bool isLast ) // : pills{pills} -: bank{bank} +: valueTreeState{valueTreeState} +, bank{bank} , textButton{String(bank)} { textButton.setConnectedEdges ( @@ -25,16 +27,18 @@ Pill::Pill( loadToggleState(); textButton.setClickingTogglesState(true); - valueTreeState.state.addListener(this); + valueTreeState.addParameterListener("bank", this); +// valueTreeState.state.addListener(this); textButton.addListener(this); } Pill::~Pill() { - valueTreeState.state.removeListener(this); + valueTreeState.removeParameterListener("bank", this); +// valueTreeState.state.removeListener(this); textButton.removeListener(this); } -Pill::loadToggleState() { +void Pill::loadToggleState() { RangedAudioParameter *param {valueTreeState.getParameter("bank")}; jassert(dynamic_cast (param) != nullptr); AudioParameterInt* castParam {dynamic_cast (param)}; @@ -57,13 +61,13 @@ void Pill::parameterChanged(const String& parameterID, float newValue) { } } -void Pill::valueTreePropertyChanged( - ValueTree& treeWhosePropertyHasChanged, - const Identifier& property) { - if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { - loadModelFrom(treeWhosePropertyHasChanged); - } -} +// void Pill::valueTreePropertyChanged( +// ValueTree& treeWhosePropertyHasChanged, +// const Identifier& property) { +// if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { +// loadModelFrom(treeWhosePropertyHasChanged); +// } +// } Pills::Pills( AudioProcessorValueTreeState& valueTreeState @@ -83,7 +87,8 @@ Pills::Pills( setOpaque (true); // populate(initiallySelectedItem); - loadModelFrom(valueTreeState.state.getChildWithName("banks")); + ValueTree banks{valueTreeState.state.getChildWithName("banks")}; + loadModelFrom(banks); valueTreeState.state.addListener(this); } @@ -109,11 +114,12 @@ void Pills::loadModelFrom(ValueTree& banks) { // rows.push_back(unique_ptr(new Pill(), [](Pill* pill) { // pill->remo // })); - pills.emplace_back( - this.valueTreeState, + pills.push_back( + make_unique( + valueTreeState, num, i == 0, - i == numChildren - 1); + i == numChildren - 1)); } } @@ -160,21 +166,51 @@ void Pills::loadModelFrom(ValueTree& banks) { // } void Pills::cycle(bool right) { - // TODO: base this on valueTree - int currentIx = static_cast(distance(pills.begin(), find(pills.begin(), pills.end(), selected))); + RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + int currentlySelectedBank{castParam->get()}; + + ValueTree banks{valueTreeState.state.getChildWithName("banks")}; +// int numChildren{banks.getNumChildren()}; + + vector bankInts; + bankInts.resize(banks.getNumChildren()); + + transform(banks.begin(), banks.end(), bankInts.begin(), [](ValueTree bank) -> int { + return bank.getProperty("num"); + }); + +// int closestBank{currentlySelectedBank}; +// for(int i{0}; i < numChildren; i++) { +// ValueTree child{banks.getChild(i)}; +// int proposedBank{child.getProperty("num")}; +// if (right && proposedBank > currentlySelectedBank) { +// closestBank = jmin(closestBank, proposedBank); +// } else if (left ) +// } + + int currentIx{static_cast(distance(bankInts.begin(), find(bankInts.begin(), bankInts.end(), currentlySelectedBank)))}; currentIx += right ? 1 : pills.size()-1; - pills[currentIx % pills.size()]->textButton.triggerClick(); + // pills[currentIx % pills.size()]->textButton.triggerClick(); + *castParam = bankInts[currentIx % bankInts.size()]; + + + // TODO: base this on valueTree + // int currentIx = static_cast(distance(pills.begin(), find(pills.begin(), pills.end(), selected))); + // currentIx += right ? 1 : pills.size()-1; + // pills[currentIx % pills.size()]->textButton.triggerClick(); } void Pills::resized() { int index = 0; Rectangle r (getLocalBounds()); - const int equalWidth = r.proportionOfWidth(buttons.size() <= 0 ? 1.0 : 1.0f/buttons.size()); - for(TextButton* t : buttons) { + const int equalWidth = r.proportionOfWidth(pills.size() <= 0 ? 1.0 : 1.0f/pills.size()); + for(auto& pill : pills) { Rectangle r2 (getLocalBounds()); r2.removeFromLeft(equalWidth * index); - r2.removeFromRight(equalWidth * (buttons.size()-index-1)); - t->setBounds (r2); + r2.removeFromRight(equalWidth * (static_cast(pills.size())-index-1)); + pill->textButton.setBounds (r2); index++; } } @@ -185,4 +221,4 @@ void Pills::resized() { void Pills::paint(Graphics& g) { g.fillAll(MyColours::getUIColourIfAvailable(LookAndFeel_V4::ColourScheme::UIColour::windowBackground, Colours::lightgrey)); -} \ No newline at end of file +} diff --git a/Source/Pills.h b/Source/Pills.h index 1440ae1..c66f756 100644 --- a/Source/Pills.h +++ b/Source/Pills.h @@ -32,8 +32,7 @@ class Pill TextButton textButton; friend class Pills; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pill) -} +}; class Pills : public Component @@ -89,4 +88,4 @@ class Pills void paint(Graphics& g) override; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pills) -}; \ No newline at end of file +}; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 6599001..b062ff3 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -21,8 +21,8 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor( , valueTreeState{valueTreeState} // sharedParams{p.getSharedParams()}, , midiKeyboard{p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard} -, tablesComponent{valueTreeState, p.getFluidSynthModel()} -, filePicker{valueTreeState, p.getFluidSynthModel()} +, tablesComponent{valueTreeState} +, filePicker{valueTreeState} , slidersComponent{valueTreeState, p.getFluidSynthModel()} { // set resize limits for this plug-in diff --git a/Source/TableComponent.cpp b/Source/TableComponent.cpp index 0023c4b..3bedb14 100644 --- a/Source/TableComponent.cpp +++ b/Source/TableComponent.cpp @@ -17,7 +17,7 @@ using namespace Util; This class shows how to implement a TableListBoxModel to show in a TableListBox. */ TableComponent::TableComponent( - AudioProcessorValueTreeState& valueTreeState, + AudioProcessorValueTreeState& valueTreeState // const vector &columns, // const vector &rows, // const function &onRowSelected, @@ -74,7 +74,8 @@ TableComponent::TableComponent( table.setWantsKeyboardFocus(false); // table.selectRow(); - loadModelFrom(valueTreeState.state.getChildWithName("presets")); + ValueTree presets{valueTreeState.state.getChildWithName("presets")}; + loadModelFrom(presets); // selectCurrentPreset(); // we could now change some initial settings.. @@ -160,9 +161,9 @@ void TableComponent::paintRowBackground ( String TableRow::getStringContents(int columnId) { if (columnId <= 1) { - return String(row.preset); + return String(preset); } - return row.name; + return name; } // This is overloaded from TableListBoxModel, and must paint any cells that aren't using custom diff --git a/Source/TableComponent.h b/Source/TableComponent.h index 8292249..41feaa3 100644 --- a/Source/TableComponent.h +++ b/Source/TableComponent.h @@ -29,7 +29,6 @@ class TableRow { String name; friend class TableComponent; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableRow) }; diff --git a/Source/TablesComponent.cpp b/Source/TablesComponent.cpp index 6549909..31284bc 100644 --- a/Source/TablesComponent.cpp +++ b/Source/TablesComponent.cpp @@ -8,105 +8,107 @@ using namespace std; using namespace placeholders; TablesComponent::TablesComponent( - AudioProcessorValueTreeState& valueTreeState, - FluidSynthModel& fluidSynthModel + AudioProcessorValueTreeState& valueTreeState + // FluidSynthModel& fluidSynthModel ) : valueTreeState{valueTreeState} -, fluidSynthModel{fluidSynthModel} -, banksToPresets{fluidSynthModel.getBanks()} -, initialised{false} +// , fluidSynthModel{fluidSynthModel} +, banks{valueTreeState} +, presetTable{valueTreeState} +// , banksToPresets{fluidSynthModel.getBanks()} +// , initialised{false} { - fluid_preset_t* currentPreset = getCurrentPreset(); - selectedBank = -1; - int selectedPreset = -1; + // fluid_preset_t* currentPreset = getCurrentPreset(); + // selectedBank = -1; + // int selectedPreset = -1; - if (currentPreset != nullptr) { - selectedBank = fluid_preset_get_banknum(currentPreset); - selectedPreset = fluid_preset_get_num(currentPreset); - } + // if (currentPreset != nullptr) { + // selectedBank = fluid_preset_get_banknum(currentPreset); + // selectedPreset = fluid_preset_get_num(currentPreset); + // } // auto rowToPresetMapper = [this](const vector &row) { // return stoi(row[0]); // }; - auto itemToBankMapper = [](const string &item) { - return stoi(item); - }; - - presetTable = new TableComponent( - valueTreeState, - // {"#", "Name"}, - // mapPresets( - // banksToPresets, - // selectedBank - // ), - // [this](int preset){ - // this->onPresetSelected(preset); - // }, - // rowToPresetMapper, - // presetToIndexMapper(selectedPreset) - ); - banks = new Pills( - "Banks", - mapBanks(banksToPresets), - [this](int bank){ - this->onBankSelected(bank); - }, - itemToBankMapper, - selectedBank - ); - - presetTable->setWantsKeyboardFocus(false); + // auto itemToBankMapper = [](const string &item) { + // return stoi(item); + // }; + + // presetTable = new TableComponent( + // valueTreeState, + // // {"#", "Name"}, + // // mapPresets( + // // banksToPresets, + // // selectedBank + // // ), + // // [this](int preset){ + // // this->onPresetSelected(preset); + // // }, + // // rowToPresetMapper, + // // presetToIndexMapper(selectedPreset) + // ); + // banks = new Pills( + // "Banks", + // mapBanks(banksToPresets), + // [this](int bank){ + // this->onBankSelected(bank); + // }, + // itemToBankMapper, + // selectedBank + // ); + + presetTable.setWantsKeyboardFocus(false); addAndMakeVisible(presetTable); addAndMakeVisible(banks); - initialised = true; + // initialised = true; - fluidSynthModel.addListener(this); + // fluidSynthModel.addListener(this); } -fluid_preset_t* TablesComponent::getCurrentPreset() { - shared_ptr synth {fluidSynthModel.getSynth()}; +// fluid_preset_t* TablesComponent::getCurrentPreset() { +// shared_ptr synth {fluidSynthModel.getSynth()}; - return fluid_synth_get_channel_preset(synth.get(), fluidSynthModel.getChannel()); -} +// return fluid_synth_get_channel_preset(synth.get(), fluidSynthModel.getChannel()); +// } -Preset TablesComponent::getFirstPresetInBank(int bank) { - pair iterators = banksToPresets.equal_range(bank); - BanksToPresets::const_iterator it = iterators.first; - return it->second; -} +// Preset TablesComponent::getFirstPresetInBank(int bank) { +// pair iterators = banksToPresets.equal_range(bank); +// BanksToPresets::const_iterator it = iterators.first; +// return it->second; +// } -void TablesComponent::onBankSelected(int bank) { - if (!initialised || bank == -1) { - return; - } - cout << "Bank " << bank << endl; - selectedBank = bank; - Preset firstPresetInBank = getFirstPresetInBank(bank); - presetTable->setRows( - mapPresets( - banksToPresets, - bank - ), - presetToIndexMapper(firstPresetInBank.getPreset()) - ); -} +// void TablesComponent::onBankSelected(int bank) { +// if (!initialised || bank == -1) { +// return; +// } +// cout << "Bank " << bank << endl; +// selectedBank = bank; +// Preset firstPresetInBank = getFirstPresetInBank(bank); +// presetTable->setRows( +// mapPresets( +// banksToPresets, +// bank +// ), +// presetToIndexMapper(firstPresetInBank.getPreset()) +// ); +// } -int TablesComponent::presetToIndexMapper(int preset) { - int ix = 0; - pair iterators = this->banksToPresets.equal_range(this->selectedBank); - for (auto it = iterators.first; it != iterators.second; ++it, ix++) { - Preset b = it->second; - if (preset == b.getPreset()) { - return ix; - } - } - return 0; -} +// int TablesComponent::presetToIndexMapper(int preset) { +// int ix = 0; +// pair iterators = this->banksToPresets.equal_range(this->selectedBank); +// for (auto it = iterators.first; it != iterators.second; ++it, ix++) { +// Preset b = it->second; +// if (preset == b.getPreset()) { +// return ix; +// } +// } +// return 0; +// } // void TablesComponent::onPresetSelected(int preset) { // if (!initialised || preset == -1) { @@ -117,25 +119,25 @@ int TablesComponent::presetToIndexMapper(int preset) { // fluidSynthModel.changePreset(selectedBank, preset); // } -TablesComponent::~TablesComponent() { - delete presetTable; - delete banks; - // fluidSynthModel.removeListener(this); -} +// TablesComponent::~TablesComponent() { +// delete presetTable; +// delete banks; +// // fluidSynthModel.removeListener(this); +// } -vector TablesComponent::mapBanks(const BanksToPresets &banksToPresets) { - vector rows; +// vector TablesComponent::mapBanks(const BanksToPresets &banksToPresets) { +// vector rows; - const auto compareKey = [](const BanksToPresets::value_type& lhs, const BanksToPresets::value_type& rhs) { - return lhs.first < rhs.first; - }; +// const auto compareKey = [](const BanksToPresets::value_type& lhs, const BanksToPresets::value_type& rhs) { +// return lhs.first < rhs.first; +// }; - for(auto i = banksToPresets.begin(); i != banksToPresets.end(); i = std::upper_bound(i, banksToPresets.end(), *i, compareKey)) { - rows.push_back(to_string(i->first)); - } +// for(auto i = banksToPresets.begin(); i != banksToPresets.end(); i = std::upper_bound(i, banksToPresets.end(), *i, compareKey)) { +// rows.push_back(to_string(i->first)); +// } - return rows; -} +// return rows; +// } // vector> TablesComponent::mapPresets(const BanksToPresets &banksToPresets, int bank) { @@ -156,18 +158,18 @@ vector TablesComponent::mapBanks(const BanksToPresets &banksToPresets) { void TablesComponent::resized() { Rectangle r (getLocalBounds()); - banks->setBounds (r.removeFromTop(27).reduced(5,0)); + banks.setBounds (r.removeFromTop(27).reduced(5,0)); - presetTable->setBounds (r); + presetTable.setBounds (r); } bool TablesComponent::keyPressed(const KeyPress &key) { if (key.getKeyCode() == KeyPress::leftKey || key.getKeyCode() == KeyPress::rightKey) { - banks->cycle(key.getKeyCode() == KeyPress::rightKey); + banks.cycle(key.getKeyCode() == KeyPress::rightKey); return true; } - return presetTable->keyPressed(key); + return presetTable.keyPressed(key); } // void TablesComponent::fontChanged(FluidSynthModel *, const String &) { diff --git a/Source/TablesComponent.h b/Source/TablesComponent.h index 69191c9..f3830cc 100644 --- a/Source/TablesComponent.h +++ b/Source/TablesComponent.h @@ -20,10 +20,10 @@ class TablesComponent : public Component/*, { public: TablesComponent( - AudioProcessorValueTreeState& valueTreeState, - FluidSynthModel& fluidSynthModel + AudioProcessorValueTreeState& valueTreeState + // FluidSynthModel& fluidSynthModel ); - ~TablesComponent(); + // ~TablesComponent(); void resized() override; @@ -32,25 +32,25 @@ class TablesComponent : public Component/*, private: AudioProcessorValueTreeState& valueTreeState; - FluidSynthModel& fluidSynthModel; - int selectedBank; + // FluidSynthModel& fluidSynthModel; + // int selectedBank; Pills banks; TableComponent presetTable; - BanksToPresets banksToPresets; + // BanksToPresets banksToPresets; // static vector> mapPresets(const BanksToPresets &banksToPresets, int bank); - static vector mapBanks(const BanksToPresets &banksToPresets); + // static vector mapBanks(const BanksToPresets &banksToPresets); - void onBankSelected(int bank); - void onPresetSelected(int preset); - int presetToIndexMapper(int preset); + // void onBankSelected(int bank); + // void onPresetSelected(int preset); + // int presetToIndexMapper(int preset); - fluid_preset_t* getCurrentPreset(); - Preset getFirstPresetInBank(int bank); + // fluid_preset_t* getCurrentPreset(); + // Preset getFirstPresetInBank(int bank); - bool initialised; + // bool initialised; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TablesComponent) }; From 11d729681389122528b69d4c8182c4444f921e8d Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 14 Jul 2019 17:45:08 +0100 Subject: [PATCH 15/33] try to correctly modify value tree and listen for changes --- Source/FilePicker.cpp | 3 ++- Source/FluidSynthModel.cpp | 15 ++++++++++++--- Source/PluginProcessor.cpp | 2 +- Source/TableComponent.cpp | 16 +++++++++++----- Source/TableComponent.h | 6 +++--- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/Source/FilePicker.cpp b/Source/FilePicker.cpp index 7a74eef..4787f49 100644 --- a/Source/FilePicker.cpp +++ b/Source/FilePicker.cpp @@ -56,7 +56,8 @@ void FilePicker::filenameComponentChanged (FilenameComponent*) { // currentPath = fileChooser.getCurrentFile().getFullPathName(); // fluidSynthModel.onFileNameChanged(fileChooser.getCurrentFile().getFullPathName(), -1, -1); Value value{valueTreeState.state.getChildWithName("soundFont").getPropertyAsValue("path", nullptr)}; - value = fileChooser.getCurrentFile().getFullPathName(); + value.setValue(fileChooser.getCurrentFile().getFullPathName()); +// value = fileChooser.getCurrentFile().getFullPathName(); } void FilePicker::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index c11ba06..5b8cef1 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -347,6 +347,7 @@ void FluidSynthModel::loadFont(const String &absPath) { banks.appendChild({ "bank", { { "num", initialBankOffset }, }, {} }, nullptr); + int greatestPersistedBank{initialBankOffset}; fluid_sfont_iteration_start(sfont); @@ -362,10 +363,11 @@ void FluidSynthModel::loadFont(const String &absPath) { // fluid_preset_get_name(preset) // ) // )); - if (bankOffset > initialBankOffset) { + if (bankOffset > greatestPersistedBank) { banks.appendChild({ "bank", { { "num", bankOffset }, }, {} }, nullptr); + greatestPersistedBank = bankOffset; } presets.appendChild({ "preset", { { "num", fluid_preset_get_num(preset) }, @@ -373,8 +375,15 @@ void FluidSynthModel::loadFont(const String &absPath) { }, {} }, nullptr); } } - valueTreeState.state.getChildWithName("banks") = banks; - valueTreeState.state.getChildWithName("presets") = presets; +// valueTreeState.state.getChildWithName("banks") = banks; +// valueTreeState.state.getChildWithName("presets") = presets; + valueTreeState.state.getChildWithName("banks").copyPropertiesAndChildrenFrom(banks, nullptr); + valueTreeState.state.getChildWithName("presets").copyPropertiesAndChildrenFrom(presets, nullptr); + +#if JUCE_DEBUG + unique_ptr xml{valueTreeState.state.createXml()}; + Logger::outputDebugString(xml->createDocument("",false,false)); +#endif } // FluidSynthModel::Listener::~Listener() { diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index e94fc05..40e6515 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -388,7 +388,7 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData) // XmlElement xml{"MYPLUGINSETTINGS"}; // sharedParams->setAttributesOnXml(xml); auto state{valueTreeState.copyState()}; - shared_ptr xml{state.createXml()}; + unique_ptr xml{state.createXml()}; // sharedParams.setAttributesOnXml(xml); // list::iterator p; diff --git a/Source/TableComponent.cpp b/Source/TableComponent.cpp index 3bedb14..021c1af 100644 --- a/Source/TableComponent.cpp +++ b/Source/TableComponent.cpp @@ -120,11 +120,17 @@ void TableComponent::parameterChanged(const String& parameterID, float newValue) } } -void TableComponent::valueTreePropertyChanged( - ValueTree& treeWhosePropertyHasChanged, - const Identifier& property) { - if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { - loadModelFrom(treeWhosePropertyHasChanged); +// void TableComponent::valueTreePropertyChanged( +// ValueTree& treeWhosePropertyHasChanged, +// const Identifier& property) { +// if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { +// loadModelFrom(treeWhosePropertyHasChanged); +// } +// } + +void TableComponent::valueTreeParentChanged(ValueTree& treeWhoseParentHasChanged) { + if (treeWhoseParentHasChanged.getType() == StringRef("presets")) { + loadModelFrom(treeWhoseParentHasChanged); } } diff --git a/Source/TableComponent.h b/Source/TableComponent.h index 41feaa3..6944ccc 100644 --- a/Source/TableComponent.h +++ b/Source/TableComponent.h @@ -79,8 +79,8 @@ class TableComponent : public Component, virtual void parameterChanged (const String& parameterID, float newValue) override; - virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, - const Identifier& property) override; + inline virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) override {}; inline virtual void valueTreeChildAdded (ValueTree& parentTree, ValueTree& childWhichHasBeenAdded) override {}; inline virtual void valueTreeChildRemoved (ValueTree& parentTree, @@ -88,7 +88,7 @@ class TableComponent : public Component, int indexFromWhichChildWasRemoved) override {}; inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, int oldIndex, int newIndex) override {}; - inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; + virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override; inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; private: void loadModelFrom(ValueTree& presets); From 99bb294eced2f46f1c0769fb8b2f1de1651d25dd Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 14 Jul 2019 19:31:05 +0100 Subject: [PATCH 16/33] render pills. use cheat to notify that tree of presets/banks has changed. --- Source/FluidSynthModel.cpp | 2 ++ Source/Pills.cpp | 27 ++++++++++++++++---- Source/Pills.h | 6 ++++- Source/PluginEditor.cpp | 2 +- Source/PluginProcessor.cpp | 4 +-- Source/TableComponent.cpp | 51 +++++++++++++++++++++++++++++++------- Source/TableComponent.h | 6 ++--- 7 files changed, 77 insertions(+), 21 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 5b8cef1..fd7ff8b 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -379,6 +379,8 @@ void FluidSynthModel::loadFont(const String &absPath) { // valueTreeState.state.getChildWithName("presets") = presets; valueTreeState.state.getChildWithName("banks").copyPropertiesAndChildrenFrom(banks, nullptr); valueTreeState.state.getChildWithName("presets").copyPropertiesAndChildrenFrom(presets, nullptr); + valueTreeState.state.getChildWithName("banks").sendPropertyChangeMessage("synthetic"); + valueTreeState.state.getChildWithName("presets").sendPropertyChangeMessage("synthetic"); #if JUCE_DEBUG unique_ptr xml{valueTreeState.state.createXml()}; diff --git a/Source/Pills.cpp b/Source/Pills.cpp index cb03df7..03d3056 100644 --- a/Source/Pills.cpp +++ b/Source/Pills.cpp @@ -19,6 +19,7 @@ Pill::Pill( , bank{bank} , textButton{String(bank)} { + setOpaque(true); textButton.setConnectedEdges ( (isFirst ? 0 : Button::ConnectedOnLeft) | (isLast ? 0 : Button::ConnectedOnRight) @@ -26,12 +27,24 @@ Pill::Pill( textButton.setRadioGroupId(34567); loadToggleState(); textButton.setClickingTogglesState(true); + + addAndMakeVisible(textButton); valueTreeState.addParameterListener("bank", this); // valueTreeState.state.addListener(this); textButton.addListener(this); } +void Pill::paint (Graphics& g) +{ + // (Our component is opaque, so we must completely fill the background with a solid colour) + g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); +} + +void Pill::resized() { + textButton.setBounds(getLocalBounds()); +} + Pill::~Pill() { valueTreeState.removeParameterListener("bank", this); // valueTreeState.state.removeListener(this); @@ -101,7 +114,9 @@ void Pills::valueTreePropertyChanged( ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { if (treeWhosePropertyHasChanged.getType() == StringRef("banks")) { - loadModelFrom(treeWhosePropertyHasChanged); + if (property == StringRef("synthetic")) { + loadModelFrom(treeWhosePropertyHasChanged); + } } } @@ -114,13 +129,15 @@ void Pills::loadModelFrom(ValueTree& banks) { // rows.push_back(unique_ptr(new Pill(), [](Pill* pill) { // pill->remo // })); - pills.push_back( - make_unique( + unique_ptr pill{make_unique( valueTreeState, num, i == 0, - i == numChildren - 1)); + i == numChildren - 1)}; + addAndMakeVisible(pill.get()); + pills.push_back(move(pill)); } + resized(); } // void Pills::populate(int initiallySelectedItem) { @@ -210,7 +227,7 @@ void Pills::resized() { Rectangle r2 (getLocalBounds()); r2.removeFromLeft(equalWidth * index); r2.removeFromRight(equalWidth * (static_cast(pills.size())-index-1)); - pill->textButton.setBounds (r2); + pill->setBounds(r2); index++; } } diff --git a/Source/Pills.h b/Source/Pills.h index c66f756..2264231 100644 --- a/Source/Pills.h +++ b/Source/Pills.h @@ -9,7 +9,8 @@ using namespace std; class Pill -: public Button::Listener +: public Component +, public Button::Listener , public AudioProcessorValueTreeState::Listener { public: @@ -23,6 +24,9 @@ class Pill void buttonClicked (Button* button) override; + void resized() override; + void paint(Graphics& g) override; + virtual void parameterChanged (const String& parameterID, float newValue) override; private: void loadToggleState(); diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index b062ff3..2c7d3eb 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -73,7 +73,7 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor( tablesComponent.setWantsKeyboardFocus(false); setWantsKeyboardFocus(true); - addAndMakeVisible (midiKeyboard); + addAndMakeVisible(midiKeyboard); addAndMakeVisible(slidersComponent); addAndMakeVisible(tablesComponent); diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 40e6515..b614540 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -73,8 +73,8 @@ AudioProcessorValueTreeState::ParameterLayout JuicySFAudioProcessor::createParam // make_unique("uiHeightTemp", "height of this plugin's GUI. Editor writes here on change (e.g. on window resize). Processor copies this into Persist before any save.", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height Temp" ), // make_unique("uiWidth", "width of this plugin's GUI", GuiConstants::minWidth, GuiConstants::maxWidth, GuiConstants::minWidth, "UI Width" ), // make_unique("uiHeight", "height of this plugin's GUI", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height" ), - // todo: check whether bank really is 0-127 - make_unique("bank", "which bank is selected in the soundfont", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "Bank" ), + // SoundFont 2.4 spec section 7.2: zero through 127, or 128. + make_unique("bank", "which bank is selected in the soundfont", MidiConstants::midiMinValue, 128, MidiConstants::midiMinValue, "Bank" ), // note: banks may be sparse, and lack a 0th preset. so defend against this. make_unique("preset", "which patch (aka patch, program, instrument) is selected in the soundfont", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "Preset" ), make_unique("attack", "volume envelope attack time", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "A" ), diff --git a/Source/TableComponent.cpp b/Source/TableComponent.cpp index 021c1af..babf668 100644 --- a/Source/TableComponent.cpp +++ b/Source/TableComponent.cpp @@ -120,19 +120,52 @@ void TableComponent::parameterChanged(const String& parameterID, float newValue) } } +void TableComponent::valueTreePropertyChanged( + ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) { + if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { + if (property == StringRef("synthetic")) { + loadModelFrom(treeWhosePropertyHasChanged); + } + } +} + +// void TableComponent::valueTreeParentChanged(ValueTree& treeWhoseParentHasChanged) { +// if (treeWhoseParentHasChanged.getType() == StringRef("presets")) { +// loadModelFrom(treeWhoseParentHasChanged); +// } +// } + // void TableComponent::valueTreePropertyChanged( // ValueTree& treeWhosePropertyHasChanged, // const Identifier& property) { -// if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { -// loadModelFrom(treeWhosePropertyHasChanged); -// } +// 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::valueTreeParentChanged(ValueTree& treeWhoseParentHasChanged) { - if (treeWhoseParentHasChanged.getType() == StringRef("presets")) { - loadModelFrom(treeWhoseParentHasChanged); - } -} // void TableComponent::setRows(const vector>& rows, int initiallySelectedRow) { // this->rows = rows; diff --git a/Source/TableComponent.h b/Source/TableComponent.h index 6944ccc..41feaa3 100644 --- a/Source/TableComponent.h +++ b/Source/TableComponent.h @@ -79,8 +79,8 @@ class TableComponent : public Component, virtual void parameterChanged (const String& parameterID, float newValue) override; - inline virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, - const Identifier& property) override {}; + virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, + const Identifier& property) override; inline virtual void valueTreeChildAdded (ValueTree& parentTree, ValueTree& childWhichHasBeenAdded) override {}; inline virtual void valueTreeChildRemoved (ValueTree& parentTree, @@ -88,7 +88,7 @@ class TableComponent : public Component, int indexFromWhichChildWasRemoved) override {}; inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, int oldIndex, int newIndex) override {}; - virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override; + inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; private: void loadModelFrom(ValueTree& presets); From fc1d45d77c54f9283a004e6a7660b74a0f3c6cb1 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Mon, 15 Jul 2019 21:28:35 +0100 Subject: [PATCH 17/33] separate functions to refresh presets, banks. update list of presets when bank changes. filter presets list by current bank. --- Source/FluidSynthModel.cpp | 144 +++++++++++++++++++++++++++++-------- Source/FluidSynthModel.h | 8 ++- 2 files changed, 123 insertions(+), 29 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index fd7ff8b..25bba1a 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -27,10 +27,14 @@ FluidSynthModel::FluidSynthModel( , channel{0}/*, mod(nullptr)*/ { + valueTreeState.addParameterListener("bank", this); + valueTreeState.addParameterListener("preset", this); valueTreeState.state.addListener(this); } FluidSynthModel::~FluidSynthModel() { + valueTreeState.removeParameterListener("bank", this); + valueTreeState.removeParameterListener("preset", this); valueTreeState.state.removeListener(this); // if (initialised) { // delete_fluid_audio_driver(driver); @@ -202,6 +206,28 @@ void FluidSynthModel::initialise() { // initialised = true; } +void FluidSynthModel::parameterChanged(const String& parameterID, float newValue) { + if (parameterID == "bank") { + RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + int value{castParam->get()}; + fluid_synth_bank_select(synth.get(), channel, value); + refreshPresets(); + // fluid_sfont_t* sfont{fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; + // fluid_sfont_iteration_start(sfont); + // fluid_preset_t* presetObj{fluid_sfont_iteration_next(sfont)}; + // int offset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; + // int bank{fluid_preset_get_banknum(presetObj) + offset}; + // int preset{fluid_preset_get_num(presetObj)}; + } else if (parameterID == "preset") { + RangedAudioParameter *param {valueTreeState.getParameter("preset")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + int value{castParam->get()}; + } +} + void FluidSynthModel::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { if (treeWhosePropertyHasChanged.getType() == StringRef("soundFont")) { @@ -339,55 +365,117 @@ void FluidSynthModel::loadFont(const String &absPath) { currentSoundFontAbsPath = absPath; sfont_id++; fluid_synth_sfload(synth.get(), absPath.toStdString().c_str(), 1); + refreshBanks(); +} + +void FluidSynthModel::refreshBanks() { fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; ValueTree banks{"banks"}; - ValueTree presets{"presets"}; if (sfont != nullptr) { - int initialBankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; + int initialBank{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; banks.appendChild({ "bank", { - { "num", initialBankOffset }, + { "num", initialBank }, }, {} }, nullptr); - int greatestPersistedBank{initialBankOffset}; + int greatestPersistedBank{initialBank}; fluid_sfont_iteration_start(sfont); for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; preset != nullptr; preset = fluid_sfont_iteration_next(sfont)) { - int bankOffset{fluid_preset_get_banknum(preset) + initialBankOffset}; - // ValueTree preset{"preset"}; - // banksToPresets.insert(BanksToPresets::value_type( - // fluid_preset_get_banknum(preset) + bankOffset, - // *new Preset( - // fluid_preset_get_num(preset), - // fluid_preset_get_name(preset) - // ) - // )); - if (bankOffset > greatestPersistedBank) { + int bank{fluid_preset_get_banknum(preset) + initialBank}; + if (bank > greatestPersistedBank) { banks.appendChild({ "bank", { - { "num", bankOffset }, + { "num", bank }, }, {} }, nullptr); - greatestPersistedBank = bankOffset; + greatestPersistedBank = bank; } - presets.appendChild({ "preset", { - { "num", fluid_preset_get_num(preset) }, - { "name", String{fluid_preset_get_name(preset)} } - }, {} }, nullptr); } } -// valueTreeState.state.getChildWithName("banks") = banks; -// valueTreeState.state.getChildWithName("presets") = presets; valueTreeState.state.getChildWithName("banks").copyPropertiesAndChildrenFrom(banks, nullptr); - valueTreeState.state.getChildWithName("presets").copyPropertiesAndChildrenFrom(presets, nullptr); valueTreeState.state.getChildWithName("banks").sendPropertyChangeMessage("synthetic"); + refreshPresets(); +} + +void FluidSynthModel::refreshPresets() { + fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; + ValueTree presets{"presets"}; + if (sfont != nullptr) { + RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + int value{castParam->get()}; + + int initialBank{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; + + fluid_sfont_iteration_start(sfont); + + for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; + preset != nullptr; + preset = fluid_sfont_iteration_next(sfont)) { + int bank{fluid_preset_get_banknum(preset) + initialBank}; + if (bank == value) { + presets.appendChild({ "preset", { + { "num", fluid_preset_get_num(preset) }, + { "name", String{fluid_preset_get_name(preset)} } + }, {} }, nullptr); + } + } + } + valueTreeState.state.getChildWithName("presets").copyPropertiesAndChildrenFrom(presets, nullptr); valueTreeState.state.getChildWithName("presets").sendPropertyChangeMessage("synthetic"); - -#if JUCE_DEBUG - unique_ptr xml{valueTreeState.state.createXml()}; - Logger::outputDebugString(xml->createDocument("",false,false)); -#endif } +// void FluidSynthModel::refreshBanksAndPresets() { +// fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; +// ValueTree banks{"banks"}; +// ValueTree presets{"presets"}; +// if (sfont != nullptr) { +// int initialBankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; +// banks.appendChild({ "bank", { +// { "num", initialBankOffset }, +// }, {} }, nullptr); +// int greatestPersistedBank{initialBankOffset}; + +// fluid_sfont_iteration_start(sfont); + +// for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; +// preset != nullptr; +// preset = fluid_sfont_iteration_next(sfont)) { +// int bankOffset{fluid_preset_get_banknum(preset) + initialBankOffset}; +// // ValueTree preset{"preset"}; +// // banksToPresets.insert(BanksToPresets::value_type( +// // fluid_preset_get_banknum(preset) + bankOffset, +// // *new Preset( +// // fluid_preset_get_num(preset), +// // fluid_preset_get_name(preset) +// // ) +// // )); +// if (bankOffset > greatestPersistedBank) { +// banks.appendChild({ "bank", { +// { "num", bankOffset }, +// }, {} }, nullptr); +// greatestPersistedBank = bankOffset; +// } +// presets.appendChild({ "preset", { +// { "num", fluid_preset_get_num(preset) }, +// { "name", String{fluid_preset_get_name(preset)} } +// }, {} }, nullptr); +// } +// } +// // valueTreeState.state.getChildWithName("banks") = banks; +// // valueTreeState.state.getChildWithName("presets") = presets; +// valueTreeState.state.getChildWithName("banks").copyPropertiesAndChildrenFrom(banks, nullptr); +// valueTreeState.state.getChildWithName("presets").copyPropertiesAndChildrenFrom(presets, nullptr); +// valueTreeState.state.getChildWithName("banks").sendPropertyChangeMessage("synthetic"); +// valueTreeState.state.getChildWithName("presets").sendPropertyChangeMessage("synthetic"); + +// #if JUCE_DEBUG +// unique_ptr xml{valueTreeState.state.createXml()}; +// Logger::outputDebugString(xml->createDocument("",false,false)); +// #endif +// } + // FluidSynthModel::Listener::~Listener() { // } diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index 89dd7ef..c5bc48f 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -17,7 +17,9 @@ using namespace std; -class FluidSynthModel: public ValueTree::Listener { +class FluidSynthModel +: public ValueTree::Listener +, public AudioProcessorValueTreeState::Listener { public: FluidSynthModel( AudioProcessorValueTreeState& valueTreeState @@ -68,6 +70,7 @@ class FluidSynthModel: public ValueTree::Listener { const String& getCurrentSoundFontAbsPath(); + virtual void parameterChanged (const String& parameterID, float newValue) override; virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) override; @@ -101,6 +104,9 @@ class FluidSynthModel: public ValueTree::Listener { // }; int handleMidiEvent(void* data, fluid_midi_event_t* event); + void refreshBanks(); + void refreshPresets(); + // void refreshBanksAndPresets(); // ValueTreeListener valueTreeListener; From d8ce91e6667951a8531d66fb42783a269b672924 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Mon, 15 Jul 2019 22:12:07 +0100 Subject: [PATCH 18/33] successfully setting banks and presets --- Source/FluidSynthModel.cpp | 68 +++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 25bba1a..138da22 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -208,11 +208,32 @@ void FluidSynthModel::initialise() { void FluidSynthModel::parameterChanged(const String& parameterID, float newValue) { if (parameterID == "bank") { - RangedAudioParameter *param {valueTreeState.getParameter("bank")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - int value{castParam->get()}; - fluid_synth_bank_select(synth.get(), channel, value); + // RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + // jassert(dynamic_cast (param) != nullptr); + // AudioParameterInt* castParam {dynamic_cast (param)}; + // int value{castParam->get()}; + + int bank, preset; + { + RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + bank = castParam->get(); + } + { + RangedAudioParameter *param {valueTreeState.getParameter("preset")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + preset = castParam->get(); + } + fluid_synth_program_select( + synth.get(), + channel, + sfont_id, + static_cast(bank), + static_cast(preset)); + + // fluid_synth_bank_select(synth.get(), channel, value); refreshPresets(); // fluid_sfont_t* sfont{fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; // fluid_sfont_iteration_start(sfont); @@ -221,10 +242,26 @@ void FluidSynthModel::parameterChanged(const String& parameterID, float newValue // int bank{fluid_preset_get_banknum(presetObj) + offset}; // int preset{fluid_preset_get_num(presetObj)}; } else if (parameterID == "preset") { - RangedAudioParameter *param {valueTreeState.getParameter("preset")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - int value{castParam->get()}; + int bank, preset; + { + RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + bank = castParam->get(); + } + { + RangedAudioParameter *param {valueTreeState.getParameter("preset")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + preset = castParam->get(); + } + // int bank{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; + fluid_synth_program_select( + synth.get(), + channel, + sfont_id, + static_cast(bank), + static_cast(preset)); } } @@ -372,18 +409,19 @@ void FluidSynthModel::refreshBanks() { fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; ValueTree banks{"banks"}; if (sfont != nullptr) { - int initialBank{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; + // int initialBankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; banks.appendChild({ "bank", { - { "num", initialBank }, + { "num", /* initialBankOffset */ 0 }, }, {} }, nullptr); - int greatestPersistedBank{initialBank}; + // int greatestPersistedBank{initialBankOffset}; + int greatestPersistedBank{0}; fluid_sfont_iteration_start(sfont); for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; preset != nullptr; preset = fluid_sfont_iteration_next(sfont)) { - int bank{fluid_preset_get_banknum(preset) + initialBank}; + int bank{fluid_preset_get_banknum(preset) /* + initialBankOffset */}; if (bank > greatestPersistedBank) { banks.appendChild({ "bank", { { "num", bank }, @@ -406,14 +444,14 @@ void FluidSynthModel::refreshPresets() { AudioParameterInt* castParam {dynamic_cast (param)}; int value{castParam->get()}; - int initialBank{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; + // int initialBank{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; fluid_sfont_iteration_start(sfont); for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; preset != nullptr; preset = fluid_sfont_iteration_next(sfont)) { - int bank{fluid_preset_get_banknum(preset) + initialBank}; + int bank{fluid_preset_get_banknum(preset) /* + initialBank */}; if (bank == value) { presets.appendChild({ "preset", { { "num", fluid_preset_get_num(preset) }, From 0587e2f68ae7e235a54e5ad2fb4974008f5ed657 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sat, 20 Jul 2019 18:49:39 +0100 Subject: [PATCH 19/33] decided against maintaining a separate 'presets' property in the ValueTree, since this is a view modelling concern that only the table cares about. instead, store as a tree. let the table component be responsible for maintaining a view over a subset of presets, instead of bothering the ValueTree with this. this is an effort to dial back the use of the store, and prefer to use it where it provides the most value rather than using it for everything. --- Source/FluidSynthModel.cpp | 119 ++++++++++++++++++++++++------------- Source/FluidSynthModel.h | 2 +- Source/PluginProcessor.cpp | 2 +- Source/TableComponent.cpp | 77 ++++++++++++++++++++---- Source/TableComponent.h | 7 ++- 5 files changed, 154 insertions(+), 53 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 138da22..4fd4891 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -234,7 +234,7 @@ void FluidSynthModel::parameterChanged(const String& parameterID, float newValue static_cast(preset)); // fluid_synth_bank_select(synth.get(), channel, value); - refreshPresets(); +// refreshPresets(); // fluid_sfont_t* sfont{fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; // fluid_sfont_iteration_start(sfont); // fluid_preset_t* presetObj{fluid_sfont_iteration_next(sfont)}; @@ -409,60 +409,99 @@ void FluidSynthModel::refreshBanks() { fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; ValueTree banks{"banks"}; if (sfont != nullptr) { - // int initialBankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; - banks.appendChild({ "bank", { - { "num", /* initialBankOffset */ 0 }, - }, {} }, nullptr); - // int greatestPersistedBank{initialBankOffset}; - int greatestPersistedBank{0}; + int greatestEncounteredBank{-1}; + ValueTree bank; fluid_sfont_iteration_start(sfont); - for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; preset != nullptr; preset = fluid_sfont_iteration_next(sfont)) { - int bank{fluid_preset_get_banknum(preset) /* + initialBankOffset */}; - if (bank > greatestPersistedBank) { - banks.appendChild({ "bank", { - { "num", bank }, - }, {} }, nullptr); - greatestPersistedBank = bank; + int bankNum{fluid_preset_get_banknum(preset)}; + if (bankNum > greatestEncounteredBank) { + if (greatestEncounteredBank > -1) { + banks.appendChild(bank, nullptr); + } + bank = { "bank", { + { "num", bankNum } + } }; + greatestEncounteredBank = bankNum; } + bank.appendChild({ "preset", { + { "num", fluid_preset_get_num(preset) }, + { "name", String{fluid_preset_get_name(preset)} } + }, {} }, nullptr); + } + if (greatestEncounteredBank > -1) { + banks.appendChild(bank, nullptr); } } valueTreeState.state.getChildWithName("banks").copyPropertiesAndChildrenFrom(banks, nullptr); valueTreeState.state.getChildWithName("banks").sendPropertyChangeMessage("synthetic"); - refreshPresets(); + +#if JUCE_DEBUG + unique_ptr xml{valueTreeState.state.createXml()}; + Logger::outputDebugString(xml->createDocument("",false,false)); +#endif } -void FluidSynthModel::refreshPresets() { - fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; - ValueTree presets{"presets"}; - if (sfont != nullptr) { - RangedAudioParameter *param {valueTreeState.getParameter("bank")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - int value{castParam->get()}; +// void FluidSynthModel::refreshBanks() { +// fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; +// ValueTree banks{"banks"}; +// if (sfont != nullptr) { +// // int initialBankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; +// banks.appendChild({ "bank", { +// { "num", /* initialBankOffset */ 0 }, +// }, {} }, nullptr); +// // int greatestPersistedBank{initialBankOffset}; +// int greatestPersistedBank{0}; - // int initialBank{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; +// fluid_sfont_iteration_start(sfont); - fluid_sfont_iteration_start(sfont); +// for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; +// preset != nullptr; +// preset = fluid_sfont_iteration_next(sfont)) { +// int bank{fluid_preset_get_banknum(preset) /* + initialBankOffset */}; +// if (bank > greatestPersistedBank) { +// banks.appendChild({ "bank", { +// { "num", bank }, +// }, {} }, nullptr); +// greatestPersistedBank = bank; +// } +// } +// } +// valueTreeState.state.getChildWithName("banks").copyPropertiesAndChildrenFrom(banks, nullptr); +// valueTreeState.state.getChildWithName("banks").sendPropertyChangeMessage("synthetic"); +// refreshPresets(); +// } - for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; - preset != nullptr; - preset = fluid_sfont_iteration_next(sfont)) { - int bank{fluid_preset_get_banknum(preset) /* + initialBank */}; - if (bank == value) { - presets.appendChild({ "preset", { - { "num", fluid_preset_get_num(preset) }, - { "name", String{fluid_preset_get_name(preset)} } - }, {} }, nullptr); - } - } - } - valueTreeState.state.getChildWithName("presets").copyPropertiesAndChildrenFrom(presets, nullptr); - valueTreeState.state.getChildWithName("presets").sendPropertyChangeMessage("synthetic"); -} +// void FluidSynthModel::refreshPresets() { +// fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; +// ValueTree presets{"presets"}; +// if (sfont != nullptr) { +// RangedAudioParameter *param {valueTreeState.getParameter("bank")}; +// jassert(dynamic_cast (param) != nullptr); +// AudioParameterInt* castParam {dynamic_cast (param)}; +// int value{castParam->get()}; + +// // int initialBank{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; + +// fluid_sfont_iteration_start(sfont); + +// for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; +// preset != nullptr; +// preset = fluid_sfont_iteration_next(sfont)) { +// int bank{fluid_preset_get_banknum(preset) /* + initialBank */}; +// if (bank == value) { +// presets.appendChild({ "preset", { +// { "num", fluid_preset_get_num(preset) }, +// { "name", String{fluid_preset_get_name(preset)} } +// }, {} }, nullptr); +// } +// } +// } +// valueTreeState.state.getChildWithName("presets").copyPropertiesAndChildrenFrom(presets, nullptr); +// valueTreeState.state.getChildWithName("presets").sendPropertyChangeMessage("synthetic"); +// } // void FluidSynthModel::refreshBanksAndPresets() { // fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index c5bc48f..92a06e6 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -105,7 +105,7 @@ class FluidSynthModel int handleMidiEvent(void* data, fluid_midi_event_t* event); void refreshBanks(); - void refreshPresets(); + // void refreshPresets(); // void refreshBanksAndPresets(); // ValueTreeListener valueTreeListener; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index b614540..2ad8085 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -47,7 +47,7 @@ JuicySFAudioProcessor::JuicySFAudioProcessor() { "path", "" }, }, {} }, nullptr); // no properties, no subtrees (yet) - valueTreeState.state.appendChild({ "presets", {}, {} }, nullptr); + // valueTreeState.state.appendChild({ "presets", {}, {} }, nullptr); // no properties, no subtrees (yet) valueTreeState.state.appendChild({ "banks", {}, {} }, nullptr); // valueTreeState.state.setProperty("soundFontPath", "", nullptr); diff --git a/Source/TableComponent.cpp b/Source/TableComponent.cpp index babf668..e067906 100644 --- a/Source/TableComponent.cpp +++ b/Source/TableComponent.cpp @@ -8,6 +8,9 @@ #include "TableComponent.h" #include "Util.h" +#include +#include +#include using namespace std; using namespace Util; @@ -74,8 +77,9 @@ TableComponent::TableComponent( table.setWantsKeyboardFocus(false); // table.selectRow(); - ValueTree presets{valueTreeState.state.getChildWithName("presets")}; - loadModelFrom(presets); + // ValueTree presets{valueTreeState.state.getChildWithName("presets")}; + ValueTree banks{valueTreeState.state.getChildWithName("banks")}; + loadModelFrom(banks); // selectCurrentPreset(); // we could now change some initial settings.. @@ -93,15 +97,63 @@ TableComponent::~TableComponent() { valueTreeState.state.removeListener(this); } -void TableComponent::loadModelFrom(ValueTree& presets) { +// void TableComponent::loadModelFrom(ValueTree& presets) { +// rows.clear(); +// int numChildren{presets.getNumChildren()}; +// for(int i{0}; i presets; + ValueTree bank{banks.getChild(bankIx)}; + int bankNum{bank.getProperty("num")}; + int bankChildren{bank.getNumChildren()}; + for(int presetIx{0}; presetIx(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; + int bank{castParam->get()}; + + BanksToPresets::iterator lowerBound{banksToPresets.lower_bound(bank)}; + BanksToPresets::iterator upperBound{banksToPresets.upper_bound(bank)}; + + // basic syntaxes for a lambda which return's a pair's .second + // https://stackoverflow.com/questions/2568194/populate-a-vector-with-all-multimap-values-with-a-given-key + // shorter syntax with mem_fn() + // https://stackoverflow.com/a/36775400/5257399 + transform( + lowerBound, + upperBound, + back_inserter(rows), + mem_fn(&BanksToPresets::value_type::second) +// [](BanksToPresets::value_type element){return element.second;} + ); } + table.deselectAllRows(); table.updateContent(); table.getHeader().setSortColumnId(0, true); @@ -123,7 +175,12 @@ void TableComponent::parameterChanged(const String& parameterID, float newValue) void TableComponent::valueTreePropertyChanged( ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { - if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { + // if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { + // if (property == StringRef("synthetic")) { + // loadModelFrom(treeWhosePropertyHasChanged); + // } + // } + if (treeWhosePropertyHasChanged.getType() == StringRef("banks")) { if (property == StringRef("synthetic")) { loadModelFrom(treeWhosePropertyHasChanged); } diff --git a/Source/TableComponent.h b/Source/TableComponent.h index 41feaa3..e5a914f 100644 --- a/Source/TableComponent.h +++ b/Source/TableComponent.h @@ -12,6 +12,7 @@ #include "PresetsToBanks.h" #include #include +#include using namespace std; @@ -91,7 +92,8 @@ class TableComponent : public Component, inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; private: - void loadModelFrom(ValueTree& presets); + // void loadModelFrom(ValueTree& presets); + void loadModelFrom(ValueTree& banks); void selectCurrentPreset(); AudioProcessorValueTreeState& valueTreeState; @@ -99,6 +101,9 @@ class TableComponent : public Component, TableListBox table; // the table component itself Font font; + typedef multimap BanksToPresets; + BanksToPresets banksToPresets; + // vector columns; vector rows; From 745adf8fdec29d571e2d73b2da90dbc2b4a08ccc Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sat, 20 Jul 2019 19:56:12 +0100 Subject: [PATCH 20/33] make table once again respond to bank changes. support bank offset again. --- Source/FluidSynthModel.cpp | 7 ++-- Source/TableComponent.cpp | 67 +++++++++++++++++++++----------------- Source/TableComponent.h | 1 + 3 files changed, 42 insertions(+), 33 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 4fd4891..d0294c0 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -226,11 +226,12 @@ void FluidSynthModel::parameterChanged(const String& parameterID, float newValue AudioParameterInt* castParam {dynamic_cast (param)}; preset = castParam->get(); } + int bankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; fluid_synth_program_select( synth.get(), channel, sfont_id, - static_cast(bank), + static_cast(bankOffset + bank), static_cast(preset)); // fluid_synth_bank_select(synth.get(), channel, value); @@ -255,12 +256,12 @@ void FluidSynthModel::parameterChanged(const String& parameterID, float newValue AudioParameterInt* castParam {dynamic_cast (param)}; preset = castParam->get(); } - // int bank{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; + int bankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; fluid_synth_program_select( synth.get(), channel, sfont_id, - static_cast(bank), + static_cast(bankOffset + bank), static_cast(preset)); } } diff --git a/Source/TableComponent.cpp b/Source/TableComponent.cpp index e067906..b43d253 100644 --- a/Source/TableComponent.cpp +++ b/Source/TableComponent.cpp @@ -91,9 +91,13 @@ TableComponent::TableComponent( // table.setMultipleSelectionEnabled (false); valueTreeState.state.addListener(this); + valueTreeState.addParameterListener("bank", this); + valueTreeState.addParameterListener("preset", this); } TableComponent::~TableComponent() { + valueTreeState.removeParameterListener("bank", this); + valueTreeState.removeParameterListener("preset", this); valueTreeState.state.removeListener(this); } @@ -114,7 +118,6 @@ TableComponent::~TableComponent() { // } void TableComponent::loadModelFrom(ValueTree& banks) { - rows.clear(); banksToPresets.clear(); int banksChildren{banks.getNumChildren()}; for(int bankIx{0}; bankIx(param) != nullptr); - AudioParameterInt* castParam{dynamic_cast(param)}; - int bank{castParam->get()}; - - BanksToPresets::iterator lowerBound{banksToPresets.lower_bound(bank)}; - BanksToPresets::iterator upperBound{banksToPresets.upper_bound(bank)}; - - // basic syntaxes for a lambda which return's a pair's .second - // https://stackoverflow.com/questions/2568194/populate-a-vector-with-all-multimap-values-with-a-given-key - // shorter syntax with mem_fn() - // https://stackoverflow.com/a/36775400/5257399 - transform( - lowerBound, - upperBound, - back_inserter(rows), - mem_fn(&BanksToPresets::value_type::second) -// [](BanksToPresets::value_type element){return element.second;} - ); - } - - table.deselectAllRows(); - table.updateContent(); - table.getHeader().setSortColumnId(0, true); - selectCurrentPreset(); - table.repaint(); + repopulateTable(); } void TableComponent::parameterChanged(const String& parameterID, float newValue) { // valueTreeState.getParameter - if (parameterID == "preset") { + if (parameterID == "bank") { + repopulateTable(); + } else if (parameterID == "preset") { selectCurrentPreset(); // RangedAudioParameter *param {valueTreeState.getParameter("preset")}; // jassert(dynamic_cast (param) != nullptr); @@ -172,6 +151,34 @@ void TableComponent::parameterChanged(const String& parameterID, float newValue) } } +void TableComponent::repopulateTable() { + rows.clear(); + RangedAudioParameter *param{valueTreeState.getParameter("bank")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; + int bank{castParam->get()}; + + BanksToPresets::iterator lowerBound{banksToPresets.lower_bound(bank)}; + BanksToPresets::iterator upperBound{banksToPresets.upper_bound(bank)}; + + // basic syntaxes for a lambda which return's a pair's .second + // https://stackoverflow.com/questions/2568194/populate-a-vector-with-all-multimap-values-with-a-given-key + // shorter syntax with mem_fn() + // https://stackoverflow.com/a/36775400/5257399 + transform( + lowerBound, + upperBound, + back_inserter(rows), + mem_fn(&BanksToPresets::value_type::second) + // [](BanksToPresets::value_type element){return element.second;} + ); + table.deselectAllRows(); + table.updateContent(); + table.getHeader().setSortColumnId(0, true); + selectCurrentPreset(); + table.repaint(); +} + void TableComponent::valueTreePropertyChanged( ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { @@ -323,7 +330,7 @@ void TableComponent::selectCurrentPreset() { for (auto it = rows.begin(); it != rows.end(); ++it) { if(it->preset == value) { - int index {static_cast(std::distance(rows.begin(), it))}; + int index {static_cast(distance(rows.begin(), it))}; table.selectRow(index); break; } diff --git a/Source/TableComponent.h b/Source/TableComponent.h index e5a914f..28e02a3 100644 --- a/Source/TableComponent.h +++ b/Source/TableComponent.h @@ -94,6 +94,7 @@ class TableComponent : public Component, private: // void loadModelFrom(ValueTree& presets); void loadModelFrom(ValueTree& banks); + void repopulateTable(); void selectCurrentPreset(); AudioProcessorValueTreeState& valueTreeState; From 304ec6ce88180250040e654df25a70a54484c9f7 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 21 Jul 2019 15:04:19 +0100 Subject: [PATCH 21/33] pills no longer listen to valueTree; I didn't like the idea of having up to 128 listeners when the parent could do dispatch with just one listener --- Source/Pills.cpp | 56 +++++++++++++++++++++++++++++++++--------------- Source/Pills.h | 15 +++++++++---- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/Source/Pills.cpp b/Source/Pills.cpp index 03d3056..82601ff 100644 --- a/Source/Pills.cpp +++ b/Source/Pills.cpp @@ -25,20 +25,19 @@ Pill::Pill( | (isLast ? 0 : Button::ConnectedOnRight) ); textButton.setRadioGroupId(34567); - loadToggleState(); + // loadToggleState(); textButton.setClickingTogglesState(true); addAndMakeVisible(textButton); - valueTreeState.addParameterListener("bank", this); + // valueTreeState.addParameterListener("bank", this); // valueTreeState.state.addListener(this); textButton.addListener(this); } -void Pill::paint (Graphics& g) -{ +void Pill::paint (Graphics& g) { // (Our component is opaque, so we must completely fill the background with a solid colour) - g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); + g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId)); } void Pill::resized() { @@ -46,19 +45,23 @@ void Pill::resized() { } Pill::~Pill() { - valueTreeState.removeParameterListener("bank", this); + // valueTreeState.removeParameterListener("bank", this); // valueTreeState.state.removeListener(this); textButton.removeListener(this); } -void Pill::loadToggleState() { - RangedAudioParameter *param {valueTreeState.getParameter("bank")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - int value{castParam->get()}; - textButton.setToggleState(value == bank, dontSendNotification); +void Pill::bankChanged(int bank) { + textButton.setToggleState(this->bank == bank, dontSendNotification); } +// void Pill::loadToggleState() { +// RangedAudioParameter *param {valueTreeState.getParameter("bank")}; +// jassert(dynamic_cast (param) != nullptr); +// AudioParameterInt* castParam {dynamic_cast (param)}; +// int value{castParam->get()}; +// textButton.setToggleState(value == bank, dontSendNotification); +// } + void Pill::buttonClicked (Button* button) { // selected = button; // onItemSelected(itemToIDMapper(button->getName().toStdString())); @@ -68,11 +71,11 @@ void Pill::buttonClicked (Button* button) { *castParam = bank; } -void Pill::parameterChanged(const String& parameterID, float newValue) { - if (parameterID == "bank") { - loadToggleState(); - } -} +// void Pill::parameterChanged(const String& parameterID, float newValue) { +// if (parameterID == "bank") { +// loadToggleState(); +// } +// } // void Pill::valueTreePropertyChanged( // ValueTree& treeWhosePropertyHasChanged, @@ -104,12 +107,30 @@ Pills::Pills( loadModelFrom(banks); valueTreeState.state.addListener(this); + valueTreeState.addParameterListener("bank", this); } Pills::~Pills() { + valueTreeState.removeParameterListener("bank", this); valueTreeState.state.removeListener(this); } +void Pills::parameterChanged(const String& parameterID, float newValue) { + if (parameterID == "bank") { + updatePillToggleStates(); + } +} + +void Pills::updatePillToggleStates() { + RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + int bank{castParam->get()}; + for (auto& pill: pills) { + pill->bankChanged(bank); + } +} + void Pills::valueTreePropertyChanged( ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { @@ -137,6 +158,7 @@ void Pills::loadModelFrom(ValueTree& banks) { addAndMakeVisible(pill.get()); pills.push_back(move(pill)); } + updatePillToggleStates(); resized(); } diff --git a/Source/Pills.h b/Source/Pills.h index 2264231..812c2a6 100644 --- a/Source/Pills.h +++ b/Source/Pills.h @@ -11,7 +11,7 @@ using namespace std; class Pill : public Component , public Button::Listener -, public AudioProcessorValueTreeState::Listener +// , public AudioProcessorValueTreeState::Listener { public: Pill( @@ -22,14 +22,16 @@ class Pill ); ~Pill(); - void buttonClicked (Button* button) override; + void buttonClicked(Button* button) override; void resized() override; void paint(Graphics& g) override; - virtual void parameterChanged (const String& parameterID, float newValue) override; + void bankChanged(int bank); + + // virtual void parameterChanged (const String& parameterID, float newValue) override; private: - void loadToggleState(); + // void loadToggleState(); AudioProcessorValueTreeState& valueTreeState; int bank; @@ -41,6 +43,7 @@ class Pill class Pills : public Component , public ValueTree::Listener +, public AudioProcessorValueTreeState::Listener { public: Pills( @@ -61,6 +64,8 @@ class Pills // void buttonClicked (Button* button) override; void cycle(bool right); + virtual void parameterChanged (const String& parameterID, float newValue) override; + virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) override; inline virtual void valueTreeChildAdded (ValueTree& parentTree, @@ -87,6 +92,8 @@ class Pills // Pill* addToList (Pill* newButton); + void updatePillToggleStates(); + void populate(int initiallySelectedItem); void resized() override; void paint(Graphics& g) override; From 5c310a16061f4777e6a8f7496355391a1d2aeffb Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 21 Jul 2019 16:25:22 +0100 Subject: [PATCH 22/33] keyboard left/right triggers pill button click (as it used to). pills now send you to first valid preset in bank (as they used to). --- Source/Pills.cpp | 66 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/Source/Pills.cpp b/Source/Pills.cpp index 82601ff..fe8a3d6 100644 --- a/Source/Pills.cpp +++ b/Source/Pills.cpp @@ -62,13 +62,35 @@ void Pill::bankChanged(int bank) { // textButton.setToggleState(value == bank, dontSendNotification); // } -void Pill::buttonClicked (Button* button) { +void Pill::buttonClicked(Button* button) { + ValueTree banks{valueTreeState.state.getChildWithName("banks")}; + int banksChildren{banks.getNumChildren()}; + ValueTree bank; + for(int bankIx{0}; bankIxbank) { + bank = currentBank; + break; + } + } + ValueTree preset{bank.getChild(0)}; + int presetNum{preset.getProperty("num")}; + // selected = button; // onItemSelected(itemToIDMapper(button->getName().toStdString())); - RangedAudioParameter *param {valueTreeState.getParameter("bank")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - *castParam = bank; + { + RangedAudioParameter *param{valueTreeState.getParameter("bank")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; + *castParam = this->bank; + } + { + RangedAudioParameter *param{valueTreeState.getParameter("preset")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; + *castParam = presetNum; + } } // void Pill::parameterChanged(const String& parameterID, float newValue) { @@ -208,31 +230,41 @@ void Pills::cycle(bool right) { RangedAudioParameter *param {valueTreeState.getParameter("bank")}; jassert(dynamic_cast (param) != nullptr); AudioParameterInt* castParam {dynamic_cast (param)}; - int currentlySelectedBank{castParam->get()}; + int bank{castParam->get()}; - ValueTree banks{valueTreeState.state.getChildWithName("banks")}; + // ValueTree banks{valueTreeState.state.getChildWithName("banks")}; // int numChildren{banks.getNumChildren()}; - vector bankInts; - bankInts.resize(banks.getNumChildren()); + // vector bankInts; + // bankInts.resize(banks.getNumChildren()); - transform(banks.begin(), banks.end(), bankInts.begin(), [](ValueTree bank) -> int { - return bank.getProperty("num"); - }); + // transform(banks.begin(), banks.end(), bankInts.begin(), [](ValueTree bank) -> int { + // return bank.getProperty("num"); + // }); -// int closestBank{currentlySelectedBank}; +// int closestBank{bank}; // for(int i{0}; i < numChildren; i++) { // ValueTree child{banks.getChild(i)}; // int proposedBank{child.getProperty("num")}; -// if (right && proposedBank > currentlySelectedBank) { +// if (right && proposedBank > bank) { // closestBank = jmin(closestBank, proposedBank); // } else if (left ) // } - int currentIx{static_cast(distance(bankInts.begin(), find(bankInts.begin(), bankInts.end(), currentlySelectedBank)))}; + // int currentIx{static_cast(distance(bankInts.begin(), find(bankInts.begin(), bankInts.end(), currentlySelectedBank)))}; + // currentIx += right ? 1 : pills.size()-1; + // // pills[currentIx % pills.size()]->textButton.triggerClick(); + // *castParam = bankInts[currentIx % bankInts.size()]; + + int currentIx = static_cast( + distance( + pills.begin(), + find_if( + pills.begin(), + pills.end(), + [bank](unique_ptr& pill){return pill->bank == bank;}))); currentIx += right ? 1 : pills.size()-1; - // pills[currentIx % pills.size()]->textButton.triggerClick(); - *castParam = bankInts[currentIx % bankInts.size()]; + pills[currentIx % pills.size()]->textButton.triggerClick(); // TODO: base this on valueTree From f87ec7c8e4898142b665123a7fa286eb8f2a5373 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sat, 27 Jul 2019 23:04:20 +0100 Subject: [PATCH 23/33] remove some unused methods on FluidSynthModel. make FilePicker update displayed text upon load. read and write into XML just the bits that we need, and take advantage of attribute names' being indexed. take more care in setting sfont_id. --- Source/FilePicker.cpp | 9 +- Source/FilePicker.h | 2 +- Source/FluidSynthModel.cpp | 241 ++++++++++++++++++++----------------- Source/FluidSynthModel.h | 16 +-- Source/PluginProcessor.cpp | 66 +++++++--- 5 files changed, 194 insertions(+), 140 deletions(-) diff --git a/Source/FilePicker.cpp b/Source/FilePicker.cpp index 4787f49..d2a7eff 100644 --- a/Source/FilePicker.cpp +++ b/Source/FilePicker.cpp @@ -66,7 +66,8 @@ void FilePicker::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged // if (&treeWhosePropertyHasChanged == &valueTree) { if (property == StringRef("path")) { String soundFontPath{treeWhosePropertyHasChanged.getProperty("path", "")}; - // DEBUG_PRINT(soundFontPath); + DEBUG_PRINT(soundFontPath); + setDisplayedFilePath(soundFontPath); // if (soundFontPath.isNotEmpty()) { // loadFont(soundFontPath); // } @@ -75,9 +76,9 @@ void FilePicker::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged } void FilePicker::setDisplayedFilePath(const String& path) { - // if (!shouldChangeDisplayedFilePath(path)) { - // return; - // } + if (!shouldChangeDisplayedFilePath(path)) { + return; + } // currentPath = path; fileChooser.setCurrentFile(File(path), true, dontSendNotification); } diff --git a/Source/FilePicker.h b/Source/FilePicker.h index f590604..5d34c8c 100644 --- a/Source/FilePicker.h +++ b/Source/FilePicker.h @@ -50,4 +50,4 @@ class FilePicker: public Component, bool shouldChangeDisplayedFilePath(const String &path); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePicker) -}; \ No newline at end of file +}; diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index d0294c0..20fd318 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -20,10 +20,10 @@ FluidSynthModel::FluidSynthModel( // , sharedParams{sharedParams} //, synth{nullptr} , settings{nullptr, nullptr} -, currentSoundFontAbsPath{} +//, currentSoundFontAbsPath{} , currentSampleRate{44100} -, initialised{false} -, sfont_id{0} +//, initialised{false} +, sfont_id{-1} , channel{0}/*, mod(nullptr)*/ { @@ -97,12 +97,15 @@ void FluidSynthModel::initialise() { // loadFont(sharedParams.getSoundFontPath()); // changePreset(sharedParams->getBank(), sharedParams->getPreset()); // } + ValueTree soundFont{valueTreeState.state.getChildWithName("soundFont")}; + String path{soundFont.getProperty("path", "")}; + loadFont(path); fluid_synth_set_gain(synth.get(), 2.0); - for(int i{SOUND_CTRL1}; i <= SOUND_CTRL10; i++) - { - setControllerValue(i, 0); + fluid_midi_control_change controllers[]{SOUND_CTRL2, SOUND_CTRL3, SOUND_CTRL4, SOUND_CTRL5, SOUND_CTRL6, SOUND_CTRL10}; + for(fluid_midi_control_change controller : controllers) { + setControllerValue(static_cast(controller), 0); } // fluid_synth_bank_select(synth, 0, 3); @@ -116,7 +119,7 @@ void FluidSynthModel::initialise() { // float env_amount(12000.0f); // http://www.synthfont.com/SoundFont_NRPNs.PDF - float env_amount(20000.0f); + float env_amount{20000.0f}; // float env_amount(24000.0f); // note: fluid_chan.c#fluid_channel_init_ctrl() @@ -200,7 +203,7 @@ void FluidSynthModel::initialise() { fluid_mod_set_amount(mod.get(), 1000.0f); fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); - valueTreeState.state.getChildWithName("soundFont").sendPropertyChangeMessage("path"); +// valueTreeState.state.getChildWithName("soundFont").sendPropertyChangeMessage("path"); // valueTree.sendPropertyChangeMessage("soundFontPath"); // initialised = true; @@ -273,7 +276,7 @@ void FluidSynthModel::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasCh if (property == StringRef("path")) { String soundFontPath{treeWhosePropertyHasChanged.getProperty("path", "")}; if (soundFontPath.isNotEmpty()) { - loadFont(soundFontPath); + unloadAndLoadFont(soundFontPath); } } } @@ -294,82 +297,82 @@ int FluidSynthModel::getChannel() { return channel; } -void FluidSynthModel::changePreset(int bank, int preset) { - if (bank == -1 || preset == -1) { - unique_ptr bankAndPreset = getFirstBankAndPreset(); - bank = bankAndPreset->getBank(); - preset = bankAndPreset->getPreset(); - } - changePresetImpl(bank, preset); -// sharedParams->setPreset(preset); -// sharedParams->setBank(bank); -} - -void FluidSynthModel::changePresetImpl(int bank, int preset) { - fluid_synth_program_select(synth.get(), channel, sfont_id, static_cast(bank), static_cast(preset)); -} - -fluid_preset_t* FluidSynthModel::getFirstPreset() { - fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth.get(), sfont_id); - - jassert(sfont != nullptr); - fluid_sfont_iteration_start(sfont); - - return fluid_sfont_iteration_next(sfont); -} - -unique_ptr FluidSynthModel::getFirstBankAndPreset() { - fluid_preset_t* preset = getFirstPreset(); - - int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); - - return make_unique(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset)); -}; - -void FluidSynthModel::selectFirstPreset() { - fluid_preset_t* preset = getFirstPreset(); - - int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); - - changePreset(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset)); -} - -BanksToPresets FluidSynthModel::getBanks() { - BanksToPresets banksToPresets; - - int soundfontCount = fluid_synth_sfcount(synth.get()); - - if (soundfontCount == 0) { - // no soundfont selected - return banksToPresets; - } - - fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth.get(), sfont_id); - if(sfont == nullptr) { - // no soundfont found by that ID - // the above guard (soundfontCount) protects us for the - // main case we're expecting. this guard is just defensive programming. - return banksToPresets; - } - - int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); +//void FluidSynthModel::changePreset(int bank, int preset) { +// if (bank == -1 || preset == -1) { +// unique_ptr bankAndPreset = getFirstBankAndPreset(); +// bank = bankAndPreset->getBank(); +// preset = bankAndPreset->getPreset(); +// } +// changePresetImpl(bank, preset); +//// sharedParams->setPreset(preset); +//// sharedParams->setBank(bank); +//} - fluid_sfont_iteration_start(sfont); +//void FluidSynthModel::changePresetImpl(int bank, int preset) { +// fluid_synth_program_select(synth.get(), channel, sfont_id, static_cast(bank), static_cast(preset)); +//} - for(fluid_preset_t* preset = fluid_sfont_iteration_next(sfont); - preset != nullptr; - preset = fluid_sfont_iteration_next(sfont)) { - banksToPresets.insert(BanksToPresets::value_type( - fluid_preset_get_banknum(preset) + offset, - *new Preset( - fluid_preset_get_num(preset), - fluid_preset_get_name(preset) - ) - )); - } +//fluid_preset_t* FluidSynthModel::getFirstPreset() { +// fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth.get(), sfont_id); +// +// jassert(sfont != nullptr); +// fluid_sfont_iteration_start(sfont); +// +// return fluid_sfont_iteration_next(sfont); +//} +// +//unique_ptr FluidSynthModel::getFirstBankAndPreset() { +// fluid_preset_t* preset = getFirstPreset(); +// +// int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); +// +// return make_unique(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset)); +//}; +// +//void FluidSynthModel::selectFirstPreset() { +// fluid_preset_t* preset = getFirstPreset(); +// +// int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); +// +// changePreset(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset)); +//} - return banksToPresets; -} +//BanksToPresets FluidSynthModel::getBanks() { +// BanksToPresets banksToPresets; +// +// int soundfontCount = fluid_synth_sfcount(synth.get()); +// +// if (soundfontCount == 0) { +// // no soundfont selected +// return banksToPresets; +// } +// +// fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth.get(), sfont_id); +// if(sfont == nullptr) { +// // no soundfont found by that ID +// // the above guard (soundfontCount) protects us for the +// // main case we're expecting. this guard is just defensive programming. +// return banksToPresets; +// } +// +// int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); +// +// fluid_sfont_iteration_start(sfont); +// +// for(fluid_preset_t* preset = fluid_sfont_iteration_next(sfont); +// preset != nullptr; +// preset = fluid_sfont_iteration_next(sfont)) { +// banksToPresets.insert(BanksToPresets::value_type( +// fluid_preset_get_banknum(preset) + offset, +// *new Preset( +// fluid_preset_get_num(preset), +// fluid_preset_get_name(preset) +// ) +// )); +// } +// +// return banksToPresets; +//} shared_ptr FluidSynthModel::getSynth() { // https://msdn.microsoft.com/en-us/library/hh279669.aspx @@ -378,38 +381,50 @@ shared_ptr FluidSynthModel::getSynth() { return synth; } -void FluidSynthModel::onFileNameChanged(const String &absPath, int bank, int preset) { - if (!shouldLoadFont(absPath)) { - return; - } - unloadAndLoadFont(absPath); - changePreset(bank, preset); - ValueTree valueTree{valueTreeState.state.getChildWithName("soundFont")}; - valueTree.setPropertyExcludingListener(this, "path", absPath, nullptr); - // valueTree.setPropertyExcludingListener(this, "soundFontPath", absPath, nullptr); -// sharedParams.setSoundFontPath(absPath); - // eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath); -} +//void FluidSynthModel::onFileNameChanged(const String &absPath, int bank, int preset) { +// if (!shouldLoadFont(absPath)) { +// return; +// } +// unloadAndLoadFont(absPath); +// changePreset(bank, preset); +// ValueTree valueTree{valueTreeState.state.getChildWithName("soundFont")}; +// valueTree.setPropertyExcludingListener(this, "path", absPath, nullptr); +// // valueTree.setPropertyExcludingListener(this, "soundFontPath", absPath, nullptr); +//// sharedParams.setSoundFontPath(absPath); +// // eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath); +//} void FluidSynthModel::unloadAndLoadFont(const String &absPath) { // in the base case, there is no font loaded if (fluid_synth_sfcount(synth.get()) > 0) { + // if -1 is returned, that indicates failure + // not really sure how to handle "fail to unload" fluid_synth_sfunload(synth.get(), sfont_id, 1); + sfont_id = -1; } loadFont(absPath); } void FluidSynthModel::loadFont(const String &absPath) { - currentSoundFontAbsPath = absPath; - sfont_id++; - fluid_synth_sfload(synth.get(), absPath.toStdString().c_str(), 1); +// currentSoundFontAbsPath = absPath; +// sfont_id++; +// fluid_synth_sfunload(synth.get(), sfont_id, 1); + if (!absPath.isEmpty()) { + sfont_id = fluid_synth_sfload(synth.get(), absPath.toStdString().c_str(), 1); + // if -1 is returned, that indicates failure + } + // refresh regardless of success, if only to clear the table refreshBanks(); } void FluidSynthModel::refreshBanks() { - fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; ValueTree banks{"banks"}; - if (sfont != nullptr) { + fluid_sfont_t* sfont{ + sfont_id == -1 + ? nullptr + : fluid_synth_get_sfont_by_id(synth.get(), sfont_id) + }; + if (sfont) { int greatestEncounteredBank{-1}; ValueTree bank; @@ -440,8 +455,8 @@ void FluidSynthModel::refreshBanks() { valueTreeState.state.getChildWithName("banks").sendPropertyChangeMessage("synthetic"); #if JUCE_DEBUG - unique_ptr xml{valueTreeState.state.createXml()}; - Logger::outputDebugString(xml->createDocument("",false,false)); +// unique_ptr xml{valueTreeState.state.createXml()}; +// Logger::outputDebugString(xml->createDocument("",false,false)); #endif } @@ -557,22 +572,22 @@ void FluidSynthModel::refreshBanks() { // FluidSynthModel::Listener::~Listener() { // } -bool FluidSynthModel::shouldLoadFont(const String &absPath) { - if (absPath.isEmpty()) { - return false; - } - if (absPath == currentSoundFontAbsPath) { - return false; - } - return true; -} +//bool FluidSynthModel::shouldLoadFont(const String &absPath) { +// if (absPath.isEmpty()) { +// return false; +// } +//// if (absPath == currentSoundFontAbsPath) { +//// return false; +//// } +// return true; +//} // void FluidSynthModel::Listener::fontChanged(FluidSynthModel * model, const String &absPath) { // } -const String& FluidSynthModel::getCurrentSoundFontAbsPath() { - return currentSoundFontAbsPath; -} +//const String& FluidSynthModel::getCurrentSoundFontAbsPath() { +// return currentSoundFontAbsPath; +//} //============================================================================== // void FluidSynthModel::addListener (FluidSynthModel::Listener* const newListener) diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index 92a06e6..3978721 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -31,12 +31,12 @@ class FluidSynthModel shared_ptr getSynth(); void initialise(); - BanksToPresets getBanks(); +// BanksToPresets getBanks(); - void changePreset(int bank, int preset); +// void changePreset(int bank, int preset); int getChannel(); - void onFileNameChanged(const String &absPath, int bank, int preset); +// void onFileNameChanged(const String &absPath, int bank, int preset); void setControllerValue(int controller, int value); //============================================================================== @@ -68,7 +68,7 @@ class FluidSynthModel void setSampleRate(float sampleRate); - const String& getCurrentSoundFontAbsPath(); +// const String& getCurrentSoundFontAbsPath(); virtual void parameterChanged (const String& parameterID, float newValue) override; @@ -123,7 +123,7 @@ class FluidSynthModel shared_ptr synth; // unique_ptr midiDriver; - String currentSoundFontAbsPath; +// String currentSoundFontAbsPath; float currentSampleRate; @@ -133,12 +133,12 @@ class FluidSynthModel void unloadAndLoadFont(const String &absPath); void loadFont(const String &absPath); - bool shouldLoadFont(const String &absPath); +// bool shouldLoadFont(const String &absPath); void changePresetImpl(int bank, int preset); - bool initialised; - unsigned int sfont_id; +// bool initialised; + int sfont_id; unsigned int channel; // fluid_mod_t* mod; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 2ad8085..e9831c5 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -218,7 +218,7 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer int time; MidiMessage m; - // TODO: factor into a MidiCollector + // TODO: factor into a MidiMessageCollector for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);) { DEBUG_PRINT ( m.getDescription() ); @@ -385,10 +385,10 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData) // as intermediaries to make it easy to save and load complex data. // Create an outer XML element.. -// XmlElement xml{"MYPLUGINSETTINGS"}; + XmlElement xml{"MYPLUGINSETTINGS"}; // sharedParams->setAttributesOnXml(xml); - auto state{valueTreeState.copyState()}; - unique_ptr xml{state.createXml()}; +// auto state{valueTreeState.copyState()}; +// unique_ptr xml{state.createXml()}; // sharedParams.setAttributesOnXml(xml); // list::iterator p; @@ -397,14 +397,44 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData) // } // Store the values of all our parameters, using their param ID as the XML attribute - // for (auto* param : getParameters()) - // if (auto* p = dynamic_cast (param)) - // xml->setAttribute (p->paramID, p->getValue()); + XmlElement* params{xml.createNewChildElement("params")}; + for (auto* param : getParameters()) { + if (auto* p = dynamic_cast (param)) { +// xml.setAttribute(p->paramID, p->getValue()); +// XmlElement* param{params->createNewChildElement("PARAM")}; +// param->setAttribute(p->paramID, p->getValue()); + params->setAttribute(p->paramID, p->getValue()); + } + } + { + ValueTree tree{valueTreeState.state.getChildWithName("uiState")}; + XmlElement* newElement{xml.createNewChildElement("uiState")}; +// Value value{tree.getPropertyAsValue("width", nullptr)}; + { + double value{tree.getProperty("width", GuiConstants::minWidth)}; + newElement->setAttribute("width", value); + } + { + double value{tree.getProperty("height", GuiConstants::minHeight)}; + newElement->setAttribute("height", value); + } + } + { + ValueTree tree{valueTreeState.state.getChildWithName("soundFont")}; + XmlElement* newElement{xml.createNewChildElement("soundFont")}; + { + String value{tree.getProperty("path", "")}; + newElement->setAttribute("path", value); + } + } + + 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); - } +// if (xml.get() != nullptr) { +// copyXmlToBinary(*xml, destData); +// } + copyXmlToBinary(xml, destData); } void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInBytes) @@ -433,16 +463,24 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt // if (xmlState->hasTagName ("MYPLUGINSETTINGS")) { if (xmlState->hasTagName(valueTreeState.state.getType())) { // valueTreeState.replaceState(ValueTree::fromXml(*xmlState)); - for (auto* param : getParameters()) - if (auto* p = dynamic_cast(param)) - p->setValue(static_cast(xmlState->getDoubleAttribute(p->paramID, p->getValue()))); +// for (auto* param : getParameters()) +// if (auto* p = dynamic_cast(param)) +// p->setValue(static_cast(xmlState->getDoubleAttribute(p->paramID, p->getValue()))); + XmlElement* params{xmlState->getChildByName("params")}; + if (params) { + for (auto* param : getParameters()) + if (auto* p = dynamic_cast(param)) + // XmlElement* xmlParam{params->getChildByAttribute("id", p->paramID)}; + // p->setValue(static_cast(xmlState->getDoubleAttribute(p->paramID, p->getValue()))); + p->setValue(static_cast(params->getDoubleAttribute(p->paramID, p->getValue()))); + } { // Value value{valueTreeState.state.getPropertyAsValue("soundFontPath", nullptr)}; // value = xmlState->getStringAttribute("soundFontPath", value.getValue()); - ValueTree tree{valueTreeState.state.getChildWithName("soundFont")}; XmlElement* xmlElement{xmlState->getChildByName("soundFont")}; if (xmlElement) { + ValueTree tree{valueTreeState.state.getChildWithName("soundFont")}; Value value{tree.getPropertyAsValue("path", nullptr)}; value = xmlState->getStringAttribute("path", value.getValue()); } From e772b4bcf031c29612f3142ecaa163fef087bf1c Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 28 Jul 2019 20:17:03 +0100 Subject: [PATCH 24/33] fix typo that was causing soundfont loading to fail --- Source/PluginProcessor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index e9831c5..94b270c 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -206,7 +206,7 @@ AudioProcessor::BusesProperties JuicySFAudioProcessor::getBusesProperties() { .withOutput ("Output", AudioChannelSet::stereo(), true); } -void JuicySFAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer& midiMessages) { +void JuicySFAudioProcessor::processBlock(AudioBuffer& buffer, MidiBuffer& midiMessages) { jassert (!isUsingDoublePrecision()); const int numSamples = buffer.getNumSamples(); @@ -482,7 +482,7 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt if (xmlElement) { ValueTree tree{valueTreeState.state.getChildWithName("soundFont")}; Value value{tree.getPropertyAsValue("path", nullptr)}; - value = xmlState->getStringAttribute("path", value.getValue()); + value = xmlElement->getStringAttribute("path", value.getValue()); } // valueTreeState.getParameter("soundFontPath")->getValue() From d4a060b7691ce2dda16d36781bab317679c4e204 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 28 Jul 2019 22:22:25 +0100 Subject: [PATCH 25/33] move message-routing, midi-rendering concerns into FluidSynthModel --- Source/FluidSynthModel.cpp | 130 ++++++++++++++++++++++++++++++++ Source/FluidSynthModel.h | 2 + Source/PluginProcessor.cpp | 148 ++----------------------------------- 3 files changed, 140 insertions(+), 140 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 20fd318..052bf28 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -610,3 +610,133 @@ void FluidSynthModel::setSampleRate(float sampleRate) { } fluid_synth_set_sample_rate(synth.get(), sampleRate); } + +void FluidSynthModel::processBlock(AudioBuffer& buffer, MidiBuffer& midiMessages) { + MidiBuffer processedMidi; + int time; + MidiMessage m; + + for (MidiBuffer::Iterator i{midiMessages}; i.getNextEvent(m, time);) { + DEBUG_PRINT(m.getDescription()); + + if (m.isNoteOn()) { + fluid_synth_noteon( + synth.get(), + channel, + m.getNoteNumber(), + m.getVelocity()); + } else if (m.isNoteOff()) { + fluid_synth_noteoff( + synth.get(), + channel, + m.getNoteNumber()); + } else if (m.isController()) { + fluid_synth_cc( + synth.get(), + channel, + m.getControllerNumber(), + m.getControllerValue()); + + switch(static_cast(m.getControllerNumber())) { + case SOUND_CTRL2: { // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) + // valueTreeState.state.setProperty({"filterResonance"}, m.getControllerValue(), nullptr); + RangedAudioParameter *param{valueTreeState.getParameter("filterResonance")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam {dynamic_cast(param)}; + *castParam = m.getControllerValue(); + break; + } + case SOUND_CTRL3: { // MIDI CC 72 Release time + RangedAudioParameter *param{valueTreeState.getParameter("release")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam {dynamic_cast(param)}; + *castParam = m.getControllerValue(); + break; + } + case SOUND_CTRL4: { // MIDI CC 73 Attack time + RangedAudioParameter *param{valueTreeState.getParameter("release")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam {dynamic_cast(param)}; + *castParam = m.getControllerValue(); + break; + } + case SOUND_CTRL5: { // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) + RangedAudioParameter *param{valueTreeState.getParameter("filterCutOff")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam {dynamic_cast(param)}; + *castParam = m.getControllerValue(); + break; + } + case SOUND_CTRL6: { // MIDI CC 75 Decay Time + RangedAudioParameter *param{valueTreeState.getParameter("decay")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam {dynamic_cast(param)}; + *castParam = m.getControllerValue(); + break; + } + case SOUND_CTRL10: { // MIDI CC 79 undefined + RangedAudioParameter *param {valueTreeState.getParameter("sustain")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + *castParam = m.getControllerValue(); + break; + } + default: { + break; + } + } + } else if (m.isProgramChange()) { + int result{fluid_synth_program_change( + synth.get(), + channel, + m.getProgramChangeNumber())}; + if (result == FLUID_OK) { + RangedAudioParameter *param{valueTreeState.getParameter("preset")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + *castParam = m.getProgramChangeNumber(); + } + } else if (m.isPitchWheel()) { + fluid_synth_pitch_bend( + synth.get(), + channel, + m.getPitchWheelValue()); + } else if (m.isChannelPressure()) { + fluid_synth_channel_pressure( + synth.get(), + channel, + m.getChannelPressureValue()); + } else if (m.isAftertouch()) { + fluid_synth_key_pressure( + synth.get(), + channel, + m.getNoteNumber(), + m.getAfterTouchValue()); +// } else if (m.isMetaEvent()) { +// fluid_midi_event_t *midi_event{new_fluid_midi_event()}; +// fluid_midi_event_set_type(midi_event, static_cast(MIDI_SYSTEM_RESET)); +// fluid_synth_handle_midi_event(synth.get(), midi_event); +// delete_fluid_midi_event(midi_event); + } else if (m.isSysEx()) { + fluid_synth_sysex( + synth.get(), + reinterpret_cast(m.getSysExData()), + m.getSysExDataSize(), + nullptr, // no response pointer because we have no interest in handling response currently + nullptr, // no response_len pointer because we have no interest in handling response currently + nullptr, // no handled pointer because we have no interest in handling response currently + static_cast(false)); + } + } + + // fluid_synth_get_cc(fluidSynth, 0, 73, &pval); + // Logger::outputDebugString ( juce::String::formatted("hey: %d\n", pval) ); + + fluid_synth_process( + synth.get(), + buffer.getNumSamples(), + 0, + nullptr, + buffer.getNumChannels(), + buffer.getArrayOfWritePointers()); +} diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index 3978721..c0df803 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -39,6 +39,8 @@ class FluidSynthModel // void onFileNameChanged(const String &absPath, int bank, int preset); void setControllerValue(int controller, int value); + void processBlock(AudioBuffer& buffer, MidiBuffer& midiMessages); + //============================================================================== /** Used to receive callbacks when a button is clicked. diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 94b270c..3b6aaee 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -101,14 +101,14 @@ void JuicySFAudioProcessor::initialiseSynth() { // fluidSynth = fluidSynthModel.getSynth(); - const int numVoices = 8; + // const int numVoices = 8; // Add some voices... - for (int i = numVoices; --i >= 0;) - synth.addVoice(new SoundfontSynthVoice(fluidSynthModel.getSynth())); + // for (int i = numVoices; --i >= 0;) + // synth.addVoice(new SoundfontSynthVoice(fluidSynthModel.getSynth())); // ..and give the synth a sound to play - synth.addSound(new SoundfontSynthSound()); + // synth.addSound(new SoundfontSynthSound()); } //============================================================================== @@ -208,148 +208,16 @@ AudioProcessor::BusesProperties JuicySFAudioProcessor::getBusesProperties() { void JuicySFAudioProcessor::processBlock(AudioBuffer& buffer, MidiBuffer& midiMessages) { jassert (!isUsingDoublePrecision()); - const int numSamples = buffer.getNumSamples(); + const int numSamples{buffer.getNumSamples()}; // Now pass any incoming midi messages to our keyboard state object, and let it // add messages to the buffer if the user is clicking on the on-screen keys - keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true); + keyboardState.processNextMidiBuffer(midiMessages, 0, numSamples, true); - MidiBuffer processedMidi; - int time; - MidiMessage m; - - // TODO: factor into a MidiMessageCollector - for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);) { - DEBUG_PRINT ( m.getDescription() ); - - // explicitly not handling note_on/off, or pitch_bend, because these are (for better or worse) - // responsibilities of SoundfontSynthVoice. - // well, by that logic maybe I should move program change onto Voice. but it doesn't feel like a per-voice concern. - if (m.isController()) { - // shared_ptr midi_event{ - // new_fluid_midi_event(), - // [](fluid_midi_event_t *event) { - // delete_fluid_midi_event(midi_event); - // }}; - fluid_midi_event_t *midi_event(new_fluid_midi_event()); - fluid_midi_event_set_type(midi_event, static_cast(CONTROL_CHANGE)); - fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel()); - fluid_midi_event_set_control(midi_event, m.getControllerNumber()); - fluid_midi_event_set_value(midi_event, m.getControllerValue()); - fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); - delete_fluid_midi_event(midi_event); - - switch(static_cast(m.getControllerNumber())) { - case SOUND_CTRL2: { // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) - // valueTreeState.state.setProperty({"filterResonance"}, m.getControllerValue(), nullptr); - RangedAudioParameter *param {valueTreeState.getParameter("filterResonance")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - *castParam = m.getControllerValue(); - break; - } - case SOUND_CTRL3: { // MIDI CC 72 Release time - RangedAudioParameter *param {valueTreeState.getParameter("release")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - *castParam = m.getControllerValue(); - break; - } - case SOUND_CTRL4: { // MIDI CC 73 Attack time - RangedAudioParameter *param {valueTreeState.getParameter("release")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - *castParam = m.getControllerValue(); - break; - } - case SOUND_CTRL5: { // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) - RangedAudioParameter *param {valueTreeState.getParameter("filterCutOff")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - *castParam = m.getControllerValue(); - break; - } - case SOUND_CTRL6: { // MIDI CC 75 Decay Time - RangedAudioParameter *param {valueTreeState.getParameter("decay")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - *castParam = m.getControllerValue(); - break; - } - case SOUND_CTRL10: { // MIDI CC 79 undefined - RangedAudioParameter *param {valueTreeState.getParameter("sustain")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - *castParam = m.getControllerValue(); - break; - } - default: { - break; - } - } - - // sharedParams->acceptMidiControlEvent(m.getControllerNumber(), m.getControllerValue()); - - // AudioProcessorEditor* editor{getActiveEditor()}; - // jassert(dynamic_cast (editor) != nullptr); - // ExposesComponents* exposesComponents{dynamic_cast(editor)}; - // exposesComponents->getSliders().acceptMidiControlEvent(m.getControllerNumber(), m.getControllerValue()); - } else if (m.isProgramChange()) { - fluid_midi_event_t *midi_event(new_fluid_midi_event()); - fluid_midi_event_set_type(midi_event, static_cast(PROGRAM_CHANGE)); - fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel()); - fluid_midi_event_set_program(midi_event, m.getProgramChangeNumber()); - fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); - delete_fluid_midi_event(midi_event); - } else if (m.isPitchWheel()) { - fluid_midi_event_t *midi_event(new_fluid_midi_event()); - fluid_midi_event_set_type(midi_event, static_cast(PITCH_BEND)); - fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel()); - fluid_midi_event_set_pitch(midi_event, m.getPitchWheelValue()); - fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); - delete_fluid_midi_event(midi_event); - } else if (m.isChannelPressure()) { - fluid_midi_event_t *midi_event(new_fluid_midi_event()); - fluid_midi_event_set_type(midi_event, static_cast(CHANNEL_PRESSURE)); - fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel()); - fluid_midi_event_set_program(midi_event, m.getChannelPressureValue()); - fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); - delete_fluid_midi_event(midi_event); - } else if (m.isAftertouch()) { - fluid_midi_event_t *midi_event(new_fluid_midi_event()); - fluid_midi_event_set_type(midi_event, static_cast(KEY_PRESSURE)); - fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel()); - fluid_midi_event_set_key(midi_event, m.getNoteNumber()); - fluid_midi_event_set_value(midi_event, m.getAfterTouchValue()); - fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); - delete_fluid_midi_event(midi_event); -// } else if (m.isMetaEvent()) { -// fluid_midi_event_t *midi_event(new_fluid_midi_event()); -// fluid_midi_event_set_type(midi_event, static_cast(MIDI_SYSTEM_RESET)); -// fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); -// delete_fluid_midi_event(midi_event); - } else if (m.isSysEx()) { - fluid_midi_event_t *midi_event(new_fluid_midi_event()); - fluid_midi_event_set_type(midi_event, static_cast(MIDI_SYSEX)); - // I assume that the MidiMessage's sysex buffer would be freed anyway when MidiMessage is destroyed, so set dynamic=false - // to ensure that fluidsynth does not attempt to free the sysex buffer during delete_fluid_midi_event() - fluid_midi_event_set_sysex(midi_event, const_cast(m.getSysExData()), m.getSysExDataSize(), static_cast(false)); - fluid_synth_handle_midi_event(fluidSynthModel.getSynth().get(), midi_event); - delete_fluid_midi_event(midi_event); - } - } - -// int pval; - // 73: 64 attack - // 75: decay - // 79: sustain - // 72: 64 release -// fluid_synth_get_cc(fluidSynth, 0, 73, &pval); -// Logger::outputDebugString ( juce::String::formatted("hey: %d\n", pval) ); + fluidSynthModel.processBlock(buffer, midiMessages); // and now get our synth to process these midi events and generate its output. - synth.renderNextBlock (buffer, midiMessages, 0, numSamples); - fluid_synth_process(fluidSynthModel.getSynth().get(), numSamples, 0, nullptr, buffer.getNumChannels(), buffer.getArrayOfWritePointers()); + // synth.renderNextBlock(buffer, midiMessages, 0, numSamples); // (see juce_VST3_Wrapper.cpp for the assertion this would trip otherwise) // we are !JucePlugin_ProducesMidiOutput, so clear remaining MIDI messages from our buffer From 39f9d86bd1f12c091477d4ad9be456f57d7f5874 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 28 Jul 2019 22:51:51 +0100 Subject: [PATCH 26/33] remove shared access to fluidsynth instance --- .../juicysfplugin.xcodeproj/project.pbxproj | 12 -- Source/FluidSynthModel.cpp | 68 ++++++++++- Source/FluidSynthModel.h | 83 ++----------- Source/PluginProcessor.cpp | 62 ++-------- Source/PluginProcessor.h | 13 -- Source/SoundfontSynthSound.cpp | 13 -- Source/SoundfontSynthSound.h | 13 -- Source/SoundfontSynthVoice.cpp | 111 ------------------ Source/SoundfontSynthVoice.h | 39 ------ 9 files changed, 81 insertions(+), 333 deletions(-) delete mode 100644 Source/SoundfontSynthSound.cpp delete mode 100644 Source/SoundfontSynthSound.h delete mode 100644 Source/SoundfontSynthVoice.cpp delete mode 100644 Source/SoundfontSynthVoice.h diff --git a/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj b/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj index 54de6c0..1dc4892 100644 --- a/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj @@ -33,7 +33,6 @@ 21AC354419419A4D80ADE43A /* include_juce_audio_plugin_client_AU_2.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7E47C0A828016F7D0D63C0D6 /* include_juce_audio_plugin_client_AU_2.mm */; }; 2918F46AFD2AB89F9FA847DC /* include_juce_events.mm in Sources */ = {isa = PBXBuildFile; fileRef = 373EF982A53046CE00BECE68 /* include_juce_events.mm */; }; 2E77C6FAF1BCDB9EB29D20B9 /* PluginProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53CAB963D5051C786D3A52D /* PluginProcessor.cpp */; }; - 305606C42BB0F2A12D382D34 /* SoundfontSynthVoice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5A057FEC371053E83A73E47 /* SoundfontSynthVoice.cpp */; }; 35099D9322CAA87D00CD4523 /* Params.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 35099D9122CAA87D00CD4523 /* Params.cpp */; }; 358E458C22BEE5090087ED8D /* RecentFilesMenuTemplate.nib in Resources */ = {isa = PBXBuildFile; fileRef = 78CC5234CCFE3B170585DDAD /* RecentFilesMenuTemplate.nib */; }; 358E458D22BEE5090087ED8D /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1616112041466F7324D7E19 /* Accelerate.framework */; }; @@ -128,7 +127,6 @@ 9C107CE4B586E4B097D9D04E /* SurjectiveMidiKeyboardComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4119A8200AC54674C00EFE66 /* SurjectiveMidiKeyboardComponent.cpp */; }; 9C2580F953071AD611EB6166 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CA077CDD21D0FEC66FC290 /* AudioToolbox.framework */; }; AC5E4EF988D864A298E3650D /* TablesComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0DD5458189C039F5A4FAD62D /* TablesComponent.cpp */; }; - B66EBD76F6051D97D56C97AB /* SoundfontSynthSound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7D7B71BE20CA213D2FCD7FEE /* SoundfontSynthSound.cpp */; }; B92F6EAB1D5ACC13AF0CD750 /* CoreAudioKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A67D09546C4E4831438F7DBD /* CoreAudioKit.framework */; }; BB7C2221DA61425A1AC65694 /* include_juce_gui_extra.mm in Sources */ = {isa = PBXBuildFile; fileRef = 44FB953DA425CBBA8AC21417 /* include_juce_gui_extra.mm */; }; BFD9EF2D67067FC1E5BA3546 /* MyColours.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 29F2CE1B40FAE1467C7876C5 /* MyColours.cpp */; }; @@ -331,11 +329,9 @@ 6FA795817D2F3B3119FDD754 /* juce_core */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_core; path = /Applications/JUCE/modules/juce_core; sourceTree = ""; }; 6FEF19AE08ED1DC1E3D9DF43 /* AppConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppConfig.h; path = ../../JuceLibraryCode/AppConfig.h; sourceTree = SOURCE_ROOT; }; 706FF998202761F30811FA6B /* juce_audio_devices */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_devices; path = /Applications/JUCE/modules/juce_audio_devices; sourceTree = ""; }; - 76724E30D8976FC4C2EE56FF /* SoundfontSynthSound.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SoundfontSynthSound.h; path = ../../Source/SoundfontSynthSound.h; sourceTree = SOURCE_ROOT; }; 78CC5234CCFE3B170585DDAD /* RecentFilesMenuTemplate.nib */ = {isa = PBXFileReference; lastKnownFileType = file.nib; path = RecentFilesMenuTemplate.nib; sourceTree = SOURCE_ROOT; }; 7C699A8B65F3F9FB5004F22D /* juce_gui_extra */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_gui_extra; path = /Applications/JUCE/modules/juce_gui_extra; sourceTree = ""; }; 7D2457AD994644752178FC82 /* include_juce_audio_plugin_client_VST_utils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_plugin_client_VST_utils.mm; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_VST_utils.mm; sourceTree = SOURCE_ROOT; }; - 7D7B71BE20CA213D2FCD7FEE /* SoundfontSynthSound.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SoundfontSynthSound.cpp; path = ../../Source/SoundfontSynthSound.cpp; sourceTree = SOURCE_ROOT; }; 7E47C0A828016F7D0D63C0D6 /* include_juce_audio_plugin_client_AU_2.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_plugin_client_AU_2.mm; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_AU_2.mm; sourceTree = SOURCE_ROOT; }; 88ADEBF51BD04FEA9422D276 /* FilePickerFragment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FilePickerFragment.h; path = ../../Source/FilePickerFragment.h; sourceTree = SOURCE_ROOT; }; 8990F3EAFFBBD6A42247C663 /* PluginEditor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PluginEditor.h; path = ../../Source/PluginEditor.h; sourceTree = SOURCE_ROOT; }; @@ -348,8 +344,6 @@ ADC93C26314F163B963461E2 /* include_juce_audio_utils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_utils.mm; path = ../../JuceLibraryCode/include_juce_audio_utils.mm; sourceTree = SOURCE_ROOT; }; AE397302E7E3F3A14A0C5F3C /* Preset.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Preset.h; path = ../../Source/Preset.h; sourceTree = SOURCE_ROOT; }; B000E7A360C0C86ADD3C911D /* BankAndPreset.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BankAndPreset.h; path = ../../Source/BankAndPreset.h; sourceTree = SOURCE_ROOT; }; - B40B7F24646CBA708718DE82 /* SoundfontSynthVoice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SoundfontSynthVoice.h; path = ../../Source/SoundfontSynthVoice.h; sourceTree = SOURCE_ROOT; }; - B5A057FEC371053E83A73E47 /* SoundfontSynthVoice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SoundfontSynthVoice.cpp; path = ../../Source/SoundfontSynthVoice.cpp; sourceTree = SOURCE_ROOT; }; B6D37AD919F9E83688578941 /* SurjectiveMidiKeyboardComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SurjectiveMidiKeyboardComponent.h; path = ../../Source/SurjectiveMidiKeyboardComponent.h; sourceTree = SOURCE_ROOT; }; BFB39134DE6876F9005CFA61 /* Pills.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Pills.h; path = ../../Source/Pills.h; sourceTree = SOURCE_ROOT; }; BFF57868318157F12F087F07 /* Info-AU.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-AU.plist"; sourceTree = SOURCE_ROOT; }; @@ -543,10 +537,6 @@ 59F9FEC807012C10B8A1FA07 /* Preset.cpp */, AE397302E7E3F3A14A0C5F3C /* Preset.h */, 69DB3A0FB3D21F87D1E4B0C1 /* PresetsToBanks.h */, - 7D7B71BE20CA213D2FCD7FEE /* SoundfontSynthSound.cpp */, - 76724E30D8976FC4C2EE56FF /* SoundfontSynthSound.h */, - B5A057FEC371053E83A73E47 /* SoundfontSynthVoice.cpp */, - B40B7F24646CBA708718DE82 /* SoundfontSynthVoice.h */, 4119A8200AC54674C00EFE66 /* SurjectiveMidiKeyboardComponent.cpp */, B6D37AD919F9E83688578941 /* SurjectiveMidiKeyboardComponent.h */, CE8C41308A31A71A1177D0D5 /* TableComponent.cpp */, @@ -953,8 +943,6 @@ BFD9EF2D67067FC1E5BA3546 /* MyColours.cpp in Sources */, 8502F736BECFB9CB752AC72F /* Pills.cpp in Sources */, DF84F5E7E386AF7A38854939 /* Preset.cpp in Sources */, - B66EBD76F6051D97D56C97AB /* SoundfontSynthSound.cpp in Sources */, - 305606C42BB0F2A12D382D34 /* SoundfontSynthVoice.cpp in Sources */, 9C107CE4B586E4B097D9D04E /* SurjectiveMidiKeyboardComponent.cpp in Sources */, 4AE057561AEA78489D9E50F0 /* TableComponent.cpp in Sources */, AC5E4EF988D864A298E3650D /* TablesComponent.cpp in Sources */, diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 052bf28..f7092a8 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -675,9 +675,9 @@ void FluidSynthModel::processBlock(AudioBuffer& buffer, MidiBuffer& midiM break; } case SOUND_CTRL10: { // MIDI CC 79 undefined - RangedAudioParameter *param {valueTreeState.getParameter("sustain")}; + RangedAudioParameter *param{valueTreeState.getParameter("sustain")}; jassert(dynamic_cast(param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; + AudioParameterInt* castParam{dynamic_cast(param)}; *castParam = m.getControllerValue(); break; } @@ -692,8 +692,8 @@ void FluidSynthModel::processBlock(AudioBuffer& buffer, MidiBuffer& midiM m.getProgramChangeNumber())}; if (result == FLUID_OK) { RangedAudioParameter *param{valueTreeState.getParameter("preset")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; *castParam = m.getProgramChangeNumber(); } } else if (m.isPitchWheel()) { @@ -740,3 +740,63 @@ void FluidSynthModel::processBlock(AudioBuffer& buffer, MidiBuffer& midiM buffer.getNumChannels(), buffer.getArrayOfWritePointers()); } + +int FluidSynthModel::getNumPrograms() +{ + return 128; // NB: some hosts don't cope very well if you tell them there are 0 programs, + // so this should be at least 1, even if you're not really implementing programs. +} + +int FluidSynthModel::getCurrentProgram() +{ + RangedAudioParameter *param{valueTreeState.getParameter("preset")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; + return castParam->get(); +} + +void FluidSynthModel::setCurrentProgram(int index) +{ + RangedAudioParameter *param{valueTreeState.getParameter("preset")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; + *castParam = index; +} + +const String FluidSynthModel::getProgramName(int index) +{ + fluid_sfont_t* sfont{ + sfont_id == -1 + ? nullptr + : fluid_synth_get_sfont_by_id(synth.get(), sfont_id) + }; + if (!sfont) { + return {}; + } + int bank, presetNum; + { + RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + bank = castParam->get(); + } + { + RangedAudioParameter *param {valueTreeState.getParameter("preset")}; + jassert(dynamic_cast (param) != nullptr); + AudioParameterInt* castParam {dynamic_cast (param)}; + presetNum = castParam->get(); + } + fluid_preset_t *preset{fluid_sfont_get_preset( + sfont, + bank, + presetNum)}; + if (!preset) { + return {}; + } + return {fluid_preset_get_name(preset)}; +} + +void FluidSynthModel::changeProgramName(int index, const String& newName) +{ + // no-op; we don't support modifying the soundfont, so let's not support modification of preset names. +} diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index c0df803..30440b0 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -11,10 +11,6 @@ #include "BankAndPreset.h" #include "PresetsToBanks.h" - -// https://stackoverflow.com/a/13446565/5257399 -//using std::shared_ptr; - using namespace std; class FluidSynthModel @@ -23,55 +19,21 @@ class FluidSynthModel public: FluidSynthModel( AudioProcessorValueTreeState& valueTreeState - // SharesParams& sharedParams - // ValueTree& valueTree ); ~FluidSynthModel(); - shared_ptr getSynth(); void initialise(); - -// BanksToPresets getBanks(); - -// void changePreset(int bank, int preset); + int getChannel(); -// void onFileNameChanged(const String &absPath, int bank, int preset); void setControllerValue(int controller, int value); void processBlock(AudioBuffer& buffer, MidiBuffer& midiMessages); - //============================================================================== - /** - Used to receive callbacks when a button is clicked. - - @see Button::addListener, Button::removeListener - */ - // class Listener - // { - // public: - // /** Destructor. */ - // virtual ~Listener(); - - // /** Called when the button is clicked. */ - // virtual void fontChanged (FluidSynthModel*, const String &absPath); - // }; - - /** Registers a listener to receive events when this button's state changes. - If the listener is already registered, this will not register it again. - @see removeListener - */ - // void addListener (Listener* newListener); - - /** Removes a previously-registered button listener - @see addListener - */ - // void removeListener (Listener* listener); void setSampleRate(float sampleRate); - -// const String& getCurrentSoundFontAbsPath(); + //============================================================================== virtual void parameterChanged (const String& parameterID, float newValue) override; virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, @@ -86,35 +48,18 @@ class FluidSynthModel inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; -private: -// class ValueTreeListener: public ValueTree::Listener { -// public: -//// ValueTreeListener(); -//// ~ValueTreeListener(); -// virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, -// const Identifier& property) override; -// inline virtual void valueTreeChildAdded (ValueTree& parentTree, -// ValueTree& childWhichHasBeenAdded) override {}; -// inline virtual void valueTreeChildRemoved (ValueTree& parentTree, -// ValueTree& childWhichHasBeenRemoved, -// int indexFromWhichChildWasRemoved) override {}; -// inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, -// int oldIndex, int newIndex) override {}; -// inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; -// inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; -// JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeListener) -// }; + //============================================================================== + int getNumPrograms(); + int getCurrentProgram(); + void setCurrentProgram(int index); + const String getProgramName(int index); + void changeProgramName(int index, const String& newName); +private: int handleMidiEvent(void* data, fluid_midi_event_t* event); void refreshBanks(); - // void refreshPresets(); - // void refreshBanksAndPresets(); - -// ValueTreeListener valueTreeListener; AudioProcessorValueTreeState& valueTreeState; - // SharesParams& sharedParams; - // ValueTree& valueTree; // https://stackoverflow.com/questions/38980315/is-stdunique-ptr-deletion-order-guaranteed // members are destroyed in reverse of the order they're declared @@ -125,8 +70,6 @@ class FluidSynthModel shared_ptr synth; // unique_ptr midiDriver; -// String currentSoundFontAbsPath; - float currentSampleRate; fluid_preset_t* getFirstPreset(); @@ -135,17 +78,11 @@ class FluidSynthModel void unloadAndLoadFont(const String &absPath); void loadFont(const String &absPath); -// bool shouldLoadFont(const String &absPath); void changePresetImpl(int bank, int preset); - -// bool initialised; + int sfont_id; unsigned int channel; -// fluid_mod_t* mod; - - // ListenerList eventListeners; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FluidSynthModel) }; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 3b6aaee..40794b4 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -10,8 +10,6 @@ #include "PluginProcessor.h" #include "PluginEditor.h" -#include "SoundfontSynthVoice.h" -#include "SoundfontSynthSound.h" #include "ExposesComponents.h" #include "MidiConstants.h" #include "Util.h" @@ -26,18 +24,14 @@ AudioProcessor* JUCE_CALLTYPE createPluginFilter(); //============================================================================== -//, sharedParams{static_pointer_cast(make_shared())} JuicySFAudioProcessor::JuicySFAudioProcessor() : AudioProcessor{getBusesProperties()} -// , sharedParams{} , valueTreeState{ *this, nullptr, "MYPLUGINSETTINGS", createParameterLayout()} , fluidSynthModel{valueTreeState} -//, fluidSynthModel{*this} -//, pluginEditor(nullptr) { valueTreeState.state.appendChild({ "uiState", { { "width", GuiConstants::minWidth }, @@ -47,32 +41,14 @@ JuicySFAudioProcessor::JuicySFAudioProcessor() { "path", "" }, }, {} }, nullptr); // no properties, no subtrees (yet) - // valueTreeState.state.appendChild({ "presets", {}, {} }, nullptr); - // no properties, no subtrees (yet) valueTreeState.state.appendChild({ "banks", {}, {} }, nullptr); - // valueTreeState.state.setProperty("soundFontPath", "", nullptr); -// valueTreeState.state.appendChild({ "soundFontPath", {} }, nullptr); initialiseSynth(); } AudioProcessorValueTreeState::ParameterLayout JuicySFAudioProcessor::createParameterLayout() { - // std::vector> params; - - // for (int i = 1; i < 9; ++i) - // params.push_back (std::make_unique (String (i), String (i), 0, i, 0)); - - -// make_unique("soundfontPath", "filepath to soundfont", 0, 127, 0, "A" ), - // https://stackoverflow.com/a/8469002/5257399 unique_ptr params[] { - // make_unique("uiWidthPersist", "width of this plugin's GUI. Editor listens for changes (e.g. on load)", GuiConstants::minWidth, GuiConstants::maxWidth, GuiConstants::minWidth, "UI Width Persist" ), - // make_unique("uiHeightPersist", "height of this plugin's GUI. Editor listens for changes (e.g. on load)", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height Persist" ), - // make_unique("uiWidthTemp", "width of this plugin's GUI. Editor writes here on change (e.g. on window resize). Processor copies this into Persist before any save.", GuiConstants::minWidth, GuiConstants::maxWidth, GuiConstants::minWidth, "UI Width Temp" ), - // make_unique("uiHeightTemp", "height of this plugin's GUI. Editor writes here on change (e.g. on window resize). Processor copies this into Persist before any save.", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height Temp" ), - // make_unique("uiWidth", "width of this plugin's GUI", GuiConstants::minWidth, GuiConstants::maxWidth, GuiConstants::minWidth, "UI Width" ), - // make_unique("uiHeight", "height of this plugin's GUI", GuiConstants::minHeight, GuiConstants::maxHeight, GuiConstants::minHeight, "UI Height" ), // SoundFont 2.4 spec section 7.2: zero through 127, or 128. make_unique("bank", "which bank is selected in the soundfont", MidiConstants::midiMinValue, 128, MidiConstants::midiMinValue, "Bank" ), // note: banks may be sparse, and lack a 0th preset. so defend against this. @@ -93,22 +69,10 @@ AudioProcessorValueTreeState::ParameterLayout JuicySFAudioProcessor::createParam JuicySFAudioProcessor::~JuicySFAudioProcessor() { -// delete fluidSynthModel; } void JuicySFAudioProcessor::initialiseSynth() { fluidSynthModel.initialise(); - -// fluidSynth = fluidSynthModel.getSynth(); - - // const int numVoices = 8; - - // Add some voices... - // for (int i = numVoices; --i >= 0;) - // synth.addVoice(new SoundfontSynthVoice(fluidSynthModel.getSynth())); - - // ..and give the synth a sound to play - // synth.addSound(new SoundfontSynthSound()); } //============================================================================== @@ -142,22 +106,23 @@ double JuicySFAudioProcessor::getTailLengthSeconds() const int JuicySFAudioProcessor::getNumPrograms() { - return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, + return fluidSynthModel.getNumPrograms(); // NB: some hosts don't cope very well if you tell them there are 0 programs, // so this should be at least 1, even if you're not really implementing programs. } int JuicySFAudioProcessor::getCurrentProgram() { - return 0; + return fluidSynthModel.getCurrentProgram(); } -void JuicySFAudioProcessor::setCurrentProgram (int index) +void JuicySFAudioProcessor::setCurrentProgram(int index) { + fluidSynthModel.setCurrentProgram(index); } -const String JuicySFAudioProcessor::getProgramName (int index) +const String JuicySFAudioProcessor::getProgramName(int index) { - return {}; + return fluidSynthModel.getProgramName(index); } void JuicySFAudioProcessor::changeProgramName (int index, const String& newName) @@ -208,11 +173,10 @@ AudioProcessor::BusesProperties JuicySFAudioProcessor::getBusesProperties() { void JuicySFAudioProcessor::processBlock(AudioBuffer& buffer, MidiBuffer& midiMessages) { jassert (!isUsingDoublePrecision()); - const int numSamples{buffer.getNumSamples()}; // Now pass any incoming midi messages to our keyboard state object, and let it // add messages to the buffer if the user is clicking on the on-screen keys - keyboardState.processNextMidiBuffer(midiMessages, 0, numSamples, true); + keyboardState.processNextMidiBuffer(midiMessages, 0, buffer.getNumSamples(), true); fluidSynthModel.processBlock(buffer, midiMessages); @@ -418,14 +382,6 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt } } -//void JuicySFAudioProcessor::subscribeToStateChanges(StateChangeSubscriber* subscriber) { -// stateChangeSubscribers.push_back(subscriber); -//} -// -//void JuicySFAudioProcessor::unsubscribeFromStateChanges(StateChangeSubscriber* subscriber) { -// stateChangeSubscribers.remove(subscriber); -//} - // FluidSynth only supports float in its process function, so that's all we can support. bool JuicySFAudioProcessor::supportsDoublePrecisionProcessing() const { return false; @@ -435,10 +391,6 @@ FluidSynthModel& JuicySFAudioProcessor::getFluidSynthModel() { return fluidSynthModel; } -//SharesParams& JuicySFAudioProcessor::getSharedParams() { -// return sharedParams; -//} - //============================================================================== // This creates new instances of the plugin.. AudioProcessor* JUCE_CALLTYPE createPluginFilter() diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index d3cfdca..2544766 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -64,34 +64,21 @@ class JuicySFAudioProcessor : public AudioProcessor bool supportsDoublePrecisionProcessing() const override; FluidSynthModel& getFluidSynthModel(); -// SharesParams& getSharedParams(); MidiKeyboardState keyboardState; -// void subscribeToStateChanges(StateChangeSubscriber* subscriber); -// void unsubscribeFromStateChanges(StateChangeSubscriber* subscriber); - private: void initialiseSynth(); -// Params sharedParams; AudioProcessorValueTreeState valueTreeState; - // ValueTree valueTree; FluidSynthModel fluidSynthModel; - // fluid_synth_t* fluidSynth; Synthesiser synth; -// // just a raw pointer; we do not own -// AudioProcessorEditor* pluginEditor; - -// list stateChangeSubscribers; - AudioProcessorValueTreeState::ParameterLayout createParameterLayout(); static BusesProperties getBusesProperties(); -// Model* model; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuicySFAudioProcessor) }; diff --git a/Source/SoundfontSynthSound.cpp b/Source/SoundfontSynthSound.cpp deleted file mode 100644 index 110fa33..0000000 --- a/Source/SoundfontSynthSound.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// -// Created by Alex Birch on 07/09/2017. -// - -#include "SoundfontSynthSound.h" - -bool SoundfontSynthSound::appliesToChannel(int) { - return true; -} - -bool SoundfontSynthSound::appliesToNote(int) { - return true; -} \ No newline at end of file diff --git a/Source/SoundfontSynthSound.h b/Source/SoundfontSynthSound.h deleted file mode 100644 index 52e0764..0000000 --- a/Source/SoundfontSynthSound.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Created by Alex Birch on 07/09/2017. -// - -#pragma once - -#include "../JuceLibraryCode/JuceHeader.h" - -class SoundfontSynthSound : public SynthesiserSound { -public: - bool appliesToNote (int /*midiNoteNumber*/) override; - bool appliesToChannel (int /*midiChannel*/) override; -}; \ No newline at end of file diff --git a/Source/SoundfontSynthVoice.cpp b/Source/SoundfontSynthVoice.cpp deleted file mode 100644 index 60590c1..0000000 --- a/Source/SoundfontSynthVoice.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// -// Created by Alex Birch on 07/09/2017. -// - -#include "SoundfontSynthVoice.h" -#include "SoundfontSynthSound.h" -#include "Util.h" - -using namespace std; - -SoundfontSynthVoice::SoundfontSynthVoice(shared_ptr synth) -: tailOff(0.0) -, level(0.0) -, currentAngle(0.0) -, angleDelta(0.0) -, midiNoteNumber(0) -, synth(synth) -{ -} - -bool SoundfontSynthVoice::canPlaySound(SynthesiserSound* sound) { - return dynamic_cast (sound) != nullptr; -} -void SoundfontSynthVoice::startNote( - int midiNoteNumber, - float velocity, - SynthesiserSound* sound, - int /*currentPitchWheelPosition*/) { - this->midiNoteNumber = midiNoteNumber; - DEBUG_PRINT ( juce::String::formatted("JUCE noteon: %d, %d\n", midiNoteNumber, velocity) ); - fluid_synth_noteon(synth.get(), 0, midiNoteNumber, static_cast(velocity * 127)); - -// currentAngle = 0.0; -// level = velocity * 0.15; -// tailOff = 0.0; -// -// double cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber); -// double cyclesPerSample = cyclesPerSecond / getSampleRate(); -// -// angleDelta = cyclesPerSample * 2.0 * double_Pi; - -// jassert(dynamic_cast (sound) != nullptr); -// SoundfontSynthSound* sfsynth = dynamic_cast (sound); -} - -void SoundfontSynthVoice::stopNote (float /*velocity*/, bool allowTailOff) { -// if (allowTailOff) { -// // start a tail-off by setting this flag. The render callback will pick up on -// // this and do a fade out, calling clearCurrentNote() when it's finished. -// -// // we only need to begin a tail-off if it's not already doing so - the -// if (tailOff == 0.0) { -// // stopNote method could be called more than once. -// tailOff = 1.0; -// } -// } else { -// // we're being told to stop playing immediately, so reset everything.. -// -// clearCurrentNote(); -// angleDelta = 0.0; -// } - DEBUG_PRINT ( juce::String("JUCE noteoff\n") ); - clearCurrentNote(); - fluid_synth_noteoff(synth.get(), 0, this->midiNoteNumber); -} - -// receives input as MIDI 0 to 16383, with 8192 being center -// this is also exactly the input fluidsynth requires -void SoundfontSynthVoice::pitchWheelMoved (int newValue) { -// fluid_synth_pitch_bend(synth, 0, newValue); -// int ppitch_bend; -// fluid_synth_get_pitch_bend(synth, 0, &ppitch_bend); -// int ppitch_bend_sens; -// fluid_synth_get_pitch_wheel_sens(synth, 0, &ppitch_bend_sens); -// Logger::outputDebugString ( juce::String::formatted("Pitch wheel: %d %d %d\n", newValue, ppitch_bend, ppitch_bend_sens) ); -} - -void SoundfontSynthVoice::controllerMoved (int controllerNumber, int newValue) { - // this seems to be "program change" event - DEBUG_PRINT ( juce::String::formatted("Controller moved: %d, %d\n", controllerNumber, newValue) ); -} - -void SoundfontSynthVoice::renderNextBlock (AudioBuffer& outputBuffer, int startSample, int numSamples) { - //fluid_synth_process(synth.get(), numSamples, 1, nullptr, outputBuffer.getNumChannels(), outputBuffer.getArrayOfWritePointers()); -} - -//void SoundfontSynthVoice::renderBlock (AudioBuffer& outputBuffer, int startSample, int numSamples) { -// fluid_synth_process(synth.get(), numSamples, 1, nullptr, outputBuffer.getNumChannels(), outputBuffer.getArrayOfWritePointers()); -// if (angleDelta == 0.0) { -// return; -// } -// while (--numSamples >= 0) { -// double qualifiedTailOff = tailOff > 0 ? tailOff : 1.0; -// auto currentSample = static_cast (std::sin (currentAngle) * level * qualifiedTailOff); -// for (int i = outputBuffer.getNumChannels(); --i >= 0;) -// outputBuffer.addSample (i, startSample, currentSample); -// -// currentAngle += angleDelta; -// ++startSample; -// -// if (tailOff > 0) { -// tailOff *= 0.99; -// -// if (tailOff <= 0.005) { -// clearCurrentNote(); -// angleDelta = 0.0; -// break; -// } -// } -// } -//} diff --git a/Source/SoundfontSynthVoice.h b/Source/SoundfontSynthVoice.h deleted file mode 100644 index 291ac3d..0000000 --- a/Source/SoundfontSynthVoice.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// Created by Alex Birch on 07/09/2017. -// - -#pragma once - -#include -#include -#include "../JuceLibraryCode/JuceHeader.h" - -using namespace std; - -class SoundfontSynthVoice : public SynthesiserVoice { -public: - SoundfontSynthVoice(shared_ptr synth); - - bool canPlaySound (SynthesiserSound* sound) override; - void startNote ( - int midiNoteNumber, - float velocity, - SynthesiserSound* /*sound*/, - int /*currentPitchWheelPosition*/) override; - - void stopNote (float /*velocity*/, bool allowTailOff) override; - void pitchWheelMoved (int /*newValue*/) override; - - void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override; - - void renderNextBlock (AudioBuffer& outputBuffer, int startSample, int numSamples) override; - -private: - double tailOff; - double level; - double currentAngle; - double angleDelta; - int midiNoteNumber; - - shared_ptr synth; -}; From cdf6f890196954184d8ac69bc01c757050a69f24 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 28 Jul 2019 23:02:22 +0100 Subject: [PATCH 27/33] switch to unique_ptr for fluidsynth --- Source/FluidSynthModel.cpp | 383 ++++--------------------------------- Source/FluidSynthModel.h | 7 +- 2 files changed, 41 insertions(+), 349 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index f7092a8..d902d02 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -12,20 +12,13 @@ using namespace std; FluidSynthModel::FluidSynthModel( AudioProcessorValueTreeState& valueTreeState - // ValueTree& valueTree - // SharesParams& sharedParams ) : valueTreeState{valueTreeState} -// , valueTree{valueTree} -// , sharedParams{sharedParams} -//, synth{nullptr} , settings{nullptr, nullptr} -//, currentSoundFontAbsPath{} +, synth{nullptr, nullptr} , currentSampleRate{44100} -//, initialised{false} , sfont_id{-1} -, channel{0}/*, -mod(nullptr)*/ +, channel{0} { valueTreeState.addParameterListener("bank", this); valueTreeState.addParameterListener("preset", this); @@ -36,28 +29,9 @@ FluidSynthModel::~FluidSynthModel() { valueTreeState.removeParameterListener("bank", this); valueTreeState.removeParameterListener("preset", this); valueTreeState.state.removeListener(this); - // if (initialised) { -// delete_fluid_audio_driver(driver); - // delete_fluid_synth(synth); -// delete_fluid_settings(settings); -// delete driver; -// delete settings; -// delete_fluid_mod(mod); - // } - } - -int FluidSynthModel::handleMidiEvent(void* data, fluid_midi_event_t* event) -{ -// DEBUG_PRINT(fluid_midi_event_get_type(event)); - // printf("event type: %d\n", fluid_midi_event_get_type(event)); - return 0; } void FluidSynthModel::initialise() { -// if (initialised) { -// delete_fluid_synth(synth); -// delete_fluid_settings(settings); -// } settings = { new_fluid_settings(), delete_fluid_settings }; // deactivate all audio drivers in fluidsynth to avoid FL Studio deadlock when initialising CoreAudio @@ -65,20 +39,6 @@ void FluidSynthModel::initialise() { const char *DRV[] {NULL}; fluid_audio_driver_register(DRV); -// handle_midi_event_func_t handler = [](void* data, fluid_midi_event_t* event) -> int { -// -// }; - -// midiDriver = unique_ptr( -// new_fluid_midi_driver( -// settings.get(), -// [](void* data, fluid_midi_event_t* event) -> int { -// -// }, -// nullptr), -// delete_fluid_midi_driver); - - // https://sourceforge.net/p/fluidsynth/wiki/FluidSettings/ #if JUCE_DEBUG fluid_settings_setint(settings.get(), "synth.verbose", 1); @@ -93,10 +53,6 @@ void FluidSynthModel::initialise() { // AudioParameterInt* castParam {dynamic_cast (param)}; // *castParam = m.getControllerValue(); -// if (sharedParams.getSoundFontPath().isNotEmpty()) { -// loadFont(sharedParams.getSoundFontPath()); -// changePreset(sharedParams->getBank(), sharedParams->getPreset()); -// } ValueTree soundFont{valueTreeState.state.getChildWithName("soundFont")}; String path{soundFont.getProperty("path", "")}; loadFont(path); @@ -202,20 +158,10 @@ void FluidSynthModel::initialise() { // clamps the range to between 0 and 1000, so we'll copy that fluid_mod_set_amount(mod.get(), 1000.0f); fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); - -// valueTreeState.state.getChildWithName("soundFont").sendPropertyChangeMessage("path"); - // valueTree.sendPropertyChangeMessage("soundFontPath"); - - // initialised = true; } void FluidSynthModel::parameterChanged(const String& parameterID, float newValue) { if (parameterID == "bank") { - // RangedAudioParameter *param {valueTreeState.getParameter("bank")}; - // jassert(dynamic_cast (param) != nullptr); - // AudioParameterInt* castParam {dynamic_cast (param)}; - // int value{castParam->get()}; - int bank, preset; { RangedAudioParameter *param {valueTreeState.getParameter("bank")}; @@ -236,15 +182,6 @@ void FluidSynthModel::parameterChanged(const String& parameterID, float newValue sfont_id, static_cast(bankOffset + bank), static_cast(preset)); - - // fluid_synth_bank_select(synth.get(), channel, value); -// refreshPresets(); - // fluid_sfont_t* sfont{fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; - // fluid_sfont_iteration_start(sfont); - // fluid_preset_t* presetObj{fluid_sfont_iteration_next(sfont)}; - // int offset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; - // int bank{fluid_preset_get_banknum(presetObj) + offset}; - // int preset{fluid_preset_get_num(presetObj)}; } else if (parameterID == "preset") { int bank, preset; { @@ -272,7 +209,6 @@ void FluidSynthModel::parameterChanged(const String& parameterID, float newValue void FluidSynthModel::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { if (treeWhosePropertyHasChanged.getType() == StringRef("soundFont")) { - // if (&treeWhosePropertyHasChanged == &valueTree) { if (property == StringRef("path")) { String soundFontPath{treeWhosePropertyHasChanged.getProperty("path", "")}; if (soundFontPath.isNotEmpty()) { @@ -283,117 +219,17 @@ void FluidSynthModel::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasCh } void FluidSynthModel::setControllerValue(int controller, int value) { - fluid_midi_event_t *midi_event(new_fluid_midi_event()); - fluid_midi_event_set_type(midi_event, static_cast(CONTROL_CHANGE)); - fluid_midi_event_set_channel(midi_event, channel); - fluid_midi_event_set_control(midi_event, controller); - fluid_midi_event_set_value(midi_event, value); - fluid_synth_handle_midi_event(synth.get(), midi_event); - delete_fluid_midi_event(midi_event); - // fluid_channel_set_cc(channel, i, 0); + fluid_synth_cc( + synth.get(), + channel, + controller, + value); } int FluidSynthModel::getChannel() { return channel; } -//void FluidSynthModel::changePreset(int bank, int preset) { -// if (bank == -1 || preset == -1) { -// unique_ptr bankAndPreset = getFirstBankAndPreset(); -// bank = bankAndPreset->getBank(); -// preset = bankAndPreset->getPreset(); -// } -// changePresetImpl(bank, preset); -//// sharedParams->setPreset(preset); -//// sharedParams->setBank(bank); -//} - -//void FluidSynthModel::changePresetImpl(int bank, int preset) { -// fluid_synth_program_select(synth.get(), channel, sfont_id, static_cast(bank), static_cast(preset)); -//} - -//fluid_preset_t* FluidSynthModel::getFirstPreset() { -// fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth.get(), sfont_id); -// -// jassert(sfont != nullptr); -// fluid_sfont_iteration_start(sfont); -// -// return fluid_sfont_iteration_next(sfont); -//} -// -//unique_ptr FluidSynthModel::getFirstBankAndPreset() { -// fluid_preset_t* preset = getFirstPreset(); -// -// int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); -// -// return make_unique(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset)); -//}; -// -//void FluidSynthModel::selectFirstPreset() { -// fluid_preset_t* preset = getFirstPreset(); -// -// int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); -// -// changePreset(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset)); -//} - -//BanksToPresets FluidSynthModel::getBanks() { -// BanksToPresets banksToPresets; -// -// int soundfontCount = fluid_synth_sfcount(synth.get()); -// -// if (soundfontCount == 0) { -// // no soundfont selected -// return banksToPresets; -// } -// -// fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth.get(), sfont_id); -// if(sfont == nullptr) { -// // no soundfont found by that ID -// // the above guard (soundfontCount) protects us for the -// // main case we're expecting. this guard is just defensive programming. -// return banksToPresets; -// } -// -// int offset = fluid_synth_get_bank_offset(synth.get(), sfont_id); -// -// fluid_sfont_iteration_start(sfont); -// -// for(fluid_preset_t* preset = fluid_sfont_iteration_next(sfont); -// preset != nullptr; -// preset = fluid_sfont_iteration_next(sfont)) { -// banksToPresets.insert(BanksToPresets::value_type( -// fluid_preset_get_banknum(preset) + offset, -// *new Preset( -// fluid_preset_get_num(preset), -// fluid_preset_get_name(preset) -// ) -// )); -// } -// -// return banksToPresets; -//} - -shared_ptr FluidSynthModel::getSynth() { - // https://msdn.microsoft.com/en-us/library/hh279669.aspx - // You can pass a shared_ptr to another function in the following ways: - // Pass the shared_ptr by value. This invokes the copy constructor, increments the reference count, and makes the callee an owner. - return synth; -} - -//void FluidSynthModel::onFileNameChanged(const String &absPath, int bank, int preset) { -// if (!shouldLoadFont(absPath)) { -// return; -// } -// unloadAndLoadFont(absPath); -// changePreset(bank, preset); -// ValueTree valueTree{valueTreeState.state.getChildWithName("soundFont")}; -// valueTree.setPropertyExcludingListener(this, "path", absPath, nullptr); -// // valueTree.setPropertyExcludingListener(this, "soundFontPath", absPath, nullptr); -//// sharedParams.setSoundFontPath(absPath); -// // eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath); -//} - void FluidSynthModel::unloadAndLoadFont(const String &absPath) { // in the base case, there is no font loaded if (fluid_synth_sfcount(synth.get()) > 0) { @@ -406,9 +242,6 @@ void FluidSynthModel::unloadAndLoadFont(const String &absPath) { } void FluidSynthModel::loadFont(const String &absPath) { -// currentSoundFontAbsPath = absPath; -// sfont_id++; -// fluid_synth_sfunload(synth.get(), sfont_id, 1); if (!absPath.isEmpty()) { sfont_id = fluid_synth_sfload(synth.get(), absPath.toStdString().c_str(), 1); // if -1 is returned, that indicates failure @@ -460,146 +293,6 @@ void FluidSynthModel::refreshBanks() { #endif } -// void FluidSynthModel::refreshBanks() { -// fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; -// ValueTree banks{"banks"}; -// if (sfont != nullptr) { -// // int initialBankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; -// banks.appendChild({ "bank", { -// { "num", /* initialBankOffset */ 0 }, -// }, {} }, nullptr); -// // int greatestPersistedBank{initialBankOffset}; -// int greatestPersistedBank{0}; - -// fluid_sfont_iteration_start(sfont); - -// for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; -// preset != nullptr; -// preset = fluid_sfont_iteration_next(sfont)) { -// int bank{fluid_preset_get_banknum(preset) /* + initialBankOffset */}; -// if (bank > greatestPersistedBank) { -// banks.appendChild({ "bank", { -// { "num", bank }, -// }, {} }, nullptr); -// greatestPersistedBank = bank; -// } -// } -// } -// valueTreeState.state.getChildWithName("banks").copyPropertiesAndChildrenFrom(banks, nullptr); -// valueTreeState.state.getChildWithName("banks").sendPropertyChangeMessage("synthetic"); -// refreshPresets(); -// } - -// void FluidSynthModel::refreshPresets() { -// fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; -// ValueTree presets{"presets"}; -// if (sfont != nullptr) { -// RangedAudioParameter *param {valueTreeState.getParameter("bank")}; -// jassert(dynamic_cast (param) != nullptr); -// AudioParameterInt* castParam {dynamic_cast (param)}; -// int value{castParam->get()}; - -// // int initialBank{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; - -// fluid_sfont_iteration_start(sfont); - -// for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; -// preset != nullptr; -// preset = fluid_sfont_iteration_next(sfont)) { -// int bank{fluid_preset_get_banknum(preset) /* + initialBank */}; -// if (bank == value) { -// presets.appendChild({ "preset", { -// { "num", fluid_preset_get_num(preset) }, -// { "name", String{fluid_preset_get_name(preset)} } -// }, {} }, nullptr); -// } -// } -// } -// valueTreeState.state.getChildWithName("presets").copyPropertiesAndChildrenFrom(presets, nullptr); -// valueTreeState.state.getChildWithName("presets").sendPropertyChangeMessage("synthetic"); -// } - -// void FluidSynthModel::refreshBanksAndPresets() { -// fluid_sfont_t* sfont {fluid_synth_get_sfont_by_id(synth.get(), sfont_id)}; -// ValueTree banks{"banks"}; -// ValueTree presets{"presets"}; -// if (sfont != nullptr) { -// int initialBankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; -// banks.appendChild({ "bank", { -// { "num", initialBankOffset }, -// }, {} }, nullptr); -// int greatestPersistedBank{initialBankOffset}; - -// fluid_sfont_iteration_start(sfont); - -// for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)}; -// preset != nullptr; -// preset = fluid_sfont_iteration_next(sfont)) { -// int bankOffset{fluid_preset_get_banknum(preset) + initialBankOffset}; -// // ValueTree preset{"preset"}; -// // banksToPresets.insert(BanksToPresets::value_type( -// // fluid_preset_get_banknum(preset) + bankOffset, -// // *new Preset( -// // fluid_preset_get_num(preset), -// // fluid_preset_get_name(preset) -// // ) -// // )); -// if (bankOffset > greatestPersistedBank) { -// banks.appendChild({ "bank", { -// { "num", bankOffset }, -// }, {} }, nullptr); -// greatestPersistedBank = bankOffset; -// } -// presets.appendChild({ "preset", { -// { "num", fluid_preset_get_num(preset) }, -// { "name", String{fluid_preset_get_name(preset)} } -// }, {} }, nullptr); -// } -// } -// // valueTreeState.state.getChildWithName("banks") = banks; -// // valueTreeState.state.getChildWithName("presets") = presets; -// valueTreeState.state.getChildWithName("banks").copyPropertiesAndChildrenFrom(banks, nullptr); -// valueTreeState.state.getChildWithName("presets").copyPropertiesAndChildrenFrom(presets, nullptr); -// valueTreeState.state.getChildWithName("banks").sendPropertyChangeMessage("synthetic"); -// valueTreeState.state.getChildWithName("presets").sendPropertyChangeMessage("synthetic"); - -// #if JUCE_DEBUG -// unique_ptr xml{valueTreeState.state.createXml()}; -// Logger::outputDebugString(xml->createDocument("",false,false)); -// #endif -// } - -// FluidSynthModel::Listener::~Listener() { -// } - -//bool FluidSynthModel::shouldLoadFont(const String &absPath) { -// if (absPath.isEmpty()) { -// return false; -// } -//// if (absPath == currentSoundFontAbsPath) { -//// return false; -//// } -// return true; -//} - -// void FluidSynthModel::Listener::fontChanged(FluidSynthModel * model, const String &absPath) { -// } - -//const String& FluidSynthModel::getCurrentSoundFontAbsPath() { -// return currentSoundFontAbsPath; -//} - -//============================================================================== -// void FluidSynthModel::addListener (FluidSynthModel::Listener* const newListener) -// { -// eventListeners.add(newListener); -// } - -// void FluidSynthModel::removeListener (FluidSynthModel::Listener* const listener) -// { -// eventListeners.remove(listener); -// } - void FluidSynthModel::setSampleRate(float sampleRate) { currentSampleRate = sampleRate; // https://stackoverflow.com/a/40856043/5257399 @@ -765,35 +458,39 @@ void FluidSynthModel::setCurrentProgram(int index) const String FluidSynthModel::getProgramName(int index) { - fluid_sfont_t* sfont{ - sfont_id == -1 - ? nullptr - : fluid_synth_get_sfont_by_id(synth.get(), sfont_id) - }; - if (!sfont) { - return {}; - } - int bank, presetNum; - { - RangedAudioParameter *param {valueTreeState.getParameter("bank")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - bank = castParam->get(); - } - { - RangedAudioParameter *param {valueTreeState.getParameter("preset")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - presetNum = castParam->get(); - } - fluid_preset_t *preset{fluid_sfont_get_preset( - sfont, - bank, - presetNum)}; - if (!preset) { - return {}; - } - return {fluid_preset_get_name(preset)}; + // fluid_sfont_t* sfont{ + // sfont_id == -1 + // ? nullptr + // : fluid_synth_get_sfont_by_id(synth.get(), sfont_id) + // }; + // if (!sfont) { + // return {}; + // } + // int bank, presetNum; + // { + // RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + // jassert(dynamic_cast (param) != nullptr); + // AudioParameterInt* castParam {dynamic_cast (param)}; + // bank = castParam->get(); + // } + // { + // RangedAudioParameter *param {valueTreeState.getParameter("preset")}; + // jassert(dynamic_cast (param) != nullptr); + // AudioParameterInt* castParam {dynamic_cast (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. + String presetName{"Preset "}; + return presetName << index; } void FluidSynthModel::changeProgramName(int index, const String& newName) diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index 30440b0..4e844dd 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -56,7 +56,6 @@ class FluidSynthModel void changeProgramName(int index, const String& newName); private: - int handleMidiEvent(void* data, fluid_midi_event_t* event); void refreshBanks(); AudioProcessorValueTreeState& valueTreeState; @@ -66,9 +65,7 @@ class FluidSynthModel // http://www.fluidsynth.org/api/ // in their examples, they destroy the synth before destroying the settings unique_ptr settings; - // TODO: shared_ptr may ruin our guarantee of synth's being destroyed first, so consider changing the access we expose - shared_ptr synth; - // unique_ptr midiDriver; + unique_ptr synth; float currentSampleRate; @@ -78,8 +75,6 @@ class FluidSynthModel void unloadAndLoadFont(const String &absPath); void loadFont(const String &absPath); - - void changePresetImpl(int bank, int preset); int sfont_id; unsigned int channel; From 84caf499b0867ac5727ab8773a4289e071fa7f92 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 28 Jul 2019 23:17:19 +0100 Subject: [PATCH 28/33] remove unused interfaces and data classes --- .../juicysfplugin.xcodeproj/project.pbxproj | 28 ---- Source/BankAndPreset.cpp | 19 --- Source/BankAndPreset.h | 23 --- Source/ExposesComponents.h | 18 -- Source/FilePicker.h | 4 +- Source/FilePickerFragment.h | 14 -- Source/FluidSynthModel.h | 7 - Source/Params.cpp | 152 ----------------- Source/Params.h | 59 ------- Source/Pills.h | 24 +-- Source/PluginEditor.cpp | 149 ----------------- Source/PluginEditor.h | 31 ---- Source/PluginProcessor.cpp | 3 - Source/PluginProcessor.h | 3 - Source/Preset.cpp | 21 --- Source/Preset.h | 24 --- Source/PresetsToBanks.h | 10 -- Source/SharesParams.h | 49 ------ Source/SlidersComponent.cpp | 24 +-- Source/SlidersComponent.h | 17 +- Source/SlidersFragment.h | 18 -- Source/TableComponent.h | 13 -- Source/TablesComponent.cpp | 156 ------------------ Source/TablesComponent.h | 24 +-- 24 files changed, 6 insertions(+), 884 deletions(-) delete mode 100644 Source/BankAndPreset.cpp delete mode 100644 Source/BankAndPreset.h delete mode 100644 Source/ExposesComponents.h delete mode 100644 Source/FilePickerFragment.h delete mode 100644 Source/Params.cpp delete mode 100644 Source/Params.h delete mode 100644 Source/Preset.cpp delete mode 100644 Source/Preset.h delete mode 100644 Source/PresetsToBanks.h delete mode 100644 Source/SharesParams.h delete mode 100644 Source/SlidersFragment.h diff --git a/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj b/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj index 1dc4892..140f999 100644 --- a/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/juicysfplugin.xcodeproj/project.pbxproj @@ -33,7 +33,6 @@ 21AC354419419A4D80ADE43A /* include_juce_audio_plugin_client_AU_2.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7E47C0A828016F7D0D63C0D6 /* include_juce_audio_plugin_client_AU_2.mm */; }; 2918F46AFD2AB89F9FA847DC /* include_juce_events.mm in Sources */ = {isa = PBXBuildFile; fileRef = 373EF982A53046CE00BECE68 /* include_juce_events.mm */; }; 2E77C6FAF1BCDB9EB29D20B9 /* PluginProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53CAB963D5051C786D3A52D /* PluginProcessor.cpp */; }; - 35099D9322CAA87D00CD4523 /* Params.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 35099D9122CAA87D00CD4523 /* Params.cpp */; }; 358E458C22BEE5090087ED8D /* RecentFilesMenuTemplate.nib in Resources */ = {isa = PBXBuildFile; fileRef = 78CC5234CCFE3B170585DDAD /* RecentFilesMenuTemplate.nib */; }; 358E458D22BEE5090087ED8D /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1616112041466F7324D7E19 /* Accelerate.framework */; }; 358E458E22BEE5090087ED8D /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CA077CDD21D0FEC66FC290 /* AudioToolbox.framework */; }; @@ -123,7 +122,6 @@ 8502F736BECFB9CB752AC72F /* Pills.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2C62C3F0621604CDB65B55A6 /* Pills.cpp */; }; 85E6C3826F86B1258C407725 /* FluidSynthModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DA25104C95E55D1822BFFBE2 /* FluidSynthModel.cpp */; }; 909EB835CB55BF0B86B4BD93 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26949DA45B5FE0F3A0355733 /* CoreMIDI.framework */; }; - 9AF2F3DE22C71A7F465B2EAD /* BankAndPreset.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DECFA95359BC1DDDD1CC86C3 /* BankAndPreset.cpp */; }; 9C107CE4B586E4B097D9D04E /* SurjectiveMidiKeyboardComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4119A8200AC54674C00EFE66 /* SurjectiveMidiKeyboardComponent.cpp */; }; 9C2580F953071AD611EB6166 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CA077CDD21D0FEC66FC290 /* AudioToolbox.framework */; }; AC5E4EF988D864A298E3650D /* TablesComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0DD5458189C039F5A4FAD62D /* TablesComponent.cpp */; }; @@ -139,7 +137,6 @@ CB8F898ACB35575C1695E223 /* include_juce_gui_basics.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5276945E14F83CA02C05B41 /* include_juce_gui_basics.mm */; }; DB7F85571650636DB9ECE092 /* include_juce_audio_plugin_client_AU.r in Rez */ = {isa = PBXBuildFile; fileRef = 5704CA923F677280C02D97C6 /* include_juce_audio_plugin_client_AU.r */; }; DDF28AD28F639A561292FE28 /* include_juce_core.mm in Sources */ = {isa = PBXBuildFile; fileRef = F69B741A63932433977CFCD8 /* include_juce_core.mm */; }; - DF84F5E7E386AF7A38854939 /* Preset.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 59F9FEC807012C10B8A1FA07 /* Preset.cpp */; }; E08B3A2AF85F9FCF991F1CA2 /* include_juce_audio_basics.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13E201F5B25AC078DB396A9C /* include_juce_audio_basics.mm */; }; FDAB0F06D8758FF0407BB851 /* include_juce_data_structures.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2C66D01D1DD9006E77E2E260 /* include_juce_data_structures.mm */; }; FE0869D2DF902682B6E4C925 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C481A535CF8A4CBC3E594003 /* WebKit.framework */; }; @@ -284,11 +281,7 @@ 2C66D01D1DD9006E77E2E260 /* include_juce_data_structures.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_data_structures.mm; path = ../../JuceLibraryCode/include_juce_data_structures.mm; sourceTree = SOURCE_ROOT; }; 307CB49DF900DE4A612FF98E /* FluidSynthModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FluidSynthModel.h; path = ../../Source/FluidSynthModel.h; sourceTree = SOURCE_ROOT; }; 35099D9022CA8EF500CD4523 /* Util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Util.h; path = ../../Source/Util.h; sourceTree = ""; }; - 35099D9122CAA87D00CD4523 /* Params.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Params.cpp; path = ../../Source/Params.cpp; sourceTree = ""; }; - 35099D9222CAA87D00CD4523 /* Params.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Params.h; path = ../../Source/Params.h; sourceTree = ""; }; 35099D9422CAB0A400CD4523 /* GuiConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GuiConstants.h; path = ../../Source/GuiConstants.h; sourceTree = ""; }; - 35099D9522CAB7CD00CD4523 /* SlidersFragment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SlidersFragment.h; path = ../../Source/SlidersFragment.h; sourceTree = ""; }; - 35099D9622CAC3C800CD4523 /* SharesParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharesParams.h; path = ../../Source/SharesParams.h; sourceTree = ""; }; 35880F58CB540AD30D1B0ED3 /* TablesComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TablesComponent.h; path = ../../Source/TablesComponent.h; sourceTree = SOURCE_ROOT; }; 358E45B422BEE53A0087ED8D /* libpcre.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libpcre.1.dylib; sourceTree = ""; }; 358E45B522BEE53A0087ED8D /* libvorbisenc.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libvorbisenc.2.dylib; sourceTree = ""; }; @@ -316,12 +309,10 @@ 5704CA923F677280C02D97C6 /* include_juce_audio_plugin_client_AU.r */ = {isa = PBXFileReference; lastKnownFileType = file.r; name = include_juce_audio_plugin_client_AU.r; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_AU.r; sourceTree = SOURCE_ROOT; }; 576A01FC6A3620A39BD1BDEE /* MyColours.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MyColours.h; path = ../../Source/MyColours.h; sourceTree = SOURCE_ROOT; }; 5896415135C635B1EB2DC202 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; - 59F9FEC807012C10B8A1FA07 /* Preset.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Preset.cpp; path = ../../Source/Preset.cpp; sourceTree = SOURCE_ROOT; }; 5A57BEB8628C7AE62ED1039F /* include_juce_audio_plugin_client_AU_1.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_plugin_client_AU_1.mm; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_AU_1.mm; sourceTree = SOURCE_ROOT; }; 60ADEC8B20DC559737F84180 /* juce_gui_basics */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_gui_basics; path = /Applications/JUCE/modules/juce_gui_basics; sourceTree = ""; }; 663ACFA11DCEC0D411B8497E /* juicysfplugin.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = juicysfplugin.entitlements; sourceTree = SOURCE_ROOT; }; 6714B050717A7500EE7AE867 /* include_juce_audio_plugin_client_VST2.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = include_juce_audio_plugin_client_VST2.cpp; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_VST2.cpp; sourceTree = SOURCE_ROOT; }; - 69DB3A0FB3D21F87D1E4B0C1 /* PresetsToBanks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PresetsToBanks.h; path = ../../Source/PresetsToBanks.h; sourceTree = SOURCE_ROOT; }; 6A7F287E4159FA5167131D2B /* juce_audio_processors */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_processors; path = /Applications/JUCE/modules/juce_audio_processors; sourceTree = ""; }; 6C5DCE19B6DC0EF5BA12F99C /* juicysfplugin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = juicysfplugin.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6D94DCB335360BDC7B3673BF /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; @@ -333,7 +324,6 @@ 7C699A8B65F3F9FB5004F22D /* juce_gui_extra */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_gui_extra; path = /Applications/JUCE/modules/juce_gui_extra; sourceTree = ""; }; 7D2457AD994644752178FC82 /* include_juce_audio_plugin_client_VST_utils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_plugin_client_VST_utils.mm; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_VST_utils.mm; sourceTree = SOURCE_ROOT; }; 7E47C0A828016F7D0D63C0D6 /* include_juce_audio_plugin_client_AU_2.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_plugin_client_AU_2.mm; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_AU_2.mm; sourceTree = SOURCE_ROOT; }; - 88ADEBF51BD04FEA9422D276 /* FilePickerFragment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FilePickerFragment.h; path = ../../Source/FilePickerFragment.h; sourceTree = SOURCE_ROOT; }; 8990F3EAFFBBD6A42247C663 /* PluginEditor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PluginEditor.h; path = ../../Source/PluginEditor.h; sourceTree = SOURCE_ROOT; }; 910F2E433646EE260D61A91B /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; 91B7A726C6FDDEE3F364ED99 /* juce_events */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_events; path = /Applications/JUCE/modules/juce_events; sourceTree = ""; }; @@ -342,8 +332,6 @@ A67D09546C4E4831438F7DBD /* CoreAudioKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = System/Library/Frameworks/CoreAudioKit.framework; sourceTree = SDKROOT; }; A6BC2528C1717DDC2B66215E /* include_juce_graphics.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_graphics.mm; path = ../../JuceLibraryCode/include_juce_graphics.mm; sourceTree = SOURCE_ROOT; }; ADC93C26314F163B963461E2 /* include_juce_audio_utils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_utils.mm; path = ../../JuceLibraryCode/include_juce_audio_utils.mm; sourceTree = SOURCE_ROOT; }; - AE397302E7E3F3A14A0C5F3C /* Preset.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Preset.h; path = ../../Source/Preset.h; sourceTree = SOURCE_ROOT; }; - B000E7A360C0C86ADD3C911D /* BankAndPreset.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BankAndPreset.h; path = ../../Source/BankAndPreset.h; sourceTree = SOURCE_ROOT; }; B6D37AD919F9E83688578941 /* SurjectiveMidiKeyboardComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SurjectiveMidiKeyboardComponent.h; path = ../../Source/SurjectiveMidiKeyboardComponent.h; sourceTree = SOURCE_ROOT; }; BFB39134DE6876F9005CFA61 /* Pills.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Pills.h; path = ../../Source/Pills.h; sourceTree = SOURCE_ROOT; }; BFF57868318157F12F087F07 /* Info-AU.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-AU.plist"; sourceTree = SOURCE_ROOT; }; @@ -356,10 +344,8 @@ D11295BAED9825695A4DEAB8 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; D53CAB963D5051C786D3A52D /* PluginProcessor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PluginProcessor.cpp; path = ../../Source/PluginProcessor.cpp; sourceTree = SOURCE_ROOT; }; DA25104C95E55D1822BFFBE2 /* FluidSynthModel.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FluidSynthModel.cpp; path = ../../Source/FluidSynthModel.cpp; sourceTree = SOURCE_ROOT; }; - DECFA95359BC1DDDD1CC86C3 /* BankAndPreset.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = BankAndPreset.cpp; path = ../../Source/BankAndPreset.cpp; sourceTree = SOURCE_ROOT; }; E4F84AFD6C449D10FDB5DB14 /* include_juce_audio_plugin_client_VST3.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = include_juce_audio_plugin_client_VST3.cpp; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_VST3.cpp; sourceTree = SOURCE_ROOT; }; E89ECA468FF133B4677F8327 /* juicysfplugin.vst */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = juicysfplugin.vst; sourceTree = BUILT_PRODUCTS_DIR; }; - F1EB35E262DC717222E2F93D /* ExposesComponents.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ExposesComponents.h; path = ../../Source/ExposesComponents.h; sourceTree = SOURCE_ROOT; }; F5276945E14F83CA02C05B41 /* include_juce_gui_basics.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_gui_basics.mm; path = ../../JuceLibraryCode/include_juce_gui_basics.mm; sourceTree = SOURCE_ROOT; }; F69B741A63932433977CFCD8 /* include_juce_core.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_core.mm; path = ../../JuceLibraryCode/include_juce_core.mm; sourceTree = SOURCE_ROOT; }; FE960C7D2CFA204401860C13 /* juce_audio_formats */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_formats; path = /Applications/JUCE/modules/juce_audio_formats; sourceTree = ""; }; @@ -521,11 +507,6 @@ 403EB0CF49CF1D62BF359002 /* Source */ = { isa = PBXGroup; children = ( - 35099D9622CAC3C800CD4523 /* SharesParams.h */, - DECFA95359BC1DDDD1CC86C3 /* BankAndPreset.cpp */, - B000E7A360C0C86ADD3C911D /* BankAndPreset.h */, - F1EB35E262DC717222E2F93D /* ExposesComponents.h */, - 88ADEBF51BD04FEA9422D276 /* FilePickerFragment.h */, C13A2FEAA636713EC7A905AF /* FilePicker.cpp */, 21828DE4341668D7E383F10A /* FilePicker.h */, DA25104C95E55D1822BFFBE2 /* FluidSynthModel.cpp */, @@ -534,9 +515,6 @@ 576A01FC6A3620A39BD1BDEE /* MyColours.h */, 2C62C3F0621604CDB65B55A6 /* Pills.cpp */, BFB39134DE6876F9005CFA61 /* Pills.h */, - 59F9FEC807012C10B8A1FA07 /* Preset.cpp */, - AE397302E7E3F3A14A0C5F3C /* Preset.h */, - 69DB3A0FB3D21F87D1E4B0C1 /* PresetsToBanks.h */, 4119A8200AC54674C00EFE66 /* SurjectiveMidiKeyboardComponent.cpp */, B6D37AD919F9E83688578941 /* SurjectiveMidiKeyboardComponent.h */, CE8C41308A31A71A1177D0D5 /* TableComponent.cpp */, @@ -551,10 +529,7 @@ 358E45F922C80DCA0087ED8D /* SlidersComponent.cpp */, 358E45FA22C80DCA0087ED8D /* SlidersComponent.h */, 35099D9022CA8EF500CD4523 /* Util.h */, - 35099D9122CAA87D00CD4523 /* Params.cpp */, - 35099D9222CAA87D00CD4523 /* Params.h */, 35099D9422CAB0A400CD4523 /* GuiConstants.h */, - 35099D9522CAB7CD00CD4523 /* SlidersFragment.h */, ); name = Source; sourceTree = ""; @@ -937,12 +912,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9AF2F3DE22C71A7F465B2EAD /* BankAndPreset.cpp in Sources */, 598516649859A6D6BB2856EF /* FilePicker.cpp in Sources */, 85E6C3826F86B1258C407725 /* FluidSynthModel.cpp in Sources */, BFD9EF2D67067FC1E5BA3546 /* MyColours.cpp in Sources */, 8502F736BECFB9CB752AC72F /* Pills.cpp in Sources */, - DF84F5E7E386AF7A38854939 /* Preset.cpp in Sources */, 9C107CE4B586E4B097D9D04E /* SurjectiveMidiKeyboardComponent.cpp in Sources */, 4AE057561AEA78489D9E50F0 /* TableComponent.cpp in Sources */, AC5E4EF988D864A298E3650D /* TablesComponent.cpp in Sources */, @@ -950,7 +923,6 @@ 7DF73014FFCCE46E228216DB /* PluginEditor.cpp in Sources */, E08B3A2AF85F9FCF991F1CA2 /* include_juce_audio_basics.mm in Sources */, C4D76C968347E2ACBAB5B6E7 /* include_juce_audio_devices.mm in Sources */, - 35099D9322CAA87D00CD4523 /* Params.cpp in Sources */, 51C9DBCA840E334DB1804133 /* include_juce_audio_formats.mm in Sources */, 358E45FB22C80DCA0087ED8D /* SlidersComponent.cpp in Sources */, 5E5B833BBDD65F0D4271CA52 /* include_juce_audio_plugin_client_utils.cpp in Sources */, diff --git a/Source/BankAndPreset.cpp b/Source/BankAndPreset.cpp deleted file mode 100644 index ebda5c4..0000000 --- a/Source/BankAndPreset.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// -// Created by Alex Birch on 13/04/2018. -// Copyright (c) 2018 Birchlabs. All rights reserved. -// - -#include "BankAndPreset.h" - -BankAndPreset::BankAndPreset(int bank, int preset) - : bank(bank), - preset(preset) -{} - -int BankAndPreset::getBank() { - return bank; -} - -int BankAndPreset::getPreset() { - return preset; -} \ No newline at end of file diff --git a/Source/BankAndPreset.h b/Source/BankAndPreset.h deleted file mode 100644 index 2898403..0000000 --- a/Source/BankAndPreset.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Created by Alex Birch on 13/04/2018. -// Copyright (c) 2018 Birchlabs. All rights reserved. -// - -#pragma once - -#include "../JuceLibraryCode/JuceHeader.h" - -class BankAndPreset { -public: - BankAndPreset(int bank, int preset); - - int getBank(); - int getPreset(); - -private: - int bank; - int preset; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BankAndPreset) -}; - diff --git a/Source/ExposesComponents.h b/Source/ExposesComponents.h deleted file mode 100644 index b0dda84..0000000 --- a/Source/ExposesComponents.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Created by Alex Birch on 11/04/2018. -// Copyright (c) 2018 Birchlabs. All rights reserved. -// - -#pragma once - -#include "FilePickerFragment.h" -#include "SlidersFragment.h" - -class ExposesComponents { -public: - virtual ~ExposesComponents() {} - - virtual FilePickerFragment& getFilePicker() = 0; - virtual SlidersFragment& getSliders() = 0; - -}; \ No newline at end of file diff --git a/Source/FilePicker.h b/Source/FilePicker.h index 5d34c8c..a8bf7c1 100644 --- a/Source/FilePicker.h +++ b/Source/FilePicker.h @@ -6,10 +6,8 @@ #include "../JuceLibraryCode/JuceHeader.h" #include "FluidSynthModel.h" -#include "FilePickerFragment.h" class FilePicker: public Component, - public FilePickerFragment, public ValueTree::Listener, private FilenameComponentListener { @@ -23,7 +21,7 @@ class FilePicker: public Component, void resized() override; void paint (Graphics& g) override; - virtual void setDisplayedFilePath(const String&) override; + void setDisplayedFilePath(const String&); virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, diff --git a/Source/FilePickerFragment.h b/Source/FilePickerFragment.h deleted file mode 100644 index 96968db..0000000 --- a/Source/FilePickerFragment.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// Created by Alex Birch on 11/04/2018. -// Copyright (c) 2018 Birchlabs. All rights reserved. -// - -#pragma once -#include "../JuceLibraryCode/JuceHeader.h" - -class FilePickerFragment { -public: - virtual ~FilePickerFragment() {} - - virtual void setDisplayedFilePath(const String&) = 0; -}; \ No newline at end of file diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index 4e844dd..f416a48 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -5,11 +5,8 @@ #pragma once #include "../JuceLibraryCode/JuceHeader.h" -#include "SharesParams.h" #include #include -#include "BankAndPreset.h" -#include "PresetsToBanks.h" using namespace std; @@ -69,10 +66,6 @@ class FluidSynthModel float currentSampleRate; - fluid_preset_t* getFirstPreset(); - void selectFirstPreset(); - unique_ptr getFirstBankAndPreset(); - void unloadAndLoadFont(const String &absPath); void loadFont(const String &absPath); diff --git a/Source/Params.cpp b/Source/Params.cpp deleted file mode 100644 index 4f6c9cc..0000000 --- a/Source/Params.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// -// Params.cpp -// juicysfplugin - Shared Code -// -// Created by Alex Birch on 01/07/2019. -// Copyright © 2019 Birchlabs. All rights reserved. -// - -#include "Params.h" -#include "../JuceLibraryCode/JuceHeader.h" -#include "MidiConstants.h" -#include "GuiConstants.h" -#include "SharesParams.h" - -using namespace std; - -Params::Params() noexcept -// : uiWidth{GuiConstants::minWidth} -// , uiHeight{GuiConstants::minHeight} -: soundFontPath{String()} -// , preset{-1} -// , bank{-1} -// , attack{0} -// , decay{0} -// , sustain{0} -// , release{0} -// , filterCutOff{0} -// .filterResonance{0} -{ -} - -void Params::setAttributesOnXml(shared_ptr xml) { - // xml->setAttribute("uiWidth", uiWidth); - // xml->setAttribute("uiHeight", uiHeight); - xml->setAttribute("soundFontPath", soundFontPath); - // xml.setAttribute("preset", preset); - // xml.setAttribute("bank", bank); - // xml.setAttribute("attack", attack); - // xml.setAttribute("decay", decay); - // xml.setAttribute("sustain", sustain); - // xml.setAttribute("release", release); - // xml.setAttribute("filterCutOff", filterCutOff); - // xml.setAttribute("filterResonance", filterResonance); -} - -void Params::loadAttributesFromXml(shared_ptr xmlState) { - // uiWidth = jmin(jmax(xmlState->getIntAttribute("uiWidth", uiWidth), GuiConstants::minWidth), GuiConstants::maxWidth); - // uiHeight = jmin(jmax(xmlState->getIntAttribute("uiHeight", uiHeight), GuiConstants::minHeight), GuiConstants::maxHeight); - soundFontPath = xmlState->getStringAttribute("soundFontPath", soundFontPath); - // preset = xmlState->getIntAttribute("preset", preset); - // bank = xmlState->getIntAttribute("bank", bank); - // attack = jmin(jmax(xmlState->getIntAttribute("attack", attack), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); - // decay = jmin(jmax(xmlState->getIntAttribute("decay", decay), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); - // sustain = jmin(jmax(xmlState->getIntAttribute("sustain", sustain), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); - // release = jmin(jmax(xmlState->getIntAttribute("release", release), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); - // filterCutOff = jmin(jmax(xmlState->getIntAttribute("filterCutOff", filterCutOff), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); - // filterResonance = jmin(jmax(xmlState->getIntAttribute("filterResonance", filterResonance), MidiConstants::midiMinValue), MidiConstants::midiMaxValue); -} - -//void Params::acceptMidiControlEvent(int controller, int value) { -// switch(static_cast(controller)) { -// case SOUND_CTRL2: // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) -// filterResonance = value; -// break; -// case SOUND_CTRL3: // MIDI CC 72 Release time -// release = value; -// break; -// case SOUND_CTRL4: // MIDI CC 73 Attack time -// attack = value; -// break; -// case SOUND_CTRL5: // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) -// filterCutOff = value; -// break; -// case SOUND_CTRL6: // MIDI CC 75 Decay Time -// decay = value; -// break; -// case SOUND_CTRL10: // MIDI CC 79 undefined -// sustain = value; -// break; -// default: -// break; -// } -//} - -void Params::setSoundFontPath(const String& value) { - soundFontPath = value; -} - -String& Params::getSoundFontPath() { - return soundFontPath; -} -//int Params::getPreset() { -// return preset; -//} -//int Params::getBank() { -// return bank; -//} -// int Params::getUiWidth() { -// return uiWidth; -// } -// int Params::getUiHeight() { -// return uiHeight; -// } -//int Params::getAttack() { -// return attack; -//} -//int Params::getDecay() { -// return decay; -//} -//int Params::getSustain() { -// return sustain; -//} -//int Params::getRelease() { -// return release; -//} -//int Params::getFilterCutOff() { -// return filterCutOff; -//} -//int Params::getFilterResonance() { -// return filterResonance; -//} - -//void Params::setPreset(int value) { -// preset = value; -//} -//void Params::setBank(int value) { -// bank = value; -//} -// void Params::setUiWidth(int value) { -// uiWidth = value; -// } -// void Params::setUiHeight(int value) { -// uiHeight = value; -// } -//void Params::setAttack(int value) { -// attack = value; -//} -//void Params::setDecay(int value) { -// decay = value; -//} -//void Params::setSustain(int value) { -// sustain = value; -//} -//void Params::setRelease(int value) { -// release = value; -//} -//void Params::setFilterCutOff(int value) { -// filterCutOff = value; -//} -//void Params::setFilterResonance(int value) { -// filterResonance = value; -//} diff --git a/Source/Params.h b/Source/Params.h deleted file mode 100644 index 678c16f..0000000 --- a/Source/Params.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "SharesParams.h" -#include "../JuceLibraryCode/JuceHeader.h" - -using namespace std; - -class Params: public SharesParams { -public: - Params() noexcept; - - virtual void setAttributesOnXml(shared_ptr xml) override; - virtual void loadAttributesFromXml(shared_ptr xmlState) override; -// virtual void acceptMidiControlEvent(int controller, int value) override; - - virtual void setSoundFontPath(const String& value) override; - virtual String& getSoundFontPath() override; - // virtual int getPreset() override; - // virtual void setPreset(int value) override; - // virtual int getBank() override; - // virtual void setBank(int value) override; - - // virtual int getUiWidth() override; - // virtual void setUiWidth(int value) override; - // virtual int getUiHeight() override; - // virtual void setUiHeight(int value) override; - - // virtual int getAttack() override; - // virtual void setAttack(int value) override; - // virtual int getDecay() override; - // virtual void setDecay(int value) override; - // virtual int getSustain() override; - // virtual void setSustain(int value) override; - // virtual int getRelease() override; - // virtual void setRelease(int value) override; - - // virtual int getFilterCutOff() override; - // virtual void setFilterCutOff(int value) override; - // virtual int getFilterResonance() override; - // virtual void setFilterResonance(int value) override; - -private: - // int uiWidth; - // int uiHeight; - - String soundFontPath; - // int preset; - // int bank; - - // int attack; - // int decay; - // int sustain; - // int release; - - // int filterCutOff; - // int filterResonance; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Params) -}; diff --git a/Source/Pills.h b/Source/Pills.h index 812c2a6..fae2e49 100644 --- a/Source/Pills.h +++ b/Source/Pills.h @@ -11,7 +11,6 @@ using namespace std; class Pill : public Component , public Button::Listener -// , public AudioProcessorValueTreeState::Listener { public: Pill( @@ -28,10 +27,7 @@ class Pill void paint(Graphics& g) override; void bankChanged(int bank); - - // virtual void parameterChanged (const String& parameterID, float newValue) override; private: - // void loadToggleState(); AudioProcessorValueTreeState& valueTreeState; int bank; @@ -48,20 +44,9 @@ class Pills public: Pills( AudioProcessorValueTreeState& valueTreeState - // string label - // const vector &items, - // const function &onItemSelected, - // const function &itemToIDMapper, - // int initiallySelectedItem ); ~Pills(); - - // void setItems( - // const vector &items, - // int initiallySelectedItem - // ); - - // void buttonClicked (Button* button) override; + void cycle(bool right); virtual void parameterChanged (const String& parameterID, float newValue) override; @@ -81,17 +66,10 @@ class Pills void loadModelFrom(ValueTree& banks); AudioProcessorValueTreeState& valueTreeState; - // string label; - // vector items; - // function onItemSelected; - // function itemToIDMapper; - // OwnedArray buttons; vector> pills; Button *selected; - // Pill* addToList (Pill* newButton); - void updatePillToggleStates(); void populate(int initiallySelectedItem); diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 2c7d3eb..76fa638 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -19,7 +19,6 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor( : AudioProcessorEditor{&p} , processor{p} , valueTreeState{valueTreeState} - // sharedParams{p.getSharedParams()}, , midiKeyboard{p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard} , tablesComponent{valueTreeState} , filePicker{valueTreeState} @@ -31,30 +30,6 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor( GuiConstants::minHeight, GuiConstants::maxWidth, GuiConstants::maxHeight); - - // int width, height; - // { - // RangedAudioParameter *param {valueTreeState.getParameter("uiWidthPersist")}; - // jassert(dynamic_cast (param) != nullptr); - // AudioParameterInt* castParam {dynamic_cast (param)}; - // width = castParam->get(); - // } - // { - // RangedAudioParameter *param {valueTreeState.getParameter("uiHeightPersist")}; - // jassert(dynamic_cast (param) != nullptr); - // AudioParameterInt* castParam {dynamic_cast (param)}; - // height = castParam->get(); - // } - - // valueTreeState.addParameterListener("uiWidthPersist", this); - // valueTreeState.addParameterListener("uiHeightPersist", this); -// valueTreeState.addParameterListener("uiWidth", this); -// valueTreeState.addParameterListener("uiHeight", this); - -// valueTreeState.state.addListener(this); - - // setSize(GuiConstants::minWidth, GuiConstants::minHeight); - // setSize(width, height); lastUIWidth.referTo(valueTreeState.state.getChildWithName("uiState").getPropertyAsValue("width", nullptr)); lastUIHeight.referTo(valueTreeState.state.getChildWithName("uiState").getPropertyAsValue("height", nullptr)); @@ -65,8 +40,6 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor( lastUIWidth.addListener(this); lastUIHeight.addListener(this); -// processor.subscribeToStateChanges(this); - midiKeyboard.setName ("MIDI Keyboard"); midiKeyboard.setWantsKeyboardFocus(false); @@ -90,76 +63,14 @@ JuicySFAudioProcessorEditor::~JuicySFAudioProcessorEditor() { lastUIWidth.removeListener(this); lastUIHeight.removeListener(this); - // valueTreeState.removeParameterListener("uiWidthPersist", this); - // valueTreeState.removeParameterListener("uiHeightPersist", this); - // valueTreeState.removeParameterListener("uiWidth", this); - // valueTreeState.removeParameterListener("uiHeight", this); -// valueTreeState.state.removeListener(this); -// processor.unsubscribeFromStateChanges(this); } -// void JuicySFAudioProcessorEditor::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, -// const Identifier& property) { -// // if (treeWhosePropertyHasChanged.getType() == Identifier("PARAM")) { -// const String propertyString{property.toString()}; -// if (propertyString == "uiWidth") { -// // String soundFontPath{treeWhosePropertyHasChanged.getProperty("soundFontPath", "")}; -// // if (soundFontPath.isNotEmpty()) { -// // loadFont(soundFontPath); -// // } -// int value{treeWhosePropertyHasChanged.getProperty("uiWidth", GuiConstants::minWidth)}; -// setSize(value, getHeight()); -// } else if (propertyString == "uiHeight") { -// int value{treeWhosePropertyHasChanged.getProperty("uiHeight", GuiConstants::minHeight)}; -// setSize(getWidth(), value); -// } -// // } -// } - -// void JuicySFAudioProcessorEditor::parameterChanged(const String& parameterID, float newValue) { -// // if (parameterID == "uiWidthPersist" -// // || parameterID == "uiHeightPersist") { -// if (parameterID == "uiWidth" -// || parameterID == "uiHeight") { -// RangedAudioParameter *param {valueTreeState.getParameter(parameterID)}; -// jassert(dynamic_cast (param) != nullptr); -// AudioParameterInt* castParam {dynamic_cast (param)}; -// int value{castParam->get()}; -// if (parameterID == "uiWidth") { -// setSize(value, getHeight()); -// } else if (parameterID == "uiHeight") { -// setSize(getWidth(), value); -// } -// // if (parameterID == "uiWidthPersist") { -// // setSize(value, getHeight()); -// // } else if (parameterID == "uiHeightPersist") { -// // setSize(getWidth(), value); -// // } -// } -// } - -//void JuicySFAudioProcessorEditor::getStateInformation (XmlElement& xml) { -// // save -// xml.setAttribute ("uiWidth", getWidth()); -// xml.setAttribute ("uiHeight", getHeight()); -//} -// -//void JuicySFAudioProcessorEditor::setStateInformation (XmlElement* xmlState) { -// // load -// setSize (xmlState->getIntAttribute ("uiWidth", getWidth()), -// xmlState->getIntAttribute ("uiHeight", getHeight())); -//} - //============================================================================== void JuicySFAudioProcessorEditor::paint (Graphics& g) { // (Our component is opaque, so we must completely fill the background with a solid colour) g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); -// g.setColour (Colours::white); -// g.setFont (15.0f); -// g.drawFittedText ("Hello World!", getLocalBounds(), Justification::centred, 1); - if (!focusInitialized) { if (!hasKeyboardFocus(false) && isVisible()) { grabKeyboardFocus(); @@ -175,13 +86,9 @@ void JuicySFAudioProcessorEditor::resized() const int padding{8}; const int pianoHeight{70}; const int filePickerHeight{25}; - // const int slidersHeight{150}; Rectangle r{getLocalBounds()}; filePicker.setBounds(r.removeFromTop(filePickerHeight + padding).reduced(padding, 0).withTrimmedTop(padding)); - // Rectangle r2 (getLocalBounds()); - // slidersComponent.setBounds(r2.removeFromLeft(filePickerWidth + padding).reduced(padding, 0).withTrimmedLeft(padding)); - midiKeyboard.setBounds (r.removeFromBottom (pianoHeight).reduced(padding, 0)); Rectangle rContent{r.reduced(0, padding)}; @@ -191,50 +98,9 @@ void JuicySFAudioProcessorEditor::resized() lastUIWidth = getWidth(); lastUIHeight = getHeight(); - - // valueTreeState.state.setPropertyExcludingListener(this, "uiWidth", getWidth(), nullptr); - // valueTreeState.state.setPropertyExcludingListener(this, "uiHeight", getHeight(), nullptr); - -// { -// // RangedAudioParameter *param {valueTreeState.getParameter("uiWidthTemp")}; -// RangedAudioParameter *param {valueTreeState.getParameter("uiWidth")}; -// // param->setValueNotifyingHost(param->convertTo0to1(getWidth())); -// jassert(dynamic_cast (param) != nullptr); -// AudioParameterInt* castParam {dynamic_cast (param)}; -// *castParam = getWidth(); -// // castParam->AudioProcessorParameter::setValue(castParam->convertTo0to1(static_cast(getWidth()))); -// } -// { -// // RangedAudioParameter *param {valueTreeState.getParameter("uiHeightTemp")}; -// RangedAudioParameter *param {valueTreeState.getParameter("uiHeight")}; -// // param->setValueNotifyingHost(param->convertTo0to1(getHeight())); -// jassert(dynamic_cast (param) != nullptr); -// AudioParameterInt* castParam {dynamic_cast (param)}; -// *castParam = getHeight(); -// // castParam->AudioProcessorParameter::setValue(castParam->convertTo0to1(static_cast(getHeight()))); -// } - -// sharedParams.setUiWidth(getWidth()); -// sharedParams.setUiHeight(getHeight()); - -// Rectangle r2 (getLocalBounds()); -// r2.reduce(0, padding); -// r2.removeFromBottom(pianoHeight); -// r2.removeFromTop(filePickerHeight); -// tablesComponent.setBounds (r2); -// -// Rectangle r3 (getLocalBounds()); -// r3.removeFromTop(filePickerHeight); -// -// filePicker.setBounds(r3); } bool JuicySFAudioProcessorEditor::keyPressed(const KeyPress &key) { -// if (!hasKeyboardFocus(false)) -// return false; -// if (key.getKeyCode() == KeyPress::upKey){ -// } -// cout << "hey\n"; const int cursorKeys[] = { KeyPress::leftKey, KeyPress::rightKey, @@ -250,24 +116,9 @@ bool JuicySFAudioProcessorEditor::keyPressed(const KeyPress &key) { } else { return midiKeyboard.keyPressed(key); } -// for(auto childComponent : getChildren()) { -// if (childComponent->keyPressed(key)) return true; -// } return false; } bool JuicySFAudioProcessorEditor::keyStateChanged (bool isKeyDown) { return midiKeyboard.keyStateChanged(isKeyDown); -// for(auto childComponent : getChildren()) { -// if (childComponent->keyStateChanged(isKeyDown)) return true; -// } -// return false; } - -//FilePickerFragment& JuicySFAudioProcessorEditor::getFilePicker() { -// return filePicker; -//} -// -//SlidersFragment& JuicySFAudioProcessorEditor::getSliders() { -// return slidersComponent; -//} diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 1e39902..ef0b8b6 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -14,10 +14,7 @@ #include "PluginProcessor.h" #include "TablesComponent.h" #include "SurjectiveMidiKeyboardComponent.h" -#include "FilePickerFragment.h" -#include "ExposesComponents.h" #include "FilePicker.h" -#include "StateChangeSubscriber.h" #include "SlidersComponent.h" //============================================================================== @@ -25,12 +22,7 @@ */ class JuicySFAudioProcessorEditor : public AudioProcessorEditor -// , public AudioProcessorValueTreeState::Listener , private Value::Listener -// , public ValueTree::Listener - /*, - , public ExposesComponents - public StateChangeSubscriber*/ { public: JuicySFAudioProcessorEditor( @@ -46,28 +38,6 @@ class JuicySFAudioProcessorEditor bool keyPressed(const KeyPress &key) override; bool keyStateChanged (bool isKeyDown) override; -// void getStateInformation (XmlElement& xml) override; -// void setStateInformation (XmlElement* xmlState) override; - - // virtual FilePickerFragment& getFilePicker() override; - // virtual SlidersFragment& getSliders() override; - // virtual void parameterChanged (const String& parameterID, float newValue) override; - - // int getWidth(); - // int getHeight(); - - // virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, - // const Identifier& property) override; - // inline virtual void valueTreeChildAdded (ValueTree& parentTree, - // ValueTree& childWhichHasBeenAdded) override {}; - // inline virtual void valueTreeChildRemoved (ValueTree& parentTree, - // ValueTree& childWhichHasBeenRemoved, - // int indexFromWhichChildWasRemoved) override {}; - // inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved, - // int oldIndex, int newIndex) override {}; - // inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; - // inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; - private: void valueChanged (Value&) override; @@ -76,7 +46,6 @@ class JuicySFAudioProcessorEditor JuicySFAudioProcessor& processor; AudioProcessorValueTreeState& valueTreeState; - // SharesParams& sharedParams; // these are used to persist the UI's size - the values are stored along with the // filter's other parameters, and the UI component will update them when it gets diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 40794b4..da7dfa3 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -10,11 +10,8 @@ #include "PluginProcessor.h" #include "PluginEditor.h" -#include "ExposesComponents.h" #include "MidiConstants.h" #include "Util.h" -#include "SharesParams.h" -#include "Params.h" #include "GuiConstants.h" using namespace std; diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 2544766..f079b73 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -12,9 +12,6 @@ #include "../JuceLibraryCode/JuceHeader.h" #include "FluidSynthModel.h" -#include "StateChangeSubscriber.h" -#include "SharesParams.h" -#include "Params.h" #include using namespace std; diff --git a/Source/Preset.cpp b/Source/Preset.cpp deleted file mode 100644 index aac7a84..0000000 --- a/Source/Preset.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// Created by Alex Birch on 17/09/2017. -// - -#include "Preset.h" - -using namespace std; - -Preset::Preset( - int preset, - string name -) : preset(preset), - name(name) {} - -int Preset::getPreset() { - return preset; -} - -string Preset::getName() { - return name; -} \ No newline at end of file diff --git a/Source/Preset.h b/Source/Preset.h deleted file mode 100644 index 03b46b5..0000000 --- a/Source/Preset.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by Alex Birch on 17/09/2017. -// - -#pragma once - -#include "../JuceLibraryCode/JuceHeader.h" - -using namespace std; - -class Preset { -public: - Preset( - int preset, - string name - ); - - int getPreset(); - string getName(); - -private: - int preset; - string name; -}; \ No newline at end of file diff --git a/Source/PresetsToBanks.h b/Source/PresetsToBanks.h deleted file mode 100644 index bbfcdba..0000000 --- a/Source/PresetsToBanks.h +++ /dev/null @@ -1,10 +0,0 @@ -// http://www.synthfont.com/Tutorial6.html -// a bank can hold many (128) presets -// bank 128 is reserved for percussion - -#pragma once - -#include "Preset.h" -#include - -typedef std::multimap BanksToPresets; \ No newline at end of file diff --git a/Source/SharesParams.h b/Source/SharesParams.h deleted file mode 100644 index 88740b3..0000000 --- a/Source/SharesParams.h +++ /dev/null @@ -1,49 +0,0 @@ -// -// Created by Alex Birch on 10/04/2018. -// Copyright (c) 2018 Birchlabs. All rights reserved. -// - -#pragma once - -#include "../JuceLibraryCode/JuceHeader.h" - -using namespace std; - -class SharesParams { -public: - virtual ~SharesParams() {} - - virtual void setAttributesOnXml(shared_ptr xml) = 0; - virtual void loadAttributesFromXml(shared_ptr xmlState) = 0; -// virtual void acceptMidiControlEvent(int controller, int value) = 0; - - virtual void setSoundFontPath(const String& value) = 0; - virtual String& getSoundFontPath() = 0; - // virtual int getPreset() = 0; - // virtual void setPreset(int value) = 0; - // virtual int getBank() = 0; - // virtual void setBank(int value) = 0; - - // virtual int getUiWidth() = 0; - // virtual void setUiWidth(int value) = 0; - // virtual int getUiHeight() = 0; - // virtual void setUiHeight(int value) = 0; - - // virtual int getAttack() = 0; - // virtual void setAttack(int value) = 0; - // virtual int getDecay() = 0; - // virtual void setDecay(int value) = 0; - // virtual int getSustain() = 0; - // virtual void setSustain(int value) = 0; - // virtual int getRelease() = 0; - // virtual void setRelease(int value) = 0; - - // virtual int getFilterCutOff() = 0; - // virtual void setFilterCutOff(int value) = 0; - // virtual int getFilterResonance() = 0; - // virtual void setFilterResonance(int value) = 0; - -}; - - - diff --git a/Source/SlidersComponent.cpp b/Source/SlidersComponent.cpp index 5a4f787..03e1a43 100644 --- a/Source/SlidersComponent.cpp +++ b/Source/SlidersComponent.cpp @@ -12,7 +12,7 @@ #include "Util.h" using SliderAttachment = AudioProcessorValueTreeState::SliderAttachment; -std::function SlidersComponent::makeSliderListener(Slider& slider, int controller/*, std::function callback*/) { +std::function SlidersComponent::makeSliderListener(Slider& slider, int controller) { return [this, controller, &slider]{ RangedAudioParameter *param {valueTreeState.getParameter("release")}; @@ -25,7 +25,6 @@ std::function SlidersComponent::makeSliderListener(Slider& slider, int c DEBUG_PRINT(s); // slider.setValue(slider.getValue(), NotificationType::dontSendNotification); fluidSynthModel.setControllerValue(controller, slider.getValue()); - // callback(); }; } @@ -95,30 +94,9 @@ void SlidersComponent::acceptMidiControlEvent(int controller, int value) { } } -// void SlidersComponent::updateAttackSlider(int value) { -// attackSlider.setValue(value, NotificationType::dontSendNotification); -// } -// void SlidersComponent::updateDecaySlider(int value) { -// decaySlider.setValue(value, NotificationType::dontSendNotification); -// } -// void SlidersComponent::updateSustainSlider(int value) { -// sustainSlider.setValue(value, NotificationType::dontSendNotification); -// } -// void SlidersComponent::updateReleaseSlider(int value) { -// releaseSlider.setValue(value, NotificationType::dontSendNotification); -// } -// void SlidersComponent::updateFilterCutOffSlider(int value) { -// filterCutOffSlider.setValue(value, NotificationType::dontSendNotification); -// } -// void SlidersComponent::updateFilterResonanceSlider(int value) { -// filterResonanceSlider.setValue(value, NotificationType::dontSendNotification); -// } - SlidersComponent::SlidersComponent( - // SharesParams& sharedParams, AudioProcessorValueTreeState& valueTreeState, FluidSynthModel& fluidSynthModel) -// : sharedParams{sharedParams} : valueTreeState{valueTreeState} , fluidSynthModel{fluidSynthModel} , envelopeGroup{"envelopeGroup", "Envelope"} diff --git a/Source/SlidersComponent.h b/Source/SlidersComponent.h index 47bdfa8..5182b68 100644 --- a/Source/SlidersComponent.h +++ b/Source/SlidersComponent.h @@ -2,18 +2,14 @@ #include "../JuceLibraryCode/JuceHeader.h" #include "FluidSynthModel.h" -#include "SharesParams.h" -#include "SlidersFragment.h" using namespace std; using SliderAttachment = AudioProcessorValueTreeState::SliderAttachment; -class SlidersComponent : public Component, - public SlidersFragment +class SlidersComponent : public Component { public: SlidersComponent( - // SharesParams& sharedParams, AudioProcessorValueTreeState& valueTreeState, FluidSynthModel& fluidSynthModel); ~SlidersComponent(); @@ -22,20 +18,11 @@ class SlidersComponent : public Component, const int getDesiredWidth(); - virtual void acceptMidiControlEvent(int controller, int value) override; - - // virtual void updateAttackSlider(int value) override; - // virtual void updateDecaySlider(int value) override; - // virtual void updateSustainSlider(int value) override; - // virtual void updateReleaseSlider(int value) override; - - // virtual void updateFilterCutOffSlider(int value) override; - // virtual void updateFilterResonanceSlider(int value) override; + void acceptMidiControlEvent(int controller, int value); private: std::function makeSliderListener(Slider& slider, int controller); - // SharesParams& sharedParams; AudioProcessorValueTreeState& valueTreeState; FluidSynthModel& fluidSynthModel; diff --git a/Source/SlidersFragment.h b/Source/SlidersFragment.h deleted file mode 100644 index 255dbe1..0000000 --- a/Source/SlidersFragment.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "../JuceLibraryCode/JuceHeader.h" - -class SlidersFragment { -public: - virtual ~SlidersFragment() {} - - virtual void acceptMidiControlEvent(int controller, int value) = 0; - - // virtual void updateAttackSlider(int value) = 0; - // virtual void updateDecaySlider(int value) = 0; - // virtual void updateSustainSlider(int value) = 0; - // virtual void updateReleaseSlider(int value) = 0; - - // virtual void updateFilterCutOffSlider(int value) = 0; - // virtual void updateFilterResonanceSlider(int value) = 0; -}; diff --git a/Source/TableComponent.h b/Source/TableComponent.h index 28e02a3..16d76a5 100644 --- a/Source/TableComponent.h +++ b/Source/TableComponent.h @@ -9,7 +9,6 @@ #pragma once #include "../JuceLibraryCode/JuceHeader.h" -#include "PresetsToBanks.h" #include #include #include @@ -40,11 +39,6 @@ class TableComponent : public Component, public: TableComponent( AudioProcessorValueTreeState& valueTreeState - // const vector &columns, - // const vector &rows, - // const function &onRowSelected, - // const function&)> &rowToIDMapper, - // int initiallySelectedRow ); ~TableComponent(); @@ -74,8 +68,6 @@ class TableComponent : public Component, void resized() override; -// void setRows(const vector& rows, int initiallySelectedRow); - bool keyPressed(const KeyPress &key) override; virtual void parameterChanged (const String& parameterID, float newValue) override; @@ -92,7 +84,6 @@ class TableComponent : public Component, inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {}; inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {}; private: - // void loadModelFrom(ValueTree& presets); void loadModelFrom(ValueTree& banks); void repopulateTable(); void selectCurrentPreset(); @@ -105,12 +96,8 @@ class TableComponent : public Component, typedef multimap BanksToPresets; BanksToPresets banksToPresets; -// vector columns; vector rows; - // function onRowSelected; -// function&)> rowToIDMapper; - // A comparator used to sort our data when the user clicks a column header class DataSorter { public: diff --git a/Source/TablesComponent.cpp b/Source/TablesComponent.cpp index 31284bc..ede0ea9 100644 --- a/Source/TablesComponent.cpp +++ b/Source/TablesComponent.cpp @@ -9,153 +9,19 @@ using namespace placeholders; TablesComponent::TablesComponent( AudioProcessorValueTreeState& valueTreeState - // FluidSynthModel& fluidSynthModel ) : valueTreeState{valueTreeState} -// , fluidSynthModel{fluidSynthModel} , banks{valueTreeState} , presetTable{valueTreeState} -// , banksToPresets{fluidSynthModel.getBanks()} -// , initialised{false} { - // fluid_preset_t* currentPreset = getCurrentPreset(); - // selectedBank = -1; - // int selectedPreset = -1; - - - - // if (currentPreset != nullptr) { - // selectedBank = fluid_preset_get_banknum(currentPreset); - // selectedPreset = fluid_preset_get_num(currentPreset); - // } - - // auto rowToPresetMapper = [this](const vector &row) { - // return stoi(row[0]); - // }; - // auto itemToBankMapper = [](const string &item) { - // return stoi(item); - // }; - - // presetTable = new TableComponent( - // valueTreeState, - // // {"#", "Name"}, - // // mapPresets( - // // banksToPresets, - // // selectedBank - // // ), - // // [this](int preset){ - // // this->onPresetSelected(preset); - // // }, - // // rowToPresetMapper, - // // presetToIndexMapper(selectedPreset) - // ); - // banks = new Pills( - // "Banks", - // mapBanks(banksToPresets), - // [this](int bank){ - // this->onBankSelected(bank); - // }, - // itemToBankMapper, - // selectedBank - // ); presetTable.setWantsKeyboardFocus(false); addAndMakeVisible(presetTable); addAndMakeVisible(banks); - - // initialised = true; - - // fluidSynthModel.addListener(this); } -// fluid_preset_t* TablesComponent::getCurrentPreset() { -// shared_ptr synth {fluidSynthModel.getSynth()}; - -// return fluid_synth_get_channel_preset(synth.get(), fluidSynthModel.getChannel()); -// } - -// Preset TablesComponent::getFirstPresetInBank(int bank) { -// pair iterators = banksToPresets.equal_range(bank); -// BanksToPresets::const_iterator it = iterators.first; -// return it->second; -// } - -// void TablesComponent::onBankSelected(int bank) { -// if (!initialised || bank == -1) { -// return; -// } -// cout << "Bank " << bank << endl; -// selectedBank = bank; -// Preset firstPresetInBank = getFirstPresetInBank(bank); -// presetTable->setRows( -// mapPresets( -// banksToPresets, -// bank -// ), -// presetToIndexMapper(firstPresetInBank.getPreset()) -// ); -// } - -// int TablesComponent::presetToIndexMapper(int preset) { -// int ix = 0; -// pair iterators = this->banksToPresets.equal_range(this->selectedBank); -// for (auto it = iterators.first; it != iterators.second; ++it, ix++) { -// Preset b = it->second; -// if (preset == b.getPreset()) { -// return ix; -// } -// } -// return 0; -// } - -// void TablesComponent::onPresetSelected(int preset) { -// if (!initialised || preset == -1) { -// return; -// } -// cout << "Preset " << preset << endl; -// // selectedPreset = preset; -// fluidSynthModel.changePreset(selectedBank, preset); -// } - -// TablesComponent::~TablesComponent() { -// delete presetTable; -// delete banks; -// // fluidSynthModel.removeListener(this); -// } - -// vector TablesComponent::mapBanks(const BanksToPresets &banksToPresets) { -// vector rows; - -// const auto compareKey = [](const BanksToPresets::value_type& lhs, const BanksToPresets::value_type& rhs) { -// return lhs.first < rhs.first; -// }; - -// for(auto i = banksToPresets.begin(); i != banksToPresets.end(); i = std::upper_bound(i, banksToPresets.end(), *i, compareKey)) { -// rows.push_back(to_string(i->first)); -// } - -// return rows; -// } - - -// vector> TablesComponent::mapPresets(const BanksToPresets &banksToPresets, int bank) { -// vector> rows; - -// pair iterators = banksToPresets.equal_range(bank); -// for (auto it = iterators.first; it != iterators.second; ++it) { -// Preset b = it->second; -// vector row; -// row.push_back(to_string(b.getPreset())); -// row.push_back(b.getName()); - -// rows.push_back(row); -// } - -// return rows; -// } - void TablesComponent::resized() { Rectangle r (getLocalBounds()); banks.setBounds (r.removeFromTop(27).reduced(5,0)); @@ -171,25 +37,3 @@ bool TablesComponent::keyPressed(const KeyPress &key) { } return presetTable.keyPressed(key); } - -// void TablesComponent::fontChanged(FluidSynthModel *, const String &) { -// banksToPresets = fluidSynthModel.getBanks(); - -// fluid_preset_t* currentPreset = getCurrentPreset(); - -// selectedBank = fluid_preset_get_banknum(currentPreset); -// int selectedPreset = fluid_preset_get_num(currentPreset); - -// presetTable->setRows( -// mapPresets( -// banksToPresets, -// selectedBank -// ), -// presetToIndexMapper(selectedPreset) -// ); - -// banks->setItems( -// mapBanks(banksToPresets), -// selectedBank -// ); -// } diff --git a/Source/TablesComponent.h b/Source/TablesComponent.h index f3830cc..ea157ff 100644 --- a/Source/TablesComponent.h +++ b/Source/TablesComponent.h @@ -7,50 +7,28 @@ #include "../JuceLibraryCode/JuceHeader.h" #include "Pills.h" #include "TableComponent.h" -#include "Preset.h" -#include "PresetsToBanks.h" #include "FluidSynthModel.h" #include #include using namespace std; -class TablesComponent : public Component/*, - public FluidSynthModel::Listener */ +class TablesComponent : public Component { public: TablesComponent( AudioProcessorValueTreeState& valueTreeState - // FluidSynthModel& fluidSynthModel ); - // ~TablesComponent(); void resized() override; bool keyPressed(const KeyPress &key) override; - // void fontChanged(FluidSynthModel *, const String &) override; private: AudioProcessorValueTreeState& valueTreeState; - // FluidSynthModel& fluidSynthModel; - // int selectedBank; Pills banks; TableComponent presetTable; - // BanksToPresets banksToPresets; - - // static vector> mapPresets(const BanksToPresets &banksToPresets, int bank); - // static vector mapBanks(const BanksToPresets &banksToPresets); - - // void onBankSelected(int bank); - // void onPresetSelected(int preset); - // int presetToIndexMapper(int preset); - - // fluid_preset_t* getCurrentPreset(); - // Preset getFirstPresetInBank(int bank); - - // bool initialised; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TablesComponent) }; From 6f802002196c510c403c98a4837dcf4bb2651f6f Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Mon, 29 Jul 2019 22:05:50 +0100 Subject: [PATCH 29/33] unused --- Source/StateChangeSubscriber.h | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 Source/StateChangeSubscriber.h diff --git a/Source/StateChangeSubscriber.h b/Source/StateChangeSubscriber.h deleted file mode 100644 index 420a26a..0000000 --- a/Source/StateChangeSubscriber.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Alex Birch on 18/03/2018. -// Copyright (c) 2018 Birchlabs. All rights reserved. -// - -#pragma once - -#include "../JuceLibraryCode/JuceHeader.h" - -class StateChangeSubscriber { -public: - virtual ~StateChangeSubscriber() {} // pass pointer ownership to another party without exposing the concrete derived class - virtual void getStateInformation (XmlElement& xml) = 0; - virtual void setStateInformation (XmlElement* xmlState) = 0; -}; From 79c023d4665e5d17d05fbf5aac0b3037627184fe Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Mon, 29 Jul 2019 23:46:38 +0100 Subject: [PATCH 30/33] restore ADSR and filter from save. shorten switch statements into map lookups. --- Source/FluidSynthModel.cpp | 154 ++++++++++++++++-------------------- Source/FluidSynthModel.h | 8 ++ Source/SlidersComponent.cpp | 15 ++-- 3 files changed, 84 insertions(+), 93 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index d902d02..d25af90 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -3,6 +3,7 @@ // #include +#include #include #include "FluidSynthModel.h" #include "MidiConstants.h" @@ -10,6 +11,26 @@ using namespace std; +const map FluidSynthModel::controllerToParam{ + {SOUND_CTRL2, "filterResonance"}, // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) + {SOUND_CTRL3, "release"}, // MIDI CC 72 Release time + {SOUND_CTRL4, "attack"}, // MIDI CC 73 Attack time + {SOUND_CTRL5, "filterCutOff"}, // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) + {SOUND_CTRL6, "decay"}, // MIDI CC 75 Decay Time + {SOUND_CTRL10, "sustain"}}; // MIDI CC 79 undefined + +const map FluidSynthModel::paramToController{[]{ + map map; + transform( + controllerToParam.begin(), + controllerToParam.end(), + inserter(map, map.begin()), + [](const pair& pair) { + return make_pair(pair.second, pair.first); + }); + return map; +}()}; + FluidSynthModel::FluidSynthModel( AudioProcessorValueTreeState& valueTreeState ) @@ -22,10 +43,16 @@ FluidSynthModel::FluidSynthModel( { valueTreeState.addParameterListener("bank", this); valueTreeState.addParameterListener("preset", this); + for (const auto &[param, controller]: paramToController) { + valueTreeState.addParameterListener(param, this); + } valueTreeState.state.addListener(this); } FluidSynthModel::~FluidSynthModel() { + for (const auto &[param, controller]: paramToController) { + valueTreeState.removeParameterListener(param, this); + } valueTreeState.removeParameterListener("bank", this); valueTreeState.removeParameterListener("preset", this); valueTreeState.state.removeListener(this); @@ -46,44 +73,26 @@ void FluidSynthModel::initialise() { synth = { new_fluid_synth(settings.get()), delete_fluid_synth }; fluid_synth_set_sample_rate(synth.get(), currentSampleRate); - - // valueTreeState.getParameter("soundFontPath")->getValue(); - // RangedAudioParameter *param {valueTreeState.getParameter("release")}; - // jassert(dynamic_cast (param) != nullptr); - // AudioParameterInt* castParam {dynamic_cast (param)}; - // *castParam = m.getControllerValue(); ValueTree soundFont{valueTreeState.state.getChildWithName("soundFont")}; String path{soundFont.getProperty("path", "")}; loadFont(path); + // I can't hear a damned thing fluid_synth_set_gain(synth.get(), 2.0); - fluid_midi_control_change controllers[]{SOUND_CTRL2, SOUND_CTRL3, SOUND_CTRL4, SOUND_CTRL5, SOUND_CTRL6, SOUND_CTRL10}; - for(fluid_midi_control_change controller : controllers) { + // note: fluid_chan.c#fluid_channel_init_ctrl() + // all SOUND_CTRL are inited with value of 64, not zero. + // "Just like panning, a value of 64 indicates no change for sound ctrls" + // and yet, I'm finding that default modulators start at MIN, so we are forced to start at 0 and climb from there + for (const auto &[controller, param]: controllerToParam) { setControllerValue(static_cast(controller), 0); } - -// fluid_synth_bank_select(synth, 0, 3); - -// fluid_handle_inst - -// driver = new_fluid_audio_driver(settings, synth); - -// changePreset(128, 13); - -// float env_amount(12000.0f); -// http://www.synthfont.com/SoundFont_NRPNs.PDF + // http://www.synthfont.com/SoundFont_NRPNs.PDF float env_amount{20000.0f}; -// float env_amount(24000.0f); - - // note: fluid_chan.c#fluid_channel_init_ctrl() - // all SOUND_CTRL are inited with value of 64, not zero. - // "Just like panning, a value of 64 indicates no change for sound ctrls" unique_ptr mod{new_fluid_mod(), delete_fluid_mod}; -// fluid_mod_set_source1(mod.get(), static_cast(SOUND_CTRL2), // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) FLUID_MOD_CC @@ -104,7 +113,6 @@ void FluidSynthModel::initialise() { | FLUID_MOD_POSITIVE); fluid_mod_set_source2(mod.get(), 0, 0); fluid_mod_set_dest(mod.get(), GEN_VOLENVRELEASE); -// fluid_mod_set_amount(mod.get(), 15200.0f); fluid_mod_set_amount(mod.get(), env_amount); fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); @@ -164,15 +172,15 @@ void FluidSynthModel::parameterChanged(const String& parameterID, float newValue if (parameterID == "bank") { int bank, preset; { - RangedAudioParameter *param {valueTreeState.getParameter("bank")}; + RangedAudioParameter *param{valueTreeState.getParameter("bank")}; jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; + AudioParameterInt* castParam{dynamic_cast(param)}; bank = castParam->get(); } { - RangedAudioParameter *param {valueTreeState.getParameter("preset")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; + RangedAudioParameter *param{valueTreeState.getParameter("preset")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; preset = castParam->get(); } int bankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; @@ -185,15 +193,15 @@ void FluidSynthModel::parameterChanged(const String& parameterID, float newValue } else if (parameterID == "preset") { int bank, preset; { - RangedAudioParameter *param {valueTreeState.getParameter("bank")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; + RangedAudioParameter *param{valueTreeState.getParameter("bank")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; bank = castParam->get(); } { - RangedAudioParameter *param {valueTreeState.getParameter("preset")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; + RangedAudioParameter *param{valueTreeState.getParameter("preset")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; preset = castParam->get(); } int bankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; @@ -203,6 +211,21 @@ void FluidSynthModel::parameterChanged(const String& parameterID, float newValue sfont_id, static_cast(bankOffset + bank), static_cast(preset)); + } else if ( + // https://stackoverflow.com/a/55482091/5257399 + auto it{paramToController.find(parameterID)}; + it != end(paramToController)) { + RangedAudioParameter *param{valueTreeState.getParameter(parameterID)}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; + int value{castParam->get()}; + int controllerNumber{static_cast(it->second)}; + + fluid_synth_cc( + synth.get(), + channel, + controllerNumber, + value); } } @@ -329,54 +352,15 @@ void FluidSynthModel::processBlock(AudioBuffer& buffer, MidiBuffer& midiM channel, m.getControllerNumber(), m.getControllerValue()); - - switch(static_cast(m.getControllerNumber())) { - case SOUND_CTRL2: { // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance) - // valueTreeState.state.setProperty({"filterResonance"}, m.getControllerValue(), nullptr); - RangedAudioParameter *param{valueTreeState.getParameter("filterResonance")}; - jassert(dynamic_cast(param) != nullptr); - AudioParameterInt* castParam {dynamic_cast(param)}; - *castParam = m.getControllerValue(); - break; - } - case SOUND_CTRL3: { // MIDI CC 72 Release time - RangedAudioParameter *param{valueTreeState.getParameter("release")}; - jassert(dynamic_cast(param) != nullptr); - AudioParameterInt* castParam {dynamic_cast(param)}; - *castParam = m.getControllerValue(); - break; - } - case SOUND_CTRL4: { // MIDI CC 73 Attack time - RangedAudioParameter *param{valueTreeState.getParameter("release")}; - jassert(dynamic_cast(param) != nullptr); - AudioParameterInt* castParam {dynamic_cast(param)}; - *castParam = m.getControllerValue(); - break; - } - case SOUND_CTRL5: { // MIDI CC 74 Brightness (cutoff frequency, FILTERFC) - RangedAudioParameter *param{valueTreeState.getParameter("filterCutOff")}; - jassert(dynamic_cast(param) != nullptr); - AudioParameterInt* castParam {dynamic_cast(param)}; - *castParam = m.getControllerValue(); - break; - } - case SOUND_CTRL6: { // MIDI CC 75 Decay Time - RangedAudioParameter *param{valueTreeState.getParameter("decay")}; - jassert(dynamic_cast(param) != nullptr); - AudioParameterInt* castParam {dynamic_cast(param)}; - *castParam = m.getControllerValue(); - break; - } - case SOUND_CTRL10: { // MIDI CC 79 undefined - RangedAudioParameter *param{valueTreeState.getParameter("sustain")}; - jassert(dynamic_cast(param) != nullptr); - AudioParameterInt* castParam{dynamic_cast(param)}; - *castParam = m.getControllerValue(); - break; - } - default: { - break; - } + + fluid_midi_control_change controllerNum{static_cast(m.getControllerNumber())}; + if (auto it{controllerToParam.find(controllerNum)}; + it != end(controllerToParam)) { + String parameterID{it->second}; + RangedAudioParameter *param{valueTreeState.getParameter(parameterID)}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; + *castParam = m.getControllerValue(); } } else if (m.isProgramChange()) { int result{fluid_synth_program_change( diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index f416a48..d545c67 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -7,6 +7,8 @@ #include "../JuceLibraryCode/JuceHeader.h" #include #include +#include +#include "MidiConstants.h" using namespace std; @@ -53,6 +55,12 @@ class FluidSynthModel void changeProgramName(int index, const String& newName); private: + // static const StringArray controllerParams; + + // there's no bimap in the standard library! + static const map controllerToParam; + static const map paramToController; + void refreshBanks(); AudioProcessorValueTreeState& valueTreeState; diff --git a/Source/SlidersComponent.cpp b/Source/SlidersComponent.cpp index 03e1a43..ba6f81c 100644 --- a/Source/SlidersComponent.cpp +++ b/Source/SlidersComponent.cpp @@ -14,15 +14,14 @@ using SliderAttachment = AudioProcessorValueTreeState::SliderAttachment; std::function SlidersComponent::makeSliderListener(Slider& slider, int controller) { return [this, controller, &slider]{ + // RangedAudioParameter *param{valueTreeState.getParameter("release")}; + // jassert(dynamic_cast(param) != nullptr); + // AudioParameterInt* castParam{dynamic_cast(param)}; + // int value{castParam->get()}; - RangedAudioParameter *param {valueTreeState.getParameter("release")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; - int value {castParam->get()}; - - String s{"slider "}; - s << slider.getComponentID() << ", controller " << controller << ", value " << slider.getValue() << ", xmlReleaseValue " << value; - DEBUG_PRINT(s); + // String s{"slider "}; + // s << slider.getComponentID() << ", controller " << controller << ", value " << slider.getValue() << ", xmlReleaseValue " << value; + // DEBUG_PRINT(s); // slider.setValue(slider.getValue(), NotificationType::dontSendNotification); fluidSynthModel.setControllerValue(controller, slider.getValue()); }; From ba6bec2d9d67eebf8eb46271ae7f6416dbc68b32 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Tue, 30 Jul 2019 21:12:49 +0100 Subject: [PATCH 31/33] remove unused, reduce duplication --- .../xcschemes/juicysfplugin - VST3.xcscheme | 1 + Source/FluidSynthModel.cpp | 24 +--- Source/FluidSynthModel.h | 2 +- Source/Pills.cpp | 132 ++---------------- 4 files changed, 13 insertions(+), 146 deletions(-) diff --git a/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST3.xcscheme b/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST3.xcscheme index b6c7b9d..f373512 100644 --- a/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST3.xcscheme +++ b/Builds/MacOSX/juicysfplugin.xcodeproj/xcshareddata/xcschemes/juicysfplugin - VST3.xcscheme @@ -44,6 +44,7 @@ allowLocationSimulation = "YES"> diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index d25af90..24e1b72 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -168,29 +168,9 @@ void FluidSynthModel::initialise() { fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD); } +const StringArray FluidSynthModel::programChangeParams{"bank", "preset"}; void FluidSynthModel::parameterChanged(const String& parameterID, float newValue) { - if (parameterID == "bank") { - int bank, preset; - { - RangedAudioParameter *param{valueTreeState.getParameter("bank")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam{dynamic_cast(param)}; - bank = castParam->get(); - } - { - RangedAudioParameter *param{valueTreeState.getParameter("preset")}; - jassert(dynamic_cast(param) != nullptr); - AudioParameterInt* castParam{dynamic_cast(param)}; - preset = castParam->get(); - } - int bankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)}; - fluid_synth_program_select( - synth.get(), - channel, - sfont_id, - static_cast(bankOffset + bank), - static_cast(preset)); - } else if (parameterID == "preset") { + if (programChangeParams.contains(parameterID)) { int bank, preset; { RangedAudioParameter *param{valueTreeState.getParameter("bank")}; diff --git a/Source/FluidSynthModel.h b/Source/FluidSynthModel.h index d545c67..af14ae3 100644 --- a/Source/FluidSynthModel.h +++ b/Source/FluidSynthModel.h @@ -55,7 +55,7 @@ class FluidSynthModel void changeProgramName(int index, const String& newName); private: - // static const StringArray controllerParams; + static const StringArray programChangeParams; // there's no bimap in the standard library! static const map controllerToParam; diff --git a/Source/Pills.cpp b/Source/Pills.cpp index fe8a3d6..654fb66 100644 --- a/Source/Pills.cpp +++ b/Source/Pills.cpp @@ -14,7 +14,6 @@ Pill::Pill( bool isFirst, bool isLast ) -// : pills{pills} : valueTreeState{valueTreeState} , bank{bank} , textButton{String(bank)} @@ -25,13 +24,9 @@ Pill::Pill( | (isLast ? 0 : Button::ConnectedOnRight) ); textButton.setRadioGroupId(34567); - // loadToggleState(); textButton.setClickingTogglesState(true); addAndMakeVisible(textButton); - - // valueTreeState.addParameterListener("bank", this); -// valueTreeState.state.addListener(this); textButton.addListener(this); } @@ -45,8 +40,6 @@ void Pill::resized() { } Pill::~Pill() { - // valueTreeState.removeParameterListener("bank", this); -// valueTreeState.state.removeListener(this); textButton.removeListener(this); } @@ -54,14 +47,6 @@ void Pill::bankChanged(int bank) { textButton.setToggleState(this->bank == bank, dontSendNotification); } -// void Pill::loadToggleState() { -// RangedAudioParameter *param {valueTreeState.getParameter("bank")}; -// jassert(dynamic_cast (param) != nullptr); -// AudioParameterInt* castParam {dynamic_cast (param)}; -// int value{castParam->get()}; -// textButton.setToggleState(value == bank, dontSendNotification); -// } - void Pill::buttonClicked(Button* button) { ValueTree banks{valueTreeState.state.getChildWithName("banks")}; int banksChildren{banks.getNumChildren()}; @@ -77,8 +62,6 @@ void Pill::buttonClicked(Button* button) { ValueTree preset{bank.getChild(0)}; int presetNum{preset.getProperty("num")}; - // selected = button; - // onItemSelected(itemToIDMapper(button->getName().toStdString())); { RangedAudioParameter *param{valueTreeState.getParameter("bank")}; jassert(dynamic_cast(param) != nullptr); @@ -93,38 +76,14 @@ void Pill::buttonClicked(Button* button) { } } -// void Pill::parameterChanged(const String& parameterID, float newValue) { -// if (parameterID == "bank") { -// loadToggleState(); -// } -// } - -// void Pill::valueTreePropertyChanged( -// ValueTree& treeWhosePropertyHasChanged, -// const Identifier& property) { -// if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { -// loadModelFrom(treeWhosePropertyHasChanged); -// } -// } - Pills::Pills( AudioProcessorValueTreeState& valueTreeState - // string label, - // const vector &items, - // const function &onItemSelected, - // const function &itemToIDMapper, - // int initiallySelectedItem ) : valueTreeState{valueTreeState} -// , label{label} - // items(items), - // onItemSelected(onItemSelected), - // itemToIDMapper(itemToIDMapper) { // faster (rounded edges introduce transparency) setOpaque (true); - // populate(initiallySelectedItem); ValueTree banks{valueTreeState.state.getChildWithName("banks")}; loadModelFrom(banks); @@ -169,9 +128,6 @@ void Pills::loadModelFrom(ValueTree& banks) { for(int i{0}; i < numChildren; i++) { ValueTree child{banks.getChild(i)}; int num{child.getProperty("num")}; - // rows.push_back(unique_ptr(new Pill(), [](Pill* pill) { - // pill->remo - // })); unique_ptr pill{make_unique( valueTreeState, num, @@ -184,99 +140,29 @@ void Pills::loadModelFrom(ValueTree& banks) { resized(); } -// void Pills::populate(int initiallySelectedItem) { -// int index = 0; -// for (string item : items) { -// TextButton* pill = addToList(new TextButton( -// item -// )); -// // pill->setColour (TextButton::buttonOnColourId, Colours::blueviolet.brighter()); -// // pill->setBounds(20 + index * 55, 260, 55, 24); -// pill->setConnectedEdges ( -// (index == 0 ? 0 : Button::ConnectedOnLeft) -// | (index == (items.size()-1) ? 0 : Button::ConnectedOnRight) -// ); -// pill->setRadioGroupId(34567); -// if (index == initiallySelectedItem) { -// pill->setToggleState(true, dontSendNotification); -// selected = pill; -// } -// pill->setClickingTogglesState(true); -// pill->addListener(this); -// index++; -// } -// } - -// void Pills::setItems( -// const vector &items, -// int initiallySelectedItem -// ) { -// this->items = items; -// for(TextButton* t : buttons) { -// t->removeListener(this); -// } -// buttons.clear(true); -// populate(initiallySelectedItem); -// resized(); -// } - -// TextButton* Pills::addToList (TextButton* newButton) { -// buttons.add (newButton); -// addAndMakeVisible (newButton); -// return newButton; -// } - void Pills::cycle(bool right) { - RangedAudioParameter *param {valueTreeState.getParameter("bank")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; + RangedAudioParameter *param{valueTreeState.getParameter("bank")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast (param)}; int bank{castParam->get()}; - // ValueTree banks{valueTreeState.state.getChildWithName("banks")}; -// int numChildren{banks.getNumChildren()}; - - // vector bankInts; - // bankInts.resize(banks.getNumChildren()); - - // transform(banks.begin(), banks.end(), bankInts.begin(), [](ValueTree bank) -> int { - // return bank.getProperty("num"); - // }); - -// int closestBank{bank}; -// for(int i{0}; i < numChildren; i++) { -// ValueTree child{banks.getChild(i)}; -// int proposedBank{child.getProperty("num")}; -// if (right && proposedBank > bank) { -// closestBank = jmin(closestBank, proposedBank); -// } else if (left ) -// } - - // int currentIx{static_cast(distance(bankInts.begin(), find(bankInts.begin(), bankInts.end(), currentlySelectedBank)))}; - // currentIx += right ? 1 : pills.size()-1; - // // pills[currentIx % pills.size()]->textButton.triggerClick(); - // *castParam = bankInts[currentIx % bankInts.size()]; - - int currentIx = static_cast( + int currentIx{static_cast( distance( pills.begin(), find_if( pills.begin(), pills.end(), - [bank](unique_ptr& pill){return pill->bank == bank;}))); + [bank](unique_ptr& pill){ + return pill->bank == bank;})))}; currentIx += right ? 1 : pills.size()-1; pills[currentIx % pills.size()]->textButton.triggerClick(); - - // TODO: base this on valueTree - // int currentIx = static_cast(distance(pills.begin(), find(pills.begin(), pills.end(), selected))); - // currentIx += right ? 1 : pills.size()-1; - // pills[currentIx % pills.size()]->textButton.triggerClick(); } void Pills::resized() { - int index = 0; - Rectangle r (getLocalBounds()); - const int equalWidth = r.proportionOfWidth(pills.size() <= 0 ? 1.0 : 1.0f/pills.size()); + int index{0}; + Rectangle r{getLocalBounds()}; + const int equalWidth{r.proportionOfWidth(pills.size() <= 0 ? 1.0 : 1.0f/pills.size())}; for(auto& pill : pills) { Rectangle r2 (getLocalBounds()); r2.removeFromLeft(equalWidth * index); From 7dd9bb4c22caadf4c14fa1a18435b881bc49b13a Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Tue, 30 Jul 2019 21:36:54 +0100 Subject: [PATCH 32/33] fix choice of preset via plugin host (safely updates table from non-message thread) --- Source/FluidSynthModel.cpp | 59 +++++++--------- Source/PluginProcessor.cpp | 91 +----------------------- Source/TableComponent.cpp | 141 +++---------------------------------- 3 files changed, 37 insertions(+), 254 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 24e1b72..9bde407 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -417,44 +417,39 @@ void FluidSynthModel::setCurrentProgram(int index) RangedAudioParameter *param{valueTreeState.getParameter("preset")}; jassert(dynamic_cast(param) != nullptr); AudioParameterInt* castParam{dynamic_cast(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; } const String FluidSynthModel::getProgramName(int index) { - // fluid_sfont_t* sfont{ - // sfont_id == -1 - // ? nullptr - // : fluid_synth_get_sfont_by_id(synth.get(), sfont_id) - // }; - // if (!sfont) { - // return {}; - // } - // int bank, presetNum; - // { - // RangedAudioParameter *param {valueTreeState.getParameter("bank")}; - // jassert(dynamic_cast (param) != nullptr); - // AudioParameterInt* castParam {dynamic_cast (param)}; - // bank = castParam->get(); - // } - // { - // RangedAudioParameter *param {valueTreeState.getParameter("preset")}; - // jassert(dynamic_cast (param) != nullptr); - // AudioParameterInt* castParam {dynamic_cast (param)}; - // presetNum = castParam->get(); - // } - // fluid_preset_t *preset{fluid_sfont_get_preset( - // sfont, - // bank, - // presetNum)}; - // if (!preset) { - // return {}; - // } - // return {fluid_preset_get_name(preset)}; + fluid_sfont_t* sfont{ + sfont_id == -1 + ? nullptr + : fluid_synth_get_sfont_by_id(synth.get(), sfont_id) + }; + if (!sfont) { + String presetName{"Preset "}; + return presetName << index; + } + RangedAudioParameter *param{valueTreeState.getParameter("bank")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; + int bank{castParam->get()}; - // I think the presets' names will be collected only at synth startup, so we won't yet have loaded the soundfont. - String presetName{"Preset "}; - return presetName << index; + fluid_preset_t *preset{fluid_sfont_get_preset( + sfont, + bank, + index)}; + if (!preset) { + String presetName{"Preset "}; + return presetName << index; + } + return {fluid_preset_get_name(preset)}; } void FluidSynthModel::changeProgramName(int index, const String& newName) diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index da7dfa3..1df3466 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -215,30 +215,17 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData) // Create an outer XML element.. XmlElement xml{"MYPLUGINSETTINGS"}; -// sharedParams->setAttributesOnXml(xml); -// auto state{valueTreeState.copyState()}; -// unique_ptr xml{state.createXml()}; -// sharedParams.setAttributesOnXml(xml); - -// list::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 XmlElement* params{xml.createNewChildElement("params")}; for (auto* param : getParameters()) { if (auto* p = dynamic_cast (param)) { -// xml.setAttribute(p->paramID, p->getValue()); -// XmlElement* param{params->createNewChildElement("PARAM")}; -// param->setAttribute(p->paramID, p->getValue()); params->setAttribute(p->paramID, p->getValue()); } } { ValueTree tree{valueTreeState.state.getChildWithName("uiState")}; XmlElement* newElement{xml.createNewChildElement("uiState")}; -// Value value{tree.getPropertyAsValue("width", nullptr)}; { double value{tree.getProperty("width", GuiConstants::minWidth)}; newElement->setAttribute("width", value); @@ -259,10 +246,6 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData) 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); } @@ -272,54 +255,24 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt // whose contents will have been created by the getStateInformation() call. // This getXmlFromBinary() helper function retrieves our XML from the binary blob.. shared_ptr xmlState{getXmlFromBinary(data, sizeInBytes)}; -// unique_ptr xmlState{getXmlFromBinary(data, sizeInBytes)}; DEBUG_PRINT(xmlState->createDocument("",false,false)); -/* - - - - - - - - - - - - */ + if (xmlState.get() != nullptr) { // make sure that it's actually our type of XML object.. -// if (xmlState->hasTagName ("MYPLUGINSETTINGS")) { if (xmlState->hasTagName(valueTreeState.state.getType())) { - // valueTreeState.replaceState(ValueTree::fromXml(*xmlState)); -// for (auto* param : getParameters()) -// if (auto* p = dynamic_cast(param)) -// p->setValue(static_cast(xmlState->getDoubleAttribute(p->paramID, p->getValue()))); XmlElement* params{xmlState->getChildByName("params")}; - if (params) { + if (params) for (auto* param : getParameters()) if (auto* p = dynamic_cast(param)) - // XmlElement* xmlParam{params->getChildByAttribute("id", p->paramID)}; - // p->setValue(static_cast(xmlState->getDoubleAttribute(p->paramID, p->getValue()))); p->setValue(static_cast(params->getDoubleAttribute(p->paramID, p->getValue()))); - } { - // Value value{valueTreeState.state.getPropertyAsValue("soundFontPath", nullptr)}; - // value = xmlState->getStringAttribute("soundFontPath", value.getValue()); XmlElement* xmlElement{xmlState->getChildByName("soundFont")}; if (xmlElement) { ValueTree tree{valueTreeState.state.getChildWithName("soundFont")}; Value value{tree.getPropertyAsValue("path", nullptr)}; value = xmlElement->getStringAttribute("path", value.getValue()); } - - // valueTreeState.getParameter("soundFontPath")->getValue() - // valueTreeState.getParameter("soundFontPath")->getValue(); - // RangedAudioParameter *param {valueTreeState.getParameter("release")}; - // jassert(dynamic_cast (param) != nullptr); - // AudioParameterInt* castParam {dynamic_cast (param)}; - // *castParam = m.getControllerValue(); } { ValueTree tree{valueTreeState.state.getChildWithName("uiState")}; @@ -334,47 +287,7 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt value = xmlElement->getIntAttribute("height", value.getValue()); } } - -// tree.getPropertyAsValue("width", nullptr) -// tree. -// valueTreeState.replaceState(ValueTree::fromXml(*xmlState)) -// value = xmlState->getStringAttribute("soundFontPath", value.getValue()); } - -// list::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 (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 (editor) != nullptr); -// ExposesComponents* exposesComponents = dynamic_cast (editor); -// exposesComponents->getFilePicker().setDisplayedFilePath(sharedParams->getSoundFontPath()); -// } - -// const String& currentSoundFontAbsPath = fluidSynthModel->getCurrentSoundFontAbsPath(); -// if (currentSoundFontAbsPath.isNotEmpty()) { -// fileChooser.setCurrentFile(File(currentSoundFontAbsPath), true, dontSendNotification); -// } } } } diff --git a/Source/TableComponent.cpp b/Source/TableComponent.cpp index b43d253..e52ea81 100644 --- a/Source/TableComponent.cpp +++ b/Source/TableComponent.cpp @@ -21,18 +21,9 @@ using namespace Util; */ TableComponent::TableComponent( AudioProcessorValueTreeState& valueTreeState - // const vector &columns, -// const vector &rows, - // const function &onRowSelected, - // const function&)> &rowToIDMapper, - // int initiallySelectedRow ) : valueTreeState{valueTreeState} , font{14.0f} -//, columns{columns} -//, rows{rows} -// , onRowSelected{onRowSelected} -// rowToIDMapper(rowToIDMapper) { // Create our table component and add it to this component.. addAndMakeVisible (table); @@ -44,19 +35,6 @@ TableComponent::TableComponent( 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 ( String("#"), columnIx++, @@ -76,20 +54,11 @@ TableComponent::TableComponent( table.setWantsKeyboardFocus(false); - // table.selectRow(); - // ValueTree presets{valueTreeState.state.getChildWithName("presets")}; ValueTree banks{valueTreeState.state.getChildWithName("banks")}; loadModelFrom(banks); - // selectCurrentPreset(); // we could now change some initial settings.. 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.addParameterListener("bank", this); valueTreeState.addParameterListener("preset", this); @@ -101,27 +70,10 @@ TableComponent::~TableComponent() { valueTreeState.state.removeListener(this); } -// void TableComponent::loadModelFrom(ValueTree& presets) { -// rows.clear(); -// int numChildren{presets.getNumChildren()}; -// for(int i{0}; i presets; ValueTree bank{banks.getChild(bankIx)}; int bankNum{bank.getProperty("num")}; int bankChildren{bank.getNumChildren()}; @@ -129,9 +81,7 @@ void TableComponent::loadModelFrom(ValueTree& banks) { ValueTree preset{bank.getChild(presetIx)}; int presetNum{preset.getProperty("num")}; String presetName{preset.getProperty("name")}; - // presets.emplace_back(presetNum, presetName); TableRow row{presetNum, move(presetName)}; - // banksToPresets.insert(BanksToPresets::value_type(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) { - // valueTreeState.getParameter if (parameterID == "bank") { repopulateTable(); } else if (parameterID == "preset") { selectCurrentPreset(); - // RangedAudioParameter *param {valueTreeState.getParameter("preset")}; - // jassert(dynamic_cast (param) != nullptr); - // AudioParameterInt* castParam {dynamic_cast (param)}; - // int value{castParam->get()}; } } @@ -170,7 +115,6 @@ void TableComponent::repopulateTable() { upperBound, back_inserter(rows), mem_fn(&BanksToPresets::value_type::second) - // [](BanksToPresets::value_type element){return element.second;} ); table.deselectAllRows(); table.updateContent(); @@ -182,11 +126,6 @@ void TableComponent::repopulateTable() { void TableComponent::valueTreePropertyChanged( ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { - // if (treeWhosePropertyHasChanged.getType() == StringRef("presets")) { - // if (property == StringRef("synthetic")) { - // loadModelFrom(treeWhosePropertyHasChanged); - // } - // } if (treeWhosePropertyHasChanged.getType() == StringRef("banks")) { if (property == StringRef("synthetic")) { 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>& 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 int TableComponent::getNumRows() { @@ -297,40 +190,24 @@ void TableComponent::sortOrderChanged ( bool isForwards ) { if (newSortColumnId != 0) { - // int selectedRowIx = table.getSelectedRow(); - // TableRow* selectedRow; - // if (selectedRowIx >= 0) { - // selectedRow = &rows[selectedRowIx]; - // } - TableComponent::DataSorter sorter (newSortColumnId, isForwards); sort(rows.begin(), rows.end(), sorter); table.updateContent(); selectCurrentPreset(); - - // if (selectedRowIx >= 0) { - // for (auto it = rows.begin(); it != rows.end(); ++it) { - // if(it->preset == value) { - // int index {static_cast(std::distance(rows.begin(), it))}; - // table.selectRow(index); - // break; - // } - // } - // } } } void TableComponent::selectCurrentPreset() { table.deselectAllRows(); - RangedAudioParameter *param {valueTreeState.getParameter("preset")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; + RangedAudioParameter *param{valueTreeState.getParameter("preset")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; 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) { - int index {static_cast(distance(rows.begin(), it))}; + int index{static_cast(distance(rows.begin(), it))}; table.selectRow(index); break; } @@ -397,12 +274,10 @@ void TableComponent::selectedRowsChanged (int row) { if (row < 0) { return; } - // onRowSelected(rowToIDMapper(rows[row])); - // onRowSelected(stoi(rows[row][0])); int newPreset{rows[row].preset}; - RangedAudioParameter *param {valueTreeState.getParameter("preset")}; - jassert(dynamic_cast (param) != nullptr); - AudioParameterInt* castParam {dynamic_cast (param)}; + RangedAudioParameter *param{valueTreeState.getParameter("preset")}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; *castParam = newPreset; } From 21caa98fd8b1c53f08f907ec65263f38b9191d2a Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Tue, 30 Jul 2019 21:52:44 +0100 Subject: [PATCH 33/33] guess we shouldn't take the easy route of defaulting to 0, since the other parts of fluidsynthmodel are diligent enough to grab their initial values from the value tree (even if it's all-but-certain that the value tree won't have been inited yet) --- Source/FluidSynthModel.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/Source/FluidSynthModel.cpp b/Source/FluidSynthModel.cpp index 9bde407..e04845e 100644 --- a/Source/FluidSynthModel.cpp +++ b/Source/FluidSynthModel.cpp @@ -82,11 +82,23 @@ void FluidSynthModel::initialise() { fluid_synth_set_gain(synth.get(), 2.0); // note: fluid_chan.c#fluid_channel_init_ctrl() - // all SOUND_CTRL are inited with value of 64, not zero. - // "Just like panning, a value of 64 indicates no change for sound ctrls" - // and yet, I'm finding that default modulators start at MIN, so we are forced to start at 0 and climb from there - for (const auto &[controller, param]: controllerToParam) { - setControllerValue(static_cast(controller), 0); + // > Just like panning, a value of 64 indicates no change for sound ctrls + // -- + // so, advice is to leave everything at 64 + // and yet, I'm finding that default modulators start at MIN, + // i.e. we are forced to start at 0 and climb from there + // -- + // let's loop through all audio params that we manage, + // restore them to whatever value we have stored for them + // (which by default would be 0) + // super likely to be 0 regardless, since JuicySFAudioProcessor::initialise() + // runs earlier than JuicySFAudioProcessor::setStateInformation() + for (const auto &[controller, parameterID]: controllerToParam) { + RangedAudioParameter *param{valueTreeState.getParameter(parameterID)}; + jassert(dynamic_cast(param) != nullptr); + AudioParameterInt* castParam{dynamic_cast(param)}; + int value{castParam->get()}; + setControllerValue(static_cast(controller), value); } // http://www.synthfont.com/SoundFont_NRPNs.PDF