使用SoudTouch实现变速变调

  1. 声明SoundTouch对象和内存变量,根据声道数和采样率初始化对象和内存
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处理
//采样个数,具体怎么获取看具体情况
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. 设置变速和变调
soundTouch->setPitch(1.0); //变调
soundTouch->setTempo(1.5);//变速
  1. SoudTouch选择处理数据是16bit还是32bit,在STTypes.h里面找到
#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处理方式:

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. 官方示例,将一个文件变速变调转为另外一个文件
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示例
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 );
 }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菩提树下的杨过

Flash在线拍摄用户头象

很多网站在上传用户头象时,除了传统方式上传外,都支持在线摄像头拍照并做简单编辑,完成之后再将图象数据提交到服务端(比如ASP.Net),这几天正好需要这个功能,...

34780
来自专栏游戏杂谈

基于SOUI开发一个简单的小工具

Duilib 很久不维护了,而很多不同的分支,似乎都不太维护。微信 Windows 的版本是基于 Duilib 进行开发的,说明应该还是很广泛的。

46330
来自专栏黑白安全

来做个Google Hack吗?

storemanager/contents/item.php?page_code=

71760
来自专栏流浪猫的golang

MongoDB 中文的全文索引

MongoDB 从3.2 版本以后添加了对中文索引的支持: 官网链接:https://docs.mongodb.com/manual/reference/t...

35530
来自专栏生信技能树

基因名变化太快,比如PAM50

当然准备把这些基因跟ensembl数据库的ID对应的时候我发现少了3个,然后我搜索发现它们的symbol其实被修改了,可以说变化比较快啦,才几年时间,3 of ...

16120
来自专栏数据结构与算法

1020 孪生蜘蛛

1020 孪生蜘蛛 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 在G城保卫...

32450
来自专栏游戏杂谈

cocos2d-x 2.x版本接入bugly的总结

最开始项目使用的是自己DIY的很简陋的上报系统,后来改成google breakpad来上报,发现其实都做的不太理想,游戏引擎因为版本历史问题存在一些崩溃问题。...

20500
来自专栏图像识别与深度学习

《Android》Lesson15-学段复习

205100
来自专栏Hongten

pygame系列_箭刺Elephant游戏_源码下载

http://www.pygame.org/docs/tut/chimp/ChimpLineByLine.html

18040
来自专栏华仔的技术笔记

Spectrum光谱链共识算法的分析

Spectrum(光谱链)是SmartMesh生态下的公链,承载去中心化Mesh网络实现万物互联dapp的底层公链。由Payment Channel的建构的Sm...

10530

扫码关注云+社区

领取腾讯云代金券