戻る


2013/09/30 18:20

はー、できたできた。

途中、エルミナージュとかやってたけど、ひとまず音声出力を使えるようにした。

dp::AudioPlayerを生成すると再生が開始し、dp::pause()で一時停止ができ、破棄すれば再生が中断され、再生が完了すればイベントハンドラで通知される。

出力データの書き込みは、必要になったらイベントハンドラでバッファ領域と共に通知される形なので、専用のスレッドを使ってループの中でどうのとか、めんどくさい処理を書く必要はない。そのような処理はライブラリ側に書かれている。

dp::pause()で一時停止はできるけど、現在一時停止中か再生中か、というデータを得られる関数がないんだよなぁ。

ライブラリ側から勝手に一時停止したりすることはないんだし、その辺はライブラリ利用側で必要なら作ってもらう形でもいいかなと思っているんだけど。

基本的に機能は最小にしたいと考えている。環境間の差異を無くすライブラリなわけだし。

しかし、pulseaudioの使い方がこれでいいのかよくわからん。

pa_threaded_mainloopは全体に1つだけ、デバイス接続・切断管理オブジェクト1つにpa_context1つ、音声出力オブジェクトは全て共通でpa_context1つ、のような形にして、できるだけ生成する量を少なくしているんだけど、ロックはpa_threaded_mainloopで行なうんだよなぁ。

dpdemosに作ったプログラムはデバイス接続・切断管理1つ、音声出力1つ程度なので問題ないようだけど、音声出力を大量に作るような場合影響が出るような気がする。

デバイス接続・切断管理オブジェクトや、音声出力オブジェクト1つ1つに、pa_threaded_mainloopを持たせるべきなんだろうか。

でも、音声出力すんぞって時に、色々作るとそのオーバーヘッドが気になってしまう。

いかんせん、サンプルやドキュメントがなさすぎて分からん。試しに作り変えてみようかな。

想定では、現状よりも分かりやすくなるはずなんだけど。現状はライブラリロード時にpa_contextの接続関数を呼んだりしてるけど、非同期な関数なもんで、処理が戻ってくる=接続完了しているにならんのでめんどい。


2013/09/27 19:45

一時停止を用意するべきだなと思った。

音声出力は普通、書き込んだ量と出力した量は一致しない。

書き込みはバッファに行なわれ、出力はバッファから行なわれる。書き込んだ量>出力した量となるのが普通だ。

想定される操作として、一旦出力を停止し、その後停止したところから出力を再開する、というような操作があるが、これを実装するのは容易ではない。

いくら書き込んだか、という情報は簡単に手に入るが、いくら出力したのか、という情報は簡単には手に入らない。

書き込んだ量を元にして出力を再開すると、書き込んだ量と出力した量の差だけ音が抜けてしまうのだ。

これはよくないなぁと思い、どう対応したものか色々考えていたのだが、どうやら音声ライブラリには出力の一時停止というものがあるらしい。

一時停止すると、バッファからの出力が止まる。バッファに空きがあるなら以降も書き込みは可能だが、出力はされない。

一時停止を解除すると、バッファに蓄積されたデータの出力が再開する、というものだ。

pulseaudioにこういう機能があることは知っていたのだが、他のライブラリにも同等の機能が用意されているのか不安だったので敬遠していた。

しかし調べたところ、alsaやらwasapiやらxaudio2やらには、同等の機能が用意されているようだった。

directsound?なんか複雑でなぁ。よく分からん。あるっぽいんだけど確証がない、というか使う気がない。

ライブラリに用意されている機能を使っても、一度閉じたストリームの出力を再開するような動作には使えない。

そういうのはもう、再生と同時に経過時間を計測して、その時間を元に再開地点を算出すればいいんじゃないかなとか。


2013/09/25 19:50

やる気減退してた。

昨日ようやく、wavファイルの解析処理を作ったところ。

それで今、音声出力のインターフェースを決めようとしてるんだけど、やはり難しい。

単純な形にしたいが、そうするとどうしても綻びが出てしまう。

