当前位置:Gxlcms > mysql > SoundTouch音频处理库源码分析及算法提取(2)

SoundTouch音频处理库源码分析及算法提取(2)

时间:2021-07-01 10:21:17 帮助过:15人阅读

SoundTouch音频处理库初始化流程剖析 定义一个变量SoundTouch m_SoundTouch; SoundTouch的派生关系 FIFOSamplePipe-FIFOProcessor-SoundTouch (流程[1]) 因此首先构造基类FIFOSamplePipe,接着派生出FIFOProcessor,然后才以FIFOProcessor派生出SoundTouch。

SoundTouch音频处理库初始化流程剖析

定义一个变量SoundTouch m_SoundTouch;

SoundTouch的派生关系

FIFOSamplePipe->FIFOProcessor->SoundTouch (流程[1])

因此首先构造基类FIFOSamplePipe,接着派生出FIFOProcessor,然后才以FIFOProcessor派生出SoundTouch。这里不得不提一下老外的C++水平真的很高,在这里基本上把类的继承发挥到了极致。能够分析这样的代码简直就是一种享受。先看一下基类FIFOSamplePipe,如下定义:

class FIFOSamplePipe

{

public:

// virtual default destructor

virtual ~FIFOSamplePipe() {}

/// 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 corrupt the book-keeping!

///

/// When using this function to output samples, also remember to 'remove' the

/// output samples from the buffer by calling the

/// 'receiveSamples(numSamples)' function

virtual SAMPLETYPE *ptrBegin() = 0;

/// Adds 'numSamples' pcs of samples from the 'samples' memory position to

/// 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(FIFOSamplePipe &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 to

/// output buffer and removes them from the sample buffer. If there are less than

/// 'numsample' samples in the buffer, returns all that available.

///

/// /return Number of samples returned.

virtual uint receiveSamples(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 the

/// 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 receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of

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;

}

这里没有实现FIFOSamplePipe类的构造函数,因此系统隐性的调用了默认的自动生成的FIFOSamplePipe()。当然他应该没有做任何的初始化,同样也不需要做任何的初始化。通过定义virtual ~FIFOSamplePipe() {}虚析构函数,使得new一个子类,例如:FIFOSamplePipe* a = new FIFOProcessor,当a销毁的时候都会执行子类FIFOProcessor的析构函数,保证不管多少层继承都会一次过全部销毁,这是作为一个基类的特点。类的继承和多态果然是C++最为强悍的一部分,有助于编写重复性很高的类。通过看这个基类的声明,我们可以留意到除了定义大多数虚函数之外,他唯独实现了moveSamples这个函数,也就是子类如果没有override moveSamples,都将调用这个方法。他做的处理也相对来说很简单,根据注释,我们不难理解,正是这个函数实现了各个派生类之间的数据共享传递的接口。

// Moves samples from the 'other' pipe instance to this instance.

moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data.

)

{

int oNumSamples = other.numSamples();

putSamples(other.ptrBegin(), oNumSamples);

other.receiveSamples(oNumSamples);

};

在创建SoundTouch类之前,经过(流程[1])的前面两个步骤,他们都隐形的调用了默认的析构函数,由于基类FIFOSamplePipe没有实现构造函数,我们可以默认他不做任何的初始化,然后FIFOProcessor简单的把成员变量FIFOSamplePipe *output;一个指向基类的指针赋值简单做了一下初始化,让他指向NULL。

FIFOProcessor()

{

output = NULL;

}

现在回到SoundTouch的构造函数,在构造完前面两个类之后,他终于可以调用自己的默认构造函数

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;

}

看一下SoundTouch类的成员变量

class SoundTouch : public FIFOProcessor

{

private:

/// Rate transposer class instance

class RateTransposer *pRateTransposer;

/// Time-stretch class instance

class TDStretch *pTDStretch;

/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.

float virtualRate;

/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.

float virtualTempo;

/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.

float virtualPitch;

/// Flag: Has sample rate been set?

BOOL bSrateSet;

/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and

/// 'virtualPitch' parameters.

void calcEffectiveRateAndTempo();

protected :

/// Number of channels

uint channels;

/// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'

float rate;

/// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'

float tempo;

...

根据构造函数他实例化pRateTransposer,pTDStretch这两个类。

首先看一下RateTransposer类的成员函数newInstance,通过一个宏定义INTEGER_SAMPLES来new一个定点还是浮点处理的类,马上就可以判断不管RateTransposerInteger还是RateTransposerFloat都应该直接从RateTransposer派生。(假设INTEGER_SAMPLES被定义)他将构造一个RateTransposerInteger。

RateTransposer *RateTransposer::newInstance()

{

#ifdef INTEGER_SAMPLES

return ::new RateTransposerInteger;

#else

return ::new RateTransposerFloat;

#endif

}

看一下RateTransposerInteger类的定义,不出所料果然由RateTransposer派生

class RateTransposer : public FIFOProcessor

{

protected:

...

FIFOSampleBuffer storeBuffer;

/// Buffer for keeping samples between transposing & anti-alias filter

FIFOSampleBuffer tempBuffer;

/// Output sample buffer

FIFOSampleBuffer outputBuffer;

...

上诉两个类他们和基类之间存在这样的关系:

FIFOSamplePipe->FIFOProcessor->RateTransposer->RateTransposerInteger

这里的构造过程不同的是:RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)

RateTransposer构造函数指明了父类FIFOProcessor的构造形式FIFOProcessor(&outputBuffer)

FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.

)

