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

Backwards-compatible option to use mathematically correct tilt noise. #7975

Merged
merged 4 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ add_library(${PROJECT_NAME}
dsp/SurgeVoice.cpp
dsp/SurgeVoice.h
dsp/SurgeVoiceState.h
dsp/TiltNoiseAdapter.cpp
dsp/TiltNoiseAdapter.h
dsp/Wavetable.cpp
dsp/Wavetable.h
dsp/WavetableScriptEvaluator.cpp
Expand Down
6 changes: 6 additions & 0 deletions src/common/SurgeStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,12 @@ enum NoiseColorChannels
MONO = 1
};

enum NoiseColorValue
{
LEGACY = 0,
TILT = 1,
};

enum CombinatorMode
{
cxm_ring = 0,
Expand Down
93 changes: 56 additions & 37 deletions src/common/dsp/SurgeVoice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,6 @@ using namespace std;
namespace mech = sst::basic_blocks::mechanics;
namespace sdsp = sst::basic_blocks::dsp;

enum lag_entries
{
le_osc1,
le_osc2,
le_osc3,
le_noise,
le_ring12,
le_ring23,
le_pfg,
};

inline void set1f(SIMD_M128 &m, int i, float f) { *((float *)&m + i) = f; }

inline void set1i(SIMD_M128 &m, int e, int i) { *((int *)&m + e) = i; }
Expand Down Expand Up @@ -170,6 +159,7 @@ SurgeVoice::SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *oscene, pdata *
this->host_note_id = host_nid;
this->originating_host_key = host_key;
this->originating_host_channel = host_chan;
this->tilt_noise.s = this;
this->paramModulationCount = 0;
assert(storage);
assert(oscene);
Expand Down Expand Up @@ -1215,36 +1205,16 @@ bool SurgeVoice::process_block(QuadFilterChainState &Q, int Qe)

if (noise)
{
float noisecol = limit_range(localcopy[scene->noise_colour.param_id_in_scene].f, -1.f, 1.f);
auto is_stereo_noise = scene->noise_colour.deform_type == NoiseColorChannels::STEREO;
for (int i = 0; i < BLOCK_SIZE_OS; i += 2)
{
((float *)tblock)[i] = sdsp::correlated_noise_o2mk2_supplied_value(
noisegenL[0], noisegenL[1], noisecol, storage->rand_pm1());
((float *)tblock)[i + 1] = ((float *)tblock)[i];
if (is_wide)
{
if (is_stereo_noise)
{
((float *)tblockR)[i] = sdsp::correlated_noise_o2mk2_supplied_value(
noisegenR[0], noisegenR[1], noisecol, storage->rand_pm1());
((float *)tblockR)[i + 1] = ((float *)tblockR)[i];
}
else
{
((float *)tblockR)[i] = ((float *)tblock)[i];
((float *)tblockR)[i + 1] = ((float *)tblock)[i + 1];
}
}
}

if (is_wide)
bool stereo =
((scene->noise_colour.deform_type & 0x1) == NoiseColorChannels::STEREO) && is_wide;
int type = scene->noise_colour.deform_type & 0x2;
if (type)
{
osclevels[le_noise].multiply_2_blocks(tblock, tblockR, BLOCK_SIZE_OS_QUAD);
generate_tilt_noise(is_wide, stereo, tblock, tblockR);
}
else
{
osclevels[le_noise].multiply_block(tblock, BLOCK_SIZE_OS_QUAD);
generate_legacy_noise(is_wide, stereo, tblock, tblockR);
}

if (route[5] < 2)
Expand Down Expand Up @@ -1740,3 +1710,52 @@ bool SurgeVoice::matchesChannelKeyId(int16_t channel, int16_t key, int32_t nid)

return chanMatch && keyMatch && nidMatch;
}

void SurgeVoice::generate_legacy_noise(bool wide, bool stereo, float *blockL, float *blockR)
{
float noisecol = limit_range(localcopy[scene->noise_colour.param_id_in_scene].f, -1.f, 1.f);
for (int i = 0; i < BLOCK_SIZE_OS; i += 2)
{
(blockL)[i] = sdsp::correlated_noise_o2mk2_supplied_value(noisegenL[0], noisegenL[1],
noisecol, storage->rand_pm1());
(blockL)[i + 1] = (blockL)[i];
if (wide)
{
if (stereo)
{
(blockR)[i] = sdsp::correlated_noise_o2mk2_supplied_value(
noisegenR[0], noisegenR[1], noisecol, storage->rand_pm1());
(blockR)[i + 1] = (blockR)[i];
}
else
{
(blockR)[i] = (blockL)[i];
(blockR)[i + 1] = (blockL)[i + 1];
}
}
}
if (wide)
{
osclevels[le_noise].multiply_2_blocks(blockL, blockR, BLOCK_SIZE_OS_QUAD);
}
else
{
osclevels[le_noise].multiply_block(blockL, BLOCK_SIZE_OS_QUAD);
}
}

void SurgeVoice::generate_tilt_noise(bool wide, bool stereo, float *blockL, float *blockR)
{
if (wide && stereo)
{
tilt_noise.processStereo(nullptr, nullptr, blockL, blockR, 0.f);
}
else
{
tilt_noise.processMonoToMono(nullptr, blockL, 0.f);
if (wide)
{
std::copy(blockL, blockL + BLOCK_SIZE_OS, blockR);
}
}
}
21 changes: 21 additions & 0 deletions src/common/dsp/SurgeVoice.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,27 @@
#include "SurgeStorage.h"
#include "Oscillator.h"
#include "SurgeVoiceState.h"
#include "TiltNoiseAdapter.h"
#include "ADSRModulationSource.h"
#include "LFOModulationSource.h"
#include <sst/voice-effects/generator/TiltNoise.h>
#include <vembertech/lipol.h>
#include "QuadFilterChain.h"
#include <array>

struct QuadFilterChainState;

enum lag_entries
{
le_osc1,
le_osc2,
le_osc3,
le_noise,
le_ring12,
le_ring23,
le_pfg,
};

class alignas(16) SurgeVoice
{
public:
Expand Down Expand Up @@ -297,6 +310,14 @@ class alignas(16) SurgeVoice

// MPE special cases
bool mpeEnabled;

private:
friend class VoiceTiltNoiseAdapter;
// Single per-voice instance.
sst::voice_effects::generator::TiltNoise<VoiceTiltNoiseAdapter> tilt_noise;
// Helper functions for doing noise generations.
void generate_legacy_noise(bool wide, bool stereo, float *blockL, float *blockR);
void generate_tilt_noise(bool wide, bool stereo, float *blockL, float *blockR);
};

void all_ring_modes_block(float *__restrict src1_l, float *__restrict src2_l,
Expand Down
114 changes: 114 additions & 0 deletions src/common/dsp/TiltNoiseAdapter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Surge XT - a free and open source hybrid synthesizer,
* built by Surge Synth Team
*
* Learn more at https://surge-synthesizer.github.io/
*
* Copyright 2018-2024, various authors, as described in the GitHub
* transaction log.
*
* Surge XT is released under the GNU General Public Licence v3
* or later (GPL-3.0-or-later). The license is found in the "LICENSE"
* file in the root of this repository, or at
* https://www.gnu.org/licenses/gpl-3.0.en.html
*
* Surge was a commercial product from 2004-2018, copyright and ownership
* held by Claes Johanson at Vember Audio during that period.
* Claes made Surge open source in September 2018.
*
* All source for Surge XT is available at
* https://github.com/surge-synthesizer/surge
*/

#include "TiltNoiseAdapter.h"
#include "SurgeVoice.h"
#include <sst/voice-effects/generator/TiltNoise.h>

float VoiceTiltNoiseAdapter::getFloatParam(const StorageContainer *o, size_t index)
{
using TN = sst::voice_effects::generator::TiltNoise<VoiceTiltNoiseAdapter>;

// Multiple float parameters.
switch (index)
{
case TN::fpLevel:
// TiltNoise expects it in dB, converts to linear internally.
return o->s->localcopy[o->s->lag_id[le_noise]].f;
case TN::fpTilt:
{
// TiltNoise scale is -6 to +6; our slider is -1 to +1.
int id = o->s->scene->noise_colour.param_id_in_scene;
float col = limit_range(o->s->localcopy[id].f, -1.f, 1.f);
return col * 6.f;
break;
}
case TN::fpStereoWidth:
return 1.f;
default:
std::cout << "Attempted to access an undefined float parameter # " << index
<< " in VoiceTiltNoiseAdapter!" << std::endl;
return 0.f;
}
return 0.f;
}

int VoiceTiltNoiseAdapter::getIntParam(const StorageContainer *o, size_t index)
{
// TiltNoise only has a single int param, stereo.
int id = o->s->scene->noise_colour.param_id_in_scene;
bool is_wide = o->s->scene->filterblock_configuration.val.i == fc_wide;
// Inverted from the deform bit.
if ((id & 1) && is_wide)
{
return 0;
}
return 1;
}

float VoiceTiltNoiseAdapter::dbToLinear(const StorageContainer *o, float db)
{
return o->s->storage->db_to_linear(db);
}

float VoiceTiltNoiseAdapter::equalNoteToPitch(const StorageContainer *o, float f)
{
return o->s->storage->note_to_pitch(f);
}

float VoiceTiltNoiseAdapter::getSampleRate(const StorageContainer *o)
{
return o->s->storage->samplerate;
}
float VoiceTiltNoiseAdapter::getSampleRateInv(const StorageContainer *o)
{
return 1.0 / o->s->storage->samplerate;
}

void VoiceTiltNoiseAdapter::setFloatParam(StorageContainer *o, size_t index, float val)
{
std::cout << "Unsupported attempt to set a float parameter in VoiceTiltNoiseAdapter."
<< std::endl;
}

void VoiceTiltNoiseAdapter::setIntParam(StorageContainer *o, size_t index, int val)
{
std::cout << "Unsupported attempt to set an int parameter in VoiceTiltNoiseAdapter."
<< std::endl;
}

void VoiceTiltNoiseAdapter::preReservePool(StorageContainer *o, size_t size)
{
std::cout << "Unsupported attempt to call preReservePool in VoiceTiltNoiseAdapter."
<< std::endl;
}

uint8_t *VoiceTiltNoiseAdapter::checkoutBlock(StorageContainer *o, size_t size)
{
std::cout << "Unsupported attempt to call checkoutBlock in VoiceTiltNoiseAdapter." << std::endl;
return NULL;
}

void VoiceTiltNoiseAdapter::returnBlock(StorageContainer *o, uint8_t *b, size_t size)
{
std::cout << "Unsupported attempt to call returnBlock in VoiceTiltNoiseAdapter." << std::endl;
}
64 changes: 64 additions & 0 deletions src/common/dsp/TiltNoiseAdapter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Surge XT - a free and open source hybrid synthesizer,
* built by Surge Synth Team
*
* Learn more at https://surge-synthesizer.github.io/
*
* Copyright 2018-2024, various authors, as described in the GitHub
* transaction log.
*
* Surge XT is released under the GNU General Public Licence v3
* or later (GPL-3.0-or-later). The license is found in the "LICENSE"
* file in the root of this repository, or at
* https://www.gnu.org/licenses/gpl-3.0.en.html
*
* Surge was a commercial product from 2004-2018, copyright and ownership
* held by Claes Johanson at Vember Audio during that period.
* Claes made Surge open source in September 2018.
*
* All source for Surge XT is available at
* https://github.com/surge-synthesizer/surge
*/

#ifndef SURGE_SRC_COMMON_DSP_TILTNOISEADAPTER_H
#define SURGE_SRC_COMMON_DSP_TILTNOISEADAPTER_H

#include "globals.h"

class SurgeVoice;

// Adapter for sst-effects TiltNoise to allow it to be used directly from SurgeVoice.
struct VoiceTiltNoiseAdapter
{
// Simple wrapper that holds the SurgeVoice storage and params. Be VERY CAREFUL with
// lifetime here. These are raw pointers! They're only valid for when they're valid in
// SurgeVoice.
struct StorageContainer
{
SurgeVoice const *s;

StorageContainer() = default;
StorageContainer(const StorageContainer &) = delete;
StorageContainer(StorageContainer &&) = delete;
};

static constexpr uint16_t blockSize{BLOCK_SIZE_OS};
using BaseClass = StorageContainer;

static float getFloatParam(const StorageContainer *o, size_t index);
static int getIntParam(const StorageContainer *o, size_t index);
static float dbToLinear(const StorageContainer *o, float db);
static float equalNoteToPitch(const StorageContainer *o, float f);
static float getSampleRate(const StorageContainer *o);
static float getSampleRateInv(const StorageContainer *o);

// None of these are used by TiltNoise and will print errors to the console if they are
// called.
static void setFloatParam(StorageContainer *o, size_t index, float val);
static void setIntParam(StorageContainer *o, size_t index, int val);
static void preReservePool(StorageContainer *o, size_t size);
static uint8_t *checkoutBlock(StorageContainer *o, size_t size);
static void returnBlock(StorageContainer *o, uint8_t *b, size_t size);
};

#endif // SURGE_SRC_COMMON_DSP_TILTNOISEADAPTER_H
2 changes: 2 additions & 0 deletions src/surge-xt/gui/SurgeGUIEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ class SurgeGUIEditor : public Surge::GUI::IComponentTagValue::Listener,
void adjustSize(float &width, float &height) const;

void update_deform_type(Parameter *p, int type);
// Only updates a single bitfield. Does not check values.
void update_deform_type_bit(Parameter *p, int type, int bit);

struct patchdata
{
Expand Down
Loading