1. 程式人生 > >【秒懂音視訊開發】12_播放WAV

【秒懂音視訊開發】12_播放WAV

對於WAV檔案來說,可以直接使用*ffplay*命令播放,而且不用像PCM那樣增加額外的引數。因為WAV的檔案頭中已經包含了相關的音訊引數資訊。 ```shell ffplay in.wav ``` 接下來演示一下如何使用SDL播放WAV檔案。 ## 初始化子系統 ```cpp // 初始化Audio子系統 if (SDL_Init(SDL_INIT_AUDIO)) { qDebug() << "SDL_Init error:" << SDL_GetError(); return; } ``` ## 載入WAV檔案 ```cpp // 存放WAV的PCM資料和資料長度 typedef struct { Uint32 len = 0; int pullLen = 0; Uint8 *data = nullptr; } AudioBuffer; // WAV中的PCM資料 Uint8 *data; // WAV中的PCM資料大小(位元組) Uint32 len; // 音訊引數 SDL_AudioSpec spec; // 載入wav檔案 if (!SDL_LoadWAV(FILENAME, &spec, &data, &len)) { qDebug() << "SDL_LoadWAV error:" << SDL_GetError(); // 清除所有的子系統 SDL_Quit(); return; } // 回撥 spec.callback = pull_audio_data; // 傳遞給回撥函式的userdata AudioBuffer buffer; buffer.len = len; buffer.data = data; spec.userdata = &buffer; ``` ## 開啟音訊裝置 ```cpp // 開啟裝置 if (SDL_OpenAudio(&spec, nullptr)) { qDebug() << "SDL_OpenAudio error:" << SDL_GetError(); // 釋放檔案資料 SDL_FreeWAV(data); // 清除所有的子系統 SDL_Quit(); return; } ``` ## 開始播放 ```cpp // 開始播放(0是取消暫停) SDL_PauseAudio(0); while (!isInterruptionRequested()) { if (buffer.len > 0) continue; // 每一個樣本的大小 int size = spec.channels * SDL_AUDIO_BITSIZE(spec.format) / 8; // 最後一次播放的樣本數量 int samples = buffer.pullLen / size; // 最後一次播放的時長 int ms = samples * 1000 / spec.freq; SDL_Delay(ms); break; } ``` ## 回撥函式 ```cpp // 等待音訊裝置回撥(會回撥多次) void pull_audio_data(void *userdata, // 需要往stream中填充PCM資料 Uint8 *stream, // 希望填充的大小(samples * format * channels / 8) int len ) { // 清空stream SDL_memset(stream, 0, len); AudioBuffer *buffer = (AudioBuffer *) userdata; // 檔案資料還沒準備好 if (buffer->
len <= 0) return; // 取len、bufferLen的最小值 buffer->pullLen = (len > (int) buffer->len) ? buffer->len : len; // 填充資料 SDL_MixAudio(stream, buffer->data, buffer->pullLen, SDL_MIX_MAXVOLUME); buffer->data += buffer->pullLen; buffer->len -= buffer->pullLen; } ``` ## 釋放資源 ```cpp // 釋放WAV檔案資料 SDL_FreeWAV(data); // 關閉裝置 SDL_CloseAudio(); // 清除所有的子系統 SDL_Qui