Skip to content
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
39 changes: 36 additions & 3 deletions src/dsp/noise_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ namespace baconpaul::six_sines
struct NoiseHelper
{
using NoiseType = Patch::SourceNode::NoiseType;
using LFSRMode = Patch::SourceNode::LFSRMode;

// This wierd constant is roughly the frequency at which the thing would turn over at 48khz in
// a CPU running at 1.789 mhz and a cycle length of 96 cpu cycles.
static constexpr float lfsrFreeReferenceHz{1.789 * 1000000 / 48000 * 96};

static constexpr float lfsrTuningRange{12.f};

// Tilt filter takes ±tiltMaxDb across the bipolar N range.
static constexpr float tiltMaxDb{6.f};
Expand All @@ -48,11 +55,17 @@ struct NoiseHelper
sst::filters::FastTiltNoiseFilter<Host> tiltFilter;
sst::basic_blocks::dsp::RNG &rng;

// 15-bit Galois LFSR shared by both chip modes. Seeded non-zero per helper
// so simultaneous voices don't lock-step.
uint16_t lfsrReg{0x0001};
double lfsrPhaseAcc{0.0};

NoiseHelper(sst::basic_blocks::dsp::RNG &r,
const sst::basic_blocks::tables::DbToLinearProvider &dbProv)
: pinkNoise(r.unifU32()), tiltFilter(host), rng(r)
{
host.db = &dbProv;
lfsrReg = static_cast<uint16_t>((r.unifU32() & 0x7FFFu) | 0x0001u);
}

void setSampleRate(double sr) { host.sri = 1.0 / sr; }
Expand All @@ -70,8 +83,9 @@ struct NoiseHelper
}

// Refill 16 samples of the chosen noise color into buf, normalized to ~±1.
// CHIP_LFSR is silent for now until the LFSR generator lands.
void fill16(float buf[16], NoiseType type, float nValue)
// baseFreq drives the LFSR shift clock when the mode is keytracked; otherwise
// a fixed reference (lfsrFreeReferenceHz) is used.
void fill16(float buf[16], NoiseType type, float nValue, float baseFreq, LFSRMode lfsrMode)
{
switch (type)
{
Expand All @@ -98,10 +112,29 @@ struct NoiseHelper
break;
}
case NoiseType::CHIP_LFSR:
{
const bool isShort =
(lfsrMode == LFSRMode::SHORT || lfsrMode == LFSRMode::SHORT_KEYTRACK);
const bool keytrack =
(lfsrMode == LFSRMode::SHORT_KEYTRACK || lfsrMode == LFSRMode::LONG_KEYTRACK);
const int xorBit = isShort ? 6 : 1;
const float refFreq = (keytrack ? baseFreq : 261.62) * lfsrFreeReferenceHz / 261.62;
const float shiftFreq = refFreq * std::exp2((nValue - 0.5f) * lfsrTuningRange);
const double dPhase = static_cast<double>(shiftFreq) * host.sri;
for (int i = 0; i < 16; ++i)
buf[i] = 0.f;
{
lfsrPhaseAcc += dPhase;
while (lfsrPhaseAcc >= 1.0)
{
uint16_t fb = ((lfsrReg >> 0) ^ (lfsrReg >> xorBit)) & 1u;
lfsrReg = static_cast<uint16_t>((lfsrReg >> 1) | (fb << 14));
lfsrPhaseAcc -= 1.0;
}
buf[i] = (lfsrReg & 1u) ? 1.f : -1.f;
}
break;
}
}
}
};
} // namespace baconpaul::six_sines
Expand Down
6 changes: 5 additions & 1 deletion src/dsp/op_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -493,9 +493,11 @@ struct alignas(16) OpSource : public EnvelopeSupport<Patch::SourceNode>,
using EM = Patch::SourceNode::ExtendedMode;
using NMode = Patch::SourceNode::NoiseMode;
using NT = Patch::SourceNode::NoiseType;
using LM = Patch::SourceNode::LFSRMode;
float nextM{0.f}, dM{0.f};
float nextN{0.f};
NT noiseType{NT::PINK};
LM lfsrMode{LM::LONG_KEYTRACK};
if constexpr (ET == EM::PHASE_REMAP || ET == EM::RESONANT_SWEEP || ET == EM::NOISE)
{
// Raw target m for this block: patch value + external mod + env / lfo contributions.
Expand Down Expand Up @@ -523,6 +525,8 @@ struct alignas(16) OpSource : public EnvelopeSupport<Patch::SourceNode>,
nextN = extendedLagN.v;
noiseType =
static_cast<NT>(static_cast<uint32_t>(std::round(sourceNode.noiseType.value)));
lfsrMode =
static_cast<LM>(static_cast<uint32_t>(std::round(sourceNode.lfsrMode.value)));
}

