視頻播放器-使用SoundTouch算法庫對聲音進行變速


視頻播放器-視頻播放前期調研

視頻播放器-使用FFMPEG技術對視頻解封裝和解碼

視頻播放器-使用SoundTouch算法庫對聲音進行變速

視頻播放器-使用OpenAL技術播放聲音

視頻播放器-使用封裝的C++插件在Unity3d中播放視頻

視頻播放器-FFMPEG官方庫,包含lib,include,bin x64和x86平台的所有文件,提取碼4v2c

視頻播放器-LQVideo實現視頻解碼C++源代碼,提取碼br9u

視頻播放器-SoundTouch實現聲音變速的C++源代碼,提取碼6htk

視頻播放器-官方openal安裝文件,提取碼yl3j

視頻播放器-OpenAL實現音頻播放功能,提取碼mjp2

 

上一篇我們使用了FFMPEG庫對視頻進行了解碼,拋開細節不談,通過使用接口IntPtr get_audio_frame(int key),我們可以獲取到音頻的數據,也就是一堆字節數組,接下來,就輪到SoundTouch上場了,我們可以把SoundTouch看做是一個工具庫,通過輸入音頻數據,輸出經過變速后的新的數據。

 

關於SoundTouch的使用我們依然分為三部分

  • 環境配置
  • 定義接口及實現接口
  • 需要注意的問題

 

環境配置

  1. 下載源代碼 https://gitlab.com/soundtouch/soundtouch/-/archive/2.1.2/soundtouch-2.1.2.tar.bz2
    沒有什么是比拿到源代碼更令人放心的了
  2. 代碼的soundtouch-master文件夾下的lib文件夾中有編譯好的dll和鏈接庫,include中有頭文件,但是,我們依然推薦自己手動編譯,根據自己的平台要求和調試要求可以生成自己需要的靜態鏈接庫(lib)和動態鏈接庫(dll)。找到source文件夾下的SoundTouch文件夾,用VS雙擊打開.sln文件,我這邊是可以直接編譯成功的
  3. 項目右鍵->屬性->常規,配置類型選擇靜態庫,編譯生成lib文件,選擇動態庫,編譯生成dll文件
  4. image

  5. 用VS新建C++工程,或者直接在SoundTouch工程中新建項目也可以,按照上一篇文件的過程分別配置“附加包含目錄”,“附加庫目錄”和“附加依賴項”。
  6. 經過第四步,我們可以在新建的工程中使用SoundTouch.h文件了。

定義接口及實現接口

配置好環境后,我們開始定義接口,在這之前,先說明一個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();
}

需要注意的問題

  • 上述代碼中的PutSampleShort方法和GetSampleShort方法不是我自己寫的,其實在官方給的源代碼中有一個SoundTouchDLL項目,里面有這個兩個方法
  • 該文章有個點沒有講到,就是聲音的音頻數據怎么轉化為short數據,對於C#來說,有官方的方法Buffer.BlockCopy()

好了,剩下的就是把文件編譯成dll文件,和SoundTouch.dll文件一起待用,下一篇將要介紹使用OpenAL接口播放聲音。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM