Пример #1
0
// Perform the non-EAX reverb pass on a given input sample, resulting in
// four-channel output.
static __inline ALvoid VerbPass(ALverbState *State, ALfloat in, ALfloat *early, ALfloat *late)
{
    ALfloat feed, taps[4];

    // Low-pass filter the incoming sample.
    in = lpFilter2P(&State->LpFilter, 0, in);

    // Feed the initial delay line.
    DelayLineIn(&State->Delay, State->Offset, in);

    // Calculate the early reflection from the first delay tap.
    in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[0]);
    EarlyReflection(State, in, early);

    // Feed the decorrelator from the energy-attenuated output of the second
    // delay tap.
    in = DelayLineOut(&State->Delay, State->Offset - State->DelayTap[1]);
    feed = in * State->Late.DensityGain;
    DelayLineIn(&State->Decorrelator, State->Offset, feed);

    // Calculate the late reverb from the decorrelator taps.
    taps[0] = feed;
    taps[1] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[0]);
    taps[2] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[1]);
    taps[3] = DelayLineOut(&State->Decorrelator, State->Offset - State->DecoTap[2]);
    LateReverb(State, taps, late);

    // Step all delays forward one sample.
    State->Offset++;
}
Пример #2
0
// Given an input sample, this function produces modulation for the late
// reverb.
static __inline ALfloat EAXModulation(ALverbState *State, ALfloat in)
{
    ALfloat sinus, frac;
    ALuint offset;
    ALfloat out0, out1;

    // Calculate the sinus rythm (dependent on modulation time and the
    // sampling rate).  The center of the sinus is moved to reduce the delay
    // of the effect when the time or depth are low.
    sinus = 1.0f - cosf(F_PI*2.0f * State->Mod.Index / State->Mod.Range);

    // The depth determines the range over which to read the input samples
    // from, so it must be filtered to reduce the distortion caused by even
    // small parameter changes.
    State->Mod.Filter = lerp(State->Mod.Filter, State->Mod.Depth,
                             State->Mod.Coeff);

    // Calculate the read offset and fraction between it and the next sample.
    frac   = (1.0f + (State->Mod.Filter * sinus));
    offset = fastf2u(frac);
    frac  -= offset;

    // Get the two samples crossed by the offset, and feed the delay line
    // with the next input sample.
    out0 = DelayLineOut(&State->Mod.Delay, State->Offset - offset);
    out1 = DelayLineOut(&State->Mod.Delay, State->Offset - offset - 1);
    DelayLineIn(&State->Mod.Delay, State->Offset, in);

    // Step the modulation index forward, keeping it bound to its range.
    State->Mod.Index = (State->Mod.Index + 1) % State->Mod.Range;

    // The output is obtained by linearly interpolating the two samples that
    // were acquired above.
    return lerp(out0, out1, frac);
}
Пример #3
0
// Given an input sample, this function mixes echo into the four-channel late
// reverb.
static __inline ALvoid EAXEcho(ALverbState *State, ALfloat in, ALfloat *late)
{
    ALfloat out, feed;

    // Get the latest attenuated echo sample for output.
    feed = AttenuatedDelayLineOut(&State->Echo.Delay,
                                  State->Offset - State->Echo.Offset,
                                  State->Echo.Coeff);

    // Mix the output into the late reverb channels.
    out = State->Echo.MixCoeff[0] * feed;
    late[0] = (State->Echo.MixCoeff[1] * late[0]) + out;
    late[1] = (State->Echo.MixCoeff[1] * late[1]) + out;
    late[2] = (State->Echo.MixCoeff[1] * late[2]) + out;
    late[3] = (State->Echo.MixCoeff[1] * late[3]) + out;

    // Mix the energy-attenuated input with the output and pass it through
    // the echo low-pass filter.
    feed += State->Echo.DensityGain * in;
    feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff);
    State->Echo.LpSample = feed;

    // Then the echo all-pass filter.
    feed = AllpassInOut(&State->Echo.ApDelay,
                        State->Offset - State->Echo.ApOffset,
                        State->Offset, feed, State->Echo.ApFeedCoeff,
                        State->Echo.ApCoeff);

    // Feed the delay with the mixed and filtered sample.
    DelayLineIn(&State->Echo.Delay, State->Offset, feed);
}
Пример #4
0
static void EarlyReflection(IDirectSoundBufferImpl* dsb, float in, float *out)
{
    float d[4], v, f[4];

    /* Obtain the decayed results of each early delay line. */
    d[0] = EarlyDelayLineOut(dsb, 0);
    d[1] = EarlyDelayLineOut(dsb, 1);
    d[2] = EarlyDelayLineOut(dsb, 2);
    d[3] = EarlyDelayLineOut(dsb, 3);

    /* The following uses a lossless scattering junction from waveguide
     * theory.  It actually amounts to a householder mixing matrix, which
     * will produce a maximally diffuse response, and means this can probably
     * be considered a simple feed-back delay network (FDN).
     *          N
     *         ---
     *         \
     * v = 2/N /   d_i
     *         ---
     *         i=1
     */
    v = (d[0] + d[1] + d[2] + d[3]) * 0.5f;
    /* The junction is loaded with the input here. */
    v += in;

    /* Calculate the feed values for the delay lines. */
    f[0] = v - d[0];
    f[1] = v - d[1];
    f[2] = v - d[2];
    f[3] = v - d[3];

    /* Re-feed the delay lines. */
    DelayLineIn(&dsb->eax.Early.Delay[0], dsb->eax.Offset, f[0]);
    DelayLineIn(&dsb->eax.Early.Delay[1], dsb->eax.Offset, f[1]);
    DelayLineIn(&dsb->eax.Early.Delay[2], dsb->eax.Offset, f[2]);
    DelayLineIn(&dsb->eax.Early.Delay[3], dsb->eax.Offset, f[3]);

    /* Output the results of the junction for all four channels. */
    out[0] = dsb->eax.Early.Gain * f[0];
    out[1] = dsb->eax.Early.Gain * f[1];
    out[2] = dsb->eax.Early.Gain * f[2];
    out[3] = dsb->eax.Early.Gain * f[3];
}
Пример #5
0
// Given an input sample, this function produces four-channel output for the
// early reflections.
static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *out)
{
    ALfloat d[4], v, f[4];

    // Obtain the decayed results of each early delay line.
    d[0] = EarlyDelayLineOut(State, 0);
    d[1] = EarlyDelayLineOut(State, 1);
    d[2] = EarlyDelayLineOut(State, 2);
    d[3] = EarlyDelayLineOut(State, 3);

    /* The following uses a lossless scattering junction from waveguide
     * theory.  It actually amounts to a householder mixing matrix, which
     * will produce a maximally diffuse response, and means this can probably
     * be considered a simple feed-back delay network (FDN).
     *          N
     *         ---
     *         \
     * v = 2/N /   d_i
     *         ---
     *         i=1
     */
    v = (d[0] + d[1] + d[2] + d[3]) * 0.5f;
    // The junction is loaded with the input here.
    v += in;

    // Calculate the feed values for the delay lines.
    f[0] = v - d[0];
    f[1] = v - d[1];
    f[2] = v - d[2];
    f[3] = v - d[3];

    // Re-feed the delay lines.
    DelayLineIn(&State->Early.Delay[0], State->Offset, f[0]);
    DelayLineIn(&State->Early.Delay[1], State->Offset, f[1]);
    DelayLineIn(&State->Early.Delay[2], State->Offset, f[2]);
    DelayLineIn(&State->Early.Delay[3], State->Offset, f[3]);

    // Output the results of the junction for all four channels.
    out[0] = State->Early.Gain * f[0];
    out[1] = State->Early.Gain * f[1];
    out[2] = State->Early.Gain * f[2];
    out[3] = State->Early.Gain * f[3];
}
Пример #6
0
static float AllpassInOut(DelayLine *Delay, unsigned int outOffset, unsigned int inOffset, float in, float feedCoeff, float coeff)
{
    float out, feed;

    out = DelayLineOut(Delay, outOffset);
    feed = feedCoeff * in;
    DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in);

    /* The time-based attenuation is only applied to the delay output to
     * keep it from affecting the feed-back path (which is already controlled
     * by the all-pass feed coefficient). */
    return (coeff * out) - feed;
}
Пример #7
0
// Basic attenuated all-pass input/output routine.
static __inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff)
{
    ALfloat out, feed;

    out = DelayLineOut(Delay, outOffset);
    feed = feedCoeff * in;
    DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in);

    // The time-based attenuation is only applied to the delay output to
    // keep it from affecting the feed-back path (which is already controlled
    // by the all-pass feed coefficient).
    return (coeff * out) - feed;
}
Пример #8
0
static void VerbPass(IDirectSoundBufferImpl* dsb, float in, float* out)
{
    float feed, late[4], taps[4];

    /* Low-pass filter the incoming sample. */
    in = lpFilter2P(&dsb->eax.LpFilter, 0, in);

    /* Feed the initial delay line. */
    DelayLineIn(&dsb->eax.Delay, dsb->eax.Offset, in);

    /* Calculate the early reflection from the first delay tap. */
    in = DelayLineOut(&dsb->eax.Delay, dsb->eax.Offset - dsb->eax.DelayTap[0]);
    EarlyReflection(dsb, in, out);

    /* Feed the decorrelator from the energy-attenuated output of the second
     * delay tap. */
    in = DelayLineOut(&dsb->eax.Delay, dsb->eax.Offset - dsb->eax.DelayTap[1]);
    feed = in * dsb->eax.Late.DensityGain;
    DelayLineIn(&dsb->eax.Decorrelator, dsb->eax.Offset, feed);

    /* Calculate the late reverb from the decorrelator taps. */
    taps[0] = feed;
    taps[1] = DelayLineOut(&dsb->eax.Decorrelator, dsb->eax.Offset - dsb->eax.DecoTap[0]);
    taps[2] = DelayLineOut(&dsb->eax.Decorrelator, dsb->eax.Offset - dsb->eax.DecoTap[1]);
    taps[3] = DelayLineOut(&dsb->eax.Decorrelator, dsb->eax.Offset - dsb->eax.DecoTap[2]);
    LateReverb(dsb, taps, late);

    /* Mix early reflections and late reverb. */
    out[0] += late[0];
    out[1] += late[1];
    out[2] += late[2];
    out[3] += late[3];

    /* Step all delays forward one sample. */
    dsb->eax.Offset++;
}
Пример #9
0
static void LateReverb(IDirectSoundBufferImpl* dsb, const float *in, float *out)
{
    float d[4], f[4];

    /* Obtain the decayed results of the cyclical delay lines, and add the
     * corresponding input channels.  Then pass the results through the
     * low-pass filters. */

    /* This is where the feed-back cycles from line 0 to 1 to 3 to 2 and back
     * to 0. */
    d[0] = LateLowPassInOut(dsb, 2, in[2] + LateDelayLineOut(dsb, 2));
    d[1] = LateLowPassInOut(dsb, 0, in[0] + LateDelayLineOut(dsb, 0));
    d[2] = LateLowPassInOut(dsb, 3, in[3] + LateDelayLineOut(dsb, 3));
    d[3] = LateLowPassInOut(dsb, 1, in[1] + LateDelayLineOut(dsb, 1));

    /* To help increase diffusion, run each line through an all-pass filter.
     * When there is no diffusion, the shortest all-pass filter will feed the
     * shortest delay line. */
    d[0] = LateAllPassInOut(dsb, 0, d[0]);
    d[1] = LateAllPassInOut(dsb, 1, d[1]);
    d[2] = LateAllPassInOut(dsb, 2, d[2]);
    d[3] = LateAllPassInOut(dsb, 3, d[3]);

    /* Late reverb is done with a modified feed-back delay network (FDN)
     * topology.  Four input lines are each fed through their own all-pass
     * filter and then into the mixing matrix.  The four outputs of the
     * mixing matrix are then cycled back to the inputs.  Each output feeds
     * a different input to form a circlular feed cycle.
     *
     * The mixing matrix used is a 4D skew-symmetric rotation matrix derived
     * using a single unitary rotational parameter:
     *
     *  [  d,  a,  b,  c ]          1 = a^2 + b^2 + c^2 + d^2
     *  [ -a,  d,  c, -b ]
     *  [ -b, -c,  d,  a ]
     *  [ -c,  b, -a,  d ]
     *
     * The rotation is constructed from the effect's diffusion parameter,
     * yielding:  1 = x^2 + 3 y^2; where a, b, and c are the coefficient y
     * with differing signs, and d is the coefficient x.  The matrix is thus:
     *
     *  [  x,  y, -y,  y ]          n = sqrt(matrix_order - 1)
     *  [ -y,  x,  y,  y ]          t = diffusion_parameter * atan(n)
     *  [  y, -y,  x,  y ]          x = cos(t)
     *  [ -y, -y, -y,  x ]          y = sin(t) / n
     *
     * To reduce the number of multiplies, the x coefficient is applied with
     * the cyclical delay line coefficients.  Thus only the y coefficient is
     * applied when mixing, and is modified to be:  y / x.
     */
    f[0] = d[0] + (dsb->eax.Late.MixCoeff * (         d[1] + -d[2] + d[3]));
    f[1] = d[1] + (dsb->eax.Late.MixCoeff * (-d[0]         +  d[2] + d[3]));
    f[2] = d[2] + (dsb->eax.Late.MixCoeff * ( d[0] + -d[1]         + d[3]));
    f[3] = d[3] + (dsb->eax.Late.MixCoeff * (-d[0] + -d[1] + -d[2]       ));

    /* Output the results of the matrix for all four channels, attenuated by
     * the late reverb gain (which is attenuated by the 'x' mix coefficient). */
    out[0] = dsb->eax.Late.Gain * f[0];
    out[1] = dsb->eax.Late.Gain * f[1];
    out[2] = dsb->eax.Late.Gain * f[2];
    out[3] = dsb->eax.Late.Gain * f[3];

    /* Re-feed the cyclical delay lines. */
    DelayLineIn(&dsb->eax.Late.Delay[0], dsb->eax.Offset, f[0]);
    DelayLineIn(&dsb->eax.Late.Delay[1], dsb->eax.Offset, f[1]);
    DelayLineIn(&dsb->eax.Late.Delay[2], dsb->eax.Offset, f[2]);
    DelayLineIn(&dsb->eax.Late.Delay[3], dsb->eax.Offset, f[3]);
}