カレンダー

04 | 2007/05 | 06
- - 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31 - -

カテゴリー

プロフィール

drednote(Mr.Ty)

Author:drednote(Mr.Ty)
既にいい年しているにも関わらずエロゲをプレイしているヤバイおっさんです。きっと還暦になってもプレイしてそうな気がする。

最近の記事

最近のコメント

最近のトラックバック

月別アーカイブ

ブロとも申請フォーム

この人とブロともになる

主にエロゲのプレイ日記。他レビューっぽい事とか色々
エロゲプレイ記
  当サイト内記事にはゲームのネタバレが含まれる場合があります。
  ネタバレをみたくない方は、当サイトの閲覧をご遠慮願いますようお願い致します。
  また、当サイトの記事自体は全年齢対象ですが、
  扱っている評価物は基本的に18歳未満プレイ禁止の物が殆どですのでご注意願います。
[------]
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

--------(--) --:-- スポンサー広告 | 編集 |
[20070531]
プレイ日記は書いてなかったけどドラクリウスをクリアしたのでレビューを書いてみます。ブランド:めろめろキュートとしては2作目のゲームです。XANUDUとかあの辺のブランドと会社的には繋がってたりするんですかね?ブランド:C:Driveとは姉妹ブランドの関係らしいですが。

独占について
実に微妙ですね。何しろリカ以外の全ヒロインについて(操はヒロインに含んでいません、悪しからず)元々親父に惚れていた連中という感じなので正直あまり面白くないかな、という感じです。まぁ例えばベルチェなんかは冒頭でいきなり親父に捨てられるシーンから始まるし、リアンは単なる憧れだっただけで惚れていたというのとは違うし、ゼノの場合は寧ろリアンだけが大事って感じなのでそれ程嫌気を感じるわけでも無いのですが、やはりひっかかる。リカに関しては元々吸血鬼敵対グループの人間だったので親父関連では完全にクリーンなんだけど、こっちはお姉さまが出てきてそっち関連で主人公とどっちが大切なんだよと思わず突っ込みたくなるような雰囲気も醸し出しているしで、正直独占的にはあまりお勧め出来る類の作品ではありません。まぁ、作中では敵に実際に性的陵辱を受けるようなシーンは無いんで(主人公が幻覚を見せられて、犯られちゃってるように見せられるシーンは存在するがすぐに幻覚と判明する)こういう暗めの作品としてみればまだマシと言えるのかもしれませんが、やはり少し面白くない気分になる部分はあるので回避推奨です。

処女について
リカさんとリアンが処女。まぁ妥当な線かなという気もします。ベルチェは元々親父の物だし。ただゼノさんが非処女だったらしいというのはちょっとメーカー空気嫁と言いたくなりますね。という事でこの点についても回避推奨。

萌え和姦について
まぁ、元々作品の雰囲気がそんなものを求められるような雰囲気じゃないんですが、それでも実は作中Hの殆どが普通に和姦と言えるのが意外といえば意外ですね。特にストーリー中ではリカさんとのHが濃厚に描かれていて良い感じです。このゲーム、メインヒロインが誰かという点についてちょっと議論されるような感じなのですが、私的にはリカさんがメインヒロインかなぁという感じなので、その点は良かったですね。実際には、話の最後で操が女性化したりして、操とのHもあるのかな?とか思ってたら無いしwそこがちょっと心残りではありますがまぁ全体的には萌え和姦面で言えばそんなに悪い作品じゃ無かったりします。

ロリについて
諦めて下さい、以上。ちなみに公式ページに出てくるベルチェのCG見て、これ良いジャンとか思っても実際にHする際には成長します。というかその時にはベルチェが成長してその代わり主人公が子供化しますw。まぁ、諦めて下さい。

ツンデレについて
作中ヒロインではリカがツンデレ担当ですね。まぁ元々敵対組織から入るヒロインなんで、自動的にツンデレ担当になるのは話の必然でしょうね。作中でリアンがツンデレを演じようとしている風な場面がありますが、そのツンデレを演じるという部分が既にネタとして使われているわけで当然ながらツンデレヒロインではありません。というか、こういうある意味楽屋落ち的ネタはあまりよくない風潮だと思いますがね。リカさんは、敵対組織から入るタイプのツンデレヒロインの割と典型的な展開を見せてくれます。
やむにやまれぬ事情による共同作戦->お互い認め合う->とある事情によりヒロイン側ピンチになり主人公が助ける->主人公に依存化
という感じで、わりとどっかで見たような展開ですが、まぁその分安心して見ていられます。

絵について
綺麗ですね。比較的リアル調な部分もありますが、まぁ劇画調作品なんでそれに相応する絵という感じでしょうか。ただまぁ、昨今この程度の絵ならそれこそ幾らでもあるのである種の個性が欲しい気はします。

総評
シナリオ展開は、時々話の展開がワープしたりするのを除いても破綻気味で、正直話を読むタイプのゲーマーには薦められません。また、エロシーンは1つ1つはそれなりに濃厚に描けているものの数が多くないので、そっち方面の期待にも答えられません。独占志向の人には薦められない作品であるのは上で書いた通りですが、かといって寝取られ好きな人に薦められる作品でもありません。吸血鬼がテーマの作品ですが、設定自体は独自設定の部分が多く、今日に罷り通っている一般的吸血鬼からは大きくかけ離れた「吸血鬼」であり、ファンタジーというよりはSF的な展開を見せる為、当然ながらファンタジー好きにも薦められませんがSF的展開といっても特に科学的考証があるわけでもなく、ただ単にノリで考えただけの設定はSF好きに薦められる物でもありません。という事で、正直この作品は一体誰にプレイして欲しくて作ったゲームなのか皆目見当がつきません。点数でいえば100点満点で

20点

