OggVorbisな話
おっぐぼるびすって読むのか?
Write: 02/02/03 UpDate:--/--/--

MP3のライセンスについては、ことゲームに関しては1タイトル当りUS$2500.00払えやこらって事のようです。
ただし販売本数が5000本以内ならチャラとおっしゃってます。
大半のエロゲーはきっと大丈夫ですね(笑)
同人でもよほどのことがない限り大丈夫でしょう。
とはいえ、いつ何時態度が豹変するか分からないのでMP3は使わない方向でプログラムを書くのが良い気がします。
大体広まってから金をせしめるという構図がGIFっぽくてなんだか嫌です。
このライセンス規定では外部のCodecを利用しても引っかかるようで、かなりいやらしいです。
そんなわけで反骨精神旺盛な(天邪鬼とも言う)僕たちは代替フォーマットを模索するわけです。
で、ライセンスの絡みを考えるとOggVorbisに行き着くことでしょう。

OggVorbisとはなんぞやということは、こちらこちらこちらなんかを参照。
そもそもOggというマルチメディアフォーマットの一部が音声フォーマットVorbisということらしいです。
2002/2現在RC3までリリースされているので完成まで秒読み段階です。
周辺ソフトもぽつぽつ出始めているのでそろそろ使い始めても良いころでしょう。
SDKやらエンコーダ、プラグインやらはこちら
手始めにWin32用のSDKとWinAmp用のプラグインを拾っておけばOKでしょう。
プログラムに関してはlibvorbis & liboggという形でライブラリがきっちり整備されているので楽勝です。
ライブラリ自体のソースも公開されているので読むと賢くなるかもしれませんが、
賢くない私はブラックボックスとして使ってます。
ちなみにライブラリをコンパイルするとキャストの手抜きが散見されてちょっとあれです。
SDKのサンプルソースにデコーダーサンプルが付いているんですが、
標準入出力を使っているので
decoder_static.exe < hoge.ogg > hoge.raw
と使ってあげましょう。
ファイルに出力されてもあまり面白くないので、ちゃんと音が出るサンプル
配列が静的だったりして大変手抜きですが、そこはそれ。
サンプルを見た感じでは、ヘッダーを読み込んだ後、
ひたすら続くデータをPCMに変換するだけのようです。
PCM化するときにはサンプリングレート、ステレオorモノラル、に従ったデータ列に変換されます。
サンプルビット数は16bitに固定されているっぽい?
少し使った感じでは、MP3より圧縮率が高め、エンコード&デコードが遅め、音質は同等といった所です。
音質に関しては専門家さんに言わせると色々あるようですが実用上問題無しです。
次回はもうちょっと真面目にドライバ化予定。


ウインドウキャプチャな話
ぴんぐー
Write: 01/12/01 UpDate:--/--/--

最近ウインドウ画面を動画としてキャプチャをする機会が多いんですよ。
いくつかのソフトでそういったニーズに答えてくれるものがあるんですが、ウインドウ画面のリフレッシュレートと
キャプチャのレートが合わないとか、ウインドウサイズが大きいとキャプチャが間に合わないとか、いまいち使い勝手が良くありません。
そんなわけで、ひたすら画面をBitmapに落として作った連番の画像ファイルをPremiereとかで動画にしてます。
ところが、640 x480 x 3 (24bitColor) で一枚約1Mbyte位、これを1000フレームキャプチャするとあっという間に1Gbyteになってしまいます。
こりゃかなわんということで、適当な圧縮フォーマットを見繕うことにしました。
可逆圧縮でフルカラーが使えてメジャーな奴はTIFFとPNG位のものです。
TIFFはローカルルールが多いので今回はPNGを使うことに決定。
もちろんPNGのセーバーを一から書くほど器用ではないので、ライブラリを拾ってきます。
libpngとzlibがあればOK。
あとは今まで使っていたBMPセーバーのルーチンをちょいちょいといじって出来上がり。
適当ですがこちらがソース。PNGルーチンのネタ元はこちら
で、うきうきして連番のPNGファイルを作ったら、Premiereが対応してませんでしたとさ、むぅ。
仕方がないのでAfterEffectを使うことにしました、めでたしめでたし。
ついでにDirectX8の画面もキャプチャーしたいなということで、libpngをソースに放り込んだら怒られました。
なにやらD3DXライブラリ内のPNGルーチンもlibpngを使っていて、
衝突しあっている模様。
それも使うなら使うで全部入れとけばいいのに png_init_io が無いとか中途半端なことをしてくれてます。
まぁlibpngのソースに手を加えて再コンパイルすればいいんですが、めんどくさいので別の手段を使いましょう。
つい先ごろ出たDirectX8.1に D3DXSaveSurfaceToFile という便利な関数が増えました。
使い方は簡単で、

