【音視頻連載-008】基礎(chǔ)學(xué)習(xí)篇-SDL 播放 PCM 音頻文件(下)

音視頻學(xué)習(xí)入門技術(shù)文章連載:

接上篇 SDL 播放 PCM 音頻文件巡验,已經(jīng)實(shí)現(xiàn)了 的模式去播放,接下來看看 的模式如何實(shí)現(xiàn)。

PCM 文件素材準(zhǔn)備

前面的文章中已經(jīng)準(zhǔn)備好了相關(guān)素材臀脏,這里就不重復(fù)了呜象,還是用同樣的 PCM 文件作為這次實(shí)驗(yàn)素材呼寸。

代碼實(shí)踐

首先還是要通過 SDL_OpenAudioDevice 方法打開一個(gè)音頻設(shè)備稳捆。

    SDL_AudioSpec audioSpec;
    audioSpec.freq = 44100;
    audioSpec.format = AUDIO_S16SYS;
    audioSpec.channels = 2;
    audioSpec.silence = 0;
    audioSpec.samples = 1024;
    // 拉的模式火的,這里要傳一個(gè)函數(shù)
    audioSpec.callback = fill_audio;

    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;
    }

不同的是躺翻,這里 callback 參數(shù)不能是 nullptr 了,要傳一個(gè)函數(shù)指針卫玖。這個(gè)函數(shù)在 模式下會(huì)不斷回調(diào)公你,從而將音頻數(shù)據(jù)填充給設(shè)備緩沖區(qū)。

函數(shù)聲明如下:

typedef void (SDLCALL * SDL_AudioCallback) (
    // 傳用戶自定義的數(shù)據(jù)
    void *userdata, 
    // 指向要填充給設(shè)備緩沖區(qū)的音頻數(shù)據(jù)Buffer的指針
    Uint8 * stream,
    // 音頻數(shù)據(jù)Buffer的長(zhǎng)度
    int len);

參數(shù) stream 是個(gè)指針類型假瞬,它指向要填充給設(shè)備緩沖區(qū)的音頻數(shù)據(jù) Buffer 陕靠,而 len 就是 Buffer 的長(zhǎng)度。userdata 是我們自定義的數(shù)據(jù)脱茉,需要的時(shí)候可以用到剪芥。

