Mac OSX Fmod 入門?

 

Fmodについて

Fmod とは クロスプラットフォーム対応のオーディオライブラリで、CD , midi, wav, mods, mp3, ogg vorbis など 様々なファイル形式を扱えます。このライブラリを使えば音楽再生はもちろんのこと、音声を録音したり、エフェクトを かけたりだけではなく、応用すると スペアナ(スペクトラムアナライザー)、VUメーター、MediaPlayer や ITunes の Visualizerといったにあるのと似た効果も作れるかもしれません。

Fmodには Mac版も用意されており、Carbon ベース でOS9でも OSX でも利用できます。Cocoa と併用しても問題ないようです。 このライブラリを組み込んだアプリケーションをリリースする場合は、シェアウェアか商用ならば有料で、 フリーウェアなら無料で利用できるとのこと。

入手と使い方

開発元サイト http://www.fmod.org/ から
Mac用のファイル fmodapi3741mac.sit をダウンロードし解凍してください。
(Fmodのサイトには、Fmodのミニバージョンもあるが違うので、間違いないように)

解凍してreadmeを見ると、 gcc / MPW / mach-o 環境の ユーザーは libfmod.a を使えとある。
(拡張子 .cfm のファイルは OSX で使うなら不要です。)
Fmod は 既にライブラリのアーカイブファイルができあがっているのでソースのコンパイルの必要がなく導入は楽です、
usr/local/include へ、fmod.h,fmod_errors.h,wincompat.h をコピー。
usr/local/lib へ、 libfmod.a をコピーして、 コンパイルのリンク時に
-lfmod -framework Carbon を追加。
なお carbon based のライブラリのため、 カーボンフレームワークをリンクしてなければコンパイル時にエラーが出ます。 一応 Fmod のサンプル simplest に入ってる Carbon.r もプロジェクトのある main.c と同じ階層に置いておく。 使いたい場合には、この3つのファイルを#includeしておく。
#include "fmod.h"
#include "fmod_errors.h"
#include "wincompat.h"
このうち fmod_errors.h は、Fmod 側でエラーが発生したときにエラーメッセージを表示するために使われるので 表示させたいという人だけ#include する。

使用感:サンプルからmake した場合には、再生される音楽ファイル名は絶対パスでなければ認識されなかったのですが Xcodeのテンプレートをベースに組み込んだ場合は 相対パス指定でも問題なく認識された。
なお、読み込む音声ファイル は Xcode で使うプロジェクトのディレクトリ内の build ディレクトリに入れておく。

試しにFmodでs3m形式のファイルを再生しながら Allegro標準のplay_sample関数を使って wav再生 を同時にやってみたが問題なく動作した。

使用感

このライブラリには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);関数を別に呼んでください。
演奏データを再生する
.MOD , .S3M , .XM , .IT , .MID , .RMI , .SGT , .FSB 形式を扱う場合は FMUSICを使う。
準備:FMUSIC_MODULE *mod = NULL;
読込:mod = FMUSIC_LoadSong("filename.mod"); 関数,  
再生:FMUSIC_PlaySong(mod);関数
停止:FMUSIC_StopSong(mod);関数
解放:FMUSIC_FreeSong(mod);関数

※メモリを解放する前に、必ず演奏を停止してください。これを忘れるとエラーになります
FMUSIC_StopAllSongs(); 関数を使えばすべてのMod演奏を中止できます。
音声データ(サンプル)を再生する
.WAV, .MP2, .MP3, .OGG, .RAW 形式を扱う場合はFSOUNDを使う。 手軽に使えるが、もっと凝ったことをしたい場合には向かない。
準備: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と同じく.WAV, .MP2, .MP3, .OGG, .RAW 形式が利用できる。 ループ再生等の設定はそれぞれの関数を使用する。
準備: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(Digital Signal Processor)ユニットについて
FMOD には、 DSP ユニットという、音楽や画像を処理するエンジンが組み込まれています。

DSPシステムの仕組みに関してはドキュメントの modapi3741mac/documentation/Tutorials/dsp.htm に書かれています。

DSPエンジンを使う場合はDSPUnitを有効にする必要があります。 すべての音声データは、MixBuffers を介し、いくつかのDSPUnitを通して、出力されるらしい。 データがDSPUnitを通過する間にコールバックして、音声データを加工すればリバーブなどの効果が得られるそうです。
(音声データにリバーブをかけるデモプログラムが fmodapi3741mac/samples/dsp/main.c にあります)

このDSPUnit はFMOD側が用意しているユニットや、ユーザーがカスタマイズして用意することもできます。 ユーザー定義の DSPユニットを用意する場合は、処理が書かれたコールバック関数を用意してください。
// ユーザーカスタマイズの 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メータ-と スペアナ表示をつくってみようと思う。 音量をグラフィカルに表示する VUメータ- を実現するには 通常再生 あるいは ストリーム再生時に FSOUND_GetCurrentLevel 関数を呼び出し再生しているチャンネル を指定することでできます。 VUメータの例は、fmodapi3741mac/samples/record/main.c に収録されているサンプルプログラムが参考になります。

それを発展させて、グラフィカルにVUメーターを表示することが可能です。  Fmod ライブラリと Allegroライブラリを使用した例を挙げます。
//通常再生
	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) );  

スペアナは、すべて自前で行うには波形に対して FFT( 高速フーリエ変換)を行わなければならないため高度な数学知識が必要ですが 幸い、FmodにはFSOUND_DSP_GetFFTUnit(高速フーリエ変換ユニット)という機能が用意されており、その敷居の高さを軽減している。 実際に機能させるには、FSOUND_DSP_GetFFTUnit 有効にして SOUND_DSP_GetSpectrumという関数を使って波形を取得する。

重要:スペアナを実現する場合に必要な再生方法は ストリーム形式です。
(通常のFSOUND_Sample_Load関数による 再生 では 波形データが取得されません)。

FSOUND_DSP_GetFFTUnitを有効にするには
FSOUND_DSP_SetActive( FSOUND_DSP_GetFFTUnit(), TRUE );
を呼び出しておく。

SOUND_DSP_GetSpectrum は、512個 の浮動小数点変数の ポインタ配列を返す。
各値がそれぞれ0〜1.0 を範囲とする値になっている。
入れ物となる変数をあらかじめ用意しておこう

FFT(高速フーリエ変換)すると その結果サンプル数 は1/2になります。
Fmodでは、512のサンプルが用意されているので、FFT後のサンプル数は 256サンプルとなるというわけです。
		//スペアナ表示 
		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

 

Last modified 2005.04.15 回転するスペアナ
Tsukubado