SDL2 —— 播放WAV音频

2017-1-13 Plan C SDL2

www.kurukurumi.com原创,转载请注明出处。

E-mail:hubenchang0515@outlook.com


        http://wiki.libsdl.org/CategoryAudio

        可以使用SDL_LoadWAV函数来加载一个WAV格式的音频文件。

#include <SDL_audio>
SDL_AudioSpec* SDL_LoadWAV(const char* file, SDL_AudioSpec* spec,
                             Uint8** audio_buf,Uint32* audio_len);
//失败返回NULL

        这个函数的第一个参数是WAV文件的路径,第二个参数返回声音的格式,第三个参数返回声音的数据指针,第四个参数返回声音数据的长度。例如:

SDL_AudioSpec spec;
Uint8* buffer;
Uint32 length;
SDL_LoadWAV("sound.wav",&spec,&buffer,&length);
/*
运行成功的话
spec里保存了声音的格式
buffer指向声音的数据
length保存了声音数据的长度
*/


        通过SDL_OpenAudio打开声音设备,它的第一个参数是声音的格式,第二个参数返回设备的实际播放格式

#include <SDL_audio.h>
int SDL_OpenAudio(SDL_AudioSpec* desired,SDL_AudioSpec* obtained);
//成功返回0,失败返回非0


        通过SDL_PauseAudio函数控制播放和停止,参数为0时播放参数为1时停止

#include <SDL_audio>
void SDL_PauseAudio(int pause_on);


        结构体SDL_AudioSpec里有两个特殊的成员SDL_AudioCallback callbackvoid* userdata需要手动添加。userdata是由我们自己定义的用户数据,callback是播放声音的回调函数。

void SDL_AudioCallback(void* userdata,Uint8* stream,int len);

        当声音设备需要数据时,就会调用这个函数,userdata就是SDL_AudioSpec里的userdata,stream指向声音设备的数据,len是设备需要的数据长度。在这个函数中需要做的事情就是将声音数据拷贝到stream。

        通过SDL_LoadWAV函数我们得到了声音的数据指针buffer,假设它是全局变量,下面是声音回调函数的一个简单示例:

/* 这个函数在播放完之后仍会拷贝数据,产生错误 */
void callback(void* userdata,Uint8* stream,int len)
{
    /* 定义静态变量pos标记要拷贝的位置 */
    static int pos = 0;
    /* 从buffer+pos的位置拷贝len长度的数据到stream */
    SDL_memcpy(stream,buffer+pos,len);
    /* pos标记到下次要拷贝的位置 */
    pos += len;
}


        下面是一段播放WAV文件的完整代码:

/*
Author : PlanC
E-mail : hubenchang0515@outlook.com
Blog   : www.kurukurumi.com
Date   : 2016-12-13
*/

#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_EVERYTHING);
    SDL_Window* win = NULL;//窗口指针
    win = SDL_CreateWindow("SDL2",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,
                            225,300,0);
    /* 创建Renderer */
    SDL_Renderer* render = NULL;
    render = SDL_CreateRenderer(win,-1,SDL_RENDERER_ACCELERATED);
    /* 清空Render */
    SDL_RenderClear(render);     
    /* 加载BMP图片 */
    SDL_Surface* surface = SDL_LoadBMP("img.bmp");
    /* 通过Surface创建Texture */
    SDL_Texture* texture = SDL_CreateTextureFromSurface(render,surface);
    /* 释放Surface */
    SDL_FreeSurface(surface);
    /* 将Texture复制到显示器 */
    SDL_RenderCopy(render,texture,NULL,NULL);
    /* 显示Render */
    SDL_RenderPresent(render);

    

    SDL_AudioSpec audio;
    Userdata userdata; //用于保存声音数据的指针和长度
    /* 加载WAV文件,声音数据保存到userdata.buffer,长度保存到userdata.length */
    SDL_LoadWAV("禁绝边境线.wav",&audio,&userdata.buffer,&userdata.length);
    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_DestroyRenderer(render);
    SDL_DestroyTexture(texture);
    /* 销毁窗口 */
    SDL_DestroyWindow(win);
    /* 关闭SDL子系统 */
    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  //如果剩余的数据小于需要的数据,拷贝剩余的数据并将pos恢复为0
	{
		SDL_memcpy(stream,buffer+pos,length-pos);
		pos = 0;
	}
}

发表评论:

Powered by emlog
鄂ICP备16003833号