やはり、古いプロジェクトで作ったようなインターフェースにするべきなのだろうか。

単純に、オープンして、書き込んで、クローズする、という仕組みにしようと思っていたんだけどなぁ。

ファイルの書き込みによく似ているから分かりやすいが、ファイルへの出力と音声の出力はやはり違うのだ。

ファイルへの出力というのは、基本的にデータを全て出力するものであり、書き込みが成功したか失敗したかに関心が向く。

音声の出力はそうではない。全て出力した場合には最後まできちんと音が鳴ることは確かに重要だが、途中で出力を中断するケースもある。

その場合に直ちに音が鳴り止むべきであるし、そんなことよりも出力している最中に出力を途切れさせて、音飛びが発生しないようにする必要もある。

あと、ファイルへの出力の場合、普通出力するデータは出力する前に全て作成しておくべきだが、音声の出力はデータを順次作成していく形にするべきだ。

音声データは基本的にサイズが大きくなりがちだし、途中で中断する場合も考えれば中断した以降のデータは無駄になる。

他にも、例えばマイクから拾ってくる音声のように、終わりがないものだってある。そういうデータは取得したデータを順次出力していくしかない。

ファイルの場合もそうだろうけど、1度に出力できる、バッファのサイズは決まっている。

ファイルの場合と違い、音声はバッファに出力したデータを全て再生しきる前に、次のデータを出力しなければならない。ファイル出力なら特に問題はないだろうけど、音声出力の場合は音飛びになってしまう。

そのためにはバッファの空きサイズなんかは重要なデータだ。しかし、それは時間経過で変化する。例えばgetEmptyBufferSize()みたいな関数を用意して、現時点でのバッファの空きサイズを取得する、なんてのも考えられるだろうけどスマートでないと思う。

取得した次の瞬間には増えているかもしれないし、なにより出力時以外に使用タイミングは無いようなものなのだ。出力時にのみ得られればいいデータを、いつでも得られるようにするのは不自然だ。

とかなんとか、色々考慮した結果、古いプロジェクトと同じように、イベントハンドラで出力データをセットする、という形にした方が良さそうだ。

インスタンス生成でストリームオープン、イベントハンドラ内でバッファにデータをセットし、インスタンス破棄で再生を中断しストリームクローズ。再生終了についても、イベントハンドラで通知する。

というような感じだろうか。古いプロジェクトのと違う点は、再生終了時のイベントハンドラが無かったのと、インスタンス破棄時の処理だろうか。前は破棄で再生終了の待機をしていたはずだ。


2013/09/18 23:00

alsaにはデバイスの接続・切断検知機能がないことを忘れていた。

udevを使ってデバイスの接続・切断及び列挙を行うと、実際には使えないと思われるデバイスまで列挙されてしまうことも忘れていた。

やはり、pulseaudioでやった方がいい気がしてきた。alsaはストリームが外部に見える仕組みとして存在しているわけではないが、結局音を重ねる場合には複数のストリームを開く必要があるわけだし。

pavucontrolのことなんぞ考えず、pulseaudioでやるべきか。

どの内部実装を使ったとしても、それなりに複雑な仕組みを構築する必要があることに変わりはないわけだしな。

それならば、比較的扱いやすい方を選んだ方が得策だ、多分。

なにより、どうせlinux側の実装なんぞ私ぐらいしか使わんのだ。やりたいようにやらせてもらう。


2013/09/18 20:50

pulseaudioは多分だめだ。

1つ音を重ねようとするたびに別のストリームを作らなければならない作りが納得いかない。

内部的にはそうする必要があっても、外部に見える仕組みとしてそうなっているのはどうなんだ?

pavucontrolで外部から音量調整しようとすると、ストリームの数だけ音量調節バーが出現するわけで、ゲームに使おうとするとどうにもうまく運用できる気がしない。

というわけで、jackの方を試してみることにする。

試そうと思ったがやめようと思った。

どうもjackは、複数のサウンドカードを同時に扱えないらしい。

やはり、alsaを直接叩いた方がいい気がする。


2013/09/17 22:50

OpenGL対応した。

公式にあったヘッダファイルから変換してなんとかした。

しかし、OpenGL1.0と1.1の定数とか、関数の一部が記述されていなかったため、その部分は過去のコミットから引っぱってきて完了した。

確認できているのはその部分、というだけであり、もしかしたら以降のバージョンでも不足があるかもしれない。めんどいので、その場合には気付いた時に修正する。

これで後は音だけだろうか。もちろんwindows側がほぼ空っぽなので、その辺は後でなんとかしなくてはならないが。


2013/09/15 05:50

OpenGL1.2を対応した。

しかし、拡張機能の対応を忘れていた。1.2から出てくるようだ。GL_EXTENSIONSの値をglGetString()で取得し、値の中に特定の文字列が含まれているならその拡張に対応している、とかいうあれ。

ここまで書いて仕様書に目を通していたら、どうも1.1の時点であったっぽいな。dpではとりあえず全てのOpenGL関数をロードして利用可能にするというだけにする予定なので、関数のバージョンや拡張機能の括りを考えたりはしないので問題ない。

しかしながら、上位ライブラリではバージョンや拡張機能単位でOpenGL関数をロードする仕組みを用意するつもりなので、どこかにその辺はっきりした資料があるといいのだけれど。

ってあれ。公式サイトを色々探してたら、glcorearb.hとかいうのが使えそうじゃないか。あとglext.h。

前者にはバージョンと拡張機能別の定義、後者にはバージョンに存在する可能性のある拡張機能を含んだバージョン別の定義が書かれている。

これだけでもいけそうかなと思ったが、びみょうにだめっぽい。後者のヘッダファイルに書かれているけど前者のヘッダファイルに書かれていない関数とかある。その辺は仕様書読まないと分からんなぁ。

しかし、ベースはglext.hでいいような気がする。

もう仕様書を読みながら関数の定義を記述しなくていいんやな。

OpenGL1.0のから書き直した方がいい気がしてきた。

拡張機能は、ARBとかいうのとそれ以外ので大別できそうな感じがするな。前者はバージョンに深く結びついていそう。

ARBのはバージョンに対応したヘッダファイルに記述してもよさそうだけど、それ以外はどうしよう。できるだけファイルを分けて、ファイル1つ辺りの行数を減らしたいしなぁ。

あまり分割しすぎてもあれだけど、行数が多すぎるとリドゥやアンドゥに時間がかかりすぎて困る。

しかし、定数と関数で別ファイルにする、2ファイル構成でもいい気がする。ううむ。


2013/09/13 19:30

昨日、OpenGL1.1まで対応した。

OpenGL1.0の関数で描画とかしてみたんだけど、やはりバージョン毎のロード処理は必要な気がする。

使う関数を全てdp::loadGLProc()するのはめんどくさい。

ロード対象の関数ポインタを配列にまとめ、for eachでロードするというのを想定していたが、参照を配列にできないため、ポインタの配列にせざるを得ない。

なので、関数ポインタのポインタを取得するような関数があった方がいいなぁ。

そんなことより、垂直同期するための関数、glXSwapInterval_EXTとか、ライブラリのロード時に取得し、ラムダ式をグローバルに設定する、ということをしてるんだけど、いざその関数を呼び出そうとすると、なぜかぬるぽになっていて非常に困る。

ロード時には確かに設定しているし、ロード時以外では代入もしていないのだけれど。一体どこでおかしくなっているのかさっぱりだ。

ってなんだよ今動かしたらまともに動いてるじゃねーかくそどういうことだよちくしょう。クリーンしてなかったからいけなかったんか。

バージョン毎の関数は、std::vectorをライブラリ側に持たせるとかいう感じにするかなぁ。今はマクロによる関数の定義をバージョン毎にヘッダファイルを分けて記述しているが、std::vectorをバージョン毎に用意するようにすれば、マクロの方は1つのファイルにまとめてもいいかもしれないなぁ。


2013/09/11 18:25

なんだよもー

結局、単純作業などいらなかった。やる前なので助かった。

昨日うまくいかなかったのは、関数ポインタの参照が問題だったようだ。関数ポインタの参照に関数の実体をつっこんでしまって変な値が出てきてしまったらしい。

そんなわけで、glClear()を直接関数ポインタにつっこむのも、glXGetProcAddress()で取得したものをつっこむのも、どちらでもうまくいったわけだ。

私としては後者で統一したいが、windowsじゃうまくいかない可能性があるし。前にやったのが失敗とかならいいのだけれど。

まーとりあえず、OpenGLの関数取り込みに関連する処理をしっかり固めよう。1.0より後のバージョンの対応はそれが済んでからだ。

あるいは、現時点では関数1つ単位でしかロードができないので、バージョン単位でのロード処理も作るべきか。しかし、それは上位ライブラリでやってもいい気がする。


2013/09/11 04:15

OpenGL1.0対応、完了は見えてきたのだが。

単純作業は終わったと思ったのだが、想定外の事態によりまだ単純作業が必要になりそうだ。

glXGetProcAddress()で、OpenGL1.0の関数のアドレスも戻ってくるんで、これを使えると思っていたのだが使えなかった。

関数の呼び出し自体は問題なく成功し、処理も帰ってくるのだが、どうも何も処理していないようだ。

glClearColor()の設定がされないし、glGetString()でGL_VERSIONやGL_VENDORの文字列を取得しようとしてもぬるぽが帰ってくるだけ。

どうもダミーっぽい香りがする。

別に、これについては想定の範囲内だ。windowsではopengl32.hに記述されている関数は、wglGetProcAddress()で関数のアドレスを取得しようとしてもぬるぽが帰ってきてた気がするし。

仕方ないので、glXGetProcAddress()の戻り値を設定するはずだった関数ポインタに、関数の実体のアドレスを設定するようにしたのだが、これもうまくいかないのは想定外だった。

どうも、同じ関数であっても、ライブラリ上から呼び出す場合のアドレスと、ライブラリ上からではない、プログラム上から呼び出す場合のアドレスは違うらしい。

なんか分かりにくいな。libGL.soにある関数を呼び出す場合に、libdp_opengl.soから呼び出す場合と、main()のあるソースから呼び出す場合のアドレスが異なるので、libdp_opengl.soから呼び出す場合のアドレスをmain()から使って呼び出そうとしてもうまくいかない、というわけだ。セグメント例外になってしまう。

これを解決するためには、main()のあるソースから呼び出す場合のアドレスをなんとかして取得するか、もしくはlibdp_opengl.soから呼び出す場合のアドレスを使い、libdp_opengl.soの関数を経由して呼び出すかのどちらかの方法になるだろう。

しかしながら、前者の方法はまるで見当がつかない。なので、後者の方法、libdp_opengl.soを経由する方法になるのだが、これはつまりlibdp_opengl.soにlibGL.soにある関数を呼び出すだけの関数を作る必要がある、というわけであり。OpenGL1.0に対応するなら、OpenGL1.0にある関数の数だけ作る必要があるわけであり。

すごいめんどくさい。ていうか、libdp_opengl.soの関数のアドレスをmain()に渡しても呼び出せるのかなぁ。まずそこの検証からか。うまくいかんかったらどうしよう。困るなぁ。


2013/09/10 06:00

OpenGL1.0対応を始めている。

予想はしていたが、やはりめんどくさい作業になりそうだ。

どのバージョンでどの関数や定数が追加されたとかいう情報が、公式から配布されている仕様書のPDFしかないっぽいもんで、それを見ながらヘッダファイルを作っている。

最終的には、関数や定数の定義は1つにまとめて、各バージョン毎に関数のリストを作る予定。なので、定数までバージョンごとにまとめるのは無駄かもしれない。

しかしながら、最新バージョンでは非推奨になってる関数とかあるわけだし、その辺の記述がもしかしたら最新バージョンの仕様書からは抜けてるかもしれない、知らんけど。なので、1つずつ仕様書を追っていこうかなーと。


