使用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 );
     }


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容