Mac OSX Fmod 入門?
Fmod とは クロスプラットフォーム対応のオーディオライブラリで、CD , midi, wav, mods, mp3, ogg vorbis など
様々なファイル形式を扱えます。このライブラリを使えば音楽再生はもちろんのこと、音声を録音したり、エフェクトを
かけたりだけではなく、応用すると スペアナ(スペクトラムアナライザー)、VUメーター、MediaPlayer や ITunes の
Visualizerといったにあるのと似た効果も作れるかもしれません。
Fmodには Mac版も用意されており、Carbon ベース でOS9でも OSX でも利用できます。Cocoa と併用しても問題ないようです。 このライブラリを組み込んだアプリケーションをリリースする場合は、シェアウェアか商用ならば有料で、 フリーウェアなら無料で利用できるとのこと。
usr/local/include へ、fmod.h,fmod_errors.h,wincompat.h をコピー。なお carbon based のライブラリのため、 カーボンフレームワークをリンクしてなければコンパイル時にエラーが出ます。 一応 Fmod のサンプル simplest に入ってる Carbon.r もプロジェクトのある main.c と同じ階層に置いておく。 使いたい場合には、この3つのファイルを#includeしておく。
usr/local/lib へ、 libfmod.a をコピーして、 コンパイルのリンク時に
-lfmod -framework Carbon を追加。
#include "fmod.h"このうち fmod_errors.h は、Fmod 側でエラーが発生したときにエラーメッセージを表示するために使われるので 表示させたいという人だけ#include する。
#include "fmod_errors.h"
#include "wincompat.h"
このライブラリには2つのAPI群 FMUSIC と FSOUND が用意されており、 どちらも FSOUND_Init();を呼び出して初期化すると使えるようになる、 使い終わったら FSOUND_Close();で終了することになっている。
演奏データを再生する初期化:FSOUND_Init( int mixrate, int maxsoftwarechannels, unsigned int flags ); 終了 :FSOUND_Close(); // FSOUND_Close(); 関数はロードされた、サンプルデータとCDTrack演奏を終了します。 ストリーム再生には効果がないので、ストリーム再生を終了する場合は、 FSOUND_Stream_Stop( stream); FSOUND_Stream_Close( stream);関数を別に呼んでください。
準備:FMUSIC_MODULE *mod = NULL;
読込:mod = FMUSIC_LoadSong("filename.mod"); 関数,
再生:FMUSIC_PlaySong(mod);関数
停止:FMUSIC_StopSong(mod);関数
解放:FMUSIC_FreeSong(mod);関数
※メモリを解放する前に、必ず演奏を停止してください。これを忘れるとエラーになります
FMUSIC_StopAllSongs(); 関数を使えばすべてのMod演奏を中止できます。
音声データ(サンプル)を再生する準備:FSOUND_SAMPLE *smpl = NULL; int channel; 読込:smpl = FSOUND_Sample_Load( FSOUND_FREE , "filename.mp3", FSOUND_LOOP_NORMAL|FSOUND_NORMAL | FSOUND_HW2D, offset, 0 ); 再生:channel=FSOUND_PlaySound(FSOUND_FREE, smpl); 関数// チャンネルハンドラ生成 停止:FSOUND_StopSound(channel);関数 解放:FSOUND_Sample_Free(smpl);関数
ということになっている。ここで重要なのはチャンネルハンドラで、 FSOUND_Get〜関数において 曲の再生位置や音量などの情報を操作・取得したいときに、 このチャンネルハンドラを指定すると その曲の情報を得る事ができます。
ストリーム再生
準備:FSOUND_STREAM *stream; int schannel;
設定: FSOUND_SetOutput(FSOUND_OUTPUT_NOSOUND_NONREALTIME);
読込:stream = FSOUND_Stream_Open("filename.mp3", 0, 0, 0);
設定:FSOUND_Stream_SetMode( sstream,FSOUND_LOOP_NORMAL);//ループ再生など
再生:schannel=FSOUND_Stream_Play( FSOUND_FREE , stream);// チャンネルハンドラ生成
音量:FSOUND_SetVolume( schannel, vol);
停止:FSOUND_Stream_Stop( stream);
解放:FSOUND_Stream_Close( stream);
ストリーム再生でも同じくチャンネルハンドラで指定する事ができます。
ネットワークストリーム再生
DSPシステムの仕組みに関してはドキュメントの modapi3741mac/documentation/Tutorials/dsp.htm に書かれています。
DSPエンジンを使う場合はDSPUnitを有効にする必要があります。
すべての音声データは、MixBuffers を介し、いくつかのDSPUnitを通して、出力されるらしい。
データがDSPUnitを通過する間にコールバックして、音声データを加工すればリバーブなどの効果が得られるそうです。
(音声データにリバーブをかけるデモプログラムが fmodapi3741mac/samples/dsp/main.c にあります)
// ユーザーカスタマイズの DSPユニットを用意している場合 準備:FSOUND_DSPUNIT *Unit = NULL; 作成:Unit = FSOUND_DSP_Create(〜); //ライブラリ側で既に用意されているユニットは、 DSPUnit FSOUND_DSP_SetActive関数を使って有効にできます。 // Unit名は、 FSOUND_DSP_SetActive(FSOUND_DSP_GetFFTUnit(), FALSE); のように指定する。 有効:FSOUND_DSP_SetActive( Unit名, TRUE); 無効:FSOUND_DSP_SetActive( Unit名, FALSE);//プログラムを終了する時に呼ぶ // ユーザーカスタマイズの DSPユニットを用意している場合 解放:FSOUND_DSP_Free(Unit );
音を目に見える形で表示できると面白いので、VUメータ-と スペアナ表示をつくってみようと思う。
音量をグラフィカルに表示する VUメータ- を実現するには 通常再生 あるいは ストリーム再生時に
FSOUND_GetCurrentLevel 関数を呼び出し再生しているチャンネル を指定することでできます。
VUメータの例は、fmodapi3741mac/samples/record/main.c に収録されているサンプルプログラムが参考になります。
//通常再生
FSOUND_SAMPLE *smpl = NULL;
int channel;
float l,r,vumat[2]={0.0,0.0};
smpl = FSOUND_Sample_Load(FSOUND_FREE,"Rats on Caos.mp3", FSOUND_NORMAL,0,0);
channel = FSOUND_PlaySound(FSOUND_FREE,smpl);
または
//ストリーム再生
FSOUND_STREAM *sstream;
int schannel;
FSOUND_SetOutput(FSOUND_OUTPUT_NOSOUND_NONREALTIME);//出力設定
FSOUND_DSP_SetActive( FSOUND_DSP_GetFFTUnit(),TRUE);//スペアナ準備
sstream = FSOUND_Stream_Open("Hotch_station.mp3", 0, 0, 0);
schannel= FSOUND_Stream_Play( FSOUND_FREE, sstream);
FSOUND_Stream_SetMode( sstream,FSOUND_LOOP_NORMAL);
FSOUND_SetVolume(FSOUND_ALL,200);
再生情報を取得するなら
描画処理//各再生方式における 情報取得 //FMUSIC textprintf_ex(buffer, font, 80, 140, makecol(0, 0, 0), -1,"File = %s", FMUSIC_GetName(mod)); textprintf_ex(buffer, font, 80, 150, makecol(0, 0, 0), -1,"order = %d/%d, row = %d/%d channels playing = %d cpu usage = %.02f%% ", FMUSIC_GetOrder(mod), FMUSIC_GetNumOrders(mod), FMUSIC_GetRow(mod), FMUSIC_GetPatternLength(mod, FMUSIC_GetOrder(mod)), FSOUND_GetChannelsPlaying(), FSOUND_GetCPUUsage()); //FSOUND textprintf_ex(buffer, font, 30, 140, makecol(0, 0, 0), -1,"FSOUND Driver:%s", FSOUND_GetDriverName(FSOUND_GetDriver())); textprintf_ex(buffer, font, 30, 150, makecol(0, 0, 0), -1,"Name:%s", FSOUND_Sample_GetName(smpl)); textprintf_ex(buffer, font, 30, 160, makecol(0, 0, 0), -1,"pos:%d/%d,cpu:%.02f,len =%d", FSOUND_GetCurrentPosition(channel),FSOUND_Sample_GetLength(smpl),FSOUND_GetCPUUsage(),FSOUND_DSP_GetBufferLength() ); textprintf_ex(buffer, font, 30, 170, makecol(0, 0, 0), -1,"Freq =%d ", FSOUND_GetFrequency( channel ) ); //FSTREAM textprintf_ex(buffer, font, 30, 140, makecol(0, 0, 0), -1,"mode:STREAM Driver:%s vol:%d", FSOUND_GetDriverName(FSOUND_GetDriver()),FSOUND_GetVolume(schannel) ); textprintf_ex(buffer, font, 30, 150, makecol(0, 0, 0), -1,"pos:%d/%d,cpu:%.02f,len =%d", FSOUND_Stream_GetPosition(sstream),FSOUND_Stream_GetLength(sstream),FSOUND_GetCPUUsage(),FSOUND_Stream_GetTime(sstream) ); FSOUND_SetFrequency(schannel,55100 );//44100->88200 2倍速再生
// VUメーター
// データ取得
FSOUND_GetCurrentLevels( channel , &l , &r);
#define VU_FADE 0.2f
if (l > vumat[0]){ vumat[0] = l;}
if (r > vumat[1]){ vumat[1] = r;}
vumat[0] -= VU_FADE;vumat[1] -= VU_FADE;
if ( vumat[0] < 0){vumat[0] = 0;}
if ( vumat[1] < 0){vumat[1] = 0;}
//グラフ描画
#define VU_SCALE 250.2f
#define VU_POSY 22
#define VU_POSX 280
textprintf_ex(buffer, font,VU_POSX-75, VU_POSY, makecol(0, 0, 0), -1,"VU:L %.02f",l);
textprintf_ex(buffer, font,VU_POSX-75, VU_POSY+10, makecol(0, 0, 0), -1,"VU:R %.02f",r);
rectfill( buffer ,VU_POSX, VU_POSY ,VU_POSX+vumat[0]*VU_SCALE, VU_POSY+10 ,makecol(0, 0, 255) );//Level
rectfill( buffer ,VU_POSX, VU_POSY+10 ,VU_POSX+vumat[1]*VU_SCALE, VU_POSY+20 ,makecol(0, 255, 0) );
rectfill( buffer ,VU_POSX+l*VU_SCALE, VU_POSY ,VU_POSX+l*VU_SCALE+3 , VU_POSY+10 ,makecol(255, 0, 0) );//Peak Marker
rectfill( buffer ,VU_POSX+r*VU_SCALE, VU_POSY+10 ,VU_POSX+r*VU_SCALE+3 , VU_POSY+20 ,makecol(255, 0, 0) );
FSOUND_DSP_SetActive( FSOUND_DSP_GetFFTUnit(), TRUE );を呼び出しておく。
//スペアナ表示
float *spec;//ここにデータを入れる
spec=FSOUND_DSP_GetSpectrum();//波形を得る
//スペアナ 棒グラフを描く
for (j=0; j<256; j=j+2){
graph = spec[j]*120.0f;
rectfill( buffer , 50+j, 250 , 50+j+1 ,250-graph , makecol(255, 0, 0));
}
//スペアナ 折れ線グラフを描く
int k=0;
for (j=0; j<256; j=j+2){
graph = spec[j]*120.0f;
#define SP_NEXT spec[j+2]*120.0f
fastline( buffer , SP_POSX+k, SP_POSY-graph , SP_POSX+k+20 ,SP_POSY-SP_NEXT , makecol(255, 0, 0));
k+=20;
}
G-Force のような、ビジュアライザ風に 折れ線グラフ自体を回転させるには、
三角関数を使って座標を変換する必要があります。
// スペアナ_ 回転する 折れ線グラフを描く。
// 点(px,py) は (cx,cy)を中心に rad 回転し、(300,200)を基準に表示する
void rotate_ex(float *px, float *py, float cx ,float cy ,float rad){
float x = *px, y = *py;
*px = (x-cx)*cos(rad) - (y-cy)*sin(rad)+300;
*py = (x-cx)*sin(rad) + (y-cy)*cos(rad)+200;
}
//メイン関数で
float rx,ry,rx2,ry2,j,rote;
int j,k;
int *spec;
spec=FSOUND_DSP_GetSpectrum();
//基本位置の設定
#define SP_POSX 151
#define SP_POSY 98
k=0;
if( rote > 3.14*2 ){ rote=0.0;}else{ rote+=0.01; }//角度
for (j=0; j<256; j=j+2){
graph = spec[j]*120.0f;
#define SP_NEXT spec[j+2]*120.0f
rx=SP_POSX+k; ry=SP_POSY-graph; rx2=SP_POSX+k+4;ry2 = SP_POSY-SP_NEXT;
rotate_ex(&rx,&ry , SP_POSX+200 , SP_POSY, rote);//回転
rotate_ex(&rx2,&ry2 , SP_POSX+200 , SP_POSY ,rote);
fastline( buffer , rx,ry ,rx2,ry2, makecol(255-j, j, 255-j));//点を結ぶ。
k+=4;
}
OpenGL など 3D描画ライブラリと組み合わせればより表現幅も広がるし
波形やリズムにあわせて反応するキャラクタ や 音ゲーなどに利用できるかもしれない。
スペアナの作り方のヒント ここ ここ ここ FMODマニュアル PCinfo コンピュータグラフィックスの話 物理を使った簡単な移動について(2/2) 任意の点を中心としての回転 Carbon in Cocoa カーボンからココアのGUIを呼び出す。 Cocoa_With_Carbon_or_CPP CocoaからCarbonを呼び出す スペアナを最初から作る場合は、高速フーリエ変換から考える必要がある...。 フーリエ変換と窓関数。 FFT(高速フーリエ変換) 高速フーリエ変換ライブラリ FFT, DCTのライブラリの仕様 フーリエ変換の実際 FFTチュートリアル Spectrum Analysis tutorial ?? Koike Library