使用SoudTouch实现变速变调

  1. 声明SoundTouch对象和内存变量,根据声道数和采样率初始化对象和内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SoundTouch *soundTouch = NULL;
SAMPLETYPE *sampleBuffer = NULL;
//采样率
int sample_rate=44100;
//声道数
int channels =2;
//变调
float pitch= 1.0f;
//变数
float speed= 1.0f;
//采样位数 SoudTouch最低支持16bit,所以使用16bit的来播放
int bits= 16;
//每秒理论PCM大小
int BUFF_SIZE =sample_rate * channels * bits/8;


sampleBuffer = static_cast<SAMPLETYPE *>(malloc(BUFF_SIZE));
soundTouch = new SoundTouch();
soundTouch->setSampleRate(sample_rate);
soundTouch->setChannels(channels);
soundTouch->setPitch(pitch);
soundTouch->setTempo(speed);
  1. PCM数据给SoundTouch处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//采样个数,具体怎么获取看具体情况
int nb=0;
//示例1 :文件读取
int size = fread();
nb = size/channels;
//示例2 :ffmpeg解码
int nb = swr_convert();

//最大采样数

int maxSamples = BUFF_SIZE / channels;

//处理数据
soundTouch->putSamples(sampleBuffer, nb);
//得到数据到sampleBuffer
int num = soundTouch->receiveSamples(sampleBuffer, maxSamples);
  1. 设置变速和变调
1
2
soundTouch->setPitch(1.0); //变调
soundTouch->setTempo(1.5);//变速
  1. SoudTouch选择处理数据是16bit还是32bit,在STTypes.h里面找到
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES)

/// Choose either 32bit floating point or 16bit integer sampletype
/// by choosing one of the following defines, unless this selection
/// has already been done in some other file.
////
/// Notes:
/// - In Windows environment, choose the sample format with the
/// following defines.
/// - In GNU environment, the floating point samples are used by
/// default, but integer samples can be chosen by giving the
/// following switch to the configure script:
/// ./configure --enable-integer-samples
/// However, if you still prefer to select the sample format here
/// also in GNU environment, then please #undef the INTEGER_SAMPLE
/// and FLOAT_SAMPLE defines first as in comments above.
//#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples
#define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples

#endif

根据你的类型注释选择对应的宏定义即可

  1. ffmpeg里面使用的时候需要注意的点:因为FFmpeg解码出来的PCM数据是8bit (uint8)的,而SoundTouch中最低
    16bit( 16bit integer samples),所以我们需要将8bit的数据转换成16bit
    后再给SoundTouch处理。

8bit->16bit处理方式:

1
2
3
4
5
6
7
8
9
10
11
SAMPLETYPE *sampleBuffer=NULL ;
uint8_t *out_buffer = NULL;

//....初始化等

//获取音频数据到out_buffer
int data_size = resampleAudio(reinterpret_cast<void **>(&out_buffer));
for(int i = 0; i < data_size / 2 + 1; i++)
{
sampleBuffer[i] = (buffer[i * 2] | ((buffer[i * 2 + 1]) << 8));
}

  1. 官方示例,将一个文件变速变调转为另外一个文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
static void
_processFile(SoundTouch *pSoundTouch, const float pitch, const float tempo, const char *inFileName,
const char *outFileName) {

SoundTouch *pSoundTouch = new SoundTouch();

//设置音调
pSoundTouch->setPitch(pitch);
//设置音速
pSoundTouch->setTempo(tempo);

int nSamples;//采样率
int nChannels;//声道
int buffSizeSamples;//每一次缓冲大小
SAMPLETYPE sampleBuffer[BUFF_SIZE];//缓冲

// open input file
WavInFile inFile(inFileName);
int sampleRate = inFile.getSampleRate();
int bits = inFile.getNumBits();
nChannels = inFile.getNumChannels();

// create output file
WavOutFile outFile(outFileName, sampleRate, bits, nChannels);

pSoundTouch->setSampleRate(sampleRate);
pSoundTouch->setChannels(nChannels);

assert(nChannels > 0);
buffSizeSamples = BUFF_SIZE / nChannels;

// Process samples read from the input file
while (inFile.eof() == 0) {
int num;

// Read a chunk of samples from the input file
num = inFile.read(sampleBuffer, BUFF_SIZE);
nSamples = num / nChannels;

// Feed the samples into SoundTouch processor
pSoundTouch->putSamples(sampleBuffer, nSamples);

// Read ready samples from SoundTouch processor & write them output file.
// NOTES:
// - 'receiveSamples' doesn't necessarily return any samples at all
// during some rounds!
// - On the other hand, during some round 'receiveSamples' may have more
// ready samples than would fit into 'sampleBuffer', and for this reason
// the 'receiveSamples' call is iterated for as many times as it
// outputs samples.
do {
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
outFile.write(sampleBuffer, nSamples * nChannels);
} while (nSamples != 0);
}

// Now the input file is processed, yet 'flush' few last samples that are
// hiding in the SoundTouch's internal processing pipeline.
pSoundTouch->flush();
do {
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
outFile.write(sampleBuffer, nSamples * nChannels);
} while (nSamples != 0);

delete (pSoundTouch);
}
  1. ffmpeg示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

SoundTouch *soundTouch = NULL;
SAMPLETYPE *sampleBuffer = NULL;
//采样率
int sample_rate=44100;
//声道数
int channels =2;
//变调
float pitch= 1.0f;
//变数
float speed= 1.0f;
//采样位数 SoudTouch最低支持16bit,所以使用16bit的来播放
int bits= 16;
//每秒理论PCM大小
int BUFF_SIZE =sample_rate * channels * bits/8;


sampleBuffer = static_cast<SAMPLETYPE *>(malloc(BUFF_SIZE));
soundTouch = new SoundTouch();
soundTouch->setSampleRate(sample_rate);
soundTouch->setChannels(channels);
soundTouch->setPitch(pitch);
soundTouch->setTempo(speed);


//获取SoundTouch处理的数据
int WlAudio::getSoundTouchData() {
int maxSamples = BUFF_SIZE / channels;
while (playstatus != NULL && !playstatus->exit) {
out_buffer = NULL;
if (finished) {
finished = false;
//获取pcm数据到out_buffer
data_size = resampleAudio(reinterpret_cast<void **>(&out_buffer));
if (data_size > 0) {
for (int i = 0; i < data_size / 2 + 1; i++) {
//解码的数据是8bit的
//8bit->16bit
sampleBuffer[i] = (out_buffer[i * 2] | ((out_buffer[i * 2 + 1]) << 8));
}
//nb 表示采样个数 在resampleAudio里面解码的时候通过swr_convert返回值回去
soundTouch->putSamples(sampleBuffer, nb);
//获取处理后的数据 到sampleBuffer
num = soundTouch->receiveSamples(sampleBuffer,maxSamples);
} else {
soundTouch->flush();
}
}
if (num == 0) {
finished = true;
continue;
} else {
if (out_buffer == NULL) {
num = soundTouch->receiveSamples(sampleBuffer, maxSamples);
if (num == 0) {
finished = true;
continue;
}
}
return num;
}
}
return 0;
}

//OpenSLES播放数据

int buffersize = wlAudio->getSoundTouchData();
if (buffersize > 0) {
//两个8bit->一个16bit 转换为char*是为了都转换成字节来处理
(*wlAudio->pcmBufferQueue)->Enqueue(wlAudio->pcmBufferQueue,
(char *) wlAudio->sampleBuffer, buffersize * nChannels * 2 );
}
-------------The End-------------