Audio effects: Flanger and Chorus

Introduction

Today we are going to discuss the two sound effects of Flanger and Chorus. They are also implemented based on Delay, and they have many similarities in implementation and principle.

We are still old rules, first introduce the specific effects of these two sound effects, analyze their implementation details, and finally give the similarities and differences between the two.

Flanger

The first is the flanger, which translates to “flange” in Chinese, which originally meant the edge of an open-reel recorder

In order to produce a flanger sound effect, two open tape recorders can be used to play the same tape at the same time. If the two recorders play exactly the same, the output audio simply enhances the original audio. In order to produce a flanger effect, we can gently press one of the recorders to reduce its playback rate and thus lower its pitch, in addition to causing a slight difference in time between the two audios. When we release the pressure, the original tone and time difference will gradually disappear, and at the same time we will gently press another recorder, so the cycle repeats, and the flanger sound effect is produced.

Flanger is also known as "flying" sound effect, it sounds like a whistling train, like a jet plane flying in the sky, let's listen to it. You can clearly feel that the sound of the flanger sounds like a plane hovering over the head.

Raw audio

flanger

When it comes to the principles resulting flanger, then we would have to mention constructive interference (Constructive Interference) and destructive interference (Destructive Interference)

Simply put, suppose there is a sine signal, which is then added to the original audio through delay. If the two signals are completely in phase, the amplitude of the output audio is twice the input, which is constructive interference; if the two signals The phases just cancel out, then the mute signal is output, which is destructive interference.

Usually an audio has many many frequencies. After delay and adding to the original signal, it will cause some frequencies to cancel each other (frequency response appears as a "valley" shape), and some frequencies enhance each other (frequency response appears as a "peak" shape) . Under the influence of LFO, the "wave crest" and "wave trough" move and change, and finally the flanger sound effect is produced. This process can be understood in conjunction with the operation of the flanger in the opening recorder: the cycle of repeatedly tapping the recorder is actually the process of generating a periodic delay, which is consistent with LFO.

Basic Flanger

Here is the difference equation and block diagram of basic flanger

y [n] = x [n] + gx [n - M [n]]

basic flanger

among them

  • g For feedback, also known as depth
  • M[n]For delay, controlled by LFO

basic flanger structure also called feedforward comb filter , because it is a spectral response like a comb. It can be explained by the transfer equation:

\begin{aligned}
Y(z) &= X(z) + gz^{-M[n]}X(z) \\
H(z) &= \frac{Y(z)}{X(z)} = 1 + gz^{-M[n]}
\end{aligned}

Order z = e ^ {j \ omega}, where the \omega \in [0, \pi]amplitude response is obtained

\begin{aligned}
H(e^{j\omega}) &= 1+ge^{-j\omega M[n]} \\
\vert H(e^{j\omega}) \vert &= \sqrt{1+2g\cos(\omega M[n]) + g^2}
\end{aligned}

It can be seen that the amplitude response \ vert H (e ^ {j \ omega}) \ vertis periodic function cos, in \omega \in [0, \pi]there the M/2two "peaks" and "valleys"

The location of the "wave crest" is:

\omega_p = 2\pi p/M

"Pogu" is located at:

\ omega_n = (2n + 1) \ pi / M

The frequency interval between "wave peak" and "wave peak", "wave valley" and "wave valley" is f_s/M

Therefore, the amplitude response looks like a comb, like, for example, following this M[n]=6magnitude response:

combfilter

When M[n]control of the changing the LFO, "peak" and "valley" is changing, which generates a sound flanger

Flanger with Feedback

According to the block diagram of the flanger with feedback, we can write the difference equation as:

y[n] = x[n] + g_{ff}d[n] \quad \text{where} \quad d[n] = x[n-M] + g_{fb}d[n-M]

Converted only with and [n]and x[n]difference equations related to:

\begin{aligned}
y[n-M] &= x[n-M] + g_{ff}d[n-M] \\
d[n] &= x[n-M] + \frac{g_{fb}}{g_{ff}}(y[n-M] - x[n-M]) \\
y[n] &= x[n] + g_{fb}y[n-M] + (g_{ff}-g_{fb})x[n-M]
\end{aligned}

flanger_with_feedback

The transfer equation is:

H(z) = \frac{Y(z)}{X(z)} = \frac{z^M+g_{ff}-g_{fb}}{z^{M}-g_{fb}}

It can be seen as g_{fb}=0, its transfer equation is consistent with the basic flanger. When g_{fb} < 1all the poles inside the unit circle, is stable.

Next, directly implement the specific implementation code

void Flanger::processBlock(AudioBuffer<float> &buffer)
{
    const int num_channels = buffer.getNumChannels();
    const int num_samples = buffer.getNumSamples();
    float phase = 0.0f;
    float channel0EndPhase = phase_;

    assert(static_cast<size_t>(num_channels) <= dlines_.getNumLines());

    for(int c = 0; c < num_channels; ++c)
    {
        phase = phase_;
        if(stereo != 0 && c != 0)
        {
            phase = fmodf(phase + 0.25f, 1.0f);
        }

        float* channel_data = buffer.getWritePointer(c);
        auto* dline = dlines_.getDelayLine(c);

        for(int i = 0; i < num_samples; ++i)
        {
            const float in = channel_data[i];

            // get delay from lfo
            float delay_second = min_delay + sweep_width*lfo_.lfo(phase, LFO::WaveformType::kWaveformSine);
            float delay_sample = delay_second * static_cast<float>(getSampleRate());

            // get interpolation delay value
            float interpolation_val = dline->getInterpolation(delay_sample);
            channel_data[i] = in + depth * interpolation_val;

            // push input to delay line
            dline->push(in + (interpolation_val * feedback));

            // update phase
            phase += lfo_freq*invert_sample_rate_;
            if(phase >= 1.0f)
            {
                phase -= 1.0f;
            }
        }

        // use channel 0 only keep the phase in sync between call processBlock()
        if(c == 0)
        {
            channel0EndPhase = phase;
        }
    }

    phase_ = channel0EndPhase;
}
复制代码

