天天看點

【音視訊連載-007】基礎學習篇-SDL 播放 PCM 音頻檔案(上)PCM 檔案素材準備代碼實踐

作者:星隕

來源:

音視訊開發進階 SDL 播放音頻檔案有兩種方法,可以了解成

推(push)

拉(pull)

兩種模式。

就是我們主動向裝置緩沖區填充 Buffer ,而

就是由裝置拉取

Buffer

填充到緩沖區。

在一些開發模型中,如果資料傳遞能夠抽象成

的形式,那麼肯定就會有

本篇文章主要是講解 SDL 以推的形式播放音頻檔案。

PCM 檔案素材準備

首先還是得準備素材,做音視訊相關實驗就是這麼麻煩~~

找一個 mp3 檔案,使用 FFmpeg 指令将它轉換成 pcm 檔案,友善的話可以直接使用代碼倉庫提供的 mp3 檔案。

不像在視訊播放中準備素材那樣簡單,音頻檔案對于參數的資訊要求多一點。首先要使用 ffmpeg 檢視 mp3 檔案的一些資訊,比如采樣率、聲道數等。

ffmpeg -i file_name.mp3           
【音視訊連載-007】基礎學習篇-SDL 播放 PCM 音頻檔案(上)PCM 檔案素材準備代碼實踐

得到如圖所示的資訊,可以看到 mp3 檔案采樣率是 44100 Hz,雙聲道,再使用 FFmpeg 轉換時要用到上面的資訊。

ffmpeg -i file_name.mp3 -acodec pcm_s16le -f s16le -ac 2 -ar 44100 file_name.pcm           

其中:

-acodec pcm_s16le

·指定編碼器

-f s16le

·指定檔案格式,是大端模式還是小端模式

-ac 2

·指定通道數,2 代表雙通道

-ar 44100

·指定采樣率,這裡是 44100 Hz

在轉換時要根據原檔案的采樣率和聲道數進行轉換,否則轉換後的 pcm 檔案播放聲音不對了。

ffplay -ar 44100 -channels 2 -f s16le -i file_name.pcm           

通過上面的指令可以驗證轉換是否正确,還是要注意聲道數和采樣率的設定,如果沒問題的話,說明 PCM 檔案素材就準備完畢,可以進行代碼實踐了。

代碼實踐

首先要通過

SDL_OpenAudioDevice

方法打開一個音頻裝置。

SDL_OpenAudioDevice(const char
                 *device,
                 int iscapture,
                 const SDL_AudioSpec * desired,
                 SDL_AudioSpec *obtained,
                 int allowed_changes);           

其中結構體

SDL_AudioSpec

指定了一系列音頻相關的參數,具體如下:

typedef struct SDL_AudioSpec
{
    int freq;                   /**< DSP frequency -- samples per second */
    SDL_AudioFormat format;     /**< Audio data format */
    Uint8 channels;             /**< Number of channels: 1 mono, 2 stereo */
    Uint8 silence;              /**< Audio buffer silence value (calculated) */
    Uint16 samples;             /**< Audio buffer size in sample FRAMES (total samples divided by channel count) */
    Uint16 padding;             /**< Necessary for some compile environments */
    Uint32 size;                /**< Audio buffer size in bytes (calculated) */
    SDL_AudioCallback callback; /**< Callback that feeds the audio device (NULL to use SDL_QueueAudio()). */
    void *userdata;             /**< Userdata passed to callback (ignored for NULL callbacks). */
} SDL_AudioSpec;           

這些參數和音頻是息息相關的,比如采樣率、聲道、音頻資料格式、采樣個數等,後面會專門去介紹它們。

SDL_OpenAudioDevice

方法有兩個參數

desired

obtained

都是

SDL_AudioSpec

類型的。

這裡的意思是我們傳入

desired

指定的音頻參數,但不一定是 SDL 支援的,是以 SDL 會傳回一個它支援的參數資訊放在

obtained

裡面。

不過為了簡單就先把它寫死好了,但即使寫死了有些資訊還是要和你的 PCM 檔案對應上才行,比如

freg

采樣率和

channels

通道數等。

SDL_AudioSpec audioSpec;
    audioSpec.freq = 44100;
    audioSpec.format = AUDIO_S16SYS;
    audioSpec.channels = 2;
    audioSpec.silence = 0;
    audioSpec.samples = 1024;
    // 因為是推模式,是以這裡為 nullptr
    audioSpec.callback = nullptr;

    SDL_AudioDeviceID deviceId;
    if ((deviceId = SDL_OpenAudioDevice(nullptr,0,&audioSpec, nullptr,SDL_AUDIO_ALLOW_ANY_CHANGE)) < 2){
        cout << "open audio device failed " << endl;
        return -1;
    }           

注意到

SDL_AudioSpec

有個參數

callback

設定為了 nullptr 。這個回調是為了在

模式中從回調取資料的,因為這裡暫時用不到就寫成了 nullptr ,下一篇文章就會用到了。

這樣就打開了音頻裝置,傳回一個檔案 Id,如果結果小于 2 說明打開失敗了。

接下來通過

SDL_PauseAudioDevice

方法去播放或者暫停音樂。

SDL_PauseAudioDevice(SDL_AudioDeviceID dev,
                   int pause_on);           

SDL_AudioDeviceID

參數就是上面傳回的檔案 Id,

pause_on

為 0 的話代表播放,1 代表暫停。

最後就要開始主動向裝置緩沖區填充 Buffer 了。

就向 SDL 播放 YUV 視訊那樣,要從 PCM 檔案中讀取一塊 Buffer ,然後通過

SDL_QueueAudio

方法進行填充。

int bufferSize = 4096;
    char* buffer = (char *)malloc(bufferSize);
    // 省略中間代碼
    num =  fread(buffer,1,bufferSize,pFile);
    if (num){
        // 填充
        SDL_QueueAudio(deviceId,buffer,bufferSize);
    }           

如上代碼,首先定義了緩沖區的大小 4096,然後 fread 方法讀取這麼大的内容,最後把它填充進去。

此時運作程式,就會聽到和原來 mp3 檔案一樣的聲音了。

不過這裡有要注意的地方,并不是填充了一下 Buffer 就馬上會有聲音播放出來的,要多填充一些才會有聲音播放。

另外,當播放聲音時,必須要讓程式不能退出,因為音頻播放并不是一個阻塞目前主線程的方法,填充完資料就不管了的話,是聽不到聲音的。要麼加個 SDL_Delay 方法要麼就把

SDL_QueueAudio

方法放在接受消息隊列資訊的循環中,我采用的就是後者。

總結

以上就是音視訊基礎學習連載的

007

篇。

本文具體代碼見倉庫:

https://github.com/glumes/av-beginner

本篇文章對應的送出

tag

av-beginner-004

,可切換至對應源碼檢視。

能力有限,文中有不對之處,歡迎加我微信 ezglumes 進行交流~~

SDL 系列文章
「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。
【音視訊連載-007】基礎學習篇-SDL 播放 PCM 音頻檔案(上)PCM 檔案素材準備代碼實踐