g_pd3dDevice->EndScene();
LPDIRECT3DSURFACE8 lpDirect3DSurface8Back = NULL;
g_pd3dDevice->GetRenderTarget( &lpDirect3DSurface8Back );
D3DXSaveSurfaceToFile( "test.bmp", D3DXIFF_BMP,
lpDirect3DSurface8Back, NULL, NULL );
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );

これだけです。
D3DXIFF_BMPの他にも、 D3DXIFF_JPG、 D3DXIFF_TGA、 D3DXIFF_PNG、
D3DXIFF_DDS、 D3DXIFF_PPM、 D3DXIFF_DIB などがあり非常に便利。
・・・に見えますがそこはそれ、MSさんの用意する関数なので当てになりません。
実際に動くのはBMP、DIB、DDSだけです。
さてさて、PNGのセーブができるようになるのはいつの事でしょう。
DirectDrawのαブレンドとかの前例もあるので永遠にできないかも知れませんが(^^;

04/11/20 追記 DIBの色順がBGRであるのをRGB順に直してからPNGに保存するように修正しました。

 

萌えるデバッグ手法な話
えんいー
Write: 01/11/22 UpDate:--/--/--

プログラミングに必要なスキルの一つにデバッグに耐えうる精神力というのがあるのではないでしょうか。
コーディングの時間よりもバグをつぶしている時間のほうが長い気がします。
経験によってある程度バグの原因を推定することはできても、
常にあらゆる可能性を頭に置きながら、精神をチリチリ痛める戦いに挑まなければなりません。
そんなわけでデバッグ作業を円滑に進めるためなら、親友を売っても構わないと常日頃考えている極悪サンデープログラマーの野村XXであります。
で、古来より究極のデバッグ手法は『printfデバッグ』であると伝えられています。
DOSのプログラムはこれで構わないわけですが、Windowsプログラムを書くときにはprintfが使えないので通常OutputDebugStringを用います。
OutputDebugStringは引数の文字列をデバッグ実行のときにアウトプットウインドウに表示する関数です。
リリースビルドの時には関数がさっくり削除されるので気持ち良く使うことができます。
しかし引数が文字列だけなので使いづらいのが難点です。
printfならprintf("%d %f %s", int, float, char *)とか様々な変数を使うことが出来ますが、OutputDebugStringだと

char tmp[256];
sprintf(tmp, "%d %f %s", int, float, char *);
OutputDebugString(tmp);

といった感じで一度文字列にするか、可変引数を使った関数をつくってやらなければなりません。
これはめんどくさい。
一方C++世界ではcout << int << float << char *;ってなストリーム表記ができてprintfより便利です。
"%d %f %s"とか書式を気にしなくて良い分、楽チンですね。
そこでcoutの使いやすさにOutputDebugStringを押し込んでWindowsでも使えるようにしている方がいます。こちらとかこちら
これは便利、dout << int << float << char *;とするだけでアウトプットウインドウに変数がずらずら表示されます。
このソースにファイルへのログ吐き機能を追加してありがたく使わせていただいています。
しかし、いくら表示法が楽になったからといって、ひたすら数字を眺めているのは大変苦痛です。
何かが足らない。何が足らないかといえば、そう、萌え要素です。
デバッグにも萌え要素を導入しなければなりません。
必要な機能としてはcoutの様な表記ができて、さらにファイルにログが取れれば文句ありません。
さてこんな要求を満たしながら、かつ萌えるものは何かないでしょうか。
・・・
あるじゃないですか。
任意たんです。
任意たんはSSTPプロトコルを使って文字列を表示することができます。
ってことはOutputDebugStringの代りにSSTPを使えば任意たんがデバッグ文字列をしゃべってくれることになります。
ついでにログを取る機能は任意たんが持っているので一石二鳥です。
うーん、萌え〜。これを萌えストリームと命名しましょう。
mout<< int << float << char *;という風に使うだけで任意たんがにこやかに変数を囁いてくれます。
ちなみに実行するとこんな感じ。
良きかな良きかな。
SSTPの使いかたはこちら
人のソースを二つ拾ってきてくっつけただけという無粋な突っ込みは無しです。
さらにSSTPは好きなIPアドレスにメッセージを送ることができるので、
マシンが二台ある場合は片方のマシンのIPを指定してそこの任意たんにデバッグ文字列をしゃべらせることができます。
いわゆるリモートデバッグですね、すげー。
フルスクリーンプログラム開発のときに役に立つでしょう。
今回のソースはこちら
任意たんを起動してから実行してください。
いつもどおりエラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます(^^;

 

DirectMusicでMP3な話(補)かなり未確認情報ですが・・・
Write: 01/11/22 UpDate:--/--/--

というわけでDirectMusicでMP3を鳴らすのに手間取っていたのですが、ループ再生に失敗する理由はどうやらCodecが原因のようです。
MP3再生でよく使われているFraunhofer IIS MPEG Layer-3 Codec のバージョンによって挙動が違うんですよ。
WinXPやメディアプレーヤー7をインストールしてあるWindowsではver1.9辺りが入っているんですが、
それより前の奴ではループでこけます。
とほほ〜
まぁソフトを配布するときに一緒にメディアプレーヤーも配ればいいのかもしれませんが…
その点を除けば実に快調で、セカンダリバッファに複数のMP3を読み込むことで多重再生も問題なく可能です。
私の師匠T氏曰く『とにかくMixerが必要なら、acmとDirectSoundのペアが良いとおもいますよ〜』
とプロフェッショナルサイドから意見をもらったんで、まじめなプログラマさんはそっちをお勧めします。
不真面目なプログラマさんは私と一緒にメディアプレーヤーを配布しましょう。
ついでにDirectShowのループ再生についての説明で舌足らずだったので師匠のお言葉を
『FilterGraphからIMediaEventとIMediaSeekingをQIして、イベント監視スレッドを1つまわします。
ループが必要なときになったらEV_COMPLETEがきますので、シークしてやります。まぁこれだとちょっと音が切れますので
1こレンダラ書くのが正しいでしょう。』
ちなみに私が
『ループ再生については3)と一緒でWindowsのメッセージループに送られるWM_DIRECTSHOWEVENTを見に行かなければなりません。』
といっていたのは
pMediaEventEx->SetNotifyWindow( (OAHWND)hWnd, >WM_DIRECTSHOWEVENT,(long)this);
ってな感じでウインドウメッセージを
飛ばしてループを監視するという事です。
まぁやってることは大差ないんですが。

 

DirectMusicでMP3な話おおぼけ、こぼけ
Write: 01/10/22 UpDate:02/02/03

引き続きサウンド周りのドライバを書いているのですが、 BGM用にMP3再生を入れることにしました。
多重再生とループ再生とメモリからの読み込みが可能であることが今回の課題です。
おちは非常にしょーもないんですが、そこんとこよろしくお願いしますということで・・・
ちなみにサウンドドライバはDirectAudio(DirectMusic)を使って作ってます。
事の起こりはDirectMusic単体でMP3の再生ができないと思い込んでしまったことです。
SDKのサンプル(PlayAudio)でMP3ファイルの再生ができなかったんで、 そうなんだなーと判断しちゃったんですね。
そこでできそうな奴を片っ端から調べていくと

1) waveOut系
2) acm系
3) mci系
4) DirectShow系
5) LAME系

