SoundTouch audio processing database initialization process profiling defines a variable SoundTouchm_SoundTouch; SoundTouch's derivation relation namely osamplepipe-processing oprocessor-SoundTouch (process [1]). Therefore, the base class paiosamplepipe is constructed first, and then the export oproces, then, SoundTouch is derived from kerberoprocessor.
SoundTouch audio processing database initialization process profiling defines a variable SoundTouch m_SoundTouch; SoundTouch's derivation relation namely osamplepipe-processing oprocessor-SoundTouch (process [1]). Therefore, the base class kerberosamplepipe is constructed first, and then the kerberoprocessor, then, SoundTouch is derived from kerberoprocessor.
SoundTouch audio processing database Initialization Process Analysis
Define a variable SoundTouch m_SoundTouch;
Derivation of SoundTouch
Export osamplepipe-> export oprocessor-> SoundTouch (process [1])
Therefore, first construct the base class kerberosamplepipe, then derive the kerberoprocessor, and then derive the SoundTouch from kerberoprocessor. I have to mention that the level of C ++ for foreigners is really high. Here, class inheritance is basically brought to the extreme. The ability to analyze such code is simply a pleasure. First, let's take a look at the basic class kerberosamplepipe, which is defined as follows:
Class program osamplepipe
{
Public:
// Virtual default destructor
Virtual ~ Export osamplepipe (){}
/// Returns a pointer to the beginning of the output samples.
/// This function is provided for accessing the output samples directly.
/// Please be careful for not to upload upt the book-keeping!
///
/// When using this function to output samples, also remember to 'delete'
/// Output samples from the buffer by calling
/// 'Your esamples (numSamples) 'function
Virtual SAMPLETYPE * ptrBegin () = 0;
/// Adds 'numsamples' pcs of samples from the 'samples' memory position
/// The sample buffer.
Virtual void putSamples (const SAMPLETYPE * samples, // <Pointer to samples.
Uint numSamples // <Number of samples to insert.
) = 0;
// Moves samples from the 'other' pipe instance to this instance.
Void moveSamples (export osamplepipe & other // <Other pipe instance where from the receive the data.
)
{
Int oNumSamples = other. numSamples ();
PutSamples (other. ptrBegin (), oNumSamples );
Other. receiveSamples (oNumSamples );
};
/// Output samples from beginning of the sample buffer. Copies requested samples
/// Output buffer and removes them from the sample buffer. If there are less
/// 'Numsample' samples in the buffer, returns all that available.
///
/// Return Number of samples returned.
Virtual uint into esamples (SAMPLETYPE * output, // <Buffer where to copy output samples.
Uint maxSamples // <How many samples to receive at max.
) = 0;
/// Adjusts book-keeping so that given number of samples are removed from beginning of
/// Sample buffer without copying them anywhere.
///
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
/// With 'ptrbegin' function.
Virtual uint parallel esamples (uint maxSamples // <Remove this parameter samples from the beginning
Pipe.
) = 0;
/// Returns number of samples currently available.
Virtual uint numSamples () const = 0;
// Returns nonzero if there aren't any samples available for outputting.
Virtual int isEmpty () const = 0;
/// Clears all the samples.
Virtual void clear () = 0;
}
The constructor of the FIFOSamplePipe class is not implemented here, so the system implicitly calls the default automatically generated FIFOSamplePipe (). Of course, he should not perform any initialization, and he does not need to perform any initialization. By defining virtual ~ FIFOSamplePipe () {} virtual destructor to create a new subclass, for example, export osamplepipe * a = new export oprocessor. When a destroys, it executes the destructor of the subclass export oprocessor, ensure that all layers of inheritance are destroyed once, which is a feature of a base class. Class inheritance and polymorphism are indeed the most powerful part of C ++, which helps to write highly repetitive classes. By looking at the declaration of this base class, we can note that apart from defining most virtual functions, he only implements the moveSamples function, that is, if the subclass does not have override moveSamples, will call this method. The processing is also relatively simple. According to the annotations, it is not difficult to understand that this function implements the data sharing transfer interface between the derived classes.
// Moves samples from the 'other' pipe instance to this instance.
MoveSamples (export osamplepipe & other // <Other pipe instance where from the receive the data.
)
{
Int oNumSamples = other. numSamples ();
PutSamples (other. ptrBegin (), oNumSamples );
Other. receiveSamples (oNumSamples );
};
Before creating the SoundTouch class, after the first two steps (process [1]), they all implicitly called the default destructor. Because the base class kerberosamplepipe does not implement the constructor, by default, we can choose not to perform any initialization. Then, mongooprocessor simply puts the member variable into osamplepipe * output. a pointer to the base class is initialized to point to NULL.
FIFOProcessor ()
{
Output = NULL;
}
Now return to the SoundTouch constructor. After constructing the first two classes, he can finally call his own default constructor.
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;
}
Let's take a look at the member variables of the SoundTouch class.
Class SoundTouch: public writable oprocessor
{
Private:
/// Rate transposer class instance
Class RateTransposer * pRateTransposer;
/// Time-stretch class instance
Class TDStretch * pTDStretch;
/// Virtual pitch parameter. Valid tive rate & tempo are calculated from these parameters.
Float virtualRate;
/// Virtual pitch parameter. Valid tive rate & tempo are calculated from these parameters.
Float virtualTempo;
/// Virtual pitch parameter. Valid tive rate & tempo are calculated from these parameters.
Float virtualPitch;
/// Flag: Has sample rate been set?
BOOL bSrateSet;
/// Calculates valid tive rate & tempo valuescfrom 'virtualrate', 'virtualtempo' and
/// 'Virtualproject' parameters.
Void calcEffectiveRateAndTempo ();
Protected:
/// Number of channels
Uint channels;
/// Valid tive 'rate' value calculated from 'virtualrate', 'virtualtempo' and 'virtualedit'
Float rate;
/// Valid tive 'tempo' value calculated from 'virtualrate', 'virtualtempo' and 'virtualedit'
Float tempo;
...
Based on the constructor, The pRateTransposer and pTDStretch classes are instantiated.
First, let's take a look at the member function newInstance of the RateTransposer class and use a macro to define INTEGER_SAMPLES to create a class with fixed points or floating points. Now we can determine whether RateTransposerInteger or RateTransposerFloat should be directly derived from. (Assuming INTEGER_SAMPLES is defined) It constructs a RateTransposerInteger.
RateTransposer * RateTransposer: newInstance ()
{
# Ifdef INTEGER_SAMPLES
Return: new RateTransposerInteger;
# Else
Return: new RateTransposerFloat;
# Endif
}
Let's take a look at the definition of the RateTransposerInteger class. As expected, it is derived from RateTransposer.
Class RateTransposer: public writable oprocessor
{
Protected:
...
Restore osamplebuffer storeBuffer;
/// Buffer for keeping samples between transposing & anti-alias filter
Using osamplebuffer tempBuffer;
/// Output sample buffer
Extends osamplebuffer outputBuffer;
...
Appeal the relationship between the two classes and the base class:
Export osamplepipe-> export oprocessor-> RateTransposer-> RateTransposerInteger
The construction process here is different: RateTransposer (): kerberoprocessor (& outputBuffer)
The RateTransposer constructor specifies the constructor of the parent class kerberoprocessor (& outputBuffer)
Export oprocessor (export osamplepipe * pOutput // <Output pipe.
)
{
Output = pOutput;
}
RateTransposer uses the class member variable outputBuffer as the parameter for passing functions. It may be strange to everyone here that the RateTransposer class has not been instantiated in the Code. How can he possibly have an external osamplebuffer outputBuffer; in fact, it reflects the c ++ polymorphism. The input here is actually a _ vfptr array, which is a pointer array pointing to the variable of each derived class. Now I understand. _ Vfptr [0] is not necessarily a value, but _ vfptr is definitely an existing value. After constructing the external oprocessor, You need to construct the RateTransposer. It has three definitions of the external osamplebuffer class.
...
Class implements osamplebuffer: public implements osamplepipe
...
Inheritance relationship with the base class
Export osamplepipe-> export osamplebuffer
/// Constructor
Export osamplebuffer (int numChannels = 2 // <Number of channels, 1 = mono, 2 = stereo.
/// <Default is stereo.
);
The constructor does not define a constructor without parameters. Therefore, the constructor with parameters is called by default.
Using osamplebuffer: Using osamplebuffer (int numChannels)
{
Assert (numChannels> 0 );
SizeInBytes = 0; // reasonable initial value
Buffer = NULL;
BufferUnaligned = NULL;
SamplesInBuffer = 0;
BufferPos = 0;
Channels = (uint) numChannels;
EnsureCapacity (32); // allocate initial capacity
}
The constructor of writable osamplebuffer is called three times.
Now we can finally execute the RateTransposer constructor.
// Constructor
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 );
}
First, let's take a look at the definition of AAFilter.
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;
};
The constructor initializes a pointer to class FIRFilter.
AAFilter: AAFilter (uint len)
{
PFIR = FIRFilter: newInstance ();
CutoffFreq = 0.5;
SetLength (len );
}
First, let's look at the FIRFilter class member function newInstance (). Hey hey, here we found a very useful function detectCPUextensions (); through this function, we can determine the type of multimedia instruction sets supported by the cpu. According to the annotations, we can quickly understand. DetectCPUextensions is added to the Favorites list. Its implementation is in the implementation of Cpu_detect_x86_win.cpp. In the US, he can only detect the CPU Of The x86 architecture. Maybe I think more. Only mmx commands are supported based on the configurations of your computer (using the sayang cpu.
FIRFilter * FIRFilter: newInstance ()
{
Uint uExtensions;
UExtensions = detectCPUextensions ();
// Check if MMX/SSE/3 DNow! Instruction set extensions supported by CPU
# Ifdef ALLOW_MMX
// MMX routines available only with integer sample types
If (uExtensions & SUPPORT_MMX)
{
Return: new FIRFilterMMX;
}
Else
# Endif // ALLOW_MMX
# Ifdef ALLOW_SSE
If (uExtensions & SUPPORT_SSE)
{
// SSE support
Return: new FIRFilterSSE;
}
Else
# Endif // ALLOW_SSE
# Ifdef ALLOW_3DNOW
If (uExtensions & SUPPORT_3DNOW)
{
// 3 DNow! Support
Return: new FIRFilter3DNow;
}
Else
# Endif // ALLOW_3DNOW
{
// ISA optimizations not supported, use plain C version
Return: new FIRFilter;
}
}
Therefore, he will return a FIRFilterMMX class through this judgment structure.
If (uExtensions & SUPPORT_MMX)
{
Return: new FIRFilterMMX;
}
View the FIRFilterMMX class definition class FIRFilterMMX: public FIRFilter, which is derived from FIRFilter. The member function uint FIRFilterMMX: evaluateFilterStereo has attracted my attention. The main algorithm uses the MMX instruction set to complete some sound computing. This is the core Rate algorithm we need. For implementation of different instruction sets, see FIRFilter3DNow and FIRFilterSSE. The default is the implementation of the evaluateFilterStereo function of FIRFilter.
// Mmx-optimized version of the filter routine for stereo sound
Uint FIRFilterMMX: evaluateFilterStereo (short * dest, const short * src, uint numSamples) const
{
// Create stack copies of the needed member variables for asm routines:
Uint I, j;
_ M64 * pVdest = (_ m64 *) dest;
If (length <2) return 0;
For (I = 0; I <(numSamples-length)/2; I ++)
{
_ M64 accu1;
_ M64 accu2;
Const _ m64 * pVsrc = (const _ m64 *) src;
Const _ m64 * pVfilter = (const _ m64 *) filterCoeffsAlign;
Accu1 = accu2 = _ mm_setzero_si64 ();
For (j = 0; j <lengthDiv8 * 2; j ++)
{
_ M64 temp1, temp2;
Temp1 = _ mm_unpacklo_pi16 (pVsrc [0], pVsrc [1]); // = l2 l0 r2 r0
Temp2 = _ mm_unpackhi_pi16 (pVsrc [0], pVsrc [1]); // = l3 l1 r3 r1
Accu1 = _ mm_add_pi32 (accu1, _ mm_madd_pi16 (temp1, pVfilter [0]); // + = l2 * f2 + l0 * f0
R2 * f2 + r0 * f0
Accu1 = _ mm_add_pi32 (accu1, _ mm_madd_pi16 (temp2, pVfilter [1]); // + = l3 * f3 + l1 * f1
R3 * f3 + r1 * f1
Temp1 = _ mm_unpacklo_pi16 (pVsrc [1], pVsrc [2]); // = l4 l2 r4 r2
Accu2 = _ mm_add_pi32 (accu2, _ mm_madd_pi16 (temp2, pVfilter [0]); // + = l3 * f2 + l1 * f0
R3 * f2 + r1 * f0
Accu2 = _ mm_add_pi32 (accu2, _ mm_madd_pi16 (temp1, pVfilter [1]); // + = l4 * f3 + l2 * f1
R4 * f3 + r2 * f1
// Accu1 + = l2 * f2 + l0 * f0 r2 * f2 + r0 * f0
// + = L3 * f3 + l1 * f1 r3 * f3 + r1 * f1
// Accu2 + = l3 * f2 + l1 * f0 r3 * f2 + r1 * f0
// L4 * f3 + l2 * f1 r4 * f3 + r2 * f1
PVfilter + = 2;
PVsrc + = 2;
}
// Accu >>= resultDivFactor
Accu1 = _ mm_srai_pi32 (accu1, resultDivFactor );
Accu2 = _ mm_srai_pi32 (accu2, resultDivFactor );
// Pack 2*2*32 bits => 4*16 bits
PVdest [0] = _ mm_packs_pi32 (accu1, accu2 );
Src + = 4;
PVdest ++;
}
_ M_empty (); // clear emms state
Return (numSamples & 0 xfffffffe)-length;
}
Therefore, if you port SoundTouch to a CPU without multimedia instruction sets, you should use the evaluateFilterStere function of FIRFilter. After the execution, we can finally construct our RateTransposerInteger (). In the constructor:
RateTransposerInteger: RateTransposerInteger (): RateTransposer ()
{
// Notice: use local function calling syntax for sake of clarity,
// To indicate the fact that C ++ constructor can't call virtual functions.
RateTransposerInteger: resetRegisters ();
RateTransposerInteger: setRate (1.0f );
. So far, pRateTransposer = RateTransposer: newInstance (); instantiation is complete. For pTDStretch = TDStretch: newInstance (); the next release is Xiao.
Http://blog.csdn.net/suhetao/archive/2010/08/28/5845667.aspx