といったところでしょうか。正直、次からはもう買わないと思います。
スポンサーサイト
[20070517]
プログラムを組むという作業をする上で数学は果たして必要か?もう既に数多の人が数多の結論を出しており、またその結論も多種多様に存在するだろうがここで私も書いてみたいと思う。
まず結論から書くと、必要となる分野が少なからずある、という事になる。けして特殊な分野ではなく、寧ろ一般的な分野であっても必要となるケースが少なからずあるだろう。但し暗号プログラム等は数学の塊のような分野だが、これは除外しても構わない。何故ならそもそも暗号理論自体数学の分野に属する物で、元々コンピュータに何かをやらせるとかそういった概念の分野では無いからだ。コンピュータがあってもなくても暗号理論は存在し、そしてそれは数学を必要とする(というか数学そのものだ)。だから暗号理論はプログラミングに数学が必要かどうかには関わらない。それ以外の分野で数学が絶対に必要な分野といえばまず私が思い浮かべるのはサウンドプログラミングの分野だ。サウンドプログラミング、つまり音を操るプログラムだが何もDirectSoundで既に用意されているWAVE波形を垂れ流しにする事がサウンドプログラミングだとは思わない。これはただのストリーミング処理であってサウンドの要素はかけらも存在しない。サウンドプログラミングというのは、波形そのものを自ら計算して生み出したり、或いは既存の波形に対して何らかの変形操作を行うという分野となる。波形を生み出すにはサイン波ならSin関数さえしっていればどうとでもなるので、あまり数学と感じない人も多いかもしれないが、しかしナイキスト周波数の範囲に収まる形で矩形波を作成したり或いはとある周波数帯域の信号を抑えたり増幅させたりといった操作は数学の理解無くしてありえない。
今の時代、多くの人が数学の必要なプログラミング分野として思い浮かべるのが3Dグラフィックス描画だろう。確かに座標計算で普通行列演算を使い、またライティングや特殊効果の為の計算等が存在している。しかし実際には3Dに限らず2Dであっても同様の計算は必要となるのでここでは3Dグラフィックスではなく、グラフィックス描画には数学が必要となると書かせてもらおう。どのようなグラフィックス描画であれ、ただ用意された絵を用意された状態のままで貼り付けるだけというわけでは無いのであれば、そこには少なからず数学的演算がなされている。数学の理解が深まればそれだけ使用出来るグラフィックス効果は増えるだろう。最新のDirectGraphicsでは各種シェーダーをプログラムする事が出来るが、このシェーダーを自ら作るには数学的理解が必要となる。何故なら各頂点が、或いは各ピクセルがどのように描画されるかを数学的に表現しなければならない為だ。シェーダーでは基本的に、条件分岐によって描画する物を変えるような処理はしない。その代わり、演算によって一般的にどのような計算でそのピクセルの色が、或いは頂点座標が導き出されるのかを記していく。その事が理解出来なければ自分でシェーダーを作る事は出来ないだろう。
人工知能分野では集合理論を理解しなければならない場面が多数ある。集合理論もまた数学の一分野である事を考えれば、やはり人工知能分野であっても数学の知識が必要となるという事である。実際人工知能の本を読み解くには数学的知識が必要となる。集合理論での計算式が大量に出てきたり、確率統計の計算知識全般が必要となる。少なくとも今の人工知能の本は大抵の場合、これらの知識があることを前提にして書かれているように思うし、実際これらの知識があることでこそ理解出来る概念といった物も存在する。まぁ、本格的な人工知能を組むような世界にいる人が基礎的な数学の知識を持ってないとは思わないけどね。
そして何よりも数学が必要な分野こそゲームプログラムだろう。上記に書いたサウンド、グラフィックス、人工知能全ての要素を兼ね備えている上に、更にゲーム中に出てくるオブジェクトの振る舞いや軌道計算などにおいても数学的知識が必要となる。実際ゲームプログラミングの為の数学や物理に関して書かれている本が結構多く出ている。これはゲームプログラミングに数学/物理の知識が必要となる証拠であろう。ついでに書けば単純なエロゲであっても数学的知識の有無により表現出来る演出が異なってくる事は理解しておかなければならない。無論、吉里吉里等を使って単純なトラジションや用意された演出しか使わないというのであれば別だが、少しでも特殊な演出効果を出したいのであれば必ず何らかの数学的知識が必要となる事は理解しておきたい。行列演算、信号処理、物理で必要となる全ての数学的知識、集合理論、確率統計、これらは全てゲームを作る上で知っておいて得こそあれ損は無い。ゲーム分野こそ数学を理解していなければならない分野であるとも言える。
駆け足で数学が必要となる分野について書いてみた。実際にはビジネス系プログラム等は私の専門外である為詳しい事は判らないのだが、確率統計理論や集合理論位は必要となる場面も多いだろう。金銭を扱う類のプログラムであるなら税金計算の時に数学が必要ともなろう。どのようなプログラムであるにせよ、数学は知っておいて得にこそなれ損にはならない。だから、全てのプログラマは基礎的な数学知識を理解しておくべきだというのが私の見解となる。

閑話休題:このブログへのコメントが今まで一度も書かれた事が無いし、トラックバックも殆ど来ないんだけど誰も読んでないのかな~とか思ってしまう。何か思うところがあるならば是非コメントを残していって欲しい。
[20070512]
久方ぶりにエロゲレビューを書いてみます。お題はついこの間クリアした恋姫†無双
このゲームは特徴的なゲームシステムも含め、普通のアドベンチャーとは少し違うレビューが必要かもしれませんね。

独占について
関羽で幾つかレズ系の微妙なものがある以外は基本的にはそれ程問題は無い気がしています。ただ、このゲームではサブキャラの数が圧倒的に多く、サブキャラにも各種エロシーンがあるのですがサブキャラに関して言えば独占外のキャラが数多くいます。まぁ私の場合は最終的に主人公のものにならないキャラについては最初から度外視しているのでわりとどうでもよかったりするんですが、そうでない人には要注意でしょうね。まぁ、そんな重度の独占好きな人がBASESONのゲームを買うとも思わない訳ですがw
処女について
これもヒロインについては3人ともに処女ですし、サブヒロインでも最終的に主人公にラブラブになる系のヒロインについては、魏の曹操以外の各将軍以外は後は黄忠が(流石に元人妻だから)秘処女ではありますが、全体的に見て処女率はかなり高めだと思います。というか曹操が処女で、最終的に主人公になつくようになるというのは中々良いツンデレですねって感じで来るものがありますね。

萌え和姦について
基本的にレイプ系の話はありません。というか女性の方がはるかに強いしw。こういった戦国系の話だと萌え和姦なんて望めない場合が非常に多かったりするのですが、このゲームでは逆で和姦オンリーです。これは良いですね。

ロリについて
ヒロイン3人のうち2人がロリと言えるし、他にも董卓がロリと言えるでしょう。もしかしたら一部、璃々のエロを期待した人もいるかもしれませんが、残念ながらというか当然というかwありません。まぁこれは致し方ないですね、流石に。大喬と小喬はロリの範疇に入るのかな?まぁ全体的に見てロリっぽいのが多い気がします……ロリな女の子に殺されまくる大人の男って何なんでしょうねw

ツンデレについて
曹操がまさにツンデレって感じですね。最初始めた時は袁紹なんかも主人公に靡いてツンデレっぽくなるのかな?と思ってたんだけどこちらは結局最後まで主人公とは全く異なる、我が道を爆走してました。というか途中から全く物語的に主人公とは全然関係無い所で勝手に暴れてるだけになるしw

絵について
独特の、癖のある絵だと思いますが総じて綺麗だと思います。ただ、萌える系の絵では決して無いので人を選ぶとも思います。まぁ、慣れればこれはこれで悪くないですよ。

ゲーム性について
このゲームでは話の筋を決める為の選択肢は存在せず、プレイヤーの関与は専らどのヒロインとの仲を進めるか、と後は戦闘シーンでの選択位となっています。で、どのヒロインとの仲を進めるかの部分はまさに直球でヒロインを選ぶだけなのでゲーム性は存在せず、ゲーム性は戦闘シーンの部分だけとなります。この戦闘シーンなんですが、まずプレイヤーが事前に知る事が出来るのは敵将と敵の軍師と敵の数だけ。後はこちらの将軍、軍師、兵数の割合(これは軍師によってある程度定まる)を決めて各ターン毎に使用する陣形と突撃/迎撃/弓兵の3択、あと必殺技が使える場合は必殺技を使うかどうかの選択となっています。問題は、敵将や敵の軍師がわかったところで敵の使用する陣形はわからないという点にあって、ある陣形はこの陣形に強いが別の陣形には弱い、という強弱関係がそれぞれ設定されている訳ですが、まさにじゃんけんのごとく敵の出してくる手が事前にわからない為にこちらも陣形を決める為の情報が全く存在しないという点にあります。相手の手がわからないのでこちらの出す手も適当に決めるしかなく、そうなるとそれはもうただのじゃんけんでしかなくなる訳ですね。まぁ、実際には戦闘ごとに敵の使う陣形は決められている為一度覚えてしまえば後はこちらの使う陣形は一意に決められる訳ですが、こうなるとそれはそれで思考する余地も存在しないという事でやはりゲーム性が無いという事になる。このゲームデザインの問題点は、ユーザビリティを考慮し過ぎた結果ゲーム性が著しく単純な物になってしまい、結果奥も深みも何も無い物になってしまったという点にあるでしょう。ゲーム性を持たせたかったのであればもう少し複雑性を増したデザインにするべきでした。極端なほど単純すぎるターン性SLGが面白い筈が無いのです(何しろ考慮すべきポイント少なすぎて悩む余地が無いのですから)

