06-SDL播放音频PCM

打开音频设备
int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired,SDL_AudioSpec *obtained);
    // desired:期望的参数。
    // obtained:实际音频设备的参数,一般情况下设置为NULL即可。
SDL_Audiospec


typedef struct SDL_AudioSpec
{
    int freq;                   // 音频采样率
    SDL_AudioFormat format;     // 音频数据格式
    Uint8 channels;             // 声道数: 1 单声道,2 立体声
    Uint8 silence;              // 设置静音的值,因为声音采样是有符号的,所以0当然就是这个值
    Uint16 samples;             // 音频缓冲区中的采样个数,要求必须是2的n次
    Uint16 padding;             // 考虑到兼容性的一个参数
    Uint32 size;                // 音频缓冲区的大小,以字节为单位
    SDL_AudioCallback callback; // 填充音频缓冲区的回调函数
    void *userdata:             // 用户自定义的数据
} SDL_AudioSpec;

SDL_AudioCallback
// userdata:SDL_AudioSpec结构中的用户自定义数据,一般情况下可以不用.
// stream:该指针指向需要填充的音频缓冲区。
// len:音频缓冲区的大小(以字节为单位)。
void (SDLCALL* SDL AudioCallback) (void *userdata, Uint8 *stream, int len);
播放音频数据
// 当pause_on设置为0的时候即可开始播放音频数据。设置为1的时候,将会播放静音的值。
void SDLCALL SDL_PauseAudio(int pause_on)

/************************************ ffmpeg 提取 PCM 音频数据 ****************************************/

提取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

/************************************ ffmpeg 提取 PCM 音频数据 ****************************************/

公共部分

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

回调函数

// void(SDLCALL *SDL AudioCallback)(void *userdata, Uint8 *stream, int len);
void fill_audio_pcm(void *udata, Uint8 *stream, int len)
{
    SDL_memset(stream, 0, len);     // stream - 音频缓冲区的数据指针
    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); // 采样率 1024 | 声道数 2 | 音频数据格式是 16 bit
    s_audio_pos += len;        // 移动缓存指针
}

主线程

int PCM_audio_display()
{
    int ret = -1;
    FILE *audio_fd = NULL;
    SDL_AudioSpec spec;
    const char *path = "/home/king/ffmpeg_src/lessonCode/ZeroVoiceEducation/01_lesson/44100_16bit_2ch.pcm";
    // 每次缓存的长度
    size_t read_buffer_len = 0;

    // SDL initialize
    SDL_Init(SDL_INIT_AUDIO); // 支持AUDIO

    // 打开PCM文件
    audio_fd = fopen(path, "rb");
    if (!audio_fd)
    {
        fprintf(stderr, "Failed to open pcm file!\n");
        goto _FAIL;
    }
    s_audio_buf = (uint8_t *)malloc(PCM_BUFFER_SIZE);

    // 音频参数设置SDL_AudioSpec
    spec.freq = 44100;              // 采样频率
    spec.format = AUDIO_S16SYS;     // 采样点格式
    spec.channels = 2;              // 2 通道
    spec.silence = 0;               // 设置静音的值,因为声音采样是有符号的,所以0当然就是这个值
    spec.samples = 1024;            // 每次读取的采样数量,多久产生一次回调和 samples 有关 <23.2ms>
    spec.callback = fill_audio_pcm; // 回调函数
    spec.userdata = NULL;

    // 打开音频设备
    if (SDL_OpenAudio(&spec, NULL))
    {
        fprintf(stderr, "Failed to open audio device,%s\n", SDL_GetError());
        goto _FAIL;
    }

    // 当pause_on设置为0的时候即可开始播放音频数据。设置为1的时候,将会播放静音的值。
    SDL_PauseAudio(0);

    int data_count = 0; // 记录读取总的字节数
    while (TRUE)
    {
        // 从文件中读取PCM数据
        read_buffer_len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);
        if (read_buffer_len == 0)
            break;

        data_count += read_buffer_len; // 统计读取的数据总字节数
        printf("读取的数据总字节数 %10d .\n", data_count);
        s_audio_end = s_audio_buf + read_buffer_len; // 更新buffer的结束位置
        s_audio_pos = s_audio_buf;                   // 更新buffer的起始位置

        // the main thread wait for a moment
        while (s_audio_pos < s_audio_end)
        {
            SDL_Delay(2); // 等待PCM数据消耗
        }
    }
    printf("play PCM finish\n");
    // 关闭音频设备
    SDL_CloseAudio();

_FAIL:
    // release some resources
    if (s_audio_buf)
        free(s_audio_buf);
    if (audio_fd)
        fclose(audio_fd);
    // quit SDL
    SDL_Quit();

    return 0;
}

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值