第11课 - 播放声音


更新时间:2012年7月8日


播放声音是游戏编程的又一个关键概念。SDL原生的声音函数可能会让人很纠结。所以,你将学习使用SDL_mixer扩展库来播放声效和音乐。SDL_mixer扩展库将使用声音变得非常容易。

SDL_mixer可从此处下载

要安装SDL_mixer,请查看前面的扩展库安装教程。安装SDL_mixer的方法与安装SDL_image极其相似,你仅需将前面教程中的SDL_image替换为SDL_mixer。

本节课介绍了使用SDL_mixer播放音乐和声效的基本方法,并播放了我通过敲打我的显示器制作出来的声效和“音乐”。
//将要播放的音乐
Mix_Music *music = NULL;

//将要使用的声效
Mix_Chunk *scratch = NULL;
Mix_Chunk *high = NULL;
Mix_Chunk *med = NULL;
Mix_Chunk *low = NULL;
这里有新的数据类型,我们将与和它们打交道。

Mix_Music 是我们用于音乐的数据类型,Mix_Chunk 是用于声效的数据类型。
bool init()
{
    //初始化SDL的所有子系统
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
    {
        return false;    
    }
    
    //设置窗口
    screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );
    
    //如果设置窗口出错
    if( screen == NULL )
    {
        return false;    
    }
    
    //初始化SDL_ttf
    if( TTF_Init() == -1 )
    {
        return false;    
    }
    
    //初始化SDL_mixer
    if( Mix_OpenAudio( 22050, MIX_DEFAULT_FORMAT, 2, 4096 ) == -1 )
    {
        return false;    
    }
    
    //设置窗口标题
    SDL_WM_SetCaption( "Monitor Music", NULL );
    
    //如果所有初始化成功
    return true;
}
在初始化函数中,我们调用Mix_OpenAudio() 来初始化SDL_mixer中的声音函数。

该函数的第一个参数是我们使用的声音频率,在本课中,它被设为22050,这也是推荐设置的值。第二个参数是声音格式,我们使用了默认的格式。第三个参数是我们计划使用的频道数量。我们将它设为2来获得立体声,如果设为1,将只有单声道声音。最后一个参数是样本大小,这里被设为4096。

对于那些使用OGG, MOD或者其他非WAV声音格式的人,请使用Mix_Init()来初始化解码器,并使用Mix_Quit()来关闭解码器。因为这里我们仅仅使用了WAV文件,所以我们不必为了用不到的东西来添加更多代码。
bool load_files()
{
    //加载背景图
    background = load_image( "background.png" );
    
    //打开字体
    font = TTF_OpenFont( "lazy.ttf", 30 );
    
    //如果加载背景出错
    if( background == NULL )
    {
        return false;    
    }
    
    //如果加载字体出错
    if( font == NULL )
    {
        return false;
    }
    
    //加载音乐
    music = Mix_LoadMUS( "beat.wav" );
    
    //如果加载音乐出了问题
    if( music == NULL )
    {
        return false;    
    }

    //加载声效
    scratch = Mix_LoadWAV( "scratch.wav" );
    high = Mix_LoadWAV( "high.wav" );
    med = Mix_LoadWAV( "medium.wav" );
    low = Mix_LoadWAV( "low.wav" );
    
    //如果加载声效出了问题
    if( ( scratch == NULL ) || ( high == NULL ) || ( med == NULL ) || ( low == NULL ) )
    {
        return false;    
    }
    
    //如果一切加载正常
    return true;    
}
这是我们的文件加载函数。

要加载音乐,我们使用Mix_LoadMUS()。这个函数接受一个音乐文件的文件名并返回其中的音乐数据。如果出错,它会返回NULL。

要加载声效,我们使用Mix_LoadWAV()。它加载了传入的文件名所对应的声音文件,并返回了一个Mix_Chunk,如果出错了,则返回NULL。
void clean_up()
{
    //释放表面
    SDL_FreeSurface( background );
    
    //释放声效
    Mix_FreeChunk( scratch );
    Mix_FreeChunk( high );
    Mix_FreeChunk( med );
    Mix_FreeChunk( low );
    
    //释放音乐
    Mix_FreeMusic( music );
    
    //关闭字体
    TTF_CloseFont( font );
    
    //退出SDL_mixer
    Mix_CloseAudio();
    
    //退出SDL_ttf
    TTF_Quit();
    
    //退出SDL
    SDL_Quit();
}
在清理函数中,我们调用Mix_FreeChunk() 来清除声效,并用Mix_FreeMusic()来释放音乐。

