Soundtouch audio processing database source code analysis and algorithm extraction (4)

Source: Internet
Author: User

A supplement to soundtouch construction process initialization.
In the soundtouch constructor, we noticed that there is such a function calceffectiverateandtempo ()
Soundtouch: soundtouch ()
{
// Initialize rate transposer and tempo changer instances
Pratetransposer = ratetransposer: newinstance ();
Ptdstretch = tdstretch: newinstance ();
Setoutpipe (ptdstretch );
Rate = tempo = 0;
Virtualpitch =
Virtualrate =
Virtualtempo = 1.0;
Calceffectiverateandtempo ();
Channels = 0;
Bsrateset = false;
}
In the soundtouch class, the six member functions void setrate (float newrate), void setratechange (float newrate), void settempo (float newtempo), void settempochange (float newtempo ), void setpitch (float newpitch) and void setpitchoctaves (float newpitch) are called respectively. It is hard to imagine that it should be some processing of audio processing parameters. Through further analysis of calceffectiverateandtempo, his implementation is as follows.
// Calculates 'effect' rate and tempo values from
// Nominal control values.
Void soundtouch: calceffectiverateandtempo ()
{
Float oldtempo = tempo;
Float oldrate = rate;
Tempo = virtualtempo/virtualpitch;
Rate = virtualpitch * virtualrate;
If (! Test_float_equal (rate, oldrate) pratetransposer-> setrate (rate );
If (! Test_float_equal (tempo, oldtempo) ptdstretch-> settempo (TEMPO );
# Ifndef prevent_click_at_rate_crossover
If (rate <= 1.0f)
{
If (output! = Ptdstretch)
{
Using osamplepipe * tempoout;
Assert (output = pratetransposer );
// Move samples in the current output buffer to the output of ptdstretch
Tempoout = ptdstretch-> getoutput ();
Tempoout-> movesamples (* output );
// Move samples in pitch transposer's store buffer to tempo changer's input
Ptdstretch-> movesamples (* pratetransposer-> getstore ());
Output = ptdstretch;
}
}
Else
# Endif
{
If (output! = Pratetransposer)
{
Using osamplepipe * transout;
Assert (output = ptdstretch );
// Move samples in the current output buffer to the output of pratetransposer
Transout = pratetransposer-> getoutput ();
Transout-> movesamples (* output );
// Move samples in tempo changer's input to pitch transposer's input
Pratetransposer-> movesamples (* ptdstretch-> getinput ());
Output = pratetransposer;
}
}
}
We have completed some parameter settings for the pratetransposer and ptdstretch classes. This gives us a preliminary understanding of the entire sound processing process.
1. Create a digital low-pass filter (aafilter) and intercept the sample by adding the Hamming window.
Let's analyze how he created this low-pass digital filter. The main implementation is to construct an aafilter class in the ratetransposer class constructor. Paafilter = new aafilter (32 );
Ratetransposer: ratetransposer (): kerberoprocessor (& outputbuffer)
{
Numchannels = 2;
Buseaafilter = true;
Frate = 0;
// Instantiates the anti-alias filter with default tap Length
// Of 32
Paafilter = new aafilter (32 );
}
Let's take a look at the definition of the aafilter class, which is simple and understandable. Class firfilter * pfir is similar to the previous analysis, pointing to a class that supports enhanced Instruction Set Optimization Based on CPU. They are also simple override data processing functions. Double cutofffreq is the low-pass cutoff frequency. Calculatecoeffs () is a class function that we should focus on. The main parameters of a digital filter are implemented by it.
Class aafilter
{
Protected:
Class firfilter * pfir;
/// Low-pass filter cut-off frequency, negative = invalid
Double cutofffreq;
/// Num of filter taps
Uint length;
/// Calculate the FIR coefficients realizing the given cutoff-Frequency
Void calculatecoeffs ();
Public:
Aafilter (uint length );
~ Aafilter ();
/// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
/// Frequency (nyquest frequency = 0.5). The filter will cut off
/// Frequencies than that.
Void setcutofffreq (double newcutofffreq );
/// Sets number of FIR Filter taps, I. e .~ Filter complexity
Void setlength (uint newlength );
Uint getlength () const;
/// Applies the filter to the given sequence of samples.
// Note: The amount of outputted samples is by value of 'filter length'
/// Smaller than the amount of input samples.
Uint evaluate (sampletype * DEST,
Const sampletype * SRC,
Uint numsamples,
Uint numchannels) const;
};
First, let's take a look at the aafilter constructor. First, create an example of an FIR filter, and then let the truncation frequency be equal to 0.5. Note that this is an angle frequency. Then, set the form width of the filter.
Aafilter: aafilter (uint Len)
{
Pfir = firfilter: newinstance ();
Cutofffreq = 0.5;
Setlength (LEN );
}

In setlength, the class member function calculatecoeffs () is called ();
// Sets number of FIR Filter taps
Void aafilter: setlength (uint newlength)
{
Length = newlength;
Calculatecoeffs ();
}
Now we will focus on the class member function calculatecoeffs (), which is the core of the implementation of the entire digital filter parameter. The source code is as follows:
// Calculates coefficients for a low-pass FIR filter using Hamming window
Void aafilter: calculatecoeffs ()
{
Uint I;
Double cnttemp, temp, tempcoeff, H, W;
Double FC2, WC;
Double scalecoeff, sum;
Double * work;
Sampletype * coeffs;
Assert (length> = 2 );
Assert (length % 4 = 0 );
Assert (cutofffreq> = 0 );
Assert (cutofffreq <= 0.5 );
Work = new double [length];
Coeffs = new sampletype [length];

Fc2= 2.0 * cutofffreq;
WC = pI * FC2;
Tempcoeff = twopi/(double) length;
Sum = 0;
For (I = 0; I <length; I ++)
{
Cnttemp = (double) I-(double) (length/2 );
Temp = cnttemp * WC;
If (temp! = 0)
{
H = FC2 * sin (temp)/temp; // Sinc Function
}
Else
{
H = 1.0;
}
W = 0.54 + 0.46 * Cos (tempcoeff * cnttemp); // Hamming window
Temp = W * h;
Work [I] = temp;
// Calc net sum of coefficients
Sum + = temp;
}
...
The first half of a class function makes some necessary judgments through assert. For example, the length must be greater than 2 and must be a multiple of 4 to ensure that length/2 is an integer, at the same time, the truncation frequency must be between 0 and 0.5. Next we use the Hamming window as the window. Note that the form of the 0.54 + 0.46 * Cos (2 * pI * cnttemp/N) and the Hamming Window Function 0.54-0.46 * Cos (2 * pI * n/(N-1) is somewhat different, in fact, it is not hard to understand:
I = (0 .. length-1) and cnttemp = I-(length/2 );
0.54 + 0.46 * Cos (2 * pI * cnttemp/N)
= 0.54-0.46 * Cos (2 * pI * cnttemp/n + PI)
= 0.54-0.46 * Cos (2 * pI * cnttemp/n + pI * n/n)
= 0.54-0.46 * Cos (2 * pI * (cnttemp + n/2)/n)
= 0.54-0.46 * Cos (2 * pI * n/n) Where n = 0 .. N-1
It's just a cos (x) =-cos (x + PI) change, which is simple but easy to think about and cannot be understood. As for why n don't use N-1, I believe the following paragraph, can clearly understand the expression, here, I would like to thank a teacher of science and technology for your advice. "If this N-1 uses N, the symmetric center n/2 is not an integer, it's not a sampling point (because it's even symmetric, and n must take an odd number ---- low-pass filter can only select parameters in theory. "note that in length, I starts from 0 and ends with length, the length must be greater than 2 and must be four Based on assert.
It is not an odd number, so (length-1)/2 must not be an integer. So here we can understand that our filter has a length + 1.
......
// Ensure the sum of coefficients is larger than zero
Assert (sum> 0 );

// Ensure we 've really designed a lowpass filter...
Assert (work [length/2]> 0 );
Assert (work [length/2 + 1]>-1e-6 );
Assert (work [length/2-1]>-1e-6 );

// Calculate a scaling coefficient in such a way that the result can be
// Divided by 16384.
Scalecoeff = 16384.0f/sum;

For (I = 0; I <length; I ++)
{
// Scale & round to nearest integer
Temp = work [I] * scalecoeff;
Temp + = (temp> = 0 )? 0.5:-0.5;
// Ensure no overfloods
Assert (temp> =-32768 & temp <= 32767 );
Coeffs [I] = (sampletype) temp;
}

// Set coefficients. Use divide factor 14 => divide result by 2 ^ 14 = 16384
Pfir-> setcoefficients (coeffs, length, 14 );

Delete [] work;
Delete [] coeffs;
}
In the second half of the class function, assert is used to verify whether the low-pass filter is valid. The rest is mainly to process a fixed point 2 ^ 14 = 16384, it is equivalent to moving 14 bits to the right (increasing by 16384 times, and then shifting 14 bits to the left to increase accuracy), and assert (temp >=- 32768 & temp <= 32767 ); to verify that temp does not overflow as a 16-digit integer. The last thing to do is to pass the low-pass filter parameter to the firxxx class. Then the firxxx class can be abstracted into a digital low-pass filter. Now, all initialization is complete, and you can enter the specific data processing process.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.