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)); }
G-Force のような、ビジュアライザ風に 折れ線グラフ自体を回転させるには、 三角関数を使って座標を変換する必要があります。//スペアナ 折れ線グラフを描く 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; }
// スペアナ_ 回転する 折れ線グラフを描く。 // 点(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; }
OpenGL など 3D描画ライブラリと組み合わせればより表現幅も広がるし 波形やリズムにあわせて反応するキャラクタ や 音ゲーなどに利用できるかもしれない。//メイン関数で 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; }
スペアナの作り方のヒント ここ ここ ここ FMODマニュアル PCinfo コンピュータグラフィックスの話 物理を使った簡単な移動について(2/2) 任意の点を中心としての回転 Carbon in Cocoa カーボンからココアのGUIを呼び出す。 Cocoa_With_Carbon_or_CPP CocoaからCarbonを呼び出す スペアナを最初から作る場合は、高速フーリエ変換から考える必要がある...。 フーリエ変換と窓関数。 FFT(高速フーリエ変換) 高速フーリエ変換ライブラリ FFT, DCTのライブラリの仕様 フーリエ変換の実際 FFTチュートリアル Spectrum Analysis tutorial ?? Koike Library