Merge pull request #5 from Birch-san/modulators

Modulators
This commit is contained in:
Birch-san 2019-06-23 23:28:54 +01:00 committed by GitHub
commit b5fb52d7b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 306 additions and 38 deletions

View File

@ -30,7 +30,8 @@ trap 'error ${LINENO}' ERR
####
declare -a BUILDS=("Debug" "Release")
# declare -a BUILDS=("Debug" "Release")
declare -a BUILDS=("Release")
for BUILD in "${BUILDS[@]}"
do
BUILDROOT="$MYDIR/build"

View File

@ -294,6 +294,7 @@
358E45BB22BEE53A0087ED8D /* libgthread-2.0.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = "libgthread-2.0.0.dylib"; sourceTree = "<group>"; };
358E45BC22BEE53A0087ED8D /* libFLAC.8.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libFLAC.8.dylib; sourceTree = "<group>"; };
358E45BD22BEE53A0087ED8D /* libogg.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libogg.0.dylib; sourceTree = "<group>"; };
358E45F422BFC00C0087ED8D /* MidiConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MidiConstants.h; path = ../../Source/MidiConstants.h; sourceTree = "<group>"; };
35D551D55292C9D0508A408A /* PluginEditor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PluginEditor.cpp; path = ../../Source/PluginEditor.cpp; sourceTree = SOURCE_ROOT; };
373EF982A53046CE00BECE68 /* include_juce_events.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_events.mm; path = ../../JuceLibraryCode/include_juce_events.mm; sourceTree = SOURCE_ROOT; };
3909EE4609ED2DCCC6B6B290 /* juce_data_structures */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_data_structures; path = /Applications/JUCE/modules/juce_data_structures; sourceTree = "<absolute>"; };
@ -545,6 +546,7 @@
420DCC01988E65E68562F9DC /* PluginProcessor.h */,
35D551D55292C9D0508A408A /* PluginEditor.cpp */,
8990F3EAFFBBD6A42247C663 /* PluginEditor.h */,
358E45F422BFC00C0087ED8D /* MidiConstants.h */,
);
name = Source;
sourceTree = "<group>";

View File

@ -4,6 +4,7 @@
#include <iostream>
#include "FluidSynthModel.h"
#include "MidiConstants.h"
using namespace std;
@ -15,8 +16,8 @@ FluidSynthModel::FluidSynthModel(SharesParams& p)
currentSampleRate(44100),
initialised(false),
sfont_id(0),
channel(0),
mod(nullptr)
channel(0)/*,
mod(nullptr)*/
{}
@ -27,7 +28,7 @@ FluidSynthModel::~FluidSynthModel() {
delete_fluid_settings(settings);
// delete driver;
// delete settings;
delete_fluid_mod(mod);
// delete_fluid_mod(mod);
}
}
@ -43,7 +44,7 @@ void FluidSynthModel::initialise() {
settings = new_fluid_settings();
// https://sourceforge.net/p/fluidsynth/wiki/FluidSettings/
// fluid_settings_setint(settings, "synth.verbose", 1);
fluid_settings_setint(settings, "synth.verbose", 1);
synth = new_fluid_synth(settings);
fluid_synth_set_sample_rate(synth, currentSampleRate);
@ -63,36 +64,73 @@ void FluidSynthModel::initialise() {
// changePreset(128, 13);
float env_amount(12700.0f);
fluid_mod_t *mod(new_fluid_mod());
// 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
// fluid_mod_set_source2(mod, 74, FLUID_MOD_CC);
// // generator for filter cutoff
// fluid_mod_set_dest(mod, GEN_FILTERFC);
// fluid_mod_set_amount(mod, 13500.0f);
//
// fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD);
fluid_mod_set_source1(mod,
static_cast<int>(SOUND_CTRL2), // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance)
FLUID_MOD_CC
| FLUID_MOD_UNIPOLAR
| FLUID_MOD_CONCAVE
| FLUID_MOD_NEGATIVE);
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);
mod = new_fluid_mod();
fluid_mod_set_source1(mod,
static_cast<int>(SOUND_CTRL3), // MIDI CC 72 Release time
FLUID_MOD_CC
| FLUID_MOD_BIPOLAR
| FLUID_MOD_CONCAVE
| FLUID_MOD_POSITIVE);
fluid_mod_set_source2(mod, 0, 0);
fluid_mod_set_dest(mod, GEN_VOLENVRELEASE);
fluid_mod_set_amount(mod, env_amount);
fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD);
delete_fluid_mod(mod);
mod = new_fluid_mod();
fluid_mod_set_source1(mod,
static_cast<int>(SOUND_CTRL4), // MIDI CC 73 Attack time
FLUID_MOD_CC
| FLUID_MOD_BIPOLAR
| FLUID_MOD_CONCAVE
| 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);
mod = new_fluid_mod();
fluid_mod_set_source1(mod,
static_cast<int>(SOUND_CTRL5), // MIDI CC 74 Brightness (cutoff frequency, FILTERFC)
FLUID_MOD_CC
| FLUID_MOD_SWITCH
| 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);
mod = new_fluid_mod();
fluid_mod_set_source1(mod,
static_cast<int>(SOUND_CTRL6), // MIDI CC 75 Decay Time
FLUID_MOD_CC
| FLUID_MOD_BIPOLAR
| FLUID_MOD_CONCAVE
| 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);
initialised = true;
}