総評
話の筋は基本的に一本道で、しかも最後の方の展開はちょっとどうよと思うような展開をしてしまいましたが、まぁそれでも全体的にあまり深刻な雰囲気にもならず、深く考えないでサクサク進められる点は疲れている時には丁度良いかもしれません。ただ、どうも話の筋にしてもゲーム性にしてもあまり考えて作られた風には思えない為、製作側も適当に作った量産ゲームの1つ程度の見方しかしてなかったんだろうなぁとも思える部分が多く微妙な感じもします。エピローグはもうちょっと余韻を大切にして欲しかった所ですが、ぶつ切り感の強いエンディングでこの風味に賛同する人はあまり居ないんじゃないかと思います。点数を付けるなら100点満点として、

50点

といったところでしょうか。楽しめた部分も存在はするのでとりあえず50点です。次回作は(も?)様子見でしょうか。
[20070511]
DirectSoundでのストリーミング再生の方法自体は山ほど書籍やWebページがあるのでそれらを見ると判るとして、ストリーミング再生を行う際に大抵の場合はDirectSoundNotifyインターフェイスを通して再生ポイントにイベントを設定し、そのイベントを待ってバッファロック->書き換えという手段をとる事と思います。しかし実際そうやってみると、何故か再生時に別アプリケーションで音が鳴るとそれだけで再生位置がずれるという現象が発生する事があります。詳しい原因はわからないのですが、どうも本来シグナル状態になる筈の無いイベントがシグナルとなってしまっているようで、その為に本来のタイミングと違うタイミングで通知を受け取ってしまいタイミングが狂ってしまうようです。
この現象について少し対応をしてみたストリーミング処理部分を示してみます。

DirectSoundNotifyの問題迂回案

この関数はDirectSoundBufferに音を流し込む為だけのスレッド用関数で、メインスレッドでDirectSoundBuffrを用意した後それをクラスのメンバ変数に設定し、クラスのスタティックメンバ関数であるこの関数を_beginthreadex経由で呼びます。
この関数を少し見ていきますと、WaitForMultipleObjectsでイベントを待っている箇所があります。ここではスレッド終了確認の為に100msでタイムアウトさせ、タイムアウトした場合はメインスレッド側から停止命令が来ていないか確認していますが、タイムアウトでなく、異常値でもなかった場合にはイベントが発生したものとして扱っています。そしてまずチェックその1、DirectSoundNotifyの起こすイベントは再生カーソルの位置をチェックして予め設定した位置に再生カーソルが到達したかどうかで発生させていますので、通常であれば同じイベントが2回連続で起こることはありえません。という事で
ans = ans - WAIT_OBJECT_0 ;
if(ans == lastBlock) {
    //  同じブロックが2回連続する事は無い。
    //  これはこのバッファへの通知ではないので無視する
    continue ;
}
という形でイベントの連続発生チェックを行っています。lastBlockはこの段階ではまだ設定されません。lastBlockは、チェックその2を通った段階で初めて設定されます。
チェックその2は上記の直後にあります、GetCurrentPosition(&playPosition, NULL) ;で再生位置を確認している部分に当たります。この箇所ではDirectSoundBufferの再生カーソル位置をplayPositionに取得し、
イベントで設定した再生位置 < playPosition < 別のイベントでの再生位置
の関係にある事を確認します。これはつまり、起こるべくタイミングでのイベント発生であることを確認しているという事になります。また、仮にこのイベント発生が本来のDirectSoundNotifyの正しいイベント発生による物では無かったとしても問題無いという事にもなります。つまり、起こるべくして起こったタイミングである事を確認する事で、本来のイベントが後から起こってもチェックその1、同じイベントは2度連続で発生しないのチェックに引っかかりそっちは無視される為問題無く再生されるという事です。なお、ここでのチェックではバッファの開始位置からdu->bufferLength/2までの範囲としてチェックしていますが、このbufferLengthはDirectSoundBufferの全体のバッファ長であり、このサンプルではこのバッファを2分割してストリーミング再生している為、半分にしています。notifyの位置についても0とbufferLength/2の位置に設定してあり、上記の長さ分だけを1つのイベント範囲として設定されています。
実際にはこれだけでは不十分なのか、ごく稀にまだ音が飛ぶ事があるようなのですが、しかし実用上は十分ではないかと思います。一度お試しください。
[20070510]
音楽を再生する際、大抵の場合はただ1回再生するだけでなく、再生が終わったら先頭に戻ってループするなどといった処理を入れたくなります。再生が終わったら一旦ov_clearで閉じて再び開きなおすという処理をしてもいいのですが、それよりはシーク処理で先頭に戻す方がコードも簡潔ですし処理も早く済みます。という事でここではOgg Vorbisの場合におけるシーク処理について見てみます。
Vorbisfileを使ってシークする場合、シークAPIは全部で10種類もあったりして何がどうなっているのかさっぱりといった感想を持つかもしれません。まずここでは大まかにどのようなシーク処理があるのかを分類して見ます。
  1. RAWシーク処理
  2. PCMシーク処理
  3. TIMEシーク処理
また、別の分類もあります。
  1. サンプル単位シーク処理
  2. ページ単位シーク処理
更に別の分類もあります。
  1. 単純シーク処理
  2. ノイズ除去機能付きシーク処理
では上から順に見ていきましょう。

・RAWシーク処理
ここで言うRAWというのは、Vorbisで圧縮されたバイト列データの事です。つまりこのシーク処理はVorbisで圧縮されたバイト列の何バイト目にシークするかを指定する為の物です。しかしここで勘違いしてはいけません。あくまでこの処理はVorbisで圧縮されたバイト列単位のシークなので、Oggで分割されたギャップは飛ばされます。つまりファイルポジションと1対1で対応したりはしません。Oggコンテナでは中に入っているデータをページという単位で分けて管理しますが、このページという概念を無視してVorbisデータが1つながりになっていると見た時の、バイト位置へのシークとなります。ただ、正直なところこれをどういった場面で使うのが効果的なのかはよく判りませんw
これに該当するシークAPIは
int ov_raw_seek(OggVorbis_File *vf,long pos);
int ov_raw_seek_lap(OggVorbis_File *vf,long pos);
となります。ページ単位を無視するシークなので、当然ページ単位シークは存在しません。

・PCMシーク処理
PCMサンプル単位でのシーク処理です。サンプル単位とはつまり、バイト数を量子化バイト数*チャンネル数で割った数なわけですが、これは量子化バイト数の指定はov_readでの読み込み時に確定するものである事から考えても妥当ですね。というか、バイト単位でのシークがしたければRAWシークがあるわけで。恐らく一般に使われるシーク処理はこれか、或いはTIMEシーク処理になるんじゃないかと思います。PCMシーク処理としては
int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos);
int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos);
int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos);
int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos);
があります。posは先ほども書いたサンプル単位ですので、例えばある再生タイミングでマークをつけて、その時のバイト数を記録しておき後でその位置にシークしたいといったような場合、実際に指定する値はバイト数ではなくバイト数を(量子化バイト数*チャンネル数)で割った数となります。この時の量子化バイト数は当然、そのバイト数を得る為に使ったov_readの引数ですので2と仮定し、BGMをシークする場合はステレオである事が多いことを考えるとバイト数/4という値が一般的な値になると思われます。