2013/09/08 02:20

直したと思ったら、修正前のソースでも問題が再現しなくなったでござるの巻

少なくとも、常にウィンドウ内全てが再描画されるというのは問題なかったようだ。

てっきり、自動的にクリッピングがかかって再描画が必要な部分にのみ描画結果が適用されるものとばかり思っていたんだけど。そんなリッチな機能が最初から有効になっているはずがなかったらしい。

で、初回の描画が表示に反映されない問題についても、何回やり直しても問題が再現しなくなってしまった。ぐぬぬ。

修正したソースは残しておくけど、とりあえずは修正前のままでいいか。

とまぁそんな感じで時間を無駄にしてしまったわけだが。

なんかやる気減退してしまったので起きてからになるかもしれないが、OpenGL関数の扱いに入るとしよう。

目指すところとしては、まずライブラリを使うプログラムが、OpenGLのライブラリ(libGL.soやらopengl32.dllやら)に直接リンクしなくてもいいということ。古いプロジェクトではwindows版のみ、opengl32.dllにリンクしなくてはならないところが気にかかっていた。環境によってはリンクしなければならない外部ライブラリがあるというのはいやだ。

とりあえずはそれだけか。あとは、バージョン1.1より後の関数はglXGetProcAddress()やらwglGetProcAddress()やらを使い、文字列を引数に関数のアドレスを取得しなければならないが、関数名をプログラム側に含めさせたくないとか。そもそもそのアドレス取得の環境依存関数はプログラム側に使わせたくないな。

ひとまず、OpenGLの関数は、何もしなくても使えるバージョン1.1以前の関数を含め、全て関数ポインタとして提供し、何もしなければ関数ポインタの値はnullでいいかなと思っている。

そこに、なんらかのロード機構を用意し、プログラムで使用する関数のみ、関数ポインタが適切な値になるようにする、といった感じで。

もちろん、環境が対応してなければ関数ポインタはnullのまま。それを利用して、プログラム側で判定を行ない描画処理に変化を持たせたりとか、後々作るシステムで、指定した関数群のポインタがnullのままだったらゲームを起動できないようにしたいなとか。

引数に何を使うかが問題だ。できることなら、関数ポインタそのものを引数にしたいのだけれど、配列にできるだろうか。ほとんどの関数、型が異なるわけだしなぁ。voidのポインタとして扱えば可能か?

なんにせよ、量が多いのが気が滅入る。まずはロードの必要がない関数についての処理を実装してしまって、次にロードが必要な関数についての処理を、ん、いや、逆がいいのか?後者の方が複雑そうだし、うーむ


2013/09/07 02:30

OpenGLコンテキストできた。

コンテキスト生成とウィンドウとの関連付けを別々にしたし、垂直同期するかしないかの指定も適切な場所で行なうようにした。

垂直同期に使用する関数も、ファンクタを使って呼び出し時にはif文が不要になるようにした。

古いプロジェクトのよりずっとよくなっていると思う。

しかし、びみょうに想定した動作をしていない。

初回の描画が表示に反映されてない場合があったり、再描画時に無効領域以外も再描画してしまう、つまり常にウィンドウ内全てを再描画してたり。

ウィンドウの描画イベント周りがだめなんだろうなぁ、ということがわかる。

直し方のイメージはできている。

かなり大幅に修正を加える必要があるが、はっきりイメージできているため途中で困ることもないだろう。

だが疲れた。

明日にさくっと終わらせて、OpenGL関数の扱いに入りたいところ。


2013/09/05 18:45

新しいデモ作るのめんどいなぁ

描画関係のイベントハンドラ対応終わらせて、再描画要求も追加した。

で、マルチスレッド関係でまずいところがあったのを修正して、他にも数点修正した。

これでOpenGLに入れる。のだが、まだ新しいデモ作ってない。

後でもいいんじゃないかなーとか思い始めている。

だって私が特に得しないし

