上一篇我们使用了FFMPEG库对视频进行了解码,抛开细节不谈,通过使用接口IntPtr get_audio_frame(int key),我们可以获取到音频的数据,也就是一堆字节数组,接下来,就轮到SoundTouch上场了,我们可以把SoundTouch看做是一个工具库,通过输入音频数据,输出经过变速后的新的数据。
关于SoundTouch的使用我们依然分为三部分
环境配置
定义接口及实现接口
配置好环境后,我们开始定义接口,在这之前,先说明一个SoundTouch的规则,SoundTouch的加速算法好像是异步的,也就是说把待加速的数据输入后不一定可以立即输出加速后的数据,所以我们需要定义一个方法用来获取当前可以输出的数据是多少:
uint GetSampleNum():获取当前加速完成的数据
void CreateInstance():创建SoundTouch实例
void DestroyInstance():销毁SoundTouch实例
void SetTempo(double value):设置变速系数
void SetChannel(uint value):设置声道数
void SetSampleRate(uint value):设置采样率
void PutSampleShort(short *data, uint sampleLength):输入数据,这个方法说明一下,SoundTouch算法接收的数据其实是4个字节的float数据,但是我们上篇文章获取音频数据的时候输出格式是16位的也就是2个字节,所以我们需要进行一下转化,转成4字节的数据。第二个参数看表面意思表示采样数据的长度,需要注意的是这个长度要区分声道数,举个例子,我们输入的数据的总字节数为1024,一个采样点是16位2个字节,音频的声道数是2,那么这个参数应该是1024/采样点字节数2/音频声道数2=256。这个方法是最重要的也最容易出错的方法,我自己在这个方法占用的时间特别长,因为我刚开始一直把两个采样点的数据合成一个4字节的float的数据,这样其实是错误的,如果输出的数据是那种呲呲的声音,基本问题都出现在了这个方法上,希望能给使用该算法的同学节省点时间。
uint GetSampleShort(short *data, uint sampleLength):获取输出数据,返回值是真实返回的数据长度,这个长度不一定==sampleLength,因为我们说过算法是异步的。
好了,基本上上面这几个API就可以对声音数据进行变速了,接下来提供代码,这个C++库更简单,只有一个头文件和一个C++文件。
LQAudio.h
#pragma once #include "SoundTouch.h" using namespace soundtouch; extern "C" _declspec(dllexport) void CreateInstance(); extern "C" _declspec(dllexport) void DestroyInstance(); extern "C" _declspec(dllexport) void SetTempo(double value); extern "C" _declspec(dllexport) void SetChannel(uint value); extern "C" _declspec(dllexport) void SetSampleRate(uint value); extern "C" _declspec(dllexport) void Flush(); extern "C" _declspec(dllexport) void PutSampleShort(short *data, uint sampleLength); extern "C" _declspec(dllexport) uint GetSampleShort(short *data, uint sampleLength); extern "C" _declspec(dllexport) uint GetSampleNum();
LQAudio.cpp
#include "LQAudio.h" SoundTouch *ins=NULL; /*创建变速算法的实例*/ void CreateInstance() { ins = new SoundTouch(); } /*销毁变速算法的实例。 其实这里面应该进行delete,但是我这边总报异常,待处理*/ void DestroyInstance() { if (ins==NULL) { return; } ins = NULL; } /*设置变速系数*/ void SetTempo(double value) { if (ins == NULL) { return; } ins->setTempo(value); } /*设置声道*/ void SetChannel(uint value) { if (ins == NULL) { return; } ins->setChannels(value); } /*设置采样率*/ void SetSampleRate(uint value) { if (ins == NULL) { return; } ins->setSampleRate(value); } /*输入采样数据*/ void PutSampleShort(short *data, uint sampleLength) { if (ins == NULL) { return; } uint numChannels = ins->numChannels(); // iterate until all samples converted & put to SoundTouch object while (sampleLength > 0) { float convert[8192]; // allocate temporary conversion buffer from stack // how many multichannel samples fit into ‘convert‘ buffer: uint convSamples = 8192 / numChannels; // convert max ‘nround‘ values at a time to guarantee that these fit in the ‘convert‘ buffer uint n = (sampleLength > convSamples) ? convSamples : sampleLength; for (uint i = 0; i < n * numChannels; i++) { convert[i] = data[i]; } // put the converted samples into SoundTouch ins->putSamples(convert, n); sampleLength -= n; data += n * numChannels; } } /*输出采样数据*/ uint GetSampleShort(short *data, uint sampleLength) { if (ins == NULL) { return 0; } uint outTotal = 0; if (data == NULL) { // only reduce sample count, not receive samples return ins->receiveSamples(sampleLength); } uint numChannels = ins->numChannels(); // iterate until all samples converted & put to SoundTouch object while (sampleLength > 0) { float convert[8192]; // allocate temporary conversion buffer from stack // how many multichannel samples fit into ‘convert‘ buffer: uint convSamples = 8192 / numChannels; // request max ‘nround‘ values at a time to guarantee that these fit in the ‘convert‘ buffer uint n = (sampleLength > convSamples) ? convSamples : sampleLength; uint out = ins->receiveSamples(convert, n); // convert & saturate received samples to int16 for (uint i = 0; i < out * numChannels; i++) { // first convert value to int32, then saturate to int16 min/max limits int value = (int)convert[i]; value = (value < SHRT_MIN) ? SHRT_MIN : (value > SHRT_MAX) ? SHRT_MAX : value; data[i] = (short)value; } outTotal += out; if (out < n) break; // didn‘t get as many as asked => no more samples available => break here sampleLength -= n; data += out * numChannels; } // return number of processed samples return outTotal; } uint GetSampleNum() { if (ins == NULL) { return 0; } return ins->numSamples(); } void Flush() { if (ins == NULL) { return; } ins->flush(); }
需要注意的问题
好了,剩下的就是把文件编译成dll文件,和SoundTouch.dll文件一起待用,下一篇将要介绍使用OpenAL接口播放声音。
原文:https://www.cnblogs.com/sauronKing/p/13344015.html