・TIMEシーク処理
再生時間を元にしたシーク処理で、秒単位で指定します。秒単位というと細かいタイミングが指定出来ないじゃないかと思うかもしれませんが、引数の型がdoubleなので実際には0.1秒であろうが0.0001秒であろうが指定出来ます(当然サンプリング周波数より短い単位で指定しても効果はありませんが)。この処理は、実は内部で時間を元にPCMサンプル単位に変換し、PCMシーク処理を呼んでいます。ですのでシークするのであればPCMシーク処理の方が高速であると言えます。TIMEシーク処理としては
int ov_time_seek(OggVorbis_File *vf, double s);
int ov_time_seek_page(OggVorbis_File *vf, double s);
int ov_time_seek_lap(OggVorbis_File *vf, double s);
int ov_time_seek_page_lap(OggVorbis_File *vf, double s);
があります。

・サンプル単位シーク処理
これはつまり、シーク先位置をサンプル単位で調整出来るという意味です。実際にはRAWシークはサンプル単位ではなくバイト単位だし、TIMEシークは秒単位なので厳密に言えばサンプル単位シーク処理としてはPCMシークしか該当しないのですが、とりあえずRAWシークは考えないとしてPCMシークとTIMEシークが該当するものとします。この処理は、実際にはページ単位シーク処理を行った後で更にVorbisで圧縮されたデータ列を順に見ていき該当するサンプル位置を探します。この処理はシーケンシャルに行われる為、終了時間に若干の差異がありますが1ページのデータ量自体はそんなに多くないので誤差程度と言えるかもしれません。しかしそれでもページ単位シーク処理に比べて余計な処理をしている事は確かなので、単純に先頭にシークするだけであればページ単位シーク処理を使った方が処理は早いでしょう。サンプル単位シーク処理に該当するのは
int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos);
int ov_time_seek(OggVorbis_File *vf, double s);
int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos);
int ov_time_seek_lap(OggVorbis_File *vf, double s);
があります。

・ページ単位シーク処理
Ogg VorbisコンテナであるOggでは、ページという概念でデータを分割して格納しています。このページ単位でシーク処理を行うのがこの処理です。しかしページ処理といっても何ページ目、として実際のページ数を指定するのではありません。実際の指定は、シーク先のサンプル位置を指定し、そのサンプル位置の含まれるページまでシークしてそのページの先頭から再生を開始するというものです。指定サンプル位置の含まれるページの先頭にシークする為、この処理では殆どの場合において指定サンプル位置よりも若干早い位置から再生が開始されます(例外として、ページ先頭位置と指定サンプル位置が同じだった場合は指定サンプル位置から再生される事になります)。ページ単位シーク処理としては
int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos);
int ov_time_seek_page(OggVorbis_File *vf, double s);
int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos);
int ov_time_seek_page_lap(OggVorbis_File *vf, double s);
があります。

・単純シーク処理
単純と言ってますが、ノイズ除去機能付きに比べたら単純という意味ですので間違えないよう。これはつまり、シークという言葉から連想されるままの機能です。単純に、次のov_readからの読み込み位置を指定するだけでそれ以外の機能を持たないシークを指します。単純シーク処理としては
int ov_raw_seek(OggVorbis_File *vf,long pos);
int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos);
int ov_time_seek(OggVorbis_File *vf, double s);
int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos);
int ov_time_seek_page(OggVorbis_File *vf, double s);
があります。

・ノイズ除去機能付きシーク処理
音声ファイルを単純シークすると、前回再生時のサンプルの値と新しい再生開始位置におけるサンプルの値との組み合わせによってはクリックノイズと呼ばれるノイズが発生する場合があります。例えば、シーク前の最後に読み込まれたサンプルの値が-32768だったとして、シークされなかった場合は-32760とかいう値だったのが、シークされることによって新しいサンプルの値が32767という値になったとすると、波形のふり幅はシークされなかった場合は8だったのが、シークされる事によって65535という値をとる事になります。ご存知の通り、音量という物は波形のふり幅によって決定される物ですので、極短い単位時間における音量という視点において、シークされることにより音量が極小さな値からいきなり最大まで上がる事になり、これが原因でノイズとなります。これを除去する為に音楽系のソフトなんかでは音を切り替える場面ではクロスフェードと呼ばれる、前回のサンプルの値と新しいサンプルの値を特定の割合でミックスしていき、このミックスの割合を新しいサンプルの値の比率を徐々に上げていく事で急激な音量変化を避ける技術を使ったりして誤魔化すわけですが、このシークは自動的に似たような事をしてノイズを除去します。当然ながらこの処理にはそれなりのCPU負荷を食うので単純なBGM再生等で使うような類の物では無いと思いますが、しかし特殊な用途では便利な機能なのかもしれません。ノイズ除去機能付きシーク処理としては
int ov_raw_seek_lap(OggVorbis_File *vf,long pos);
int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos);
int ov_time_seek_lap(OggVorbis_File *vf, double s);
int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos);
int ov_time_seek_page_lap(OggVorbis_File *vf, double s);
があります。

駆け足でシーク処理を夫々紹介してまいりましたが、恐らく一般的にゲームのBGMで使うのであれば、単純に先頭に戻れれば良いという事でoc_pcm_seek_pageを使うのが最も早くて良いのではないかと思います。あるいはサウンドプレイヤーなんかで使うのであればov_pcm_seekかov_time_seek辺りで良いんじゃないでしょうか?サウンドプレイヤーの場合は或いはノイズ除去機能付きシークでも良いかもしれませんね。
[20070509]
Ogg Vorbisに限らずDirectSoundで音を再生する為には、今から鳴らす音のフォーマットとそのフォーマット情報の設定方法について知っておかなければなりません。Ogg Vorbisの場合について以下にWAVEFORMATEXの設定を記します。この知識はOgg Vorbisに限らずDirectSoundで音を鳴らす場合には必要な知識です。
まず、音を鳴らす為に事前に知っておかなければならないパラメータが3つあります。
  1. サンプリング周波数
  2. 量子化ビット数
  3. チャンネル数
この3つは基本的に音声ファイルによって一意に決定されます(Ogg Vorbisの場合は、量子化ビット数についてはov_readに渡すパラメータで決定されます)夫々が何者なのかについて少し書いてみましょう。

・サンプリング周波数について
サンプリング周波数は、アナログ波形をデジタル化する際に一定時間内にどれ位の時間的頻度で波の大きさを記録するかという、時間軸に関するパラメータです(時間の離散化)。一般にWindowsでこの値を扱う時、単位は秒単位となります。例えば44.1KHzは44100Hzで、数値は44100ですね。この数値の意味は、1秒間に44100回の頻度でサンプルを記録しているという事で、音としては半分の22.05KHzの音まで表現する事が可能です。なぜ半分になるかというとナイキストという偉い人が考案したサンプリング定理というのが出てくるのですがここでは割愛します。とりあえずこのサンプリング周波数の典型的な値としては44100、48000、96000、等がありますがゲーム等でBGMを流す場合には大体44100か或いはその半分の22050になると思います。Ogg Vorbisでこの値を取得する為にはov_open_callbacksを呼んでOggVorbis_File構造体を準備した後、ov_infoを呼びます。
vorbis_info *ov_info(OggVorbis_File *vf,int link);
という事で戻り値にvorbis_info構造体へのポインタが返されます。このポインタは実際にはOggVorbis_File構造体の中のメンバから算出される位置のポインタなので、ov_clearするまでは有効な値として使えますし解放する必要もありません。linkについては基本的には-1を指定しておけば問題ありません。vorbis_info構造体は
typedef struct vorbis_info{
  int version;
  int channels;
  long rate;
  
  long bitrate_upper;
  long bitrate_nominal;
  long bitrate_lower;
  long bitrate_window;

  void *codec_setup;

} vorbis_info;
このように宣言されており、この中のrateがそのものズバリのサンプリング周波数となります。44.1KHzのWaveを圧縮したファイルだったらこの値は44100になります。

