Skip to content

Commit

Permalink
Backwards-compatible option to use mathematically correct tilt noise.
Browse files Browse the repository at this point in the history
  • Loading branch information
mx committed Feb 13, 2025
1 parent 6f141d6 commit e386125
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 23 deletions.
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
158 changes: 138 additions & 20 deletions src/common/dsp/SurgeVoice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,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,27 +1216,13 @@ 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)
bool stereo = ((scene->noise_colour.deform_type & 0x1) == NoiseColorChannels::STEREO) && is_wide;
int type = scene->noise_colour.deform_type & 0x2;
if (type)
{
((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];
}
}
generate_tilt_noise(is_wide, stereo, tblock, tblockR);
} else {
generate_legacy_noise(is_wide, stereo, tblock, tblockR);
}

if (is_wide)
Expand Down Expand Up @@ -1740,3 +1727,134 @@ 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];
}
}
}
}

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

// Methods for adapting sst-effects TiltNoise
// -----------------------------------------------
float SurgeVoice::TiltNoiseAdapter::getFloatParam(const StorageContainer *o, size_t index)
{
using TN = sst::voice_effects::generator::TiltNoise<SurgeVoice::TiltNoiseAdapter>;

// 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 SurgeVoice::TiltNoiseAdapter!" << std::endl;
return 0.f;
}
return 0.f;
}

int SurgeVoice::TiltNoiseAdapter::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 SurgeVoice::TiltNoiseAdapter::dbToLinear(const StorageContainer *o, float db)
{
return o->s->storage->db_to_linear(db);
}

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

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

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

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

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

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

void SurgeVoice::TiltNoiseAdapter::returnBlock(StorageContainer *o, uint8_t *b, size_t size)
{
std::cout << "Unsupported attempt to call returnBlock in SurgeVoice::TiltNoiseAdapter."
<< std::endl;
}
42 changes: 42 additions & 0 deletions src/common/dsp/SurgeVoice.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "SurgeVoiceState.h"
#include "ADSRModulationSource.h"
#include "LFOModulationSource.h"
#include <sst/voice-effects/generator/TiltNoise.h>
#include <vembertech/lipol.h>
#include "QuadFilterChain.h"
#include <array>
Expand Down Expand Up @@ -297,6 +298,47 @@ class alignas(16) SurgeVoice

// MPE special cases
bool mpeEnabled;

private:
// Adapter for sst-effects TiltNoise to allow it to be used directly from SurgeVoice.
struct TiltNoiseAdapter
{
// 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 inline float getFloatParam(const StorageContainer *o, size_t index);
static inline int getIntParam(const StorageContainer *o, size_t index);
static inline float dbToLinear(const StorageContainer *o, float db);
static inline float equalNoteToPitch(const StorageContainer *o, float f);
static inline float getSampleRate(const StorageContainer *o);
static inline float getSampleRateInv(const StorageContainer *o);

// None of these are used by TiltNoise and will print errors to the console if they are
// called.
static inline void setFloatParam(StorageContainer *o, size_t index, float val);
static inline void setIntParam(StorageContainer *o, size_t index, int val);
static inline void preReservePool(StorageContainer *o, size_t size);
static inline uint8_t *checkoutBlock(StorageContainer *o, size_t size);
static inline void returnBlock(StorageContainer *o, uint8_t *b, size_t size);
};
friend class TiltNoiseAdapter;
// Single per-voice instance.
sst::voice_effects::generator::TiltNoise<TiltNoiseAdapter> 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
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
37 changes: 34 additions & 3 deletions src/surge-xt/gui/SurgeGUIEditorValueCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2478,27 +2478,50 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c
.filterblock_configuration.val.i == fc_wide)
{
contextMenu.addSeparator();
auto dt = p->deform_type;
auto dt = p->deform_type & 0x1;

Surge::Widgets::MenuCenteredBoldLabel::addToMenuAsSectionHeader(
contextMenu, "NOISE GENERATOR MODE");

contextMenu.addItem(
"Mono", true, dt == NoiseColorChannels::MONO, [this, p]() {
undoManager()->pushParameterChange(p->id, p, p->val);
update_deform_type(p, NoiseColorChannels::MONO);
update_deform_type_bit(p, NoiseColorChannels::MONO, 0);
synth->storage.getPatch().isDirty = true;
frame->repaint();
});
contextMenu.addItem(
"Stereo", true, dt == NoiseColorChannels::STEREO, [this, p]() {
undoManager()->pushParameterChange(p->id, p, p->val);
update_deform_type(p, NoiseColorChannels::STEREO);
update_deform_type_bit(p, NoiseColorChannels::STEREO, 0);
synth->storage.getPatch().isDirty = true;
frame->repaint();
});
}
{
contextMenu.addSeparator();
auto dt = (p->deform_type & 0x2) >> 1;

Surge::Widgets::MenuCenteredBoldLabel::addToMenuAsSectionHeader(
contextMenu, "NOISE GENERATOR COLOR TYPE");

contextMenu.addItem(
"Legacy (Pre-XT 1.4)", true, dt == NoiseColorValue::LEGACY,
[this, p]() {
undoManager()->pushParameterChange(p->id, p, p->val);
update_deform_type_bit(p, NoiseColorValue::LEGACY, 1);
synth->storage.getPatch().isDirty = true;
frame->repaint();
});
contextMenu.addItem(
"Tilt", true, dt == NoiseColorValue::TILT,
[this, p]() {
undoManager()->pushParameterChange(p->id, p, p->val);
update_deform_type_bit(p, NoiseColorValue::TILT, 1);
synth->storage.getPatch().isDirty = true;
frame->repaint();
});
}
break;
}
case ct_amplitude_ringmod:
Expand Down Expand Up @@ -3369,6 +3392,14 @@ void SurgeGUIEditor::update_deform_type(Parameter *p, int type)
(float)type, .0, .0, "");
}

void SurgeGUIEditor::update_deform_type_bit(Parameter *p, int type, int bit)
{
int old = p->deform_type & ~(1 << bit);
p->deform_type = old | (type << bit);
juceEditor->processor.paramChangeToListeners(p, true, juceEditor->processor.SCT_EX_DEFORM,
(float)type, .0, .0, "");
}

void SurgeGUIEditor::valueChanged(Surge::GUI::IComponentTagValue *control)
{
if (!frame)
Expand Down

0 comments on commit e386125

Please sign in to comment.