//
// Created by simp on 2021/7/7.
//
#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 *userdata, Uint8 *stream, int len) {
//SDL_AudioCallback
//userdata:SDL_AudioSpec 结构中的用户自定义数据,一般情况下可以不用。
//stream:该指针指向需要填充的音频缓冲区
//len:音频缓冲区的大小(以字节为单位)1024*2*2
SDL_memset(stream, 0, len);
if (s_audio_pos >= s_audio_end)//数据读取完毕
{
printf("read complete\n");
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 -t20 -codec:a pcm_s16le -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm
//测试pcm
//ffplay -ar 44100 -ac2 -f s16le 44100_16bit_2ch.pcm
#undef main
int main(int argc, char *argv[]) {
int ret = -1;
FILE *audio_fd = NULL;
SDL_AudioSpec spec;
// C:\Users\simp\CLionProjects\SDLaction\cmake-build-debug\44100_16bit_2ch.pcm
const char *path = "44100_16bit_2ch.pcm";
size_t read_buffer_len = 0;
//SDL_initialize
if (SDL_Init(SDL_INIT_AUDIO)) {//确认设备是否支持AUDIO
fprintf(stderr, "Could not initalize SDL -%s\n", SDL_GetError());
return ret;
}
//打开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;//声道数:1单声道 2立体声
spec.silence = 0;//设置静音的值,因为声音采样是有符号的,所以0当然就是这个值
spec.samples = 1024;//23.2ms -> 46.4ms每次读取的采样数量,多久产生一次回调和samples 音频缓冲区中的采样个数,要求必须是2的n次方
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;
}
//play audio
SDL_PauseAudio(0);//当pause_on为0的时候即可开始播放音频数据。设置为1的时候,将会播放静音的值。
int data_count = 0;
while (1) {
//从文件读取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("now playing %10d bytes data.\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(10);//等待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);
SDL_Quit();
return 0;
}
SDL播放PCM