・量子化ビット数について
量子化ビット数とは、サンプリングされる波の大きさを表す為に使用されるビット数です。例えばアナログのふり幅の、+の最大から-の最大までを表現するのに量子化ビット数が16だった場合、最大で65536段階の表現が可能という事になります。Windowsで16Bitの量子化ビット数を扱う場合は符号ありの値として扱う事になっていますので、+の最大が32767、-の最大が-32768となります。この数値はov_readに渡す値によって決まる訳ですが、ov_readの使用法説明の時書いたように16Bitで扱うべきです。基本的にOgg Vorbisでの量子化ビット数は8Bitか16Bit、それ以上の量子化ビット数を扱いたい場合はov_read_floatを使用して一旦浮動小数点数としてサンプルを取り出した後、改めて整数値に変換しなおします。これには大きなオーバーヘッドを伴いますので殆どの場合は使用されないでしょう。そして現実的な範囲でのデコードで最も音質の良いのが16Bitという事になります。ですので、以降はOgg Vorbisでデコードしたデータの量子化ビット数は16Bitであるとして扱います。

・チャンネル数
殆どの場合、この数値の意味する所はつまり音がモノラルなのかステレオなのかという事です。規格としては6チャンネル等のサラウンドの音を内包する事が出来ますが、それを再生する環境が整っている確率は結構低いと思われる為、考えるだけ無駄になる可能性が高いと思われます。BGMをOgg Vorbisとして圧縮した場合はステレオつまり2、人物等の音声類をOgg Vorbisとして圧縮した場合はモノラルつまり1となるでしょう。この数値はvorbis_info構造体のchannelsが保持しています。これもそのままの数値、モノラルなら1、ステレオなら2となります。

以上の数値は音質に直接関わってくる数値なのでエンコード時に良く考えて決定しなければなりません。また、音質とCPU負荷はトレードオフの関係にあります。音質を上げるとCPU負荷も上がりますし、CPU負荷を下げようとすると音質も下がりますので、どこかで妥協点を見つけるようにしてください。基本的に1秒間に処理しなければならないデータバイト数は
バイト数=サンプリング周波数*量子化バイト数*チャンネル数
となります。注意しなければならないのは、この計算の時は量子化ビット数ではなく量子化バイト数、つまり量子化ビット数を8で割った値を使うという点です。典型的なBGMの音質、即ち44.1KHz16BitStereoの場合は
44100*2*2=176400
となり、176400Byteのデータを1秒間で処理しなければならない事がわかります。

・WAVEFORMATEXについて
DirectSoundBufferを作る際、DSBUFFERDESC構造体のlpwfxFormatメンバにWAVEFORMATEX構造体変数へのポインタを渡さなければなりません。このポインタはCreateSoundBufferした後は解放して構わないものですが、実際にはクラスのインスタンスメンバ変数として持っておき、lpwfxFormatメンバには&でアドレスを渡すという形にするのが何かと良いのではないかと思います。WAVEFORMATEX構造体は
typedef struct{ 
  WORD  wFormatTag; 
  WORD  nChannels; 
  DWORD nSamplesPerSec; 
  DWORD nAvgBytesPerSec; 
  WORD  nBlockAlign; 
  WORD  wBitsPerSample; 
  WORD  cbSize; 
} WAVEFORMATEX; 
こう宣言されており、この内nChannels、nSamplesPerSec、wBitsPerSampleが上記で出した値そのものと対応します。以下夫々のメンバについて簡単に見ていきます。

wFormatTag。これはこのWaveデータのフォーマットが何であるかを示す変数で、DirectSoundで鳴らす場合は必ずWAVE_FORMAT_PCM(数値は1)でなければなりません。

nChannels。これは上で書いたチャンネル数そのものです。vorbis_info構造体のchannelsをそのまま代入してください。

nSamplesPerSec。これは上で書いたサンプリング周波数そのものです。vorbis_info構造体のrateをそのまま代入してください。

nAvgBytesPerSec。これは上で書いた、1秒間で何バイト処理しなければならないかの値です。この値はOgg Vorbisの場合、vorbis_info構造体の変数名をviとすると
vi.channels * vi.rate * 2
となります。

nBlockAlign。これは1サンプルに要するバイト数です。この数値は一般には
量子化バイト数 * チャンネル数
となります。つまりOgg Vorbisの場合は
2 * vi.channels
となる事になります。

wBitsPerSample。これは量子化ビット数です。Ogg Vorbisの場合は何も考えずに16と代入しましょう。

cbSize。これはこの後に続くデータのバイト数です。WAVEFORMATEX構造体の場合はここで終わりなので0となります。この数値は3チャンネル以上の音を鳴らす場合に必要となるであろうWAVEFORMATEXTENSIBLE構造体を指定する場合に使う物で、殆どの場合は何も考えずに0としても問題ありません。

とりあえず以上でOgg Vorbisファイルから情報を得てWAVEFORMATEX構造体を設定する事が出来るようになる筈です。ここまで出来れば後はDirectSoundの通常の再生とほぼ同じで、バッファをロックしてPCMデータを書き込んでいくだけです。
[20070508]
ov_open_callbacksで開いたOgg Vorbisファイルはov_readでPCMデータとして読み込みます。まずは使用法サンプルを張ります。

ov_readサンプル

サンプルには2つの関数が定義されていますが、これらは夫々PCMデータを取得してくる方法の別バージョンです。

1つ目の関数、getData1。これは恐らく最も単純なPCM読み込みです。ov_readで読み込んだバッファから単純に1バイトずつ返します。クラス内には、Ogg Vorbisでまだ読み込まれていないPCMデータが何Byteあるかを示す変数pcmLeft、まだ使用されていないバッファが何Byteあるかを示す変数bufferLeftがあり、この2つが共に0だった場合はOgg Vorbisファイルの読み込み終了と判断されます。bufferLeftが0の時はバッファにデータが入っていないのでov_readでPCMデータを読んできます。ov_readは読み込んだバイト数を返し、これをbufferLeftの新しい値として保持します。この時、読み込んだバイト数が0だった場合は読み込み終了という事になります。また、マイナスの値だった場合には読み込みエラーとなります。
ov_read(&ovFile, extractBuf, bufferLength, 0, 2, 1, &currentOVSection) ;
上記の説明ですが、1つ目ovFileはOgg VorbisファイルハンドルOggVorbis_File構造体です。これはov_open_callbacksで初期化される物です。2つ目extractBufはPCMデータ読込先のバッファへのポインタです。このバッファはchar *形という事になっています。bufferLengthは、このバッファに読み込む長さという事になっています。但し、実際には4096以上の値を指定しても4096バイトまでしか一度に読み込まれないようです。よく判らないのですが、Ogg Vorbisの1パケットの長さがそれ位の大きさとなっているようです。続く0, 2, 1ですが、これはエンディアンネスの指定、量子化ビット数、符号ありデータか?、を示す値です。エンディアンネスですが、Windowsでのプログラムなら何も考えずに0を指定しておけば間違いありません。続く量子化ビット数、2を指定していますがここでは1か2のどちらかを指定します。1だと量子化ビット数は8Bit、2だと16Bitとなります。サウンドの音質を考えるとこれも何も考えずに2を指定するべきです。もし出来上がりのサウンドの大きさを小さくしたいのであれば量子化ビット数を減らすのではなくサンプリングレートを予め半分にしてエンコードしましょう。その方が殆どの場合は間違いなく高音質になります。そして量子化ビット数が16Bitの時、出来上がるデータフォーマットは符号付として扱われなければならないので符号ありデータの指定は1となります。基本的にこの0, 2, 1という部分は変えない事を推奨します。続く&currentOVSectionですが、実はよく判りません。ただ、Vorbisfile内部では、指定されていた場合はOggVorbis内の何らかのパラメータを代入するという処理を行っているだけで、NULLが指定されていた場合は特に何もしません。サンプルでは一応値を指定していますが、実際にはNULLで構いません。

