SoundTouch音频处理库源码分析及算法提取(2)
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, ///
uint numSamples ///
) = 0;
// Moves samples from the 'other' pipe instance to this instance.
void moveSamples(FIFOSamplePipe &other ///
)
{
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, ///
uint maxSamples ///
) = 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 ///
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 ///
)
{
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 = 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 ///
///
);
他没有定义不带参的构造函数,因此这个带参数的构造函数将以默认的方式给调用
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
for (i = 0; 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
{
__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

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

1、首先我们右击任务栏空白处,选择【任务管理器】选项,或者右击开始徽标,然后再选择【任务管理器】选项。2、在打开的任务管理器界面,我们点击最右端的【服务】选项卡。3、在打开的【服务】选项卡,点击下方的【打开服务】选项。4、在打开的【服务】窗口,右击【InternetConnectionSharing(ICS)】服务,然后选择【属性】选项。5、在打开的属性窗口,将【打开方式】修改为【禁用】,点击【应用】后点击【确定】。6、点击开始徽标,然后点击关机按钮,选择【重启】,完成电脑重启就行了。

写在前面&笔者的个人理解目前,在整个自动驾驶系统当中,感知模块扮演了其中至关重要的角色,行驶在道路上的自动驾驶车辆只有通过感知模块获得到准确的感知结果后,才能让自动驾驶系统中的下游规控模块做出及时、正确的判断和行为决策。目前,具备自动驾驶功能的汽车中通常会配备包括环视相机传感器、激光雷达传感器以及毫米波雷达传感器在内的多种数据信息传感器来收集不同模态的信息,用于实现准确的感知任务。基于纯视觉的BEV感知算法因其较低的硬件成本和易于部署的特点,以及其输出结果能便捷地应用于各种下游任务,因此受到工业

C++中机器学习算法面临的常见挑战包括内存管理、多线程、性能优化和可维护性。解决方案包括使用智能指针、现代线程库、SIMD指令和第三方库,并遵循代码风格指南和使用自动化工具。实践案例展示了如何利用Eigen库实现线性回归算法,有效地管理内存和使用高性能矩阵操作。

C++sort函数底层采用归并排序,其复杂度为O(nlogn),并提供不同的排序算法选择,包括快速排序、堆排序和稳定排序。

人工智能(AI)与执法领域的融合为犯罪预防和侦查开辟了新的可能性。人工智能的预测能力被广泛应用于CrimeGPT(犯罪预测技术)等系统,用于预测犯罪活动。本文探讨了人工智能在犯罪预测领域的潜力、目前的应用情况、所面临的挑战以及相关技术可能带来的道德影响。人工智能和犯罪预测:基础知识CrimeGPT利用机器学习算法来分析大量数据集,识别可以预测犯罪可能发生的地点和时间的模式。这些数据集包括历史犯罪统计数据、人口统计信息、经济指标、天气模式等。通过识别人类分析师可能忽视的趋势,人工智能可以为执法机构

在PHP开发过程中,处理特殊字符是一个常见的问题,尤其是在字符串处理中经常会遇到特殊字符转义的情况。其中,将特殊字符转换单引号是一个比较常见的需求,因为在PHP中,单引号是一种常用的字符串包裹方式。在本文中,我们将介绍如何在PHP中处理特殊字符转换单引号,并提供具体的代码示例。在PHP中,特殊字符包括但不限于单引号(')、双引号(")、反斜杠()等。在字符串

01前景概要目前,难以在检测效率和检测结果之间取得适当的平衡。我们就研究出了一种用于高分辨率光学遥感图像中目标检测的增强YOLOv5算法,利用多层特征金字塔、多检测头策略和混合注意力模块来提高光学遥感图像的目标检测网络的效果。根据SIMD数据集,新算法的mAP比YOLOv5好2.2%,比YOLOX好8.48%,在检测结果和速度之间实现了更好的平衡。02背景&动机随着远感技术的快速发展,高分辨率光学远感图像已被用于描述地球表面的许多物体,包括飞机、汽车、建筑物等。目标检测在远感图像的解释中

1、打开PPT,在菜单栏点击【插入】,再点击【音频】。2、在打开的文件选择框中,选择自己要插入的第一个音频文件。3、插入成功后,会在PPT中显示喇叭图标代表刚才插入的文件,可进行播放试听,进行音量调整,以控制其在PPT放映过程中的音量大小。4、按照同样的方法,插入第二个音频文件,这时PPT中显示两个喇叭图标,分别代表两个音频。5、点击选中其中第一个音频文件图标,再点击【播放】菜单。6、在工具栏中的开始中,选择【跨幻灯片播放】,将此音频设置成在每个幻灯片中都播放,不需要人为干预。7、按照第6步中的