{

output = pOutput;

}

RateTransposer把类成员变量outputBuffer作为传递函数参数,这里可能大家就会很奇怪,代码里面根本还没有实例化RateTransposer类,他怎么可能存在一个FIFOSampleBuffer outputBuffer;其实正是体现了c++的多态性,这里传入的实际上是一个__vfptr数组,这个数组就是指向实例化各个派生类的这个变量的指针数组。这下子明白了。__vfptr[0]不一定有值,但是__vfptr肯定是一个存在的值。构造完FIFOProcessor,此时要构造RateTransposer,他有三个FIFOSampleBuffer类定义。

...

class FIFOSampleBuffer : public FIFOSamplePipe

...

与基类的继承关系

FIFOSamplePipe->FIFOSampleBuffer

/// Constructor

FIFOSampleBuffer(int numChannels = 2 ///< Number of channels, 1=mono, 2=stereo.

///< Default is stereo.

);

他没有定义不带参的构造函数,因此这个带参数的构造函数将以默认的方式给调用

FIFOSampleBuffer::FIFOSampleBuffer(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

}

FIFOSampleBuffer的构造函数将被调用三次。

现在终于可以执行RateTransposer的构造函数

// Constructor

RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)

{

numChannels = 2;

bUseAAFilter = TRUE;

fRate = 0;

// Instantiates the anti-alias filter with default tap length

// of 32

pAAFilter = new AAFilter(32);

}

首先看一下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 (nyquist frequency = 0.5). The filter will cut off the

/// 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;

};

在其构造函数中初始化了一个指向class FIRFilter的指针

AAFilter::AAFilter(uint len)

{

pFIR = FIRFilter::newInstance();

cutoffFreq = 0.5;

setLength(len);

}

首先我们看看FIRFilter类成员函数newInstance(),嘿嘿,在这里我们发现了一个非常有用的函数detectCPUextensions();通过这个函数我们可以判断cpu到底支持什么类型的多媒体指令集。根据注释我们也可以很快理解。detectCPUextensions收藏了。他的实现就在Cpu_detect_x86_win.cpp的实现中。美中不足的是,他只能检测x86结构体系的CPU。可能我多想了。根据本人电脑的配置(采用的赛扬cpu),所以只支持mmx指令。

FIRFilter * FIRFilter::newInstance()

{

uint uExtensions;

uExtensions = detectCPUextensions();

// Check if MMX/SSE/3DNow! 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)

{

// 3DNow! support

return ::new FIRFilter3DNow;

}

else

#endif // ALLOW_3DNOW

{

// ISA optimizations not supported, use plain C version

return ::new FIRFilter;

}

}

为此他将通过这个判断构造返回一个FIRFilterMMX类

if (uExtensions & SUPPORT_MMX)

{

return ::new FIRFilterMMX;

}

查看FIRFilterMMX的类定义class FIRFilterMMX : public FIRFilter,他从FIRFilter派生。成员函数uint FIRFilterMMX::evaluateFilterStereo引起了我的高度注意,主要的算法采用MMX指令集来完成某些声音计算。这个就是我们需要的Rate的核心算法。不同指令集的实现,可以参考FIRFilter3DNow,FIRFilterSSE,默认是FIRFilter的evaluateFilterStereo函数的实现。

// 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*32bits => 4*16 bits

pVdest[0] = _mm_packs_pi32(accu1, accu2);

src += 4;

pVdest ++;

}

_m_empty(); // clear emms state

return (numSamples & 0xfffffffe) - length;

}

因此,如果把SoundTouch移植到arm等没有多媒体指令集的CPU时,应使用FIRFilter的evaluateFilterStere函数。执行完这里,终于可以真正意义上构造我们的RateTransposerInteger()。在构造函数中:

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);

}进行了一些必要的初始化。至此pRateTransposer = RateTransposer::newInstance();实例化完毕。至于pTDStretch = TDStretch::newInstance();下回分晓。

http://blog.csdn.net/suhetao/archive/2010/08/28/5845667.aspx

人气教程排行