You've already forked ivan-torvalds-GuitarPedal
forked from AllSpiceMirrors/torvalds-GuitarPedal
Back when mixing was each effect's own business, they all did a plain linear blend except for the vibrato, which did an equal-power one with a sin/cos pair. When the mix moved into the effect chain that distinction got flattened, and the vibrato quietly lost it. It shouldn't have. The two laws differ in what they assume about whether the wet and dry are correlated, and each is wrong by 3dB in the other's case. When the wet is a filtered version of the dry - which is most of what's here - the amplitudes add, half of each gives you back the original, and an equal-power mix puts a 3dB hump right in the middle of the sweep where everyone leaves the knob. When they're uncorrelated the powers add instead, and it's the linear mix that leaves a 3dB hole there. So it isn't one law for everything, it is a property of the effect, and the effect header is where it should say so. Add a 'MIX:' annotation alongside PRIORITY and DEFAULT_MIX, defaulting to LINEAR, and mark the four whose wet really is decorrelated from the dry: vibrato, echo, reverb and the pitch shifter. Phaser and flanger stay linear on purpose, even though it's tempting. An allpass or comb has the same magnitude spectrum as the input and only differs in phase, and the cancellation is the whole point - equal power would fill the notches back in by 3dB and make them sound weak. Nothing uses the field yet; do_effect_step() still does what it did. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
67 lines
1.9 KiB
C
67 lines
1.9 KiB
C
// NAME: Pitch [PITCH]
|
|
// PRIORITY: 100
|
|
// MIX: POWER // shifted, so it decorrelates almost immediately
|
|
// POT: "Octave" LINEAR(-2.0 2.0) = 1.0
|
|
// POT: "Feedback" LINEAR(0.0 1.0) = 0.5
|
|
// DEFAULT_MIX: 0.5
|
|
//
|
|
// Entirely random pitch shifting effect walking the
|
|
// sample buffer at varying speeds, and hiding the
|
|
// discontinuities in the sequence by picking two
|
|
// different delays, and multiplying them with sin/cos
|
|
//
|
|
// sin/cos are zero at the respective discontinuities,
|
|
// and the signal power is proportional to the square
|
|
// of the voltage. With sin^2 * cos^2 = 1, the result
|
|
// should be a unity signal power gain.
|
|
//
|
|
|
|
#define DISCONT_SHIFT 12
|
|
#define DISCONT_STEPS (1 << DISCONT_SHIFT)
|
|
|
|
struct {
|
|
float step, feedback;
|
|
unsigned phase, idx;
|
|
float array[4*DISCONT_STEPS];
|
|
} pitch;
|
|
|
|
static void pitch_init(unsigned char pot[10])
|
|
{
|
|
// Which direction do we walk the samples?
|
|
// Walking backwards lowers the pitch
|
|
// Walking forwards raises the pitch
|
|
// Staying at the same delay keeps the pitch the same
|
|
//
|
|
float step = pow2(pitch_pot0(pot[0])); // 0.25 .. 4
|
|
pitch.step = step - 1; // -0.75 .. 3
|
|
pitch.feedback = pitch_pot1(pot[1]);
|
|
}
|
|
|
|
// i is discontinuous when sin**2 is 0
|
|
// ni is discontinuous when cos**2 (aka 1-sin**2) is 0
|
|
static float pitch_step(float in)
|
|
{
|
|
const u32 mask = DISCONT_STEPS-1;
|
|
u32 phase = pitch.phase++;
|
|
|
|
u32 i = phase & mask;
|
|
u32 ni = (i + DISCONT_STEPS/2) & mask;
|
|
|
|
// The 31 is because we only use half the phase,
|
|
// so sin walks 0..0.5 and cos walks 0.25..0.75
|
|
phase <<= 31-DISCONT_SHIFT;
|
|
struct sincos w = fastsincos(u32_to_fraction(phase));
|
|
|
|
float step = pitch.step;
|
|
float delay = (step > 0) ? DISCONT_STEPS*step : 1;
|
|
|
|
float d1 = sample_array_read(delay - i*step, &pitch.idx, pitch.array) * w.sin;
|
|
float d2 = sample_array_read(delay - ni*step, &pitch.idx, pitch.array) * w.cos;
|
|
|
|
float out = d1+d2;
|
|
|
|
sample_array_write(linear(pitch.feedback, in, out), &pitch.idx, pitch.array);
|
|
|
|
return out;
|
|
}
|