[ ホーム | TSG について | 活動 / 分科会 | イベント | メンバー | メーリングリスト | 部報 | 掲示板 | リンク ]

SDL / OpenGL 分科会 - 第 4 回

今回はサウンド (効果音 & BGM) の使い方を紹介します。 また、次回から OpenGL に入るので、これまで説明できなかった細々としたことを簡単に補足しておきます。

サウンド

サウンドの使い方もマニュアルに載っているので、まあ大体その通りにやれば分かるでしょう…と言いたいところなのですが、実は SDL のサウンド機能はあまり使いやすいものではありません。 というのも、SDL はその名 (Simple DirectMedia Layer) の通り、非常にシンプルな (厳選された機能を高性能で提供することを目指した) API 群なので、ユーザに近い高レベルの機能はそれほど多くないのです。 サウンドに関して言えば、波形データを定期的にメモリに書き込んで演奏を指示する、というちょっと面倒なことをしなければいけません。

しかしそれではさすがに不便なので、代わりに SDL_mixer というライブラリがよく使われます。 これは SDL の低レベル API を使いやすくまとめた高レベル API で、WAVE や MOD (MIDI に音色データを加えたようなもので、Winamp などでも再生できます。 cf. 波平会)、さらに別のライブラリと組み合わせることによって MP3 なども簡単に演奏することができます。 ここではこの SDL_mixer の使い方を紹介したいと思います。

SDL_mixer のインストール

まずインストールを行います。 手順は SDL の場合と大体同じです。 SDL_mixer のページ から SDL_mixer-1.2.4.tar.gz をダウンロードしてホームディレクトリに保存します。 そして :

$ cd /usr/local/src
$ tar zxf ~/SDL_mixer-1.2.4.tar.gz
$ cd SDL_mixer-1.2.4
$ cp ../SDL-1.2.4/libtool .
$ cp ../SDL-1.2.4/ltmain.sh .
$ cp ../SDL-1.2.4/ltconfig .
$ ./configure
$ make
$ make install
$ cp /usr/local/lib/SDL_mixer.dll c:/WINDOWS/SYSTEM/ (Windows 95,98,ME,XP の場合)
$ cp /usr/local/lib/SDL_mixer.dll c:/WINNT/SYSTEM32/ (Windows NT,2000 の場合)

インストールすると、テスト用のプログラムがいくつか付いてきます。 試しにちょっと音楽を鳴らしてみましょう。 何でもいいのですが、ここでは Made to Last という曲をダウンロードしてホームディレクトリに展開しておきます (WinZip などで適当に)。 で、コマンドラインから :

$ playmus ~/an_2lst8.xm

どうでしょう、鳴りましたか?

なお、この playmus のソースも /usr/local/src/SDL_mixer-1.2.4/playmus.c にあるので、以下の説明を読んだ後にでも見てみると良いでしょう。

ドキュメント

使い方を説明する前に、有用なドキュメントへのポインタを載せておきます。 参考にして下さい。 英語ですが、まあ何とかなるでしょう。

まず初めに

SDL_mixer を使ったプログラムでは

#include <SDL_mixer.h>

が必要です。 さらに、SDL_INIT_AUDIO をつけて SDL_Init() を実行しておきましょう。

また、コンパイル時には

$ gcc -o hoge hoge.c `sdl-config --cflags --libs` -lSDL_mixer

という風に「-lSDL_mixer」を付けなければいけません。 これは SDL_mixer のライブラリをリンクすることを指示するためのものです。

初期化と終了

SDL_mixer の初期化と終了はそれぞれ Mix_OpenAudio()Mix_CloseAudio() で行います。 こんな感じです :

/* 初期化 */
if(Mix_OpenAudio(MIX_DEFAULT_FREQUENCY,MIX_DEFAULT_FORMAT,2,1024)<0) {
    fprintf(stderr,"failed to initialize SDL_mixer.\n");
    SDL_Quit();
    exit(-1);
}

/* 終了 */
Mix_CloseAudio();

Mix_OpenAudio() には引数をいくつか指定する必要があるので、順番に説明しておきます。

  1. frequency : サンプリング周波数 (Hz) です。 特に気にしない場合は MIX_DEFAULT_FREQUENCY で良いでしょう。 ゲームでは 22050 や 44100 (CD と同等の音質) などがよく使われるようです。
  2. format : 波形サンプルの型 (8 ビットか 16 ビットか、など) です。 これも普通は MIX_DEFAULT_FORMAT で良いでしょう。
  3. channels : チャンネル数です。 1 ならモノラル、2 ならステレオになります。
  4. chunksize : サウンドバッファのサイズ (バイト数) です。 あまり気にする必要はないので、1024 などで良いでしょう。

BGM を鳴らす

はっきり言って超簡単です。 Mix_LoadMUS() で曲をロードして Mix_PlayMusic() で再生するだけです。

Mix_Music *music;

/* ファイルから読み込む */
music=Mix_LoadMUS("hoge.mod");
if(!music) {
    fprintf(stderr,"failed to load music.\n");
    return -1;
}

