前面几篇关于SDL的文章介绍的是以画面为主,这里介绍下SDL中针对音频播放提供的机制,以及如何应用。
对于音频而言,有几个概念需要事先了解下,采样率、声道数、量化位数,如果你不清楚的话,麻烦先了解下这几本参数的概念。
常用的采样率有8kHz(语音、电话)、48kHz(电视),声道数常用的是双声道或5.1声道,量化位数8bit或16bit。
SDL提供了音频设备、音频输入/输出的控制机制。
比较常规的做法是先创建音频设备,然后按照你的实际需求进行音频输入输出。
由于音频处理相对简单,这里仅在一个cpp中实现所有wav播放的功能。
工程创建
为了演示期间,我们在vs10中创建控制台程序,并包含SDL头文件和库的路径。,工程名为3_wav_play。我从网上下了一个wav音频,名字为“congtouzailai.wav”。(音量比较大的,播放的时候注意下)
wav文件加载
SDL提供了SDL_LoadWAV函数,用于加载wav文件,加载的代码如下:
SDL_AudioSpec wav_spec; Uint32 wav_length; Uint8 *wav_buffer; /* Load the WAV */ if (SDL_LoadWAV("congtouzailai.wav", &wav_spec, &wav_buffer, &wav_length) == NULL) { fprintf(stderr, "Could not open test.wav: %s ", SDL_GetError()); return -1; }
函数调用成功之后,就可以获得wav文件中的音频参数,比如采样率、声道数、量化位数等,其中原始的音频数据(PCM)存储在wav_buffer中,长度为wav_length。
创建回调参数上下文
struct AudioPlayContext { Uint8 * data; // pcm buffer int offset; // current read pos int data_len; // left data length bool is_exit; // is audio play buffer empty? };
提供这个参数,只是为了提供数据从主函数到回调函数传递参数的机制。
创建音频设备
AudioPlayContext context; context.data = wav_buffer; context.offset = 0; context.data_len = wav_length; context.is_exit = false; // open audio device SDL_AudioSpec want, have; want = wav_spec; want.callback = MyAudioCallback; // you wrote this function elsewhere. want.userdata = &context; if (SDL_OpenAudio(&want, &have) < 0) { printf("Failed to open audio: %s ", SDL_GetError()); return -2; } if (have.format != want.format) printf("We didn't get Float32 audio format. ");
这里最主要的结构体SDL_AudioSpec的初始化,这里的回调函数MyAudioCallback,提供了向播放设备写入音频数据的方式。
播放音频数据
在主线程中,我们启动播放,并等待当前wav文件播放完成。代码如下
SDL_PauseAudio(0); // start audio playing. // wait for the end while(!context.is_exit) { SDL_Delay(1000); }
在回调函数中我们需要将数据填充到音频设备的播放缓冲中,并设置是否播放完成的标志位,代码如下
void MyAudioCallback(void* userdata, Uint8* stream, int len) { AudioPlayContext * context = reinterpret_cast<AudioPlayContext *>(userdata); int copy_len = __min(len, context->data_len); memcpy(stream, context->data+context->offset, copy_len); context->data_len -= copy_len; context->offset += copy_len; if (context->data_len <= 0) { context->is_exit = true; } if (copy_len < len) { memset(stream+copy_len, 0, len-copy_len); } }
反初始化操作
程序退出时,别忘记释放wav文件加载时的数据。
SDL_CloseAudio(); /* Do stuff with the WAV data, and then... */ SDL_FreeWAV(wav_buffer); SDL_Quit();
上面几个合并在一起就是最简单的wav文件播放器。
相关代码可以从我的git下载,url如下:https://git.oschina.net/Tocy/SampleCode.git,位于TocySDL2VisualTutorial目录下。