View File

@ -88,7 +88,7 @@ private:
unsigned int sfont_id;
unsigned int channel;
fluid_mod_t* mod;
// fluid_mod_t* mod;
ListenerList<Listener> eventListeners;

164
Source/MidiConstants.h Normal file
View File

@ -0,0 +1,164 @@
#pragma once
/* Taken from:
* fluidsynth/src/midi/fluid_midi.h
* fluidsynth/src/utils/fluid_conv_tables.h
* https://github.com/FluidSynth/fluidsynth/blob/master/src/midi/fluid_midi.h
* https://github.com/FluidSynth/fluidsynth/blob/master/src/utils/fluid_conv_tables.h
*
* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*/
enum fluid_midi_event_type
{
/* channel messages */
NOTE_OFF = 0x80,
NOTE_ON = 0x90,
KEY_PRESSURE = 0xa0,
CONTROL_CHANGE = 0xb0,
PROGRAM_CHANGE = 0xc0,
CHANNEL_PRESSURE = 0xd0,
PITCH_BEND = 0xe0,
/* system exclusive */
MIDI_SYSEX = 0xf0,
/* system common - never in midi files */
MIDI_TIME_CODE = 0xf1,
MIDI_SONG_POSITION = 0xf2,
MIDI_SONG_SELECT = 0xf3,
MIDI_TUNE_REQUEST = 0xf6,
MIDI_EOX = 0xf7,
/* system real-time - never in midi files */
MIDI_SYNC = 0xf8,
MIDI_TICK = 0xf9,
MIDI_START = 0xfa,
MIDI_CONTINUE = 0xfb,
MIDI_STOP = 0xfc,
MIDI_ACTIVE_SENSING = 0xfe,
MIDI_SYSTEM_RESET = 0xff,
/* meta event - for midi files only */
MIDI_META_EVENT = 0xff
};
enum fluid_midi_control_change
{
BANK_SELECT_MSB = 0x00,
MODULATION_MSB = 0x01,
BREATH_MSB = 0x02,
FOOT_MSB = 0x04,
PORTAMENTO_TIME_MSB = 0x05,
DATA_ENTRY_MSB = 0x06,
VOLUME_MSB = 0x07,
BALANCE_MSB = 0x08,
PAN_MSB = 0x0A,
EXPRESSION_MSB = 0x0B,
EFFECTS1_MSB = 0x0C,
EFFECTS2_MSB = 0x0D,
GPC1_MSB = 0x10, /* general purpose controller */
GPC2_MSB = 0x11,
GPC3_MSB = 0x12,
GPC4_MSB = 0x13,
BANK_SELECT_LSB = 0x20,
MODULATION_WHEEL_LSB = 0x21,
BREATH_LSB = 0x22,
FOOT_LSB = 0x24,
PORTAMENTO_TIME_LSB = 0x25,
DATA_ENTRY_LSB = 0x26,
VOLUME_LSB = 0x27,
BALANCE_LSB = 0x28,
PAN_LSB = 0x2A,
EXPRESSION_LSB = 0x2B,
EFFECTS1_LSB = 0x2C,
EFFECTS2_LSB = 0x2D,
GPC1_LSB = 0x30,
GPC2_LSB = 0x31,
GPC3_LSB = 0x32,
GPC4_LSB = 0x33,
SUSTAIN_SWITCH = 0x40,
PORTAMENTO_SWITCH = 0x41,
SOSTENUTO_SWITCH = 0x42,
SOFT_PEDAL_SWITCH = 0x43,
LEGATO_SWITCH = 0x44,
HOLD2_SWITCH = 0x45,
SOUND_CTRL1 = 0x46,
SOUND_CTRL2 = 0x47,
SOUND_CTRL3 = 0x48,
SOUND_CTRL4 = 0x49,
SOUND_CTRL5 = 0x4A,
SOUND_CTRL6 = 0x4B,
SOUND_CTRL7 = 0x4C,
SOUND_CTRL8 = 0x4D,
SOUND_CTRL9 = 0x4E,
SOUND_CTRL10 = 0x4F,
GPC5 = 0x50,
GPC6 = 0x51,
GPC7 = 0x52,
GPC8 = 0x53,
PORTAMENTO_CTRL = 0x54,
EFFECTS_DEPTH1 = 0x5B,
EFFECTS_DEPTH2 = 0x5C,
EFFECTS_DEPTH3 = 0x5D,
EFFECTS_DEPTH4 = 0x5E,
EFFECTS_DEPTH5 = 0x5F,
DATA_ENTRY_INCR = 0x60,
DATA_ENTRY_DECR = 0x61,
NRPN_LSB = 0x62,
NRPN_MSB = 0x63,
RPN_LSB = 0x64,
RPN_MSB = 0x65,
ALL_SOUND_OFF = 0x78,
ALL_CTRL_OFF = 0x79,
LOCAL_CONTROL = 0x7A,
ALL_NOTES_OFF = 0x7B,
OMNI_OFF = 0x7C,
OMNI_ON = 0x7D,
POLY_OFF = 0x7E,
POLY_ON = 0x7F
};
/*
Attenuation range in centibels.
Attenuation range is the dynamic range of the volume envelope generator
from 0 to the end of attack segment.
fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation.
However the spec makes no distinction between 16 or 24 bit synths, so use
96 dB here.
Note about usefulness of 24 bits:
1)Even fluidsynth is a 24 bit synth, this format is only relevant if
the sample format coming from the soundfont is 24 bits and the audio sample format
choosen by the application (audio.sample.format) is not 16 bits.
2)When the sample soundfont is 16 bits, the internal 24 bits number have
16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of
this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db)
even if this sample is produced by the audio driver using an audio sample format
compatible for a 24 bit DAC.
3)When the audio sample format settings is 16 bits (audio.sample.format), the
audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB
even if the initial sample comes from a 24 bits soundfont.
In both cases (2) or (3), the real dynamic range is only 96 dB.
Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3):
- for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB).
- 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

View File

@ -13,6 +13,7 @@
#include "SoundfontSynthVoice.h"
#include "SoundfontSynthSound.h"
#include "ExposesComponents.h"
#include "MidiConstants.h"
AudioProcessor* JUCE_CALLTYPE createPluginFilter();
@ -154,6 +155,63 @@ void JuicySFAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer
// add messages to the buffer if the user is clicking on the on-screen keys
keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
MidiBuffer processedMidi;
int time;
MidiMessage m;
// TODO: factor into a MidiCollector
for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);) {
Logger::outputDebugString ( 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()) {
fluid_midi_event_t *midi_event(new_fluid_midi_event());
fluid_midi_event_set_type(midi_event, static_cast<int>(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(fluidSynth, midi_event);
delete_fluid_midi_event(midi_event);
} else if (m.isProgramChange()) {
fluid_midi_event_t *midi_event(new_fluid_midi_event());
fluid_midi_event_set_type(midi_event, static_cast<int>(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);
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<int>(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);
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<int>(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(fluidSynth, 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<int>(MIDI_SYSTEM_RESET));
fluid_synth_handle_midi_event(fluidSynth, 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<int>(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<juce::uint8*>(m.getSysExData()), m.getSysExDataSize(), static_cast<int>(false));
fluid_synth_handle_midi_event(fluidSynth, midi_event);
delete_fluid_midi_event(midi_event);
}
}
// 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());

View File

@ -58,12 +58,17 @@ void SoundfontSynthVoice::stopNote (float /*velocity*/, bool allowTailOff) {
clearCurrentNote();
fluid_synth_noteoff(synth, 0, this->midiNoteNumber);
}
void SoundfontSynthVoice::pitchWheelMoved (int /*newValue*/) {
// who cares?
// receives input as MIDI 0 to 16383, with 8192 being center
// this is also exactly the input fluidsynth requires
void SoundfontSynthVoice::pitchWheelMoved (int newValue) {
Logger::outputDebugString ( juce::String::formatted("Pitch wheel: %d\n", newValue) );
fluid_synth_pitch_bend(synth, 0, newValue);
}
void SoundfontSynthVoice::controllerMoved (int /*controllerNumber*/, int /*newValue*/) {
// what's a controller?
void SoundfontSynthVoice::controllerMoved (int controllerNumber, int newValue) {
// this seems to be "program change" event
Logger::outputDebugString ( juce::String::formatted("Controller moved: %d, %d\n", controllerNumber, newValue) );
}
void SoundfontSynthVoice::renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) {