然后,我们结束了SDL_mixer的使用,调用了Mix_CloseAudio()
    //当用户还没退出
    while( quit == false )
    {
        //当有事件需要处理
        while( SDL_PollEvent( &event ) )
        {
            //如果一个按键被按下
            if( event.type == SDL_KEYDOWN )
            {
在main函数中,在初始化、文件加载、背景和信息的blit完成后,我们进入了主循环。然后我们开始处理事件,首先是按键事件。
                //如果数字键“1”被按下
                if( event.key.keysym.sym == SDLK_1 )
                {
                    //播放“挠”的声效
                    if( Mix_PlayChannel( -1, scratch, 0 ) == -1 )
                    {
                        return 1;    
                    }
                }
                //如果数字键“2”被按下
                else if( event.key.keysym.sym == SDLK_2 )
                {
                    //播放“重击”的声效
                    if( Mix_PlayChannel( -1, high, 0 ) == -1 )
                    {
                        return 1;    
                    }
                }
                //如果数字键“3”被按下
                else if( event.key.keysym.sym == SDLK_3 )
                {
                    //播放“中度击打”的声效
                    if( Mix_PlayChannel( -1, med, 0 ) == -1 )
                    {
                        return 1;    
                    }
                }
                //如果数字键“4”被按下
                else if( event.key.keysym.sym == SDLK_4 )
                {
                    //播放“轻击”的声效
                    if( Mix_PlayChannel( -1, low, 0 ) == -1 )
                    {
                        return 1;    
                    }
                }
在检查按键按下的事件时,首先我们检查数字键1, 2, 3, 或 4是否被按下。这些是播放声效的按键。

如果这些键中的一个被按下,我们就调用Mix_PlayChannel()来播放与其关联的声效。

这个函数的第一个参数是我们将要播放声音的混音频道。由于它被设为了-1,这个函数会自动寻找第一个可用的频道,并播放声音。

第二个参数是将要播放的Mix_Chunk。第三个参数是声效将要重复的次数。由于它被设为0,声效将只会播放一次。

当播放声音出了问题时,Mix_PlayChannel() 会返回 -1。
                //如果数字键“9”被按下
                else if( event.key.keysym.sym == SDLK_9 )
                {
                    //如果没有音乐在播放
                    if( Mix_PlayingMusic() == 0 )
                    {
                        //播放音乐
                        if( Mix_PlayMusic( music, -1 ) == -1 )
                        {
                            return 1;
                        }    
                    }
接下来,我们处理数字键“9”被按下的事件,这里我们应该播放或暂停音乐。

首先,我们通过Mix_PlayingMusic()检查是否有音乐正在播放。如果没有音乐在播放,我们调用Mix_PlayMusic() 来播放音乐。

函数Mix_PlayMusic()的第一个参数是我们将要播放的音乐。第二个参数是音乐将要循环的次数。由于它被设为了-1,音乐将在手动停止前不停地循环下去。

如果播放音乐出现问题,该函数会返回-1。
                    //如果音乐已进入播放状态
                    else
                    {
                        //如果音乐被暂停了
                        if( Mix_PausedMusic() == 1 )
                        {
                            //恢复播放
                            Mix_ResumeMusic();
                        }
                        //如果音乐正在播放
                        else
                        {
                            //暂停音乐
                            Mix_PauseMusic();
                        }
                    }
                }
现在,如果当用户按下“9”时,音乐正在播放,我们则暂停或恢复音乐播放。

首先,我们使用Mix_PausedMusic()检查音乐是否已被暂停。如果是的,我们通过Mix_ResumeMusic()来恢复播放。如果音乐没有被暂停,我们使用Mix_PauseMusic()来将它暂停。
                //如果数字键“0”被按下
                else if( event.key.keysym.sym == SDLK_0 )
                {
                    //停止音乐
                    Mix_HaltMusic();
                }
            }
最后,我们检查用户是否按下了“0”。

如果用户按下了0,我们使用Mix_HaltMusic()来停止音乐的播放。

我十分推荐你下载SDL_mixer的文档并将它放在手边,以方便查阅。你可以从此处下载

本课所用的多媒体文件和源代码可从此处下载

下一课:计时