2つ目の関数fillBufferは、1つ目の関数getData1の問題点を解消したバージョンです。getData1の問題点ですが、この関数は1回呼ばれる毎に1バイトずつPCMデータを返す関数です。非常に単純でバグも起こりにくい構造なのですが、1回で1バイトしか返さないという事でもある為にサウンド再生中に凄まじい回数呼ばれる事になります。具体的には、例えば44.1KHz16BitStereo、つまりCDと同じ音質で再生する場合を考えると1秒間の間に44100*2*2=176400回呼ばれるという事になり、パフォーマンスに大きな影響を与えます。なので、実際にサウンドを再生する際のパフォーマンスを考慮すると一度に大量のデータが扱える方が好ましいと言えます。fillBuffer関数は、渡されたバッファの渡された長さ分を埋めるまで処理を返しません。先ほど書いたようにov_readで一度に読まれるPCMのバイト数は基本的に4096Byteなので、渡されたバッファを埋めるまでov_readを繰り返し何度か呼ぶ構造となっています。実際にはov_readで直接渡されたバッファに読ませるようにしても良いのですがこのサンプルではgetData1と交互に呼ばれても問題の無いようにこういう構造になっています。getData1は実際には使われないであろう事を考慮すると、

ov_readサンプル2

こういう形に単純化出来ます。これ位の長さなら間違いも起き難いですね。
サウンドを再生する際にはov_readでPCM化したデータをサウンドバッファに流し込んでやることで実現出来ます。次回はDirectSoundを使用してov_readから読み込みつつサウンドを再生する方法を見ていきます。
[20070507]
ov_open_callbacksに登録する各種コールバック関数ですが、1で書いたように基本的には標準Cライブラリに含まれている各種ファイル操作関数と互換性のある動作を組めば良いという事になります(SEEKについてだけは何故かFILEポインタのNULLチェックを行うラッパが実装されていますが、これは多分ov_openにシーク出来ない類のビットストリームを渡そうとした時の動作なのだと思います。結局ov_openではFILE以外はまともに扱えないわけですがw)
一応私が実装しているサンプルを以下にリンクします。

ov_open_callbacks関数用コールバック関数実装サンプル

エラー処理はかなり甘い実装なのですが、とりあえず過不足無く動作します。ではまずリード関数から。
ファイルリードコールバック関数ですが、freadと互換性を持たなければならないので本来ならばエラー発生時にはerrnoにエラー番号を入れて0を返さなければならないのですが、ここでは省略しています。サンプルを見ればわかる通り、datasourceをVorbisfileUserクラスのthisとして受け取り、VorbisfileUserクラスのメンバ変数srcFileを用いてWin32の標準ファイルリードAPIにて読み込んでいます。戻り値はファイルの読み込んだバイト数です。エラー発生時には戻り値を0にした後errnoを0以外の値にします(libvorbisfile内部ではerrnoの値の真偽で判断している為、errnoが0だとov_read,ov_pcm_seek等の関数は実際には読み込みエラーが起こっていたとしても、ファイル終了として返します)但し、エラー処理を独自に行っているか、或いは行う気が無いw等の場合には特にerrnoの設定をしなくても構いません。まぁ、オープンが成功しているのに読み込みに失敗するという状況は恐らくかなり特殊な状況じゃないかと思うので、それで良い気もします。
シーク関数。これも基本的にはfseekと同じなので、fseekと同じ反応を返せばそれで良い事になります。1つ気をつけなければならないのは、libvorbisfileは対象としているビットストリームがシーク出来る物であるなら、シーク関数は常に成功する物として扱うという事です。基本的にシークのコールバック関数の戻り値でのエラーチェックはオープン直後の1回しか行われません。以後、オープン時にシークエラーを返した場合はそのビットストリームではシークは行われません。また、オープン時にエラーを返さなかった場合には、シークは常に成功しているものとして扱われます(戻り値をチェックして成否判定をしていません)。その為、同一ライブラリ内でシークの出来る対象物出来ない対象物を同時に扱う場合には注意が必要となります。基本的には、シーク出来る対象物を扱っているのであれば常時0を返すようにしておけば問題ありません。またシーク出来ない対象物(例えばインターネットからダウンロードしているOggVorbisファイルのストリーミング再生等の場合)の場合は常時-1を返すようにします。offsetは64Bit変数ですが、OggVorbisファイルが2Gを越すような状況というのはそう滅多に起こらないと思うので、殆どの場合は32Bit変数にキャストしても問題無いと思います。
クローズ関数。これは事前にオープンしておいたファイル等をクローズさせる為の物です。このコールバック関数はov_clearで呼ばれますので、ov_open_callbacksを呼ぶ時には事前にファイルを開いておかなければなりませんが、ov_open_callbacksでOggVorbis_File構造体を初期化した後は、再生終了時にov_clearを呼べば一々ファイルクローズをする必要はないという事です。但しこれも、別にファイルクローズの機構を設けてはいけないという事ではなく、単にクローズする機会を与えられるだけに過ぎません。独自のタイミングでのファイルクローズをする必要があるシステムの場合は、クローズのコールバック関数では何もしなくても構いません。単に0を返せば良いでしょう。ちなみにこの戻り値も実際には何でも良いようです。
テル関数。これはつまり、現在のファイルポインタを返す関数です。実はこの関数は一番呼ばれる機会の少ない関数で、実際にはオープン直後に1回呼ばれるだけで後は一切呼ばれません。libvorbisfileは内部で独自のファイルオフセット情報を持っている為殆ど必要としないようなのですね。ただ、シーク関数でシーク可能として0を返している状況だとtell関数もちゃんとした動きをしないと、いきなりファイルが壊れていると判断されて再生失敗という事になります。逆に、シーク関数でシーク不可能として-1を返している場合はtell関数は呼ばれませんので、その場合にはov_open_callbacksに登録する際にnullを登録しても問題無い事になります。
とりあえずこの4種類の関数を実装してやる事でov_open_callbacksが使える事になります。例えばアプリで使うOggVorbisファイルを全て1ファイルにパックして、パックされたファイルのOggVorbisを再生したい場合なんかだとこれらの知識を持っていると役に立つでしょう。
次回はPCMサンプルの読み込み処理周りをやってみます。
[20070507]
Vorbisfileを用いてOgg Vorbisファイルを再生する場合、プロジェクトにはlibogg.lib、libvorbis.lib、libvorbisfile.libの3つのLibをリンクさせる必要があります。また、EXEのあるディレクトリに成果物の3つのDLLを配置しておかなければなりません。まぁこの辺りは基本中の基本なので一々言うまでもないですね。
vorbisfileを使用するにはまずvorbis\vorbisfile.hとvorbis\codec.hをincludeした後、OggVorbis_File構造体をグローバル変数、或いはクラスのメンバ変数として宣言しておきます。この構造体はVorbisfileライブラリを使用する際に使われるハンドルなので、VorbisfileライブラリのAPIを使用する全ての箇所で同一のインスタンスを参照出来なければなりません。
基本的にはVorbisfileでOgg Vorbisファイルの使用を開始する際にはov_openを使用することになっていますが、しかしWindowsで再生するのであればov_open_callbacksを使用した方が何かとトラブルが少ないのではないかと予想します。vorbisfileのソースはlibvorbis\lib\vorbisfile.cがそうなのですが、これを開いてov_openを見てみると
int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){
  ov_callbacks callbacks = {
    (size_t (*)(void *, size_t, size_t, void *))  fread,
    (int (*)(void *, ogg_int64_t, int))           _fseek64_wrap,
    (int (*)(void *))                             fclose,
    (long (*)(void *))                            ftell
  };

  return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks);
}
こうなっているのがわかると思いますが、これはつまりov_openで開いても各種コールバック関数を標準C関数に設定してov_open_callbacksを呼んでいるだけですので、逆に言えば標準C関数の代わりの関数を用意したり、或いは同じく標準C関数をコールバック関数として登録してov_open_callbacksを呼んでやればov_openを呼んだのと全く同じ結果を期待出来るという事になります(libvorbisfile.cのバージョンにも拠ると思われますが。この記事は2007/5/7現在の最新linvorbis-1.1.2を基準に書かれています)
ov_open_callbacksはプロトタイプで

