首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Linux ALSA PCM 音訊裝置操作完整指南:初始化、參數設定與資料寫入

     編輯:關於C++

核心 API 概覽

使用 ALSA (Advanced Linux Sound Architecture) 操作 PCM 裝置,主要分為硬體參數初始化與PCM 介面操作兩類函式。

硬體參數初始化函式

  • snd_pcm_hw_params_malloc():配置 snd_pcm_hw_params_t 結構記憶體
  • snd_pcm_hw_params_any():以裝置完整參數空間初始化
  • snd_pcm_hw_params_set_access():設定存取模式(交錯/非交錯)
  • snd_pcm_hw_params_set_format():設定取樣格式(S16_LE, S32_LE 等)
  • snd_pcm_hw_params_set_channels():設定聲道數
  • snd_pcm_hw_params_set_rate_near():設定最接近目標的取樣率
  • snd_pcm_hw_params():提交參數配置
  • snd_pcm_hw_params_free():釋放參數結構

PCM 介面操作函式

  • snd_pcm_open():開啟 PCM 裝置
  • snd_pcm_prepare():準備裝置接收資料
  • snd_pcm_writei():寫入交錯音訊資料
  • snd_pcm_close():關閉裝置

關鍵參數說明

裝置名稱

plughw:0,0:自動轉換格式的 plug 介面(推薦),hw:0,0 需手動確認硬體支援。兩數字分別為卡號與子裝置號。

存取模式

  • SND_PCM_ACCESS_RW_INTERLEAVED:交錯模式,單幀包含所有聲道連續取樣(如 LRLR...)
  • SND_PCM_ACCESS_RW_NONINTERLEAVED:非交錯模式,各聲道資料分開存放

取樣格式常用巨集

SND_PCM_FORMAT_S16_LE  // 有號 16-bit Little Endian
SND_PCM_FORMAT_S16_BE  // 有號 16-bit Big Endian
SND_PCM_FORMAT_U16_LE  // 無號 16-bit Little Endian
SND_PCM_FORMAT_S32_LE  // 有號 32-bit Little Endian
// ... 以此類推

完整初始化範例

int open_device(struct mad_header const *header) {
    int err;
    snd_pcm_hw_params_t *hw_params;
    char *pcm_name = "plughw:0,0";
    int rate = header->samplerate;
    int channels = (header->mode == 0) ? 1 : 2;

    if ((err = snd_pcm_open(&playback_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
        fprintf(stderr, "cannot open audio device %s (%s)\n", pcm_name, snd_strerror(err));
        return -1;
    }
    if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) return -1;
    if ((err = snd_pcm_hw_params_any(playback_handle, hw_params)) < 0) return -1;
    if ((err = snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) return -1;
    if ((err = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S32_LE)) < 0) return -1;
    if ((err = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &rate, 0)) < 0) return -1;
    if ((err = snd_pcm_hw_params_set_channels(playback_handle, hw_params, channels)) < 0) return -1;
    if ((err = snd_pcm_hw_params(playback_handle, hw_params)) < 0) return -1;

    snd_pcm_hw_params_free(hw_params);
    if ((err = snd_pcm_prepare(playback_handle)) < 0) return -1;
    return 0;
}

PCM 資料寫入與錯誤恢復

資料格式轉換(32-bit → 16-bit)

while (nsamples--) {
    signed int sample = pcm->samples[0][j];
    *(OutputPtr++) = sample & 0xff;
    *(OutputPtr++) = (sample >> 8);
    *(OutputPtr++) = (sample >> 16);
    *(OutputPtr++) = (sample >> 24);
    if (nchannels == 2) {
        sample = pcm->samples[1][j];
        *(OutputPtr++) = sample & 0xff;
        *(OutputPtr++) = (sample >> 8);
        *(OutputPtr++) = (sample >> 16);
        *(OutputPtr++) = (sample >> 24);
    }
    j++;
}
if ((err = snd_pcm_writei(playback_handle, buf, samples)) < 0) {
    err = xrun_recovery(playback_handle, err);
    if (err < 0) { fprintf(stderr, "Write error: %s\n", snd_strerror(err)); return -1; }
}

xrun_recovery() - 底流/掛起恢復

int xrun_recovery(snd_pcm_t *handle, int err) {
    if (err == -EPIPE) { // under-run
        err = snd_pcm_prepare(handle);
        if (err < 0) fprintf(stderr, "Can't recovery from underrun: %s\n", snd_strerror(err));
        return 0;
    } else if (err == -ESTRPIPE) { // suspend
        while ((err = snd_pcm_resume(handle)) == -EAGAIN) sleep(1);
        if (err < 0) {
            err = snd_pcm_prepare(handle);
            if (err < 0) fprintf(stderr, "Can't recovery from suspend: %s\n", snd_strerror(err));
        }
        return 0;
    }
    return err;
}

-EPIPE 表示應用程式未及時送資料導致底流;-ESTRPIPE 表示裝置被掛起。恢復策略分別呼叫 snd_pcm_prepare() 或 snd_pcm_resume()。

libmad 整合解碼流程

使用 mad_stream_buffer() 關聯 MP3 資料流,循環呼叫 mad_frame_decode() 取得 PCM 幀,處理 MAD_ERROR_BUFLEN 緩衝不足情況,最後送入上述寫入流程播放。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved