Chorus pans shared delay taps instead of per-channel offset
This commit is contained in:
parent
2cf6beaf9d
commit
4782f1ff32
34
chorus.h
34
chorus.h
@ -40,6 +40,7 @@ struct ChorusSTFX : public BaseEffect {
|
||||
ParamRange mix{0.6};
|
||||
ParamRange depthMs{15};
|
||||
ParamRange detune{5};
|
||||
ParamRange stereo{1};
|
||||
|
||||
template<class Storage>
|
||||
void state(Storage &storage) {
|
||||
@ -59,6 +60,10 @@ struct ChorusSTFX : public BaseEffect {
|
||||
.info("detune", "detuning depth")
|
||||
.range(1, 8, 50)
|
||||
.unit(" s.t.", 0);
|
||||
|
||||
stfx::units::rangePercent(storage.range("stereo", stereo)
|
||||
.info("stereo", "")
|
||||
.range(0, 0.5, 1.5));
|
||||
}
|
||||
|
||||
template<class Config>
|
||||
@ -74,12 +79,16 @@ struct ChorusSTFX : public BaseEffect {
|
||||
void reset() {
|
||||
delay.reset();
|
||||
phase = 0;
|
||||
stereoPhase = 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;
|
||||
Sample stereoPhaseStep = phaseStep/Sample(M_PI);
|
||||
Complex stereoComplexPerOutput = std::polar(Sample(1), Sample(2*M_PI)/config.outputChannels);
|
||||
Complex stereoComplexPerInternal = std::polar(Sample(1), Sample(1));
|
||||
|
||||
bool fading = depthMs.from() != depthMs.to();
|
||||
Sample depthSamples = depthMs.to()*0.001*config.sampleRate;
|
||||
@ -90,6 +99,8 @@ struct ChorusSTFX : public BaseEffect {
|
||||
|
||||
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));});
|
||||
bool notMono = (config.outputChannels > 1);
|
||||
auto width = block.smooth(stereo);
|
||||
|
||||
for (size_t i = 0; i < block.length; ++i) {
|
||||
for (size_t c = 0; c < 6; ++c) {
|
||||
@ -98,8 +109,7 @@ struct ChorusSTFX : public BaseEffect {
|
||||
}
|
||||
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));
|
||||
Complex phaseComplex = std::polar(Sample(1), phase*Sample(2*M_PI));
|
||||
for (size_t c = 0; c < 6; ++c) {
|
||||
Sample osc = (phaseComplex*oscillatorOffsets[c]).real();
|
||||
delaySamples[c] = depthSamples*Sample(0.5)*(1 + osc);
|
||||
@ -121,11 +131,28 @@ struct ChorusSTFX : public BaseEffect {
|
||||
|
||||
// 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);
|
||||
if (notMono) {
|
||||
Complex stereoComplex = std::polar(Sample(width.at(i)), stereoPhase*Sample(2*M_PI));
|
||||
for (size_t oc = 0; oc < config.outputChannels; ++oc) {
|
||||
Sample sumC = sum;
|
||||
Complex rot = stereoComplex;
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
sumC += rot.real()*multiOut[i];
|
||||
rot *= stereoComplexPerInternal;
|
||||
}
|
||||
|
||||
io.output[oc][i] = multiIn[oc]*dry.at(i) + sumC*wet.at(i);
|
||||
stereoComplex *= stereoComplexPerOutput;
|
||||
}
|
||||
stereoPhase += stereoPhaseStep;
|
||||
} else {
|
||||
io.output[0][i] = multiIn[0]*dry.at(i) + sum*wet.at(i);
|
||||
}
|
||||
|
||||
phase += phaseStep;
|
||||
}
|
||||
phase -= std::floor(phase);
|
||||
stereoPhase -= std::floor(stereoPhase);
|
||||
}
|
||||
|
||||
template<class Storage>
|
||||
@ -136,6 +163,7 @@ private:
|
||||
|
||||
signalsmith::delay::MultiDelay<Sample, signalsmith::delay::InterpolatorLagrange7> delay;
|
||||
Sample phase = 0;
|
||||
Sample stereoPhase = 0;
|
||||
};
|
||||
|
||||
}} // namespace
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user