2009年04月20日

SDL_Mixerを使ってみたけどしっくりこない。

libavcodecを使ったFLVやmp4の再生は多分使用頻度の高いアプリになるのだろうが
glibもgtk2もなしでデフォルトでメモリーを20Mbyteも食うのが楽しくない。
動画を使わないで音声だけの場合を考えると、
やはりSDL_Mixerを選ばない理由は特にないと思ったが
ちょちょっと音を出すには便利だがFlashの再生ということになると微妙な部分を多々感じた。
ビルドでは

`sdl-config --cflgs --libs` -lSDL_mixer

と入れれば無難。

自分の場合は展開して

gcc -Wall -pipe -O2 -march=pentium3 \
-I/usr/local/include/SDL -I/usr/local/include \
-L/usr/local/lib -Wl,-rpath,/usr/local/lib -lSDL -pthread -lSDL_mixer \
-o sdl_m1 sdl_m1.c

という感じになる。

string.hとかいらないが今回の宣言部
使うファイルを指定argv[1]とかでもよい。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL.h>
#include <SDL_mixer.h>

#define MUSIC_FILE "c_1.mid"
#define SOUND_FILE "snd1.wav"


SDLイニシャライズまで

int main(int argc, char *argv[])
{
//var
SDL_Surface *screen;
Mix_Music *music;
Mix_Chunk *chunk;

int done,play,chan1=1,vol=64 ;
int lv=128;
//begen
if (SDL_Init(SDL_INIT_VIDEO)) return(1);
//atexit(releaceALL);

atexitという関数を使えば途中で抜ける時に実行する関数を指定できるが
グローバル変数とか作っていないので止めた。
今回話が分かりにくくなるから画像関係は一切操作しない。
Xtstを使うとごちゃつくのでキー入力のために画面を出した。

画面の作成とMixerオブジェクトの作成

screen = SDL_SetVideoMode(320, 240, 8, SDL_SWSURFACE);
if (Mix_OpenAudio(44100, AUDIO_S16, 2, 4096)) {
puts("can't open audio");
goto release0;
}
music = Mix_LoadMUS(MUSIC_FILE);
chunk = Mix_LoadWAV(SOUND_FILE);
Mix_AllocateChannels(2) ;
Mix_Volume(-1, 80);//0-128
Mix_PlayMusic(music, -1);
chan1=1;


音楽と効果音と別の扱いの様だ。
音楽は開始時間を指定できる。Mix_PlayMusicで-1を指定することでずっと繰り返す。
変数chan1に1を設定し以降効果音はchan1で操作する。
チャンネルは終了した時にコールバックを呼べるようにでたりとそこそこ柔軟な形の様だ。

音楽としてはWavの他MID,Ogg,MP3そしてMod(MOD, S3M, IT, XM等)が使えるが
効果音はWAV,Ogg等でMIDとMP3は使えない様だ。

いわゆるメインループ部分

done = SDL_FALSE;
while (!done)
{
SDL_Event ev;

SDL_WaitEvent(&ev);
switch (ev.type)
{
case SDL_KEYUP :
switch(ev.key.keysym.sym)
{
case SDLK_ESCAPE:
puts("key_escape");
done=SDL_TRUE;
break;
case SDLK_p:
puts("key_p");
//if(Mix_PlayingMusic() == 0)
play=!play;
if (play)Mix_ResumeMusic() ;
else Mix_PauseMusic();
break;

case SDLK_SPACE:
Mix_PlayChannel(chan1, chunk, 0); // play
break;
case SDLK_UP://int volume
if (vol<128)vol++;
Mix_VolumeMusic(vol);
printf("vol:%d\n",vol);
break;
case SDLK_DOWN:
if (vol>0)vol--;
Mix_VolumeMusic(vol);
printf("vol:%d\n",vol);

break;
case SDLK_LEFT:
if (lv<256)lv++;
Mix_SetPanning(chan1, lv, 256-lv) ;
printf("pan:%d\n",lv);
break;
case SDLK_RIGHT:
if (lv>0)lv--;
Mix_SetPanning(chan1, lv, 256-lv) ;
printf("pan:%d\n",lv);

break;

default:
break;
}//key

break;

case SDL_QUIT :
done = SDL_TRUE;
break;

default:
break;
}
}


基本的な操作はチャンネルの割り当て以外はかなり上流言語で扱いやすい。

switch文は実質「存在しない」のであまり使わない方が良い。
やるなら具体的に値をチェックして二分木にif文でわかれたほうがいいだろう。
ここではpキーで音楽のポーズと再スタート
矢印上下で音楽のボリウム
矢印左右で効果音の場所を変更している。
音楽は停止は検出できるがポーズは検出できない様だ。
また使いやすい形での現位置取得はできない。

開放から終了
ラベルrelease1は今回使わなかったが「goto」指向というかFlashのframeライク
アルゴリズムを時系列で区切る方法はもう少し抽象化したい。


release1:
Mix_HaltMusic();
if(music)Mix_FreeMusic(music);

if(chunk)Mix_FreeChunk(chunk);
Mix_CloseAudio();

release0:
if(screen)SDL_FreeSurface(screen);

SDL_Quit();
return(0);
}

音楽を止めて
リソース開放→Mixerオブジェクト開放→SDL_Surface開放→SDL開放
という手順。
開放はもし値があれば開放するという手法を使っている。
SDL_Quitが登録したものを全部開放してくれるならばこんなていねいにやる必要はない。

個別のチャンネルでpanとボリウムの設定が出来る他
独自に作ったエフェクタに値を渡せるようにできるみたいなのだが
「音楽側」はボリウムと任意位置からの再生やフェードイン、フェードアウト以外は操作できない。

MP3を音源にした場合はせいぜい鳴らすことぐらいしか出来ない。

これが現実の様だ。
しかし一般的にゲームなりアプリではだらっと背景音楽を流す事がほとんどだからとりあえず事足りるだろう。
効果音はwavとoggの他にも数種使えるようだがこの二つが現実的だろう。
となるとMP3等もwavに展開して効果音としてロードしてならした方が使いやすいようにも思える。

実質SDL_Mixerでのデコードはsmpegとliboggとlibvorbis、libmikmodに依存しているはずだ。
均一に取りまとめたのはいいが実質MP3だけでいいのならば直接smpegを操作する方が良いのかもしれない。
SDL_MixerないしはsmpegによるMP3再生はメモリ消費が4Mbyte程度だった。ffmpegの20Mbyte強に比べるとずいぶんと小さい。

またMIDI音源は独自に持っているようでtimidity++とは音が違っていた。
MIDI再生時もメモリ消費は4Mbyte程度だったがtimidity++は10Mbyte程度
しかしtimidity++はそれ自体でmodも再生できるので
timidity++とsmpegでpcm化しながらミキサー系オブジェクトにちょっとずつ食わすという方が使いやすく
だったらSDL_Mixer使うこともないとも言える。

逆に短いコードでデモとかで音楽を鳴らす場合も
timidity++やsmpegを別プロセスで動かすという選択肢もある。
インライン必須の動画と違い音は出所不明だからだ。
smpegはmpeg1も再生できるが10年前ならともかくランタイムで負荷が低くても
あえてmpeg1にして再生する需要はないだろう。使う場合は音楽再生に絞りたい。

まぁでもまだffmpegは構造体とかもまとめていないから
SDL_Mixerがいつでも使えるようになったことを良とする。


ラベル:SDL SDL_Mixer
posted by Xo_ox at 23:39| Comment(0) | クロスプラットフォーム | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。