在這個(gè)函數(shù)中我們要做的就是將讀取的 PCM 音頻數(shù)據(jù)傳給 stream 指向的 Buffer ,而且還不能超出 len 的長(zhǎng)度琴许,如果超出了截?cái)嘁幌滤胺荆麓位卣{(diào)時(shí)傳剩下的部分。

因此就有了如下的實(shí)現(xiàn):

// 讀取出 pcm 數(shù)據(jù)長(zhǎng)度
static Uint32 audio_len;
// 讀取出的音頻數(shù)據(jù) Buffer
static Uint8 *audio_pos;

// 函數(shù)實(shí)現(xiàn)
void fill_audio(void *udata, Uint8 *stream, int len) {
    SDL_memset(stream, 0, len);
    if (audio_len == 0) {
        return;
    }
    // 數(shù)據(jù)大小不能超過 len
    len = len > audio_len ? audio_len : len;
    
    // 將 stream 和 audio_pos 進(jìn)行混合播放
//    SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);

    // 單獨(dú)播放 audio_pos,也就是解碼出來的音頻數(shù)據(jù)
    memcpy(stream, audio_pos, len);

    audio_pos += len;
    audio_len -= len;

    if (audio_len <= 0){
        // 讀取完了榜田,通知繼續(xù)讀取數(shù)據(jù)
        notifyGetAudioFrame();
    }
}

首先將 stream 數(shù)據(jù)清空益兄。然后比較讀出的 pcm 數(shù)據(jù)長(zhǎng)度 audio_lenlen 的大小,保證數(shù)據(jù)大小不超過 len 的要求箭券。

在播放時(shí)净捅,也就是給 stream 寫數(shù)據(jù)時(shí)有兩種方式。一種時(shí)直接 memcpy 將音頻數(shù)據(jù) audio_pos 拷貝到 Buffer 上就好了辩块。另一種是通過 SDL_MixAudio 方法蛔六。

SDL_MixAudio 方法顧名思義就是混音了,將 stream 和音頻數(shù)據(jù) audio_pos 混合播放废亭,由于一開始就將 stream 數(shù)據(jù)清空為 0 了国章,所以看似混音,實(shí)際上和直接播放音頻數(shù)據(jù)效果一致的豆村。

最后液兽,如果讀出的 pcm 數(shù)據(jù)長(zhǎng)度大于 len,那說明數(shù)據(jù)還沒有全部填充完你画,下一次回調(diào)把剩下的填充到緩沖區(qū)抵碟,同時(shí)移動(dòng)相應(yīng)的指針位置。

如果小于坏匪,就得通知繼續(xù)讀取數(shù)據(jù)了拟逮,這里自定義了一個(gè)事件去通知應(yīng)用讀取音頻數(shù)據(jù)。

// 自定義事件适滓,通知讀取音頻數(shù)據(jù)
void notifyGetAudioFrame(){
    SDL_Event sdlEvent;
    sdlEvent.type = SDL_EVENT_BUFFER_END;
    SDL_PushEvent(&sdlEvent);
}

// 在程序事件循環(huán)中去響應(yīng)事件敦迄,讀取音頻 Buffer
 while (!bQuit) {
        while (SDL_PollEvent(&windowEvent)) {
            switch (windowEvent.type) {
                case SDL_EVENT_BUFFER_END:
                    // 讀取音頻數(shù)據(jù)
                    if (fread(buffer, 1, bufferSize, pFile)) {
                        data_count += bufferSize;
                        audio_chunk = reinterpret_cast<Uint8 *>(buffer);
                        audio_len = bufferSize;
                        audio_pos = audio_chunk;
                    }
                default:
                    break;
            }
        }
    }

在事件的消息循環(huán)中進(jìn)行響應(yīng),讀取音頻 Buffer 凭迹。如果讀取的到的長(zhǎng)度等于 0 了罚屋,也可以通過 fseek 方法將指針 seek 到 0,循環(huán)讀取嗅绸。

最后運(yùn)行一下程序脾猛,就會(huì)播放出和原來 mp3 一樣的音樂了。

總結(jié)

以上就是音視頻基礎(chǔ)學(xué)習(xí)連載的 008 篇鱼鸠。

通過兩篇文章講解了 SDL 播放音頻的兩種方式猛拴,后續(xù)會(huì)主要以 的模式進(jìn)行開發(fā)。

本文具體代碼見倉(cāng)庫(kù):

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

本篇文章對(duì)應(yīng)的提交 tagav-beginner-004蚀狰,可切換至對(duì)應(yīng)源碼查看愉昆。

能力有限,文中有不對(duì)之處麻蹋,歡迎加我微信 ezglumes 進(jìn)行交流~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末跛溉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扮授,更是在濱河造成了極大的恐慌芳室,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刹勃,死亡現(xiàn)場(chǎng)離奇詭異渤愁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)深夯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門抖格,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人咕晋,你說我怎么就攤上這事雹拄。” “怎么了掌呜?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵滓玖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我质蕉,道長(zhǎng)势篡,這世上最難降的妖魔是什么翩肌? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮禁悠,結(jié)果婚禮上念祭,老公的妹妹穿的比我還像新娘。我一直安慰自己碍侦,他們只是感情好粱坤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瓷产,像睡著了一般站玄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上濒旦,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天株旷,我揣著相機(jī)與錄音,去河邊找鬼尔邓。 笑死灾常,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的铃拇。 我是一名探鬼主播钞瀑,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼慷荔!你這毒婦竟也來了雕什?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤显晶,失蹤者是張志新(化名)和其女友劉穎贷岸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磷雇,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡偿警,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了唯笙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片螟蒸。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖崩掘,靈堂內(nèi)的尸體忽然破棺而出七嫌,到底是詐尸還是另有隱情,我是刑警寧澤苞慢,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布诵原,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏绍赛。R本人自食惡果不足惜蔓纠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吗蚌。 院中可真熱鬧腿倚,春花似錦、人聲如沸褪测。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)侮措。三九已至,卻和暖如春乖杠,著一層夾襖步出監(jiān)牢的瞬間分扎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工胧洒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留畏吓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓卫漫,卻偏偏與公主長(zhǎng)得像菲饼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子列赎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 教程一:視頻截圖(Tutorial 01: Making Screencaps) 首先我們需要了解視頻文件的一些基...
    90后的思維閱讀 4,697評(píng)論 0 3
  • 在上一篇筆記中我們已經(jīng)完成了使用SDL播放聲音和視頻宏悦,聲音播放沒有什么問題,而視頻播放太快包吝,很明顯視頻沒有同步饼煞。在...
    762683ff5d3d閱讀 1,325評(píng)論 0 1
  • 根據(jù)ffmpeg官方網(wǎng)站上的例子程序開始學(xué)習(xí)ffmpeg和SDL編程。 SDL是一個(gè)跨平臺(tái)的多媒體開發(fā)包诗越。適用于游...
    762683ff5d3d閱讀 1,799評(píng)論 0 2
  • 小時(shí)候砖瞧,總以為父親就是全世界,他會(huì)做很多事情嚷狞,會(huì)裝飾漂亮房子块促,制作美味佳肴,編制漂亮玩具床未,他會(huì)做的一切都是...
    廣電1701B夏珊珊閱讀 300評(píng)論 0 2
  • 大陸男星胡歌宣布到美國(guó)進(jìn)修即硼,令大批粉絲非常不舍逃片,沒想到近日被爆是為愛而讀,感情生活引起外界高度關(guān)注。面對(duì)傳言四起褥实,...
    BoBo520閱讀 365評(píng)論 0 2