for (int i = 0; i < blockSize; ++i)
Expand Down Expand Up @@ -587,7 +591,7 @@ struct alignas(16) OpSource : public EnvelopeSupport<Patch::SourceNode>,
{
if (noisePos >= 16)
{
noiseHelper.fill16(noiseBuf, noiseType, nextN);
noiseHelper.fill16(noiseBuf, noiseType, nextN, baseFrequency, lfsrMode);
noisePos = 0;
}
float noise = noiseBuf[noisePos++];
Expand Down
27 changes: 25 additions & 2 deletions src/synth/patch.h
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,14 @@ struct Patch : pats::PatchBase<Patch, Param>
CHIP_LFSR = 3,
};

enum struct LFSRMode : uint32_t
{
SHORT = 0,
SHORT_KEYTRACK = 1,
LONG = 2,
LONG_KEYTRACK = 3,
};

// Noise samples come out of NoiseHelper normalized to ~±1. ADD_TO_PHASE
// attenuates further since one full cycle of phase jitter is too much.
static constexpr float noisePhaseScale = 0.33f;
Expand Down Expand Up @@ -761,6 +769,7 @@ struct Patch : pats::PatchBase<Patch, Param>
.withID(id(176, idx))),
extendedModeN(floatMd(version_120b)
.asPercent()
.withPolarity(md_t::Polarity::BIPOLAR)
.withName(name(idx) + " Extended Mode N")
.withGroupName(name(idx))
.withDefault(0.5f)
Expand Down Expand Up @@ -854,7 +863,19 @@ struct Patch : pats::PatchBase<Patch, Param>
{(int)NoiseType::PINK, "Pink"},
{(int)NoiseType::TILT, "Tilt"},
{(int)NoiseType::CHIP_LFSR, "Chip LFSR"},
}))
})),
lfsrMode(intMd(version_120f)
.withRange(0, 3)
.withDefault((int)LFSRMode::LONG_KEYTRACK)
.withID(id(187, idx))
.withName(name(idx) + " LFSR Mode")
.withGroupName(name(idx))
.withUnorderedMapFormatting({
{(int)LFSRMode::SHORT, "Short"},
{(int)LFSRMode::SHORT_KEYTRACK, "Short Keytrack"},
{(int)LFSRMode::LONG, "Long"},
{(int)LFSRMode::LONG_KEYTRACK, "Long Keytrack"},
}))

{
index = idx;
Expand Down Expand Up @@ -899,6 +920,7 @@ struct Patch : pats::PatchBase<Patch, Param>
Param resonantSweepFrequencyDepth;
Param noiseMode;
Param noiseType;
Param lfsrMode;

std::array<Param, numModsPer> modtarget;

Expand Down Expand Up @@ -931,7 +953,8 @@ struct Patch : pats::PatchBase<Patch, Param>
&resonantSweepWindowShape,
&resonantSweepFrequencyDepth,
&noiseMode,
&noiseType};
&noiseType,
&lfsrMode};
for (int i = 0; i < numModsPer; ++i)
res.push_back(&modtarget[i]);
appendDAHDSRParams(res);
Expand Down
40 changes: 33 additions & 7 deletions src/ui/source-sub-panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ struct ResSweepPlotter : juce::Component
*/
struct NoisePainter : juce::Component
{
const Param &wf, &ph, &mp, &np, &noiseMode, &noiseType;
const Param &wf, &ph, &mp, &np, &noiseMode, &noiseType, &lfsrMode;
SixSinesEditor &editor;
SinTable st;

Expand All @@ -431,8 +431,9 @@ struct NoisePainter : juce::Component
}

NoisePainter(const Param &w, const Param &p, const Param &mParam, const Param &nParam,
const Param &nMode, const Param &nType, SixSinesEditor &e)
: wf(w), ph(p), mp(mParam), np(nParam), noiseMode(nMode), noiseType(nType), editor(e)
const Param &nMode, const Param &nType, const Param &lMode, SixSinesEditor &e)
: wf(w), ph(p), mp(mParam), np(nParam), noiseMode(nMode), noiseType(nType), lfsrMode(lMode),
editor(e)
{
}

Expand Down Expand Up @@ -473,8 +474,10 @@ struct NoisePainter : juce::Component

using NM = Patch::SourceNode::NoiseMode;
using NT = Patch::SourceNode::NoiseType;
using LM = Patch::SourceNode::LFSRMode;
const auto nm = static_cast<NM>(static_cast<uint32_t>(std::round(noiseMode.value)));
const auto nt = static_cast<NT>(static_cast<uint32_t>(std::round(noiseType.value)));
const auto lm = static_cast<LM>(static_cast<uint32_t>(std::round(lfsrMode.value)));
const float m = mp.value;
const float nVal = np.value;

Expand Down Expand Up @@ -506,7 +509,7 @@ struct NoisePainter : juce::Component

