Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Morphable and Driveable and Parallel filters #336

Merged
merged 25 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2030fa8
add extra parameter for filters
m-m-adams Aug 9, 2023
0b1108b
add enum for filter routing
m-m-adams Aug 9, 2023
e1119db
create enum entries and basic menu. Menu crashes with more options added
m-m-adams Aug 9, 2023
73514dd
working menu
m-m-adams Aug 9, 2023
a1fad47
enable series/parallel switching of filters
m-m-adams Aug 9, 2023
4ba8ed8
rearrange filter_set functions to reduce code duplication
m-m-adams Aug 9, 2023
586c16e
working warning free intellisense for c/cpp and python
m-m-adams Aug 10, 2023
6f22f84
working parallel routing option
m-m-adams Aug 10, 2023
3394959
enable save/load of filterroute
m-m-adams Aug 10, 2023
b449632
add hpf mode param and switch lpfmode initialization to base class to…
m-m-adams Aug 10, 2023
dfc6a95
add saving and loading for hpfmode
m-m-adams Aug 10, 2023
a7a4b2d
create HPF mode menu item, define number of HPF modes
m-m-adams Aug 10, 2023
baf1b38
move private variables to use trailing underscore
m-m-adams Aug 10, 2023
380c4b6
working high pass SVF
m-m-adams Aug 10, 2023
dfea86e
remove static saturation render parameter, replace with the used values
m-m-adams Aug 11, 2023
4295e3f
inline lpf input scaling
m-m-adams Aug 11, 2023
610bbb2
switch hpf mode on gold buttons
m-m-adams Aug 11, 2023
4293665
tweak SVF return values to balance volume with ladders
m-m-adams Aug 11, 2023
bfa5742
fix hpf saturation to match original value
m-m-adams Aug 11, 2023
24d55f4
reset default parameter in filter call
m-m-adams Aug 11, 2023
c29c439
create LPF and HPF morph parameter menus
m-m-adams Aug 11, 2023
ce6f5da
create shortcut entries for morph
m-m-adams Aug 11, 2023
9a77435
remove default UB for oled display function to enable warnings
m-m-adams Aug 11, 2023
7e73acf
enable morphing through BP for SVF and drive control for ladder filters
m-m-adams Aug 11, 2023
448379c
format fixes
m-m-adams Aug 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 33 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"configurations": [
{
"name": "Mac",
"includePath": [
"src/**",
"${default}"
],
"macFrameworkPath": [
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks"
],
"compilerPath": "${workspaceFolder}/toolchain/darwin-arm64/arm-none-eabi-gcc/bin/arm-none-eabi-gcc",
"compilerArgs": [
"-mcpu=cortex-a9",
"-marm",
"-mthumb-interwork",
"-mlittle-endian",
"-mfloat-abi=hard",
"-mfpu=neon",
"-DHAVE_OLED=1"
],
"cStandard": "gnu17",
"cppStandard": "gnu++20",
"intelliSenseMode": "gcc-arm",
"defines": [
"HAVE_OLED=1",
"${default}"
],
//"compileCommands": "${workspaceFolder}/build/compile_commands.json"
}
],
"version": 4
}
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
"python.formatting.provider": "none",
"files.associations": {
"compare": "cpp",
"ranges": "cpp"
},
"C_Cpp.errorSquiggles": "disabled",
"cSpell.words": [
"SDRAM",
"Synthstrom"
],
}
20 changes: 12 additions & 8 deletions src/definitions_cxx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ enum : ParamType {
HPF_RESONANCE,
ENV_0_SUSTAIN,
ENV_1_SUSTAIN,
LPF_MORPH,
HPF_MORPH,

// Local hybrid params begin
OSC_A_PHASE_WIDTH,
Expand Down Expand Up @@ -576,7 +578,7 @@ enum class SynthMode {
FM,
RINGMOD,
};
constexpr int kNumSynthModes = util::to_underlying(SynthMode::RINGMOD) + 1;
constexpr int kNumSynthModes = util::to_underlying(::SynthMode::RINGMOD) + 1;

enum class ModFXType {
NONE,
Expand Down Expand Up @@ -619,18 +621,20 @@ enum class FilterMode {
TRANSISTOR_24DB_DRIVE, //filter logic relies on ladders being first and contiguous
SVF,
HPLADDER, //first HPF mode
OFF, //Keep last as a sentinel. Signifies that the filter is not on, used for filter reset logic
HPSVF,
OFF, //Keep last as a sentinel. Signifies that the filter is not on, used for filter reset logic
};
constexpr FilterMode kLastLadder = FilterMode::TRANSISTOR_24DB_DRIVE;
//Off is not an LPF mode but is used to reset filters
constexpr int32_t kNumLPFModes = util::to_underlying(FilterMode::HPLADDER);

enum class HPFMode {
HPLADDER,
OFF, //Keep last as a sentinel. Signifies that the filter is not on, used for filter reset logic
constexpr int32_t kNumHPFModes = util::to_underlying(FilterMode::OFF) - kNumLPFModes;
enum class FilterRoute {
HIGH_TO_LOW,
LOW_TO_HIGH,
PARALLEL,
};
//Off is not an LPF mode but is used to reset filters
constexpr int32_t kNumHPFModes = util::to_underlying(HPFMode::OFF);

constexpr int32_t kNumFilterRoutes = util::to_underlying(FilterRoute::PARALLEL) + 1;

constexpr int32_t kNumAllpassFiltersPhaser = 6;

Expand Down
13 changes: 7 additions & 6 deletions src/deluge/dsp/filter/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ class Filter {
public:
Filter() = default;
//returns a gain compensation value
q31_t configure(q31_t frequency, q31_t resonance, FilterMode lpfMode, q31_t filterGain) {
return static_cast<T*>(this)->setConfig(frequency, resonance, lpfMode, filterGain);
q31_t configure(q31_t frequency, q31_t resonance, FilterMode lpfMode, q31_t lpfMorph, q31_t filterGain) {
//lpfmorph comes in q28 but we want q31
return static_cast<T*>(this)->setConfig(frequency, resonance, lpfMode, 4 * lpfMorph, filterGain);
}
/**
* Filter a buffer of mono samples from startSample to endSample incrememnting by the increment
Expand All @@ -51,17 +52,17 @@ class Filter {
* @param sampleIncrement increment between samples
* @param extraSaturation extra saturation value
*/
void filterMono(q31_t* startSample, q31_t* endSample, int32_t sampleIncrememt = 1, int32_t extraSaturation = 1) {
static_cast<T*>(this)->doFilter(startSample, endSample, sampleIncrememt, extraSaturation);
void filterMono(q31_t* startSample, q31_t* endSample, int32_t sampleIncrememt = 1) {
static_cast<T*>(this)->doFilter(startSample, endSample, sampleIncrememt);
}
/**
* Filter a buffer of interleaved stereo samples from startSample to endSample incrememnting by the increment
* @param startSample pointer to first sample in buffer
* @param endSample pointer to last sample
* @param extraSaturation extra saturation value
*/
void filterStereo(q31_t* startSample, q31_t* endSample, int32_t extraSaturation = 1) {
static_cast<T*>(this)->doFilterStereo(startSample, endSample, extraSaturation);
void filterStereo(q31_t* startSample, q31_t* endSample) {
static_cast<T*>(this)->doFilterStereo(startSample, endSample);
;
}
/**
Expand Down
151 changes: 112 additions & 39 deletions src/deluge/dsp/filter/filter_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,90 +31,162 @@ FilterSet::FilterSet() {
lpladder = LpLadderFilter();
hpladder = HpLadderFilter();
}
q31_t tempRenderBuffer[SSI_TX_BUFFER_NUM_SAMPLES];

void FilterSet::renderHPFLong(q31_t* startSample, q31_t* endSample, FilterMode lpfMode, int32_t sampleIncrement,
int32_t extraSaturation) {

hpladder.filterMono(startSample, endSample, sampleIncrement, extraSaturation);
void FilterSet::renderHPFLong(q31_t* startSample, q31_t* endSample, int32_t sampleIncrement) {
if (HPFOn) {
if (hpfMode_ == FilterMode::HPLADDER) {
hpladder.filterMono(startSample, endSample, sampleIncrement);
}
else if (hpfMode_ == FilterMode::HPSVF) {
hpsvf.filterMono(startSample, endSample, sampleIncrement);
}
}
}
void FilterSet::renderHPFLongStereo(q31_t* startSample, q31_t* endSample) {
if (HPFOn) {
hpladder.filterStereo(startSample, endSample);
}
}
void FilterSet::renderHPFLongStereo(q31_t* startSample, q31_t* endSample, int32_t extraSaturation) {

hpladder.filterStereo(startSample, endSample, extraSaturation);
void FilterSet::renderLPFLong(q31_t* startSample, q31_t* endSample, int32_t sampleIncrement) {
if (LPFOn) {
if (lpfMode_ == FilterMode::SVF) {
lpsvf.filterMono(startSample, endSample, sampleIncrement);
}
else {
lpladder.filterMono(startSample, endSample, sampleIncrement);
}
}
}

void FilterSet::renderLPFLong(q31_t* startSample, q31_t* endSample, FilterMode lpfMode, int32_t sampleIncrement,
int32_t extraSaturation, int32_t extraSaturationDrive) {
void FilterSet::renderLPFLongStereo(q31_t* startSample, q31_t* endSample) {
if (LPFOn) {
if (lpfMode_ == FilterMode::SVF) {

if (lpfMode == FilterMode::SVF) {
if (lastLPFMode != FilterMode::SVF) {
lpsvf.reset();
lpsvf.filterStereo(startSample, endSample);
}
lpsvf.filterMono(startSample, endSample, sampleIncrement);
}
else {
if (lastLPFMode > kLastLadder) {
lpladder.reset();
else {

lpladder.filterStereo(startSample, endSample);
}
lpladder.filterMono(startSample, endSample, sampleIncrement, extraSaturation);
}
lastLPFMode = lpfMode;
}
void FilterSet::renderLong(q31_t* startSample, q31_t* endSample, int32_t numSamples, int32_t sampleIncrememt) {
switch (routing_) {
case FilterRoute::HIGH_TO_LOW:

renderHPFLong(startSample, endSample, sampleIncrememt);
renderLPFLong(startSample, endSample, sampleIncrememt);

void FilterSet::renderLPFLongStereo(q31_t* startSample, q31_t* endSample, int32_t extraSaturation) {
break;
case FilterRoute::LOW_TO_HIGH:

if (lpfMode == FilterMode::SVF) {
renderLPFLong(startSample, endSample, sampleIncrememt);
renderHPFLong(startSample, endSample, sampleIncrememt);

lpsvf.filterStereo(startSample, endSample, extraSaturation);
break;

case FilterRoute::PARALLEL:
//render one filter in the temp buffer so we can add
//them together
int32_t length = endSample - startSample;
memcpy(tempRenderBuffer, startSample, length * sizeof(q31_t));

renderHPFLong(tempRenderBuffer, tempRenderBuffer + length, sampleIncrememt);
renderLPFLong(startSample, endSample, sampleIncrememt);

for (int i = 0; i < length; i++) {
startSample[i] += tempRenderBuffer[i];
}
break;
}
else {
}
//expects to receive an interleaved stereo stream
void FilterSet::renderLongStereo(q31_t* startSample, q31_t* endSample) {
// Do HPF, if it's on
switch (routing_) {
case FilterRoute::HIGH_TO_LOW:

renderHPFLongStereo(startSample, endSample);

renderLPFLongStereo(startSample, endSample);

break;
case FilterRoute::LOW_TO_HIGH:

lpladder.filterStereo(startSample, endSample, extraSaturation);
renderLPFLongStereo(startSample, endSample);

renderHPFLongStereo(startSample, endSample);

break;
case FilterRoute::PARALLEL:
int32_t length = endSample - startSample;

memcpy(tempRenderBuffer, startSample, length * sizeof(q31_t));

renderHPFLongStereo(tempRenderBuffer, tempRenderBuffer + length);

renderLPFLongStereo(startSample, endSample);

for (int i = 0; i < length; i++) {
startSample[i] += tempRenderBuffer[i];
}
break;
}
}

int32_t FilterSet::setConfig(int32_t lpfFrequency, int32_t lpfResonance, bool doLPF, FilterMode lpfmode,
int32_t hpfFrequency, int32_t hpfResonance, bool doHPF, FilterMode hpfmode,
int32_t filterGain, bool adjustVolumeForHPFResonance, int32_t* overallOscAmplitude) {
int32_t FilterSet::setConfig(int32_t lpfFrequency, int32_t lpfResonance, bool doLPF, FilterMode lpfmode, q31_t lpfMorph,
int32_t hpfFrequency, int32_t hpfResonance, bool doHPF, FilterMode hpfmode, q31_t hpfMorph,
int32_t filterGain, FilterRoute routing, bool adjustVolumeForHPFResonance,
int32_t* overallOscAmplitude) {
LPFOn = doLPF;
HPFOn = doHPF;
lpfMode = lpfmode;
hpfMode = hpfmode;
lpfMode_ = lpfmode;
hpfMode_ = hpfmode;
routing_ = routing;
hpfResonance =
(hpfResonance >> 21) << 21; // Insanely, having changes happen in the small bytes too often causes rustling

if (LPFOn) {
if (lpfmode == FilterMode::SVF) {
if (lastLPFMode != FilterMode::SVF) {
if (lastLPFMode_ != FilterMode::SVF) {
lpsvf.reset();
}
filterGain = lpsvf.configure(lpfFrequency, lpfResonance, lpfmode, filterGain);
filterGain = lpsvf.configure(lpfFrequency, lpfResonance, lpfmode, lpfMorph, filterGain);
}
else {
if (lastLPFMode > kLastLadder) {
if (lastLPFMode_ > kLastLadder) {
lpladder.reset();
}
filterGain = lpladder.configure(lpfFrequency, lpfResonance, lpfmode, filterGain);
filterGain = lpladder.configure(lpfFrequency, lpfResonance, lpfmode, lpfMorph, filterGain);
}
lastLPFMode = lpfMode;
lastLPFMode_ = lpfMode_;
}
else {
lastLPFMode = FilterMode::OFF;
lastLPFMode_ = FilterMode::OFF;
}
// This changes the overall amplitude so that, with resonance on 50%, the amplitude is the same as it was pre June 2017
filterGain = multiply_32x32_rshift32(filterGain, 1720000000) << 1;

// HPF
if (HPFOn) {
if (hpfMode == FilterMode::HPLADDER) {
filterGain = hpladder.configure(hpfFrequency, hpfResonance, hpfMode, filterGain);
if (lastHPFMode != hpfMode) {
if (hpfMode_ == FilterMode::HPLADDER) {
filterGain = hpladder.configure(hpfFrequency, hpfResonance, hpfmode, hpfMorph, filterGain);
if (lastHPFMode_ != hpfMode_) {
hpladder.reset();
}
}
lastHPFMode = hpfMode;
else if (hpfMode_ == FilterMode::HPSVF) {
filterGain = hpsvf.configure(hpfFrequency, hpfResonance, hpfmode, hpfMorph, filterGain);
if (lastHPFMode_ != hpfMode_) {
hpsvf.reset();
}
}
lastHPFMode_ = hpfMode_;
}
else {
lastHPFMode = FilterMode::OFF;
lastHPFMode_ = FilterMode::OFF;
}

return filterGain;
Expand All @@ -123,6 +195,7 @@ int32_t FilterSet::setConfig(int32_t lpfFrequency, int32_t lpfResonance, bool do
void FilterSet::reset() {
hpladder.reset();
lpsvf.reset();
hpsvf.reset();
lpladder.reset();
noiseLastValue = 0;
}
Expand Down