/*
 * 演奏開始。2 番目の引数には演奏回数を指定する。
 * -1 なら無限に繰り返す。
 */
Mix_PlayMusic(music,-1);

終了するときは Mix_HaltMusic() で演奏を停止して Mix_FreeMusic() で曲を解放します。

/* 演奏停止 */
Mix_HaltMusic();

/* 読み込んだ曲を解放 */
Mix_FreeMusic(music);

効果音を鳴らす

BGM の再生とほとんど同じです。 ただし、効果音は BGM と違って同時に複数鳴らすことがあるため、再生時に少し注意する点があります。 それはチャンネル番号を指定しなければいけないことです。

チャンネルというのはまあ、音を鳴らすための窓口のようなものです。 1 つのチャンネルでは同時に 1 つの音しか再生できません。 ですから、同時に複数の音を鳴らすためには複数のチャンネルを使う必要があります。 つまり「この音はチャンネル 0 で再生、その音はチャンネル 1 で再生、…」と指定しなければいけません。 また、再生を停止するときも「チャンネル○番を停止」という風に指定するので、それぞれの音を再生しているチャンネル番号を覚えておかなければいけません。

具体的には次のようになります。 Mix_LoadWAV() で音をロードして Mix_PlayChannel() で再生します。

Mix_Chunk *chunk;
int channel;

/* ファイルから読み込む */
chunk=Mix_LoadWAV("hoge.wav");
if(!chunk) {
    fprintf(stderr,"failed to load sound.\n");
    return -1;
}

/*
 * 再生する。
 * ● 1 番目の引数にはチャンネル番号を指定する。
 *    -1 にすると空いているチャンネルを自動的に割り当ててくれる。
 * ● 3 番目の引数には再生繰り返し数を指定する。
 *    0 なら 1 回再生、1 なら 2 回再生となる (変な仕様…)。
 *    -1 なら無限に繰り返す。
 * ● 返り値は実際に割り当てられたチャンネル番号。
 */
channel=Mix_PlayChannel(-1,chunk,0);

後始末には Mix_HaltChannel()Mix_FreeChunk() を使います。

/* 再生を停止する。-1 を与えると全チャンネルの再生を停止する。 */
Mix_HaltMusic(channel);

/* 音を解放 */
Mix_FreeChunk(chunk);

とりあえず、普通に使うだけならこれで十分でしょう。 もう少し凝ったことがしたくなったら前述のドキュメントに目を通してみて下さい。

タイマー

ゲームプログラムではメインループを一定の速度 (例えば秒間 60 回とか) で回すことが多いと思います。 このためには高精度で時間を得る手段が必要になりますが、SDL では SDL_GetTicks() という関数が用意されています。 呼び出すと、 SDL_Init() が実行されてからの経過時間をミリ秒単位で返してくれます。

また、一定時間待つためには SDL_Delay() を使います。 指定した時間 (ミリ秒単位) 待ってくれます。

ということで、次のようにして時間調整をすることができます (まあ他にもやり方はいろいろあると思いますが、一例ということで) :

#define LOOP_TIME_MS 16 /* 毎ループ 16 ミリ秒 */

Uint32 last_tick=SDL_GetTicks();
while(1) { /* メインループ */
    Uint32 tick;

    /* ここで各種処理を実行… */

    tick=SDL_GetTicks();
    if(tick-last_tick<LOOP_TIME_MS) {  /* 時間が余っていたら */
        draw_screen();                               /* 描画 */
        tick=SDL_GetTicks();
        if(tick-last_tick<LOOP_TIME_MS)    /* まだ余っていたら */
            SDL_Delay(LOOP_TIME_MS-(tick-last_tick));  /* 待つ */
    }
    last_tick+=LOOP_TIME_MS;
}

2D グラフィックス補足

第 2 回で 2D グラフィックスについて簡単に紹介しましたが、BMP の表示だけだったので「これだけ ?」と思った人もいるかもしれません。 なので、ちょっと補足しておきますが、サウンドと同じくグラフィックスについても SDL の機能はあまり多くありません。 例えば点の描画なんてのもなかったりします。

まあゲームを作るにはパターン描画ルーチン + αだけで十分なことも多いですが、やっぱり不便なこともあるわけです。 こういう場合どうするかと言うと

  1. 自分でルーチンを書く
  2. SDL 上で動く別のライブラリを使う

というのが考えられます。 やっぱり 1 は面倒なので 2 になるのですが、サウンドの場合 (SDL_mixer) のようにコレというものがありません。 もちろんいくつかのライブラリが作られてはいるのですが、決定的なものがないのです。 なので、ここでは特にどれかを解説するということはしません (というか筆者がこっち方面に深入りしてないのでよく分かりません; ゴメンナサイ)。 各自、良いと思えるものを探してみるのが良いでしょう。

次回から OpenGL を紹介します。 OpenGL は基本的に 3D グラフィックスのために使われるライブラリですが、2D グラフィックスもきちんと扱うことができます。 ですので、2D グラフィックスで適当なライブラリが見つからなければ OpenGL を使ってみるというのも悪くない選択肢でしょう。