if (noisePos >= 16)
{
helper.fill16(noiseBuf, nt, nVal);
helper.fill16(noiseBuf, nt, nVal, 220.f, lm);
noisePos = 0;
}
float n = noiseBuf[noisePos++];
Expand Down Expand Up @@ -828,9 +831,16 @@ void SourceSubPanel::setSelectedIndex(size_t idx)
noiseTypeL->setText("Type");
addChildComponent(*noiseTypeL);

noisePainter =
std::make_unique<NoisePainter>(sn.waveForm, sn.startingPhase, sn.extendedModeM,
sn.extendedModeN, sn.noiseMode, sn.noiseType, editor);
createComponent(editor, *this, sn.lfsrMode, lfsrMode, lfsrModeD);
addChildComponent(*lfsrMode);
traverse(lfsrMode);
lfsrModeL = std::make_unique<jcmp::Label>();
lfsrModeL->setText("LFSR");
addChildComponent(*lfsrModeL);

noisePainter = std::make_unique<NoisePainter>(sn.waveForm, sn.startingPhase, sn.extendedModeM,
sn.extendedModeN, sn.noiseMode, sn.noiseType,
sn.lfsrMode, editor);
addChildComponent(*noisePainter);

// Live-repaint extended-mode painters when any of their inputs change.
Expand All @@ -855,18 +865,22 @@ void SourceSubPanel::setSelectedIndex(size_t idx)
{
if (!w)
return;
w->setExtendedModeVisibility();
w->setEnabledState();
w->resized();
if (w->noisePainter)
w->noisePainter->repaint();
};
noiseTypeD->onGuiSetValue = noiseTypeChanged;
lfsrModeD->onGuiSetValue = repaintExt;
editor.componentRefreshByID[sn.extendedModeM.meta.id] = repaintExt;
editor.componentRefreshByID[sn.extendedModeN.meta.id] = repaintExt;
editor.componentRefreshByID[sn.phaseMapModeShape.meta.id] = repaintExt;
editor.componentRefreshByID[sn.resonantSweepWindowShape.meta.id] = repaintExt;
editor.componentRefreshByID[sn.resonantSweepFrequencyDepth.meta.id] = repaintExt;
editor.componentRefreshByID[sn.noiseMode.meta.id] = repaintExt;
editor.componentRefreshByID[sn.noiseType.meta.id] = noiseTypeChanged;
editor.componentRefreshByID[sn.lfsrMode.meta.id] = repaintExt;

setExtendedModeVisibility();

Expand Down Expand Up @@ -1118,6 +1132,7 @@ void SourceSubPanel::resized()
auto leftStack = jlo::VList().withAutoGap(uicMargin);
leftStack.add(labelJogRow(*noiseModeL, *noiseMode));
leftStack.add(labelJogRow(*noiseTypeL, *noiseType));
leftStack.add(labelJogRow(*lfsrModeL, *lfsrMode));
auto leftCol = jlo::VList().withWidth(leftColW);
leftCol.add(leftStack.centerInParent());
topLo.add(leftCol);
Expand Down Expand Up @@ -1184,6 +1199,15 @@ void SourceSubPanel::setExtendedModeVisibility()
noiseTypeL->setVisible(isNoise);
if (noisePainter)
noisePainter->setVisible(isNoise);

using NT = Patch::SourceNode::NoiseType;
auto &sn = editor.patchCopy.sourceNodes[index];
auto nt = static_cast<NT>(static_cast<int>(std::round(sn.noiseType.value)));
auto showLfsr = isNoise && (nt == NT::CHIP_LFSR);
if (lfsrMode)
lfsrMode->setVisible(showLfsr);
if (lfsrModeL)
lfsrModeL->setVisible(showLfsr);
}

void SourceSubPanel::setEnabledState()
Expand Down Expand Up @@ -1239,6 +1263,8 @@ void SourceSubPanel::setEnabledState()
noiseMode->setEnabled(!isAudioIn);
if (noiseType)
noiseType->setEnabled(!isAudioIn);
if (lfsrMode)
lfsrMode->setEnabled(!isAudioIn);

using NT = Patch::SourceNode::NoiseType;
auto nt = static_cast<NT>(static_cast<int>(std::round(sn.noiseType.value)));
Expand Down
3 changes: 3 additions & 0 deletions src/ui/source-sub-panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ struct SourceSubPanel : juce::Component,
std::unique_ptr<jcmp::JogUpDownButton> noiseType;
std::unique_ptr<PatchDiscrete> noiseTypeD;
std::unique_ptr<jcmp::Label> noiseTypeL;
std::unique_ptr<jcmp::JogUpDownButton> lfsrMode;
std::unique_ptr<PatchDiscrete> lfsrModeD;
std::unique_ptr<jcmp::Label> lfsrModeL;
std::unique_ptr<juce::Component> noisePainter;

void setExtendedModeVisibility();
Expand Down
Loading