int ov_open_callbacks(void *datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks);

こう宣言されています。この内1番目の引数、datasourceですが、これがov_openを使う時に渡されるFILE *に相当します。例えばVorbisfileを扱うクラスを作成するとして、そのクラス内でov_open_callbacksを呼ぶ時にはここにthisを入れることで各種コールバック関数内でクラスのインスタンス変数にアクセス出来るようになります。例えば、

ov_open_callback使用例

こんな感じでしょうか。登録している各種コールバック関数は
static size_t readFunc(void *ptr, size_t size, size_t nmemb, void *datasource) ;
static int seekFunc(void *datasource, ogg_int64_t offset, int whence) ;
static int closeFunc(void *datasource) ;
static long tellFunc(void *datasource) ;
このように宣言されています。これらはクラスのprotected staticメンバ関数として宣言されていますので、各クラス内のprivate変数にアクセスする事が出来ます。
長くなったので一旦記事終了。次回は各種コールバック関数の実装の詳細について見ていきます。
[20070506]
とりあえずOgg Vorbisファイルの再生ですが、Vorbisfileを使用する事でかなり簡単に再生させる事が出来るようになります。
まず、今現在において、初めてOgg Vorbisの再生に挑戦してみようとした人がまずしなければならない事はXiph.orgのOgg Vorbisライブラリダウンロードページからliboggとlibvorbisの最新版ライブラリを落としてきて、展開し、夫々をコンパイルする事です。バイナリは付属していませんので必ずコンパイルが必要になりますが、この時liboggの方からコンパイルしなければなりません。libvorbisライブラリはlibogg.libをコンパイル時に必要としますが、バイナリが付属していない関係上当然これはliboggをコンパイルするまで入手出来ませんので。兎に角そういうわけでliboggライブラリを展開し、中のWin32ディレクトリの更に中にあるVS2003\liboggディレクトリを見てみますとlibogg.vcprojが1つだけあります。このファイルはVisualStudio.NET 2003用のプロジェクトファイルですが、VisualStudio2005で開くと自動的にVisualStudio2005用のプロジェクトにコンバートされます。とりあえずここではこのままVisualStudio2005でコンパイルする事を前提に話を進めてみます。で、liboggは大抵の場合、特に何の問題も無くコンパイル完了すると思います。ソリューション構成はとりあえずReleaseかRelease_SSE辺りでいいんじゃないでしょうか?Release_SSE2だと、まだSSE2に対応していないCPUを使用している層も少なくない数居ると予想されますので互換性の面で問題がありますし。でliboggは良いんですが問題はlibvorbisです。liboggと同じように、libvorbisでもWin32の中のVS2003\libvorbisディレクトリでコンパイルを試みるわけですが(ここでも順番がありまして、libvorbisfileはliboggとlibvorbis両方を使用していますので、まずlibvorbisをコンパイルする必要があります)普通にliboggと同じ感覚でコンパイルしようとするとコンパイルエラーが21個ほど出てくると思います。これは必要なincludeファイルが開けなかったというエラーなんですが、実は落としてきたlibvorbisライブラリに含まれているincludeディレクトリには、libvorbisに関連するヘッダしか含まれておらず、libogg関連のヘッダをliboggライブラリのディレクトリからコピーしてくる必要があるのでした。というわけで、libogg\include\oggディレクトリを丸ごとlibvorbis\includeディレクトリにコピーしてきます。すると今度は山ほどのコンパイル時のワーニングが出てきますがこの辺りは無視してもいいです。使用されているCライブラリよりもセキュリティ性の高いライブラリがあるからそっち使え、という警告ですのでバッファオーバーラン等によるハッキングが心配だって人は、まぁ警告に従って書き換えにチャレンジしてみるのも良いかもしれませんね。とりあえずここでは警告は無視します。で、リンク時に未解決のシンボルエラーが出ると思うんですがここで先ほどliboggをコンパイルした時に出来たlibogg.libをlibvorbis\win32\VS2003\libvorbisディレクトリにコピーしてきまして、VisualStudioのプロジェクトのプロパティ、リンカの入力の追加の依存ファイルでlibogg.libと指定してやればOKです。再度ビルドすると今度は通ります。同じ要領で今度はlibvorbisfileをコンパイルするんですが、libvorbisfileではlibogg.libとlibvorbis.libの2つのlibファイルを指定する必要があります。とりあえずコンパイル時のワーニングは全部無視で構いません。Ogg VorbisライブラリはWindows、というかVisualStudioでコンパイルすることだけを前提にしたライブラリではなくLinux環境やMac環境などでも同じようにコンパイルする事が出来るように書かれている為、どうしてもそういったワーニングは出てしまいますので。
以上のようにしてやる事で、libogg.lib、libvorbis.lib、libvorbisfile.libの3つのlibファイル及びlibogg.dll、libvorbis.dll、libvorbisfile.dllの3つのDLLが手元に出来上がります。次回はこのlibvorbisfileを使ったOgg Vorbisの再生について簡単に書いてみたいと思います。
[20070505]
いや、ハーレムエンドって感じはあんましないけどな。寧ろ袁紹が目立ってたし。
それはともかく、まぁ他のエンドに比べたら一応まだある程度の余韻は感じられた。正直まだまだ物足りなくはあるんだけど、まぁ他のエンドに比べりゃマシって程度にはなってるな。しかし、やはり最後は結果だけ書いて終わってしまった。物語は、始めがあって経過があって終わりがあるものだし、それは物語中に挿入されるエピソードについても同じで、一つ一つのエピソードについてもやはり基本的には始めがあって経過があって終わりがある、という形が望ましい。しかしこの作品だと始めと終わりだけで経過が無い、とかそんなんばっかで経過を楽しむ事が出来ない。まぁハーレムエンドだと一応経過もほ~~~~んの少しだけ書いてはあったけれど、それでも全然全く足りない分量で、しかもまた話す機会があったら云々とか言ってるし。それってつまりファンディスク出すから買えよなって事なのだろうか?いやまぁ、出たら買うのかもしれんが、ちょっとあざとすぎやしないか?という気がする。何せ、1つの物語として入っているべき内容を別売りにするって事なんだから。
[20070504]
久々にエロゲプレイの日記でも書いてみようかw。うな天を放置して、今プレイしてるのは恋姫†無双。まぁ、ちょっと時期が古くなった感もあるけど気にしない。
このゲーム、ジャンルはADVに分類されてるし実際そうだと思うんだけど、所々にミニゲームというか、戦争シミュレーションまではいかないけどそんな感じの戦闘が挟まれている。正直中途半端な代物で、しかも2週目以降だとスキップ出来る仕様になってるのでなんでこんなもん入れたのかもよくわからん物なんだけど、これのせいでADVというよりはSLGもどきと言った方が良い物になっていると思う。選択肢も、途中挟まる拠点画面においてどのヒロインの所に行くか?だけでこれは言ってみればヒロインルート選択そのものだから攻略要素としてはシナリオ攻略じゃなくてこのSLGの攻略がメインになってしまうし。ただ、変に難しくなく誰にでもクリア出来る(事実、もし負けたとしてもコンティニューしてやりなおせば味方の兵数が増えて再戦出来るので、続けていればいずれ必ず勝てるようになる)難易度の、シミュレーションっぽい感じにした事自体は正解なように思える。最初からSLGとして出していたのであれば兎も角、ADVと銘打って出していながら本格的な、ソフトハウスキャラばりのバリバリのSLGを付けられてもこちらはそんな事想定してないわけで。だからADVに挟むゲームならこれ位で良いと思う。もしかしたらヒロイン選択だけで進むゲーム性があまりに単調すぎるという理由で付けられた物かもしれないし、それは確かにその通りだとも思うので。
シナリオは良くも無く悪くも無く、普通といったところか。一部、独占好きには気になる部分もあるにはあるが、軽度の独占好きなら軽くスルー出来る程度の物で、少なくとも私はそんなに気にならなかった(張遼と関羽とのイベントなんかはもやもやする人もいるのかもしれないが、まぁ私はそんなのもアリかな?程度に思った。長引くイベントでもない、その場限りの物だし)。ただ、まだハーレムエンドは到達していないが、関羽エンド、張飛エンドを見た限りではエピローグがあまりにもあっさりし過ぎているのがちょっとどうかなぁという気がする。うな天もそうなんだけど、お話という物においてエピローグというのはとてもとても重要な物で、これがどう作られるか如何によってそれまでのシナリオが生きたり死んだりするんだけど、少なくともうな天も恋姫†無双もその辺りについて何も考えられていないように思えて仕方が無い。私的に考えるに、名作の条件の1つとして、エピローグが素晴らしいというのがあると思うんだけど、どうもこの2作におけるシナリオライターはこの辺りへの配慮が足りないのではないかと思えてしまう。エピローグはつまり、余韻の引き方なわけなんだけど余韻が無茶苦茶というのは食べ物で言えば後味が最悪な食べ物という事であり、いくら食べている最中が美味しいからといって後味が最悪な食べ物を好んで食べようとはしないわけで、その辺りもうちょっと考えて欲しいところである。まぁでも昨今ではかなり珍しいロリ成分多目のゲームでその辺りは貴重かな、という気もします。とりあえずハーレムエンドまで見て、それからもう一度感想を書く気になるようなら書いてみよう。
[20070502]
ある人に言わせれば、プログラム時にコードを書くこともまた設計の一部だそうだけれど、それはそれとしてやはりインターフェイス設計と実装とは違う物であるのは確かだと思う。オブジェクト指向で再利用が叫ばれて久しいけれど、昔読んだ本でこういう内容の事が書いてあった。曰く、オブジェクト指向で再利用出来るのはコードではなく設計であると。まぁここでいう設計というのはつまりインターフェイスの事になると思うんだけど、それを読んだ時はじゃあ再利用の意味って殆ど無いんじゃね?とか思ったものだ。だけどそれは私が浅はかなだけだったようだ。実際、コードの詳細部分というのはプロジェクト毎に書き直した方が何かと都合が良い場面が多かったりするのだが(詳細部分はプロジェクト毎に異なっている場合が多い為。ライブラリとして存在するコードは無論別だが)オブジェクトそのもののインターフェイスが予めある程度決まっているとかなり進めやすかったりする。とりあえずインターフェイスさえ確定していれば詳細部分はとりあえずスタブコードを置いておくことで、かなり初期段階からテストを行う事も出来るようになるし全体の動作チェックにかなり役に立つ。昔はそういうところにまでは気が回らなかったが、インターフェイスの再利用というのはコードの再利用よりも実は価値があるのかもしれない。そして、だからこそオブジェクト指向という技術がどんどん進化していき、オブジェクト指向言語がトレンドになり、UMLなんてものも出てきているわけだ。この辺りは正直、8BitCPUでアセンブラをガリガリ書いていた頃から考えると考え方に随分違いが出てきているんだけど、結局のところ小手先の技術は今はもうそれ程重要ではなくなり、寧ろ実装効率を高める技術こそが必要になってきたという事なのだろう。昔は、如何にしてメモリ消費を抑え、如何にして少ないメモリでやりくりするかという点で四苦八苦していたものだが今は携帯電話のJavaプログラムですらプログラムサイズの制限からは開放されつつある。
無論、信頼出来る、十分にテストされたコードというのはそれはそれで価値がある物だし、ライブラリ化出来るならするべきなのだろうとは思うけれど、必ずしもそれが次のプロジェクトの似たような場面でも使えるとは限らない、というよりそのままでは使えない場面が往々にしてあり、結局コードに手を加えなければならない羽目になって今までの信頼性に水を差すような場面も少なくないのだけれどインターフェイスの場合はその可能性はかなり減らす事が可能だったりする。だから最近の私は、まず使えるインターフェイスを探して取り込んできて、その中にコードをコピペしてくる事が多い。コピペだと結構単純ミスで後々バグの原因になる事も多かったりはするんだけど、とりあえず即効動かす役にはたつ。つまりコードもインターフェイスも再利用はしてるんだけど、コードの再利用性自体はオブジェクト指向の恩恵には与っていないという事になる。何しろオブジェクト指向におけるコードの再利用性はクラスの継承によるのが基本であって、コピペなんてするもんじゃないという事になっているからな。だけど継承によるコードの再利用は詳細部分のブラックボックス性が高すぎるせいで、後々のバグの原因になった際に発見するのが遅れるのだ。情報の隠蔽こそがクラス化、カプセル化のやりたい事ではあるんだけれど、隠蔽しすぎると後でその情報が必要になってしまった時に困る事になるという事だ。何しろどんなに信頼性の高いコードであっても、どんな状況であっても100%信頼し切る事が出来るコードなんていうのは余りに少ない、というか無いといっても良いものなのだから。
HOME
copyright © 2005 drednote(Mr.Ty) all rights reserved.

Template By innerlife02

RSS1.0 ,
RSSフィード

応援バナー

検索フォーム

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。