とまぁ大体こんなもんだったんで、それぞれMP3ファイルを単に鳴らすだけのサンプルを作ってみました。
1)のwaveOut系ですが、楽勝です。ヘッダから周波数等のデータを取得して構造体にぶち込んで
waveOutOpen
waveOutPrepareHeader
waveOutWrite
waveOutReset
waveOutUnprepareHeader
waveOutClose
こんな感じで命令を呼び出すだけです。面白いことにwaveOutWriteという命令はメモリから出力デバイスに
データを書き込む命令なんですが、再生途中で呼び出しても良いんですね。
普通の感覚だとwaveOutWriteを呼んだ瞬間に再生しているデータが指定されたデータに切り替わりそうなもんですが、
waveOut世界では再生途中でwaveOutWriteを呼び出すとバッファリングされて現在再生しているデータが
終了した後に再生されるようになってます。ストリーミングを実現しようと思ったら確かに理にかなってますね。
ループ再生にはコールバック関数で対応できます。
ちなみにwaveOutOpen可能なデバイス数は1つの模様。
もしかすると複数のサウンドカードを搭載しているとデバイス数が増えるのかもしれませんが、
普通の環境ではwaveOutGetNumDevsの返値が1になります。
そんなわけでどうやら多重再生はできなさそうです。

2) acm系命令は比較的使用例が多い手法ですが、acm単体ではデコードができるだけで再生はDirectSoundなんかの
手を借りないとできないようです。というか再生をサポートしたのがwaveOut系命令と言えるでしょう。
1)と同じなんですがストリーム処理を自前でやらなければならないのがめんどくさいです。
初期化時に一気にデコードすれば楽なんですが、いかんせん非圧縮状態だと1曲数十Mバイトになるので
メモリを食いまくってしまいます。それにそんなことすると再生する瞬間にCPUがデコード処理で
手一杯になってしまって再生開始に手間取ってしまいます。