There is one detail that needs to be explained, that is the stereo mode

When the stereo mode is turned on , we want to create a pseudo-stereo effect. The so-called "pseudo-stereo" means that there is a difference between the left and right channels, but the listener cannot specifically distinguish their orientation. The reason for the stereo sound: the time difference, frequency difference and volume difference between the sound and the left and right ears. In our implementation, there is a difference in the phase of the LFO in the left and right channels, resulting in a delay difference, which results in pseudo stereo sound.

Chorus sound

The next thing to introduce is Chorus chorus sound effects. Flanger and chorus are delay-based sound effects, which are almost the same in implementation.

The biggest difference between the two is the length of the delay: the delay used by chorus is longer, usually 20-30ms or more, and the flagr is usually less than 10ms, which is far below the resolution limit of human hearing to echo (about 50-70 ms)

In music, when several sounds of similar pitch and timbre are played independently, a chorus sound effect appears. This kind of sound effect is very common, for example, a group of people singing the same song, or playing the violin at the same time, they will always show slight differences in pitch or time even if they are playing in unison. These subtle changes make the sound richer and brighter, which is one of the reasons why the performance of large string orchestra sounds very shocking. The chorus sound effect simulates these time and pitch differences, making the sound source of even one instrument sound as if several instruments are playing together.

Let's listen to chorus sound effects

Raw audio

chorus

Basic Chorus

Here is the difference equation and block diagram of basic chorus

y [n] = x [n] + gx [n - M [n]]

basic chorus

It can be seen that the structure of the basic chorus and the basic flanger is the same, the main difference between them is the length of the delay: the delay length of chorus is usually 20-30ms, and the flag is usually less than 10ms

Basic chorus is the same as basic flanger, and their amplitude response is like a comb:

\begin{aligned}
\vert H(e^{j\omega}) \vert &= \sqrt{1+2g\cos(\omega M[n]) + g^2}
\end{aligned}

We assume that the delay of chorus = 30ms, then at the sampling rate of 48kHz M = 1440, so the interval between the two peaks is f_s/M = 33.3 Hz, that is to say that a peak appears every 33.3 Hz. For our hearing system, the frequency difference at 33.3 Hz is too small to be resolved.

Directly below the code

void Chorus::processBlock(AudioBuffer<float> &buffer)
{
    const auto num_channels = buffer.getNumChannels();
    const auto num_samples = buffer.getNumSamples();

    float ph = 0.0f;

    for(size_t c = 0; c < num_channels; ++c)
    {
        auto* channel_data = buffer.getWritePointer(c);
        auto* dline = dlines_.getDelayLine(c);

        ph = phase_;

        for(size_t i = 0; i < num_samples; ++i)
        {
            const float in = channel_data[i];
            float weight = 0.0f;
            float phase_offset = 0.0f;

            for(int j  = 0; j < num_voices - 1; ++j)
            {
                if(stereo != 0 && num_voices > 2)
                {
                    weight = (float)(j) / (float)(num_voices-2);

                    if(c != 0)
                    {
                        weight = 1.0f - weight;
                    }

                } else
                {
                    weight = 1.0f;
                }

                if(weight != 0.0f)
                {
                    // get delay from lfo
                    float delay_second = min_delay + sweep_width*lfo_.lfo(fmodf(ph+phase_offset,1.0f), LFO::WaveformType::kWaveformSine);
                    float delay_sample = delay_second * static_cast<float>(getSampleRate());

                    // get interpolation value
                    float interpolation_val = dline->getInterpolation(delay_sample);
                    channel_data[i] = in + weight * depth * interpolation_val;
                }

                if(num_voices < 3)
                {
                    phase_offset += 0.25f;
                } else
                {
                    phase_offset += 1.0f / (float)(num_voices - 1);
                }
            }

            dline->push(in);

            // update phase
            ph += lfo_freq*invert_sample_rate_;
            if(ph >= 1.0f)
            {
                ph -= 1.0f;
            }
        }
    }

    phase_ = ph;
}

复制代码

The more complicated part is the pseudo-stereo part. In this part of the implementation, it includes the frequency difference and the volume difference, in which the phase_offsetfrequency difference is weightcontrolled and the volume difference is controlled.

to sum up

The chorus and flanger are basically the same in structure. The main difference lies in the choice of parameters: chorus uses a longer delay than flanger, and usually has a larger sweep width. These parameters together result in a greater sense of separation and more pitch modulation between the original sound and the delayed copy.

Another structural difference is that flanger can use feedback to produce a more intense effect, which is almost absent in chorus. On the other hand, chorus will use more than one delayed copy, while flanger usually uses only one copy.

Guess you like

Origin juejin.im/post/5e9e5616e51d4546ca30c439