Beginning SDL 2.0(6) 音频渲染及wav播放

前面几篇关于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\n", 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\n", SDL_GetError());
        return -2;
    }
    
    if (have.format != want.format)
        printf("We didn't get Float32 audio format.\n");

这里最主要的结构体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目录下。

你可能感兴趣的:(Beginning SDL 2.0(6) 音频渲染及wav播放)