SDL播放PCM音频数据

#include <stdio.h>
#include <SDL.h>

// 每次读取2帧数据, 以1024个采样点一帧 2通道 16bit采样点为例
#define PCM_BUFFER_SIZE (1024*2*2*2)

// 音频PCM数据缓存
static Uint8 *s_audio_buf = NULL;
// 目前读取的位置
static Uint8 *s_audio_pos = NULL;
// 缓存结束位置
static Uint8 *s_audio_end = NULL;


// 音频设备回调函数
void fill_audio_pcm(void *udata, Uint8 *stream, int len)
{
    SDL_memset(stream, 0, len);

    if(s_audio_pos >= s_audio_end) // 数据读取完毕
    {
        return;
    }

    // 数据够了就读预设长度,数据不够就只读部分(不够的时候剩多少就读取多少)
    int remain_buffer_len = s_audio_end - s_audio_pos;
    len = (len < remain_buffer_len) ? len : remain_buffer_len;
    // 拷贝数据到stream并调整音量
    SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME/8);
    printf("len = %d\n", len);
    s_audio_pos += len;  // 移动缓存指针
}

// 提取PCM文件
// ffmpeg -i input.mp4 -t 20 -codec:a pcm_s16le -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm
// 测试PCM文件
// ffplay -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm

#undef main
int main(int argc, char* argv[])
{
    // 1.初始化SDL
    if (SDL_Init(SDL_INIT_AUDIO)) {
        fprintf(stderr, "SDL_Init error: %s\n", SDL_GetError());
        return -1;
    }

    // 2.打开PCM文件
    FILE *audio_fd = fopen("44100_16bit_2ch.pcm", "rb");
    if (audio_fd == NULL) {
        printf("fopen pcm file error.\n");
        return -1;
    }

    // 3.打开音频设备
    SDL_AudioSpec spec;
    spec.freq = 44100;          // 采样频率
    spec.format = AUDIO_S16SYS; // 采样格式
    spec.channels = 2;          // 通道
    spec.silence = 0;           // 静音值
    spec.samples = 1024;        // 采样个数
    spec.callback = fill_audio_pcm;
    spec.userdata = NULL;
    if (SDL_OpenAudio(&spec, NULL)) {
        fprintf(stderr, "SDL_OpenAudio error: %s\n", SDL_GetError());
        fclose(audio_fd);
        SDL_Quit();
        return -1;
    }

    // 5.播放音频
    SDL_PauseAudio(0);
    int data_count = 0;
    s_audio_buf = (uint8_t *)malloc(PCM_BUFFER_SIZE);
    while(1) {
        // 读取pcm数据
        size_t len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);
        if (len == 0) {
            break;
        }
        data_count += len;
        printf("now playing %10d bytes data.\n",data_count);
        s_audio_end = s_audio_buf + len;
        s_audio_pos = s_audio_buf;
        while(s_audio_pos < s_audio_end)
        {
            SDL_Delay(10);  // 等待PCM数据消耗
        }
    }

    // 6.关闭音频设备
    printf("play PCM finish\n");
    SDL_CloseAudio();

    // 7.退出
    if (s_audio_buf) {
        free(s_audio_buf);
    }
    if (audio_fd) {
        fclose(audio_fd);
    }
    SDL_Quit();
    return 0;
}

 

posted @ 2020-08-15 19:47  Vzf  阅读(517)  评论(0编辑  收藏  举报