そんなわけでOpenGLを始めよう。まずはコンテキストからだな。

古いプロジェクトで作ったやつだと、生成処理でコンテキスト生成とウィンドウへの関連付けを一緒にやってて、破棄で関連付け解除とコンテキスト破棄を一緒にやってる形になってるんだけど、コンテキストの生成・破棄とウィンドウへの関連付けは別々にした方がいい気がする。

コンテキストの生成・破棄は、ウィンドウが存在していようがいまいができるからなぁ。先に生成しておいて、ウィンドウを生成してそれに関連付ける、とかそういう風にできてもいいはずだ。

やったことがないから分からないけど、他のウィンドウに関連付けたコンテキストを、関連付け解除して他のウィンドウに関連付ける、とかもできるんじゃないかなぁ、多分。


2013/09/04 17:50

昨日書いた残件に、ウィンドウクローズ要求書いてなかったな。

でもまぁ、要求系は全部終わらせたわけだ。あとは描画関係のイベントハンドラ対応を終わらせればいいだけになった。

しかし、動作確認用に作っているデモが、でかくなりすぎている気がする。

現時点で641行ある。行数だけならまだしも、ファンクタとか使いまくっていて、少々ではないレベルで読みにくい気がするんだよなぁ。

今は、大体1ライブラリにつき1デモという感じになっているんだけど、もっと細かく分けるべきかもしれない。

デモのソースを見れば、使い方が分かるような感じのを目指しているので。量や、変に複雑そうな処理で読みにくくなるのはいただけない。

まーとりあえず描画関係のイベントハンドラ対応を終わらせよう。今あるデモにその動作確認のコードも追加するが、その後にそれとは別のデモを作ることにしよう。


2013/09/03 22:45

ウィンドウを動かした時と、サイズ変更した時のイベントハンドラ設定、呼び出し処理を追加した。

数日、セブンスドラゴンばかりやっててほぼ進んでいなかった。おもしろいがよろしくないので自重することにした。数日というのは土日も含まれるが、まぁ関係ない。

で、ウィンドウの残件は以下のような感じだと思っている。

描画関係のイベントハンドラ対応(描画開始、描画、描画終了)

ウィンドウタイトル変更要求

ウィンドウ移動要求

ウィンドウリサイズ要求

こんなところか。

フルスクリーンはやはり後回し。まだ描画すらできていないし、そんな状況でフルスクリーン対応しても楽しくないしなぁという意見。

ウィンドウを常に最前面だとか、リサイズ不可だとか、その辺の属性?も変更できるようにするべきなのかなぁと思わないではないのだが、それさえなければ普通のウィンドウとフルスクリーンウィンドウを同じように扱えるような気がしているので、実装しない方がいいかなぁと。

というか、そんなんウィンドウ作り直せばいいじゃんと思う。

早いところ片付けて、OpenGL対応してなんか描画したい。

しかし、OpenGL対応も色々考えんとなぁ。

過去に作ったプロジェクトのだと、とりあえず関数呼び出せるようにすりゃいいだろという感じでやっていたが、実際には環境によって、ある関数が使えないとか使えるとかあるわけで。

ゲームごとに、起動にはこの関数群が必要でそれらが使える環境じゃないとゲームを起動させることができないだとか、この関数群が使えなくてもゲームをプレイできるが、演出が地味になるかもよだとか、そういうの設定できた方がよさそう。

過去に作ったやつは、OpenGLのバージョンごとにヘッダファイル分けてたけど、今回はどうしよう。結局のところバージョンなんてのは、そのバージョンで実装するべきAPIを全て実装してあればバージョン準拠を名乗れるだとかそんな感じなわけだし。

しかし実際、ハードウェアはそういった括りで作られているはずなので「OpenGLのバージョンX.Xに対応してれば起動できるよ」とかいう表示の方が、起動に必要な関数名を列挙するよりずっと分かりやすそう。あるいは、詳細な情報として列挙を見られるようにするというのもありか。

なんにせよ、早いところウィンドウ対応を終わらせよう。


戻る