Compare commits
No commits in common. "fd8032872726de0321b4939b395cfd0278afa3ee" and "1797274fbb3343c35ee8f8db942dc9a32e847104" have entirely different histories.
fd80328727
...
1797274fbb
141
chorus.h
141
chorus.h
@ -1,141 +0,0 @@
|
|||||||
/* Copyright 2022 Signalsmith Audio Ltd. / Geraint Luff
|
|
||||||
Released under the Boost Software License (see LICENSE.txt) */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "stfx/stfx-library.h"
|
|
||||||
|
|
||||||
#include "dsp/delay.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace signalsmith { namespace basics {
|
|
||||||
|
|
||||||
template<class BaseEffect>
|
|
||||||
struct ChorusSTFX;
|
|
||||||
|
|
||||||
using ChorusFloat = stfx::LibraryEffect<float, ChorusSTFX>;
|
|
||||||
using ChorusDouble = stfx::LibraryEffect<double, ChorusSTFX>;
|
|
||||||
|
|
||||||
template<class BaseEffect>
|
|
||||||
struct ChorusSTFX : public BaseEffect {
|
|
||||||
using typename BaseEffect::Sample;
|
|
||||||
using Complex = std::complex<Sample>;
|
|
||||||
using typename BaseEffect::ParamRange;
|
|
||||||
using typename BaseEffect::ParamStepped;
|
|
||||||
|
|
||||||
// Chosen so that points when the delays cross over and
|
|
||||||
static constexpr Complex oscillatorOffsets[6] = {
|
|
||||||
{Sample(-0.267409), Sample(0.195109)},
|
|
||||||
{Sample(-0.680406), Sample(-0.363478)},
|
|
||||||
{Sample(0.802226), Sample(0.551654)},
|
|
||||||
{Sample(0.188176), Sample(-0.980498)},
|
|
||||||
{Sample(0.973565), Sample(-0.204023)},
|
|
||||||
{Sample(-0.742801), Sample(0.669512)}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Sample maxDepthMs;
|
|
||||||
|
|
||||||
ChorusSTFX(Sample maxDepthMs=50) : maxDepthMs(maxDepthMs) {}
|
|
||||||
|
|
||||||
ParamRange mix{0.6};
|
|
||||||
ParamRange depthMs{15};
|
|
||||||
ParamRange detune{5};
|
|
||||||
|
|
||||||
template<class Storage>
|
|
||||||
void state(Storage &storage) {
|
|
||||||
storage.info("[Basics] Chorus", "");
|
|
||||||
storage.version(0);
|
|
||||||
|
|
||||||
stfx::units::rangePercent(storage.range("mix", mix)
|
|
||||||
.info("mix", "")
|
|
||||||
.range(0, 0.5, 1));
|
|
||||||
|
|
||||||
storage.range("depthMs", depthMs)
|
|
||||||
.info("depth", "chorus delay range")
|
|
||||||
.range(2, 15, maxDepthMs)
|
|
||||||
.unit("ms", 1, 0, 9.9).unit("ms", 0);
|
|
||||||
|
|
||||||
storage.range("detune", detune)
|
|
||||||
.info("detune", "detuning depth")
|
|
||||||
.range(1, 8, 50)
|
|
||||||
.unit(" s.t.", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Config>
|
|
||||||
void configureSTFX(Config &config) {
|
|
||||||
sampleRate = config.sampleRate;
|
|
||||||
config.outputChannels = config.inputChannels;
|
|
||||||
config.auxInputs = config.auxOutputs = {};
|
|
||||||
|
|
||||||
Sample maxDelaySamples = maxDepthMs*0.001*config.sampleRate;
|
|
||||||
delay.resize(6, maxDelaySamples);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
delay.reset();
|
|
||||||
phase = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Io, class Config, class Block>
|
|
||||||
void processSTFX(Io &io, Config &config, Block &block) {
|
|
||||||
Sample detuneHz = detune*0.45f/depthMs; // 0.45ms oscillation at 1Hz is about 1 semitone
|
|
||||||
Sample phaseStep = detuneHz/config.sampleRate;
|
|
||||||
|
|
||||||
bool fading = depthMs.from() != depthMs.to();
|
|
||||||
Sample depthSamples = depthMs.to()*0.001*config.sampleRate;
|
|
||||||
Sample depthSamplesFrom = depthMs.from()*0.001*config.sampleRate;
|
|
||||||
|
|
||||||
std::array<Sample, 6> multiIn, multiOut;
|
|
||||||
std::array<Sample, 6> delaySamples;
|
|
||||||
|
|
||||||
auto dry = block.smooth(mix, [](double m){return 1 - m*m;});
|
|
||||||
auto wet = block.smooth(mix, [](double m){return m*(2 - m)/std::sqrt(Sample(6));});
|
|
||||||
|
|
||||||
for (size_t i = 0; i < block.length; ++i) {
|
|
||||||
for (size_t c = 0; c < 6; ++c) {
|
|
||||||
size_t inputC = c%config.inputChannels;
|
|
||||||
multiIn[c] = io.input[inputC][i];
|
|
||||||
}
|
|
||||||
delay.write(multiIn);
|
|
||||||
|
|
||||||
for (size_t oc = 0; oc < config.outputChannels; ++oc) {
|
|
||||||
Complex phaseComplex = std::polar(Sample(1), phase*Sample(2*M_PI) + oc*Sample(2.632));
|
|
||||||
for (size_t c = 0; c < 6; ++c) {
|
|
||||||
Sample osc = (phaseComplex*oscillatorOffsets[c]).real();
|
|
||||||
delaySamples[c] = depthSamples*Sample(0.5)*(1 + osc);
|
|
||||||
}
|
|
||||||
delay.readMulti(delaySamples, multiOut);
|
|
||||||
|
|
||||||
if (fading) {
|
|
||||||
// read a second set of delay times, and fade between them
|
|
||||||
std::array<Sample, 6> multiOutFrom;
|
|
||||||
for (size_t c = 0; c < 6; ++c) {
|
|
||||||
Sample osc = (phaseComplex*oscillatorOffsets[c]).real();
|
|
||||||
delaySamples[c] = depthSamplesFrom*Sample(0.5)*(1 + osc);
|
|
||||||
}
|
|
||||||
delay.readMulti(delaySamples, multiOutFrom);
|
|
||||||
for (size_t c = 0; c < 6; ++c) {
|
|
||||||
multiOut[c] = block.fade(i, multiOutFrom[c], multiOut[c]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arbitrarily chosen gains, 4 positive, 2 negative
|
|
||||||
Sample sum = multiOut[0] - multiOut[1] + multiOut[2] + multiOut[3] - multiOut[4] + multiOut[5];
|
|
||||||
io.output[oc][i] = multiIn[oc]*dry.at(i) + sum*wet.at(i);
|
|
||||||
}
|
|
||||||
phase += phaseStep;
|
|
||||||
}
|
|
||||||
phase -= std::floor(phase);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Storage>
|
|
||||||
void meterState(Storage &storage) {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Sample sampleRate;
|
|
||||||
|
|
||||||
signalsmith::delay::MultiDelay<Sample, signalsmith::delay::InterpolatorLagrange7> delay;
|
|
||||||
Sample phase = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
}} // namespace
|
|
||||||
@ -4,7 +4,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "signalsmith-basics/analyser.h"
|
#include "signalsmith-basics/analyser.h"
|
||||||
#include "signalsmith-basics/chorus.h"
|
|
||||||
#include "signalsmith-basics/crunch.h"
|
#include "signalsmith-basics/crunch.h"
|
||||||
#include "signalsmith-basics/limiter.h"
|
#include "signalsmith-basics/limiter.h"
|
||||||
#include "signalsmith-basics/reverb.h"
|
#include "signalsmith-basics/reverb.h"
|
||||||
@ -13,21 +12,6 @@
|
|||||||
|
|
||||||
static stfx::clap::Plugins plugins;
|
static stfx::clap::Plugins plugins;
|
||||||
bool clap_init(const char *path) {
|
bool clap_init(const char *path) {
|
||||||
plugins.add<signalsmith::basics::ChorusSTFX>({
|
|
||||||
.clap_version = CLAP_VERSION,
|
|
||||||
.id = "uk.co.signalsmith.basics.chorus",
|
|
||||||
.name = "[Basics] Chorus",
|
|
||||||
.vendor = "Signalsmith Audio",
|
|
||||||
.url = "",
|
|
||||||
.manual_url = "",
|
|
||||||
.support_url = "",
|
|
||||||
.version = "1.0.0"
|
|
||||||
}, {
|
|
||||||
CLAP_PLUGIN_FEATURE_AUDIO_EFFECT,
|
|
||||||
CLAP_PLUGIN_FEATURE_CHORUS,
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
plugins.add<signalsmith::basics::CrunchSTFX>({
|
plugins.add<signalsmith::basics::CrunchSTFX>({
|
||||||
.clap_version = CLAP_VERSION,
|
.clap_version = CLAP_VERSION,
|
||||||
.id = "uk.co.signalsmith.basics.crunch",
|
.id = "uk.co.signalsmith.basics.crunch",
|
||||||
@ -80,9 +64,9 @@ bool clap_init(const char *path) {
|
|||||||
.support_url = "",
|
.support_url = "",
|
||||||
.version = "1.0.0"
|
.version = "1.0.0"
|
||||||
}, {
|
}, {
|
||||||
CLAP_PLUGIN_FEATURE_ANALYZER,
|
CLAP_PLUGIN_FEATURE_AUDIO_EFFECT,
|
||||||
|
CLAP_PLUGIN_FEATURE_DISTORTION,
|
||||||
});
|
});
|
||||||
//*/
|
|
||||||
|
|
||||||
return plugins.clap_init(path);
|
return plugins.clap_init(path);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
#include "../../chorus.h"
|
|
||||||
Loading…
x
Reference in New Issue
Block a user