remove shared access to fluidsynth instance

This commit is contained in:
Alex Birch
2019-07-28 22:51:51 +01:00
parent d4a060b769
commit 39f9d86bd1
9 changed files with 81 additions and 333 deletions

View File

@ -675,9 +675,9 @@ void FluidSynthModel::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiM
break;
}
case SOUND_CTRL10: { // MIDI CC 79 undefined
RangedAudioParameter *param {valueTreeState.getParameter("sustain")};
RangedAudioParameter *param{valueTreeState.getParameter("sustain")};
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
*castParam = m.getControllerValue();
break;
}
@ -692,8 +692,8 @@ void FluidSynthModel::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiM
m.getProgramChangeNumber())};
if (result == FLUID_OK) {
RangedAudioParameter *param{valueTreeState.getParameter("preset")};
jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
*castParam = m.getProgramChangeNumber();
}
} else if (m.isPitchWheel()) {
@ -740,3 +740,63 @@ void FluidSynthModel::processBlock(AudioBuffer<float>& 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<AudioParameterInt*>(param) != nullptr);
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
return castParam->get();
}
void FluidSynthModel::setCurrentProgram(int index)
{
RangedAudioParameter *param{valueTreeState.getParameter("preset")};
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(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<AudioParameterInt*> (param) != nullptr);
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
bank = castParam->get();
}
{
RangedAudioParameter *param {valueTreeState.getParameter("preset")};
jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
presetNum = castParam->get();
}
fluid_preset_t *preset{fluid_sfont_get_preset(
sfont,
bank,
presetNum)};
if (!preset) {
return {};
}
return {fluid_preset_get_name(preset)};
}
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.
}

View File

@ -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<fluid_synth_t> 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<float>& 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 @@ public:
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 @@ private:
shared_ptr<fluid_synth_t> synth;
// unique_ptr<fluid_midi_driver_t, decltype(&delete_fluid_midi_driver)> midiDriver;
// String currentSoundFontAbsPath;
float currentSampleRate;
fluid_preset_t* getFirstPreset();
@ -135,17 +78,11 @@ private:
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<Listener> eventListeners;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FluidSynthModel)
};

View File

@ -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<SharesParams>(make_shared<Params>())}
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<std::unique_ptr<AudioParameterInt>> params;
// for (int i = 1; i < 9; ++i)
// params.push_back (std::make_unique<AudioParameterInt> (String (i), String (i), 0, i, 0));
// make_unique<AudioParameter>("soundfontPath", "filepath to soundfont", 0, 127, 0, "A" ),
// https://stackoverflow.com/a/8469002/5257399
unique_ptr<AudioParameterInt> params[] {
// make_unique<AudioParameterInt>("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<AudioParameterInt>("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<AudioParameterInt>("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<AudioParameterInt>("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<AudioParameterInt>("uiWidth", "width of this plugin's GUI", GuiConstants::minWidth, GuiConstants::maxWidth, GuiConstants::minWidth, "UI Width" ),
// make_unique<AudioParameterInt>("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<AudioParameterInt>("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<float>& 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()

View File

@ -64,34 +64,21 @@ public:
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<StateChangeSubscriber*> stateChangeSubscribers;
AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
static BusesProperties getBusesProperties();
// Model* model;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuicySFAudioProcessor)
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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<fluid_synth_t> 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<SoundfontSynthSound*> (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<int>(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<SoundfontSynthSound*> (sound) != nullptr);
// SoundfontSynthSound* sfsynth = dynamic_cast<SoundfontSynthSound*> (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<float>& outputBuffer, int startSample, int numSamples) {
//fluid_synth_process(synth.get(), numSamples, 1, nullptr, outputBuffer.getNumChannels(), outputBuffer.getArrayOfWritePointers());
}
//void SoundfontSynthVoice::renderBlock (AudioBuffer<float>& 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<FloatType> (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;
// }
// }
// }
//}

View File

@ -1,39 +0,0 @@
//
// Created by Alex Birch on 07/09/2017.
//
#pragma once
#include<memory>
#include<fluidsynth.h>
#include "../JuceLibraryCode/JuceHeader.h"
using namespace std;
class SoundfontSynthVoice : public SynthesiserVoice {
public:
SoundfontSynthVoice(shared_ptr<fluid_synth_t> 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<float>& outputBuffer, int startSample, int numSamples) override;
private:
double tailOff;
double level;
double currentAngle;
double angleDelta;
int midiNoteNumber;
shared_ptr<fluid_synth_t> synth;
};