2018-02-27 08:25:20 +08:00
|
|
|
//
|
|
|
|
// Created by Alex Birch on 10/09/2017.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include "FluidSynthModel.h"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
2018-04-10 08:20:23 +08:00
|
|
|
FluidSynthModel::FluidSynthModel(SharesParams& p)
|
|
|
|
: sharesParams(p),
|
2018-04-10 07:51:21 +08:00
|
|
|
synth(nullptr),
|
|
|
|
settings(nullptr),
|
2018-04-11 06:29:32 +08:00
|
|
|
currentSoundFontAbsPath(),
|
2018-04-16 04:32:26 +08:00
|
|
|
currentSampleRate(44100),
|
2018-04-10 07:51:21 +08:00
|
|
|
initialised(false),
|
|
|
|
sfont_id(0),
|
2018-06-23 04:32:16 +08:00
|
|
|
channel(0),
|
|
|
|
mod(nullptr)
|
2018-04-10 07:51:21 +08:00
|
|
|
|
|
|
|
{}
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
FluidSynthModel::~FluidSynthModel() {
|
|
|
|
if (initialised) {
|
|
|
|
// delete_fluid_audio_driver(driver);
|
|
|
|
delete_fluid_synth(synth);
|
|
|
|
delete_fluid_settings(settings);
|
|
|
|
// delete driver;
|
|
|
|
// delete settings;
|
2018-06-23 04:32:16 +08:00
|
|
|
delete_fluid_mod(mod);
|
2018-02-27 08:25:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-10 08:20:23 +08:00
|
|
|
void FluidSynthModel::initialise() {
|
2018-02-28 07:42:16 +08:00
|
|
|
// if (initialised) {
|
|
|
|
// delete_fluid_synth(synth);
|
|
|
|
// delete_fluid_settings(settings);
|
|
|
|
// }
|
2018-04-18 05:10:01 +08:00
|
|
|
// 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};
|
|
|
|
fluid_audio_driver_register(DRV);
|
|
|
|
|
2018-02-27 08:25:20 +08:00
|
|
|
settings = new_fluid_settings();
|
|
|
|
// https://sourceforge.net/p/fluidsynth/wiki/FluidSettings/
|
2019-06-24 01:12:25 +08:00
|
|
|
fluid_settings_setint(settings, "synth.verbose", 1);
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
synth = new_fluid_synth(settings);
|
2018-04-16 04:32:26 +08:00
|
|
|
fluid_synth_set_sample_rate(synth, currentSampleRate);
|
2018-02-27 08:25:20 +08:00
|
|
|
|
2018-04-13 07:40:27 +08:00
|
|
|
if (sharesParams.getSoundFontPath().isNotEmpty()) {
|
|
|
|
loadFont(sharesParams.getSoundFontPath());
|
2018-04-13 08:14:07 +08:00
|
|
|
changePreset(sharesParams.getBank(), sharesParams.getPreset());
|
2018-04-13 07:40:27 +08:00
|
|
|
}
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
fluid_synth_set_gain(synth, 2.0);
|
|
|
|
|
|
|
|
// fluid_synth_bank_select(synth, 0, 3);
|
|
|
|
|
|
|
|
// fluid_handle_inst
|
|
|
|
|
|
|
|
// driver = new_fluid_audio_driver(settings, synth);
|
|
|
|
|
|
|
|
// changePreset(128, 13);
|
2019-06-23 05:59:28 +08:00
|
|
|
|
|
|
|
|
2018-02-27 08:25:20 +08:00
|
|
|
|
2019-06-23 05:59:28 +08:00
|
|
|
// mod = new_fluid_mod();
|
|
|
|
//
|
|
|
|
// // modulator's primary source controller and flags
|
|
|
|
// // fluid_mod_src:
|
|
|
|
// // https://github.com/FluidSynth/fluidsynth/blob/master/include/fluidsynth/mod.h#L61
|
|
|
|
// // fluid_mod_flags:
|
|
|
|
// // https://github.com/FluidSynth/fluidsynth/blob/master/include/fluidsynth/mod.h#L41
|
|
|
|
// // diagrams showing what negative and concave mean:
|
|
|
|
// // https://musescore.org/en/user/527826/blog/2016/05/23/volume-fluidsynth
|
|
|
|
// // fluid_gen_type:
|
|
|
|
// // https://github.com/FluidSynth/fluidsynth/blob/master/include/fluidsynth/gen.h#L36
|
|
|
|
// // https://github.com/FluidSynth/fluidsynth/blob/master/src/synth/fluid_gen.c#L27
|
|
|
|
// fluid_mod_set_source1(mod,
|
|
|
|
// FLUID_MOD_KEYPRESSURE,
|
|
|
|
// FLUID_MOD_CC |
|
|
|
|
// FLUID_MOD_POSITIVE |
|
|
|
|
// FLUID_MOD_UNIPOLAR |
|
|
|
|
// FLUID_MOD_CONCAVE);
|
|
|
|
// // modulator's secondary source controller and flags
|
|
|
|
// // MIDI CC 74
|
2019-06-24 01:12:25 +08:00
|
|
|
//// fluid_mod_set_source2(mod, 74, FLUID_MOD_CC);
|
|
|
|
// fluid_mod_set_source2(mod, 0, 0);
|
2019-06-23 05:59:28 +08:00
|
|
|
// // generator for filter cutoff
|
2019-06-24 01:12:25 +08:00
|
|
|
// fluid_mod_set_dest(mod, GEN_MODENVATTACK);
|
2019-06-23 05:59:28 +08:00
|
|
|
// fluid_mod_set_amount(mod, 13500.0f);
|
|
|
|
//
|
|
|
|
// fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD);
|
|
|
|
|
|
|
|
|
2018-06-23 04:32:16 +08:00
|
|
|
|
2018-02-27 08:25:20 +08:00
|
|
|
initialised = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int FluidSynthModel::getChannel() {
|
|
|
|
return channel;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FluidSynthModel::changePreset(int bank, int preset) {
|
2018-04-13 07:40:27 +08:00
|
|
|
if (bank == -1 || preset == -1) {
|
2018-04-13 08:14:07 +08:00
|
|
|
unique_ptr<BankAndPreset> bankAndPreset = getFirstBankAndPreset();
|
|
|
|
bank = bankAndPreset->getBank();
|
|
|
|
preset = bankAndPreset->getPreset();
|
2018-04-13 07:40:27 +08:00
|
|
|
}
|
|
|
|
changePresetImpl(bank, preset);
|
|
|
|
sharesParams.setPreset(preset);
|
|
|
|
sharesParams.setBank(bank);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FluidSynthModel::changePresetImpl(int bank, int preset) {
|
2018-02-27 08:25:20 +08:00
|
|
|
fluid_synth_program_select(synth, channel, sfont_id, static_cast<unsigned int>(bank), static_cast<unsigned int>(preset));
|
|
|
|
}
|
|
|
|
|
2018-06-18 00:53:32 +08:00
|
|
|
fluid_preset_t* FluidSynthModel::getFirstPreset() {
|
2018-02-27 08:25:20 +08:00
|
|
|
fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, sfont_id);
|
|
|
|
|
|
|
|
jassert(sfont != nullptr);
|
2018-06-18 00:53:32 +08:00
|
|
|
fluid_sfont_iteration_start(sfont);
|
2018-02-27 08:25:20 +08:00
|
|
|
|
2018-06-18 00:53:32 +08:00
|
|
|
return fluid_sfont_iteration_next(sfont);
|
2018-02-27 08:25:20 +08:00
|
|
|
}
|
|
|
|
|
2018-04-13 08:14:07 +08:00
|
|
|
unique_ptr<BankAndPreset> FluidSynthModel::getFirstBankAndPreset() {
|
2018-06-18 00:53:32 +08:00
|
|
|
fluid_preset_t* preset = getFirstPreset();
|
2018-04-13 08:14:07 +08:00
|
|
|
|
|
|
|
int offset = fluid_synth_get_bank_offset(synth, sfont_id);
|
|
|
|
|
2018-06-18 00:53:32 +08:00
|
|
|
return make_unique<BankAndPreset>(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset));
|
2018-04-13 08:14:07 +08:00
|
|
|
};
|
|
|
|
|
2018-02-27 08:25:20 +08:00
|
|
|
void FluidSynthModel::selectFirstPreset() {
|
2018-06-18 00:53:32 +08:00
|
|
|
fluid_preset_t* preset = getFirstPreset();
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
int offset = fluid_synth_get_bank_offset(synth, sfont_id);
|
|
|
|
|
2018-06-18 00:53:32 +08:00
|
|
|
changePreset(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset));
|
2018-02-27 08:25:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
BanksToPresets FluidSynthModel::getBanks() {
|
|
|
|
BanksToPresets banksToPresets;
|
|
|
|
|
2018-03-06 06:33:20 +08:00
|
|
|
int soundfontCount = fluid_synth_sfcount(synth);
|
|
|
|
|
|
|
|
if (soundfontCount == 0) {
|
|
|
|
// no soundfont selected
|
|
|
|
return banksToPresets;
|
|
|
|
}
|
|
|
|
|
2018-02-27 08:25:20 +08:00
|
|
|
fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, sfont_id);
|
2018-03-06 06:33:20 +08:00
|
|
|
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;
|
|
|
|
}
|
2018-02-27 08:25:20 +08:00
|
|
|
|
|
|
|
int offset = fluid_synth_get_bank_offset(synth, sfont_id);
|
|
|
|
|
2018-06-18 00:53:32 +08:00
|
|
|
fluid_sfont_iteration_start(sfont);
|
2018-02-27 08:25:20 +08:00
|
|
|
|
2018-06-18 00:53:32 +08:00
|
|
|
for(fluid_preset_t* preset = fluid_sfont_iteration_next(sfont);
|
|
|
|
preset != nullptr;
|
|
|
|
preset = fluid_sfont_iteration_next(sfont)) {
|
2018-02-27 08:25:20 +08:00
|
|
|
banksToPresets.insert(BanksToPresets::value_type(
|
2018-06-18 00:53:32 +08:00
|
|
|
fluid_preset_get_banknum(preset) + offset,
|
2018-02-27 08:25:20 +08:00
|
|
|
*new Preset(
|
2018-06-18 00:53:32 +08:00
|
|
|
fluid_preset_get_num(preset),
|
|
|
|
fluid_preset_get_name(preset)
|
2018-02-27 08:25:20 +08:00
|
|
|
)
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
return banksToPresets;
|
|
|
|
}
|
|
|
|
|
|
|
|
fluid_synth_t* 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;
|
|
|
|
}
|
|
|
|
|
2018-04-13 08:14:07 +08:00
|
|
|
void FluidSynthModel::onFileNameChanged(const String &absPath, int bank, int preset) {
|
2018-04-11 06:29:32 +08:00
|
|
|
if (!shouldLoadFont(absPath)) {
|
|
|
|
return;
|
|
|
|
}
|
2018-02-27 08:25:20 +08:00
|
|
|
unloadAndLoadFont(absPath);
|
2018-04-13 08:14:07 +08:00
|
|
|
changePreset(bank, preset);
|
2018-04-11 06:29:32 +08:00
|
|
|
sharesParams.setSoundFontPath(absPath);
|
2018-04-10 07:51:21 +08:00
|
|
|
eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath);
|
2018-02-27 08:25:20 +08:00
|
|
|
}
|
|
|
|
|
2018-04-11 06:29:32 +08:00
|
|
|
void FluidSynthModel::unloadAndLoadFont(const String &absPath) {
|
2018-03-06 06:38:51 +08:00
|
|
|
// in the base case, there is no font loaded
|
|
|
|
if (fluid_synth_sfcount(synth) > 0) {
|
|
|
|
fluid_synth_sfunload(synth, sfont_id, 1);
|
|
|
|
}
|
2018-02-27 08:25:20 +08:00
|
|
|
loadFont(absPath);
|
|
|
|
}
|
|
|
|
|
2018-04-11 06:29:32 +08:00
|
|
|
void FluidSynthModel::loadFont(const String &absPath) {
|
|
|
|
currentSoundFontAbsPath = absPath;
|
2018-02-27 08:25:20 +08:00
|
|
|
sfont_id++;
|
2018-04-11 06:29:32 +08:00
|
|
|
fluid_synth_sfload(synth, absPath.toStdString().c_str(), 1);
|
2018-02-27 08:25:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
FluidSynthModel::Listener::~Listener() {
|
|
|
|
}
|
|
|
|
|
2018-04-11 06:29:32 +08:00
|
|
|
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) {
|
2018-02-27 08:25:20 +08:00
|
|
|
}
|
|
|
|
|
2018-04-11 07:08:15 +08:00
|
|
|
const String& FluidSynthModel::getCurrentSoundFontAbsPath() {
|
|
|
|
return currentSoundFontAbsPath;
|
|
|
|
}
|
|
|
|
|
2018-02-27 08:25:20 +08:00
|
|
|
//==============================================================================
|
|
|
|
void FluidSynthModel::addListener (FluidSynthModel::Listener* const newListener)
|
|
|
|
{
|
|
|
|
eventListeners.add(newListener);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FluidSynthModel::removeListener (FluidSynthModel::Listener* const listener)
|
|
|
|
{
|
|
|
|
eventListeners.remove(listener);
|
2018-04-16 04:32:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FluidSynthModel::setSampleRate(float sampleRate) {
|
|
|
|
currentSampleRate = sampleRate;
|
|
|
|
if (!initialised) {
|
|
|
|
// don't worry; we'll do this in initialise phase regardless
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fluid_synth_set_sample_rate(synth, sampleRate);
|
2018-06-18 00:53:32 +08:00
|
|
|
}
|