SDL2新手入门 —— 加载并播放WAV音频

SDL2 阅读(231)

# 加载并播放WAV音频  
SDL2本身仅支持加载WAVE格式(.wav)的音频文件,加载其他音频文件需要进行音频解码,可以使用SDL扩展库[SDL_mixer](https://www.libsdl.org/projects/SDL_mixer/)。  

使用[`SDL_LoadWAV`](http://wiki.libsdl.org/SDL_LoadWAV)函数来加载一个WAV格式的音频文件 :  
```C
#include <SDL_audio>
SDL_AudioSpec* SDL_LoadWAV(const char* file, SDL_AudioSpec* spec,
                             Uint8** audio_buf,Uint32* audio_len);
//成功返回指针,失败返回NULL
```
* `file`参数是wav文件的路径
* `spec`参数**返回**音频的[参数](http://wiki.libsdl.org/SDL_AudioSpec)
* `audio_buf`参数**返回**音频的数据
* `audio_len`参数**返回**`audio_buf`的长度


使用[`SDL_FreeWAV`](http://wiki.libsdl.org/SDL_FreeWAV)函数释放不在需要的声音数据 :  
```C
void SDL_FreeWAV(Uint8* audio_buf);
```

使用[`SDL_OpenAudio`](http://wiki.libsdl.org/SDL_OpenAudio)函数打开声音设备 :  
```C
#include <SDL_audio.h>
int SDL_OpenAudio(SDL_AudioSpec* desired,SDL_AudioSpec* obtained);
//成功返回0,失败返回负值
```
* `desired`参数为期望的音频设备参数
* `obtained`参数**返回**实际打开的音频设备参数

结构体[`SDL_AudioSpec`](http://wiki.libsdl.org/SDL_AudioSpec) `desired`里有两个特殊的成员[`SDL_AudioCallback`](http://wiki.libsdl.org/SDL_AudioSpec#callback) `callback`和`void* userdata`需要手动添加。  
```C
void SDL_AudioCallback(void* userdata, Uint8* stream, int len);
```
* 当声音设备每次需要数据是时就会调用回调函数`callback`  
* `userdata`参数等于`desired`里的`userdata`,可以通过强制类型转换传递任何数据  
* `stream`参数指向设备,需要将数据拷贝到这里  
* `len`参数表示建议本次拷贝的数据长度  

通过[`SDL_PauseAudio`](http://wiki.libsdl.org/SDL_PauseAudio)函数控制设备播放和停止,参数为`0`时播放,参数为`1`时停止。  
```C
#include <SDL_audio>
void SDL_PauseAudio(int pause_on);
```

使用[`SDL_CloseAudio`](http://wiki.libsdl.org/SDL_CloseAudio)函数关闭设备。
```C
void SDL_CloseAudio(void);
```

## 加载并播放WAV文件的完整代码
```C
#include <SDL2/SDL.h>
 
/* 保存声音数据的指针和长度 */
typedef struct Userdata
{
    Uint8* buffer;
    int length;
}Userdata;
 
/* 声明声音回调函数 */
void getData(void* userdata,Uint8* stream,int len);
 
int main(int argc,char* argv[])
{
    /* 初始化音频子系统 */
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);

    SDL_AudioSpec audio;
    Userdata userdata; //用于保存声音数据的指针和长度

    /* 加载WAV文件,声音数据保存到userdata.buffer,长度保存到userdata.length */
    if(NULL == SDL_LoadWAV(argv[1], &audio, &(userdata.buffer), &(userdata.length)))
    {
        SDL_Log("%s", SDL_GetError());
        return 1;
    }
    audio.callback = getData; //将回调函数赋给audio.callback
    audio.userdata = &userdata; //将userdata赋值给audio.callback
    SDL_OpenAudio(&audio,NULL); //打开声音设备
    SDL_PauseAudio(0); //开始播放
 
 
    SDL_Event e;//定义保存事件的变量
    while(1)
    {
        SDL_PollEvent(&e); //捕获事件
        if(e.type == SDL_QUIT) //如果事件类型是退出,则跳出循环
        {
            break;
        }
    }
     
    /* 停止播放、释放内存退出程序 */
    SDL_PauseAudio(1);
    SDL_FreeWAV(userdata.buffer);
    SDL_CloseAudio();
    SDL_Quit();
     
    return 0;
}
 
/* 回调函数,userdata里保存了声音数据指针和长度 */
void getData(void* userdata, Uint8* stream, int len)
{
    static int pos = 0; //保存要拷贝的位置
    Uint8* buffer = ((Userdata*)userdata)->buffer; //声音数据的指针
    int length = ((Userdata*)userdata)->length;    //声音数据的长度
 
    if( length-pos > len)  //如果剩余的数据大于需要的数据,则拷贝len长度的数据
    {
        SDL_memcpy(stream,buffer+pos,len);
        pos += len;
    }
    else if(length > pos)  //如果剩余的数据小于需要的数据,则拷贝剩余的数据
    {
        SDL_memcpy(stream,buffer+pos,length-pos);
        pos = length;
    }
    else  // 已经播放完
    {
        SDL_Event e;
        e.type = SDL_QUIT;
        SDL_PushEvent(&e); // 产生一个退出事件
    }
    
    SDL_Log("%d/%d", pos, length);
}
```

Markdown使用 帮助


匿名

在山的那边,海的那边,有一群蓝精灵。

匿名

我有一头小毛驴,我从来也不骑。

匿名

富强 民主 文明 和谐 自由 平等 公正 法治 爱国 敬业 诚信 友善