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

基於 libmad 的 MP3 解碼播放器實作:從資料結構到完整播放流程

     編輯:關於C++

libmad 核心資料結構

結構體用途
mad_stream儲存待解碼的位元流資料
mad_frameMPEG 幀解碼後資訊 (含 mad_header:層級、聲道模式、位元率、取樣率)
mad_synth合成濾波後的 PCM 輸出
mad_pcmPCM 參數:取樣率、聲道數、取樣資料指標

解碼流程架構

libmad 採回呼函式機制,三大回呼:

  • input:提供輸入位元流
  • output:處理解碼後的 PCM 資料 (播放/存檔)
  • error:錯誤處理

回呼回傳 mad_flow 控制流程:MAD_FLOW_CONTINUE/STOP/BREAK。

音訊裝置開啟與設定

static int sfd;
if ((sfd = open("/dev/dsp", O_WRONLY)) < 0) { perror("open dsp"); return 1; }

// 立體聲、16-bit Little Endian、速率 = 取樣率 * 2 (雙聲道交錯)
int fmt = AFMT_S16_LE;
int speed = pcm->samplerate * 2;
ioctl(sfd, SNDCTL_DSP_SETFMT, &fmt);
ioctl(sfd, SNDCTL_DSP_CHANNELS, &channels);
ioctl(sfd, SNDCTL_DSP_SPEED, &speed);

輸出回呼實作 (PCM 寫入聲卡)

static enum mad_flow output(void *data, struct mad_header const *header, struct mad_pcm *pcm) {
    unsigned int nchannels = pcm->channels;
    unsigned int nsamples = pcm->length;
    mad_fixed_t const *left_ch = pcm->samples[0];
    mad_fixed_t const *right_ch = pcm->samples[1];

    unsigned char Output[6912], *OutputPtr = Output;
    while (nsamples--) {
        signed int sample = scale(*left_ch++);
        *OutputPtr++ = sample >> 0;
        *OutputPtr++ = sample >> 8;
        if (nchannels == 2) {
            sample = scale(*right_ch++);
            *OutputPtr++ = sample >> 0;
            *OutputPtr++ = sample >> 8;
        }
    }
    int n = pcm->length * 4; // bytes = samples * channels * 2(bytes/sample)
    OutputPtr = Output;
    while (n > 0) {
        int wrote = write(sfd, OutputPtr, n);
        if (wrote < 0) { perror("write"); return MAD_FLOW_STOP; }
        OutputPtr += wrote;
        n -= wrote;
    }
    return MAD_FLOW_CONTINUE;
}

定點數縮放 (32-bit → 16-bit)

static inline signed int scale(mad_fixed_t sample) {
    // 四捨五入 + 飽和限幅
    sample += (1L << (MAD_F_FRACBITS - 16));
    if (sample >= MAD_F_ONE) sample = MAD_F_ONE - 1;
    else if (sample < -MAD_F_ONE) sample = -MAD_F_ONE;
    return sample >> (MAD_F_FRACBITS + 1 - 16);
}

輸入回呼 (檔案映射記憶體餵流)

static enum mad_flow input(void *data, struct mad_stream *stream) {
    struct buffer *buf = (struct buffer *)data;
    if (!buf->length) return MAD_FLOW_STOP;
    mad_stream_buffer(stream, buf->start, buf->length);
    buf->length = 0;
    return MAD_FLOW_CONTINUE;
}

主程式完整流程

int main(int argc, char *argv[]) {
    int fd = open(argv[1], O_RDONLY);
    struct stat st; fstat(fd, &st);
    void *fdm = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);

    struct buffer buf = { (unsigned char*)fdm, st.st_size };
    struct mad_decoder decoder;
    mad_decoder_init(&decoder, &buf, input, 0, 0, output, error, 0);
    mad_decoder_options(&decoder, 0);
    mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
    mad_decoder_finish(&decoder);

    munmap(fdm, st.st_size);
    close(sfd);
    return 0;
}

關鍵點:使用 mmap 將 MP3 檔案映射記憶體,input 回呼一次性餵入完整流,libmad 內部處理幀同步與解碼,output 即時播放。

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