3) mci系命令の命令は1)に輪をかけて簡単です
MCIWndCreate
MCIWndOpen
MCIWndPlay
MCIWndStop
MCIWndClose
の順で命令を呼ぶだけでOK。ヘッダを読み下す必要もありません。
MCIWndOpenの引数にファイル名を指定するだけです。
逆にいうとファイル名を指定するしか方法がないのでメモリから読み出すことはできなさそうです。
再生開始にちょっと手間取るのが難点。あとループ再生するためにWindowsのメッセージループに送られる
MCIWNDM_NOTIFYMODEメッセージを見に行かないといけないというのがライブラリの独立性を損ねそうで嫌な感じです。

4)DirectShow系も簡単です。
CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraphBuilder )
pGraphBuilder->QueryInterface( IID_IMediaControl, (void **)&pMediaControl )
pGraphBuilder->RenderFile( pwcFileName, NULL )
pMediaControl->Run()
pMediaControl->Stop()
RELEASE( pMediaControl );
RELEASE( pGraphBuilder );
ってなもんです。これまたファイル名指定だけで再生できてしまうお手軽さなんですが、
メモリからの読み出し方が不明。もちろんDirectShowはストリーミングに主眼を置いて作られているので
できるんでしょうけど、なんだかよく分かりません。多分IMemAllocator辺りを使うんだと思うのですが…
CMemAllocatorクラスをnewしようとするとAssertに引っかかってうまくいきません。
資料も出回ってないのでお手上げ状態。どなたかアドバイスPlease〜
ループ再生については3)と一緒でWindowsのメッセージループに送られるWM_DIRECTSHOWEVENTを見に行かなければなりません。

5)はフリーのmp3デコードルーチン使おうという話ですが2)同様再生にDirectSound等が必要になります。
第一、難しそうです(^^;

そんなこんなで、どれもニーズに合わないんで困ってしまいました。
しまいにはTwinVQやらOgg Vorbisでも使おうかと血迷うありさま。
で、メルトダウン寸前の脳味噌でMSDNを漁っていると、

DirectMusicローダーは、オーディオ圧縮マネージャ (ACM) でサポートされているフォーマットの圧縮されたウェーブフォーム オーディオ ファイル、
および非圧縮ウェーブ ファイルをロードできる。

って書いてあるじゃないですか。
ぐはぁ、なんだCodecさえあればDirectMusicでも再生できるっつーことか(DirectX7以前は知しらないが少なくともDirectX8では)。
でも、前に試したときはMP3ファイル再生できなかったんだけどな〜とうだうだ考えていて、
ふと思い出したことにはMP3形式のWaveとかいうのがあったなぁという事。
チャンク内にMP3を放り込んでやることでWAVEの格好をするという奴です。
早速BlizTagEditorなるヘッダ書き換えツールを使って新しいMP3ファイルを作成してみたもののやはり再生できません。
ちっ、またMSに騙されたか!、ACM越しの再生なんてできないじゃないですか〜
と怒り心頭だったんですが、最後に念のためサウンドレコーダーでADPCMを作って試してみるとあら不思議ちゃんと再生できます。
ついでにこれまたサウンドレコーダーでMP3形式のWaveを作ってみると・・・、再生できますねぇ。
すまんMS、早とちりだったらしい。
どういうことかというとRIFF/WAVE MP3ファイルとMP3形式のWaveファイルを混同していたのが駄目だったらしいです。
後者にID3タグとかの付加情報を付けたのが前者という位置付けのようです。
つまりヘッダ書き換えなんて姑息な手段を使わないでまじめにWAVE->WAVE形式MP3にエンコードしておけば問題なかったんですね〜。
といった訳で午後のコーダーなら
gogo ファイル名 -riff wave
SCMPXなら
Encoding MPx -> WAVEヘッダ
みたいにエンコードしてあげると問題なくDirectMusicでも再生できます。
あーしょもない。
でも本来チャンク構造をとっているはずのRIFF/WAVE MP3ファイルが再生できない方が問題ありな気もするんですけどね。
TAGデータとかは読み飛ばしてデータ部分を読むだけでいいんじゃないんだっけ?
まぁその辺差し引いてもDirectMusicに再生を任せると自動的にストリーミングしてくれるんでありがたいこと限りなし。
もちろん多重再生とループ再生とメモリからの読み込みもOK…じゃないですね〜
なんかループ再生が挙動不審です。
長めのMP3ファイルを再生するとループに失敗します。
デバッガによると
DSWAVE: ERROR: Create (Viewport): Attempt to open a conversion stream failed.
とかおっしゃってます。
だぁぁ、相変わらず一筋縄ではいかないです。
てなわけで次回に続く(笑)

02/03/07追記 waveOutOpen可能なデバイス数は1ですが、スレッドを分けると複数回Open可能で多重再生もいける模様、要検証。

 


戻る