流程:
打开音频设备->给设备设置音频参数->向声卡喂pcm 数据->播放音频->关闭设备
设置音频参数,包括声道,采样率,位深,使得设备知道怎么播放这些音频
播放音频的基本原则:
1.声卡向你要数据而不是你主动推给声卡,无论使用sdl还是其他库都是这样,这是声卡的工作原理。当声卡需要数据时它会调用回调函数,来向我们要数据。我们要写这个回调函数。
2.每次要的数据大小由之前设置的音频参数决定。且声卡会维护一个缓冲区,用来存放接收的数据。
SDL_OpenAudio()/SDL_CloseAudio() //打开音频设备和关闭音频设备
SDL_PauseAudio() //暂停或播放,有两个状态
SDL_MixAudio //混音,多路音频叠加,要注意值越界带来的失真。
每次从文件中读取4M的pcm放在缓冲区buf,声卡缓冲区不足时调用回调函数从buf中读取,若buf被读完数据,则再次去文件中读取4M,直到文件中的数据被读取完毕。
推荐一套音视频免费课程,笔者听完了,nice!免费报名可听!
#include <stdio.h>
#include <SDL.h>
#define BLOCK_SIZE 4096000
static Uint8 *audio_buf = NULL;
static Uint8 *audio_pos = NULL;
static size_t buffer_len = 0;
//callback function for audio devcie
void read_audio_data(void *udata, Uint8 *stream, int len){
if(buffer_len == 0){
return;
}
SDL_memset(stream, 0, len);
len = (len < buffer_len) ? len : buffer_len;
SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
audio_pos += len;
buffer_len -= len;
}
int main(int argc, char *argv[])
{
int ret = -1;
FILE *audio_fd = NULL;
SDL_AudioSpec spec;
char *path = "./test.pcm";
//SDL initialize
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){
fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
return ret;
}
//open pcm file
audio_fd = fopen(path, "r");
if(!audio_fd){
fprintf(stderr, "Failed to open pcm file!\n");
goto __FAIL;
}
//alloc memory for audio
audio_buf = (Uint8*)malloc(BLOCK_SIZE);
if(!audio_buf){
goto __FAIL;
}
//SDL_AudioSpec
spec.freq = 44100;
spec.format = AUDIO_S16SYS;
spec.channels = 2;
spec.silence = 0;
spec.samples = 2048;
spec.callback = read_audio_data;
spec.userdata = NULL;
//open audio devcie
if(SDL_OpenAudio(&spec, NULL)){
fprintf(stderr, "Failed to open audio device, %s\n", SDL_GetError());
goto __FAIL;
}
//play audio
SDL_PauseAudio(0);//此时已启动线程,开始调用回调函数从缓冲区读取数据,并且播放。
do{
//read data from pcm file
buffer_len = fread(audio_buf, 1, BLOCK_SIZE, audio_fd);
fprintf(stderr, "block size is %zu\n", buffer_len);
audio_pos = audio_buf;
//the main thread wait for a moment
while(audio_pos < (audio_buf + buffer_len)) {
SDL_Delay(1);//主线程每隔1ms检查一下audio_buf中的数据是否已被回调函数读取完毕。
}
}while(buffer_len !=0);
//close audio device
SDL_CloseAudio();
ret = 0;
__FAIL:
//release some resources
if(audio_buf){
free(audio_buf);
}
if(audio_fd){
fclose(audio_fd);
}
//quit SDL
SDL_Quit();
return ret;
}
//这个程序还有一个小问题,就是最后一次从文件中读取的内容可能还没来得及全部被回调函数读完,程序就停止了。