SDL 声音库 (SoundBank)
本系列教程来自Dev Hub。
英文原文地址: http://www.sdltutorials.com/sdl-soundbank/
在这附加的教程里,我们将会增加一个声音库用于加载我们所有的声音。并且,我们通过ID来播放这些文件,不管我们什么时候想要。这个教程只会涉及到声音,没有音乐,对于你来说这是一个非常基本的教程,仅仅让这声音处理的事情完成了。有很多东西可以被加到这个类里,通道,组等等,但是在这里我们只处理基本事情。我们这个教程将会基于我先前的SDL Events 教程。所以,使用这些项目文件如果你想要下面的例子正常工作的话。
第一件你要去做的事情是从 SDL website下载SDL mixer。那里也有我主页上提供的SDL库。确保include文件和SDL的inlcude文件在同一个目录下。lib目录和SDL的lib文件在同一目录下,使得事情变得简单。
通过打开你的项目来开始吧,然后进入到linker设置。吧SDL_mixer加在SDLmain以后,就像:
mingw32
SDLmain
SDL_mixer
SDL
如果你不记得在哪里去找这些设置,你可以去看看第一个教程,温习一下链接设置。如果你有一个.lib的文件,而不是.a的文件,通过browse按钮去找到库文件。
仅仅在一个小方注意一下声音。SDL_mixer把任何在内存里声音都当成是数据块(chunk)。数据块(chunk)包含了频率数据。SDL_mixer的好处是,由来它处理这些数据,并把它发送到音频输出,那意味着SDL_mixer做了所有这些困难的工作。所以所有我们要做的事情只需要加载我们的数据。比如从一个WAV文件里,然后将它发送到SDL_mixer去播放。
首先,创建两个新文件,CSoundBank.h和CSoundBank.cpp。先打开头文件:
#define _CSOUNDBANK_H_
#include <SDL.h>
#include <SDL_mixer.h>
#include <vector>
class CSoundBank {
public:
static CSoundBank SoundControl;
std::vector<Mix_Chunk*> SoundList;
public:
CSoundBank();
int OnLoad(char* File);
void OnCleanup();
public:
void Play(int ID);
};
#endif
我们先有了一个静态的控制对象,这让我们能在程序的任何位置增加声音和播放声音。想象一下这个东西就像是增加/播放声音的主控件。然后我们有了我们的SoundList,这是一个SDL_mixer声音的列表。现在,一个Mix_Chunk的对象包含了需要去播放一个声音的信息。我们接下来要做的事情是加载一个wav文件到Mix_Chunk对象里。想象一下,声音就像SDL_Surface。那么,我们有了一些加载,清除,和播放声音的的基本功能了。很简单吧。
接下来,打开CSoundBank.cpp:
CSoundBank CSoundBank::SoundControl;
CSoundBank::CSoundBank() {
}
int CSoundBank::OnLoad(char* File) {
Mix_Chunk* TempSound = NULL;
if((TempSound = Mix_LoadWAV(File)) == NULL) {
return -1;
}
SoundList.push_back(TempSound);
return (SoundList.size() - 1);
}
void CSoundBank::OnCleanup() {
for(int i = 0;i < SoundList.size();i++) {
Mix_FreeChunk(SoundList[i]);
}
SoundList.clear();
}
void CSoundBank::Play(int ID) {
if(ID < 0 || ID >= SoundList.size()) return;
if(SoundList[ID] == NULL) return;
Mix_PlayChannel(-1, SoundList[ID], 0);
}
这些东西也是非常直观的。我们有一个静态控件,我们空的构造函数,并且还有我们的加载函数。我们基本上能够让声音文件被加载,并且尝试通过Mix_LoadWAV加载到Mix_Chunk对象里。 注意,我先是创建一个临时对象,然后把它扔到列表里。在快接近函数结束的地方,我返回最后被插入声音的ID。因此,例如,我加载了SoundA和SoundB:
int SoundB = CSoundBank::SoundControl.OnLoad("soundb.wav");
用来播放这个声音的ID会被OnLoad函数返回。
OK,接下来我们有cleanup函数,它遍历这个列表并且释放声音数据块。就像SDL_FreeSurface一样简单吧。
最后,我们有了真正播放声音的Play函数。我们先传一个ID(从Load函数得到的返回值)给Play函数。我们做一小点边界测试,确保ID是有效的。然后,试着去播放声音。Mix_PlayChannel有三个参数:
第一个参数指定要播放的通道,-1代表使用第一个可用的通道。
第二个参数指定要播放的数据块。
最后一个参数指定要播放多长的时间。-1代表无限。
我们这个类里没有做大量的关于通道的工作,但是这个类很容被修改,增加功能。基本的,通道允许你修改特定声音的设定。例如,你可以设置音量和偏移在第一个通道上,因此任何在那个通道上播放的声音都有同样的效果。
现在那么让我们做一点小小的测试。打开CApp.h,并且加上头文件,然后加上一些变量和函数到CApp类里:
//…
class CApp {
public:
int SoundA;
int SoundB;
//… other code
public:
void OnKeyDown(SDLKey sym, SDLMod mod, Uint16 unicode);
};
现在,打开CApp_Init.cpp,要是我们能够加载一些声音:
return false;
}
if((SoundB = CSoundBank::SoundControl.OnLoad("soundb.wav")) == -1) {
return false;
}
现在,为了能让我们按下一个按键是发出声音,打开CApp_OnEvent.cpp,然后增加下面的函数:
if(sym == SDLK_1) {
CSoundBank::SoundControl.Play(SoundA);
}
if(sym == SDLK_2) {
CSoundBank::SoundControl.Play(SoundB);
}
}
我们还没有完成,我们必须做一点点事情去启动SDL_mixer。这就像是启动SDL一样,但是我们需要的是去启动SDL_mixer去访问声音。打开CApp_Init.cpp并且加入下面的代码,在SDL_Init下面一点的地方。
return false;
}
Mix_OpenAudio简单的初始话SDL_Mixer。 这个函数接受4个参数。第一个参数用来指定频率,44100通常是一个比较好的频率。但是你依据的WAV文件,你也许要变更频率。另一个通用的频率是22050。我不想谈论过的关于频率的细节,让它在那里,除非你真的需要改变它。下一个参数是格式(format),最好是不要管它除非你有很多关于声音的知识(基本上是采样的大小,8位或16位)。再下一个参数是通道数,1 代表单声道,2代表立体声。注意,这里的通道和以前我在其地方谈的通道是不一样的。这里的通道,其实是声音的输出数目。想象一下,他就像是你有的音箱数目。两个音箱,两个通道。最后一个参数是用来设置声音的大小。除非你真的知道怎么做,最好不要去管它。
为了让代码整洁,当我们完成时我们需要确保我们停止了SDL_mixer的服务,就像是释放任何我们加载的声音。
所以打开CApp_OnCleanup.cpp,然后加入下面的代码:
Mix_CloseAudio();
我们完成了!试着去编译,并且按下1 和 2 键去听一些声音。如果你需要一些WAV文件,下载下面的项目文件去获取它们。
SDL 声音库 (SoundBank) - 课程文件:
Win32: Zip, Rar
Linux: Tar (Thanks Gaten)