/*	
 *	SofdecHelper.c
 *
 *	Sofdecヘルパー関数
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2016-2017 Naoyuki Sawa
 *
 *	* Fri Apr 01 21:48:44 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	- 使用したコンパイラとライブラリは、以下の通りです。
 *	  │Microsoft Visual Studio Community 2015 Version 14.0.25123.00 Update 2
 *	  │CRI ADX2 SDK 2.08a (cri_adx2_sdk_pc_v2_08a_j.zip)
 *	  │CRI Sofdec2 SDK 2.08 (cri_sofdec2_pc_v2_08_j.zip)
 *	* Sat Apr 02 15:05:45 JST 2016 Naoyuki Sawa
 *	- 同時再生するムービーの数を、固定ではなく、SofdecHelper_Init()の引数で指定出来るように変更しました。
 *	- プレイヤーid=(管理用構造体のインデクス+1)としました。
 *	  アプリケーションの観点から、プレイヤーidには0を使わない方が使い易いと思ったからです。
 *	- SofdecHelper_Play()を、SofdecHelper_Load()とSofdecHelper_Play()に分離しました。
 *	  SofdecHelper_Stop()を、SofdecHelper_Stop()とSofdecHelper_Free()に分離しました。
 *	  この変更により、一回のSofdecHelper_Load()～SofdecHelper_Free()の間に、複数回のSofdecHelper_Play()～SofdecHelper_Stop()を実行出来るようになりました。
 *	* Mon Apr 04 21:27:10 JST 2016 Naoyuki Sawa
 *	- プロジェクトのプロパティを変更しました。
 *	  │【変更前】
 *	  │・構成=Debug  ,プラットフォーム=すべてのプラットフォーム:[C/C++]⇒[ランタイムライブラリ]⇒マルチスレッドデバッグDLL(/MDd)
 *	  │・構成=Release,プラットフォーム=すべてのプラットフォーム:[C/C++]⇒[ランタイムライブラリ]⇒マルチスレッドDLL(/MT)
 *	  │【変更後】
 *	  │・構成=Debug  ,プラットフォーム=すべてのプラットフォーム:[C/C++]⇒[ランタイムライブラリ]⇒マルチスレッドデバッグ(/MTd)
 *	  │・構成=Release,プラットフォーム=すべてのプラットフォーム:[C/C++]⇒[ランタイムライブラリ]⇒マルチスレッド(/MTd)
 *	  変更した理由は、以下の通りです。
 *	- 前回までは、SofdecHelper.dllを使うプログラムを実行しようとした時に、「vcruntime140.dllが無い」というエラーが出る事が有りました。
 *	  SofdecHelper.dllを、VC++2015のDLL版Cランタイム(vcruntime140.dll)を使う設定で、ビルドしてしまっていたからです。
 *	  手作業で、VC++2015の再配布可能ランタイムライブラリがインストールしないと、上記のエラーが解決出来ない問題が有りました。
 *	  これまでexeをビルドする時はVC++のDLL版Cランタイムでなくスタティックリンクするように気を付けていたのですが、dllのビルドはあまりしなかったので今回うっかり忘れていました。
 *	- 変更後は、vcruntime140.dllに依存しなくなったので、vcruntime140.dllがインストールされていない環境でも、エラーは出なくなりました。
 *	  ちなみに、SofdecHelper.dllがvcruntime140.dllに依存しているかどうかは、以下のコマンドで確認出来ます。
 *	  │vcvars32.bat				VC++6.0のbinにパスを通す。
 *	  │dumpbin.exe /DEPENDENTS SofdecHelper.dll	依存しているdllファイル名だけ確認する場合はこちら。
 *	  │,又は,
 *	  │dumpbin.exe /IMPORTS SofdecHelper.dll	依存しているdllファイル名とインポートしている関数も確認する場合はこちら。
 *	- SofdecHelper_Load()が呼び出す関数を、criManaPlayer_Prepare()⇒criManaPlayer_DecodeHeader()に変更しました。
 *	  変更した理由は、SofdecHelper_Load()内のコメントを参照して下さい。
 *	* Wed Apr 06 21:47:40 JST 2016 Naoyuki Sawa
 *	- ムービーファイルをパッキングファイルに含められるようにしました。
 *	  又は、これまで通り、ムービーファイルをパッキングファイルに含めない事も可能です。
 *	  さらに、パッキングファイルに含めたムービーファイルと、含めないムービーファイルを、併用する事も可能です。(マルチバンイド)
 *	- パッキングファイルのファイル名は、'default.cpk'固定です。
 *	  詳細は、SofdecHelper_Init()内のコメントを参照して下さい。
 *	- user_error_callback_func()が呼び出された時の処理を変えました。
 *	  【変更前】デバッグビルドでも、リリースビルドでも、die()で終了していた。
 *	  【変更後】デバッグビルドでは、これまで通りdie()で終了する。リリースビルドでは、メッセージボックスを表示して終了する。
 *	  変更した理由は、まだSofdec2の挙動を十分に理解出来ていないので、リリースビルドのDLLを使用中にエラーが発生する可能性も高いからです。
 *	  リリースビルドのDLLを使用中にエラーが発生した時も、なるべくエラー情報が得られるように、メッセージボックスを表示するようにしました。
 *	* Wed Apr 06 21:53:01 JST 2016 Naoyuki Sawa
 *	- カレントフォルダに'default.cpk'が有るかどうかを調べるために、stat()ではなくGetFileAttributes()を使うように変更しました。
 *	  変更した理由は、VC++2015のランタイムのバグのために、WinXpではstat()が常に-1を返すからです。
 *	  詳細は、下記の資料を参照して下さい。(下記の資料は_stat()について述べていますが、stat()でも同じです。
 *	  尚、2016/04/06現在のVC++2015 Update2でもまだ直っていませんでした。
 *	  □「Stack Overflow - Visual C++ 2015 express: _stat not working on Windows XP」
 *	  │(http://stackoverflow.com/questions/32452777/visual-c-2015-express-stat-not-working-on-windows-xp)
 *	  │As pointed out in the comments, it is a bug in the runtime.
 *	  │Right now (2015-09-09) the fix is not yet available in an update, but probably will be soon.
 *	  │A workaround is to use GetFileAttributesEx instead.
 *	* Thu Apr 07 21:58:31 JST 2016 Naoyuki Sawa
 *	- USE_ADXを定義有りに変更しました。
 *	  定義無しだと'CRI Serve,'スレッドの中でアクセス違反で落ちる事が有ったからです。
 *	  確かにサンプルプログラムは全てcriAtomEx_Initialize_WASAPI()を使用しており、criAtomEx_Initialize()を使用しているサンプルプログラムは有りません。
 *	  これが原因と確定した訳では有りませんが、定義有りにすると今のところ落ちないので、何か関連は有りそうです。
 *	  現在の所サウンドはDxLibで行うから、ADXは無効にしたいのですが仕方がありません。
 *	  まだサウンド付きムービーを使用していないので、もしDxLibのサウンド処理と衝突した時にどうなるか未検証です。(ADXはOnでCRIMANA_AUDIO_TRACK_OFFだけ設定すれば良いのかな?)
 *	- USE_ADXを定義有りに変更した事により、WindowsXpでは動作しなくなりました。	←←←←←←←←←←■■■■■注意■■■■■
 *	  USE_ADXを定義するとcriAtomEx_Initialize_WASAPI()を呼び出し、criAtomEx_Initialize_WASAPI()はDirectX11に依存しているので、DirectX9までしかインストール出来ないWindowsXpでは初期化エラーになるからです。
 *	  USE_ADXを定義しなければcriAtomEx_Initialize()を呼び出し、criAtomEx_Initialize()はDirectXに依存していないのでWindowsXpでも動作するのですが、criAtomEx_Initialize()には上記の問題があるため、USE_ADXを定義してcriAtomEx_Initialize_WASAPI()を使わざるを得ません。
 *	* Fri Apr 15 23:25:13 JST 2016 Naoyuki Sawa
 *	- エラー停止時に表示するMessageBox()のタイプを、MB_SYSTEMMODAL⇒MB_TASKMODALに変更しました。
 *	  MB_SYSTEMMODALではなくMB_TASKMODALが適切です。
 *	  名前とは裏腹にアプリケーションのスレッドに属する全てのウインドウにフォーカスを移せなくするのはMB_SYSTEMMODALではなくMB_TASKMODALの方です。
 *	* Wed May 04 21:28:21 JST 2016 Naoyuki Sawa
 *	- AdxHelper関数群を追加しました。
 *	  一応基本的な動作確認は行いましたが、まだ十分に検証出来ていません。	⇒{{2016/05/16追記:しばらく使ってこれまでの所問題無く動作しています。}}
 *	* Mon May 16 21:56:02 JST 2016 Naoyuki Sawa
 *	- カレントフォルダに有る全ての'*.cpk'(有れば)にバインドするように変更しました。
 *	  これまではカレントフォルダに有る'default.cpk'(有れば)だけにバインドしていました。
 *	  変更後も'default.cpk'は自動的にバインド対象になりますので、既存のアプリケーションに対する互換性の問題は生じません。
 *	* Thu May 19 22:52:12 JST 2016 Naoyuki Sawa
 *	- 警告は無視するようにしました。
 *	  詳細は、user_error_callback_func()内のコメントを参照して下さい。
 *	- SofdecHelper_Init()のサウンドの初期化処理の箇所に、CriAtomExDbasConfigを明示的な指定についてコメントを追記しました。
 *	  要検討であり、まだコードは変更していません。
 *	* Thu Jul 28 21:15:13 JST 2016 Naoyuki Sawa
 *	- ライブラリをバージョンアップした事に伴い、当モジュールもリビルドしました。
 *	  当モジュールのソースコードには変更有りません。
 *	- 使用したコンパイラとライブラリは、以下の通りです。
 *	  │Microsoft Visual Studio Community 2015 Version 14.0.25123.00 Update 2	←前回と同じ
 *	  │CRI ADX2 SDK 2.09 (cri_adx2_sdk_pc_v2_09_j.zip)				←バージョンアップ
 *	  │CRI Sofdec2 SDK 2.09 (cri_sofdec2_pc_v2_09_j.zip)				←バージョンアップ
 *	* Fri Jul 29 21:34:34 JST 2016 Naoyuki Sawa
 *	- SofdecHelper_CopyFrameYUV()を追加しました。
 *	  まだ当モジュールを利用する描画側がYUVの描画に未対応なのですが、まずは当モジュール側でYUV形式で返せるようにしておく事にしました。
 *	  YUV形式を試してみようと思った理由は、以下の通りです。
 *	- 2016/07/29現在、大きなムービーを再生すると、極端に遅くなる問題が有ります。
 *	  まだ原因が見つけられていないのですが、criManaPlayer_CopyFrameToBufferARGB32()が遅い可能性も有ると考えて、criManaPlayer_CopyFrameToBuffersYUV()を使うと高速化できるかどうかを検証するために、SofdecHelper_CopyFrameYUV()を追加しました。
 *	  もし上記が関係無かったとしても、SofdecHelper_CopyFrameYUV()を追加した事によるその他の機能への影響は無いので、SofdecHelper_CopyFrameYUV()は残しておいて問題ありません。
 *	* Fri Jul 29 22:19:58 JST 2016 Naoyuki Sawa
 *	- Visual Studio 2015をバージョンアップした事に伴い、当モジュールもリビルドしました。
 *	  当モジュールのソースコードには変更有りません。
 *	- 使用したコンパイラとライブラリは、以下の通りです。
 *	  │Microsoft Visual Studio Community 2015 Version 14.0.25424.00 Update 3
 *	  │CRI ADX2 SDK 2.09 (cri_adx2_sdk_pc_v2_09_j.zip)				←前回と同じ
 *	  │CRI Sofdec2 SDK 2.09 (cri_sofdec2_pc_v2_09_j.zip)				←前回と同じ
 *	* Sat Jul 30 20:01:40 JST 2016 Naoyuki Sawa
 *	- SofdecHelper_CopyFrameYUV()を呼び出すアプリケーションが、アルファ付きムービーであるかアルファ無しムービーであるかを意識しないで済むようにしました。
 *	  詳細は、SofdecHelper_CopyFrameYUV()関数内の、同日のコメントを参照して下さい。
 *	- SofdecHelper_GetAlpha()を追加しました。
 *	  本日、SofdecHelper_CopyFrameYUV()を呼び出すアプリケーションが、アルファ付きムービーであるかアルファ無しムービーであるかを意識しないで済むようにしましたが、
 *	  アルファ無しムービーの場合はa_imagebuf引数にNULLポインタを指定する方がSofdecHelper_CopyFrameYUV()のフィル処理が省けて高速になるので、
 *	  アプリケーションがアルファ付きムービーであるかアルファ無しムービーであるかを区別出来るように、SofdecHelper_GetAlpha()を追加しました。
 *	* Sat Jul 30 21:25:46 JST 2016 Naoyuki Sawa
 *	- SofdecHelper_GetFrameYUV()を追加しました。
 *	  用途はSofdecHelper_CopyFrameYUV()と同じで、どちらか一方を使用して下さい。
 *	  SofdecHelper_GetFrameYUV()の方が、メモリコピーが減るので、高速化が期待出来ます。
 *	- 当関数を追加したきっかけは、\cri\pc\include\cri_mana.hのCriManaFrameInfo構造体定義を見ていた時に、image_info[4]フィールドを直接参照出来る事に気付いたからです。
 *	  Sofdec2のAPI「criManaPlayer_DiscardFrame()」は、上記のフィールドをそのままコピーしているだけではないかと思います。
 *	  そうだとすると、上記のフィールドを直接参照する方が、メモリコピーが減って、高速化が期待出来ると思いました。
 *	  ただし、そのような使い方をして良いかどうかがマニュアルに載っていないので、もしかすると今後のライブラリバージョンアップ時に、問題が生じる恐れは有ります。
 *	- SofdecHelper_GetFrameYUV()は、上記のフィールドを直接返すのではなく、アプリケーション定義のコールバック関数を呼び出すようにしました。
 *	  そうした理由は、参照終了後にフレームを解放(criManaPlayer_DiscardFrame())する必要が有るからです。
 *	  明示的にフレームを開放する関数を追加する方法も有りますが、そうすると既存のSofdecHelper_CopyFrame()やSofdecHelper_CopyFrameYUV()と使い方が変わってしまうので、
 *	  同じ使い方を維持するために、アプリケーション定義のコールバック関数を呼び出すようにしました。
 *	  アプリケーション定義のコールバック関数が処理を返した後に、SofdecHelper_GetFrameYUV()が自動的にフレームを解放します。
 *	* Mon Aug 01 21:12:56 JST 2016 Naoyuki Sawa
 *	- Sofdec2ムービー,及び,ADX2サウンドのシームレス連続再生に対応しました。
 *	  この変更に伴い、SofdecHelper_Init()の引数仕様を変更しました。
 *	  シームレス連続再生を利用するアプリケーションは、ST_SofdecHelperInfo構造体のfnGetNextMovie,及び,fnGetNextSoundに関数ポインタを指定して、SofdecHelper_Init()を呼び出して下さい。
 *	  詳細は、ST_SofdecHelperInfo構造体定義箇所のコメントを参照して下さい。
 *	* Sat Aug 06 21:14:59 JST 2016 Naoyuki Sawa
 *	- Visual Studio 2015をバージョンアップした事に伴い、当モジュールもリビルドしました。
 *	  当モジュールのソースコードには変更有りません。
 *	- 使用したコンパイラとライブラリは、以下の通りです。
 *	  │Microsoft Visual Studio Community 2015 Version 14.0.25425.01 Update 3
 *	  │CRI ADX2 SDK 2.09 (cri_adx2_sdk_pc_v2_09_j.zip)				←前回と同じ
 *	  │CRI Sofdec2 SDK 2.09 (cri_sofdec2_pc_v2_09_j.zip)				←前回と同じ
 *	* Mon Aug 08 21:14:30 JST 2016 Naoyuki Sawa
 *	- 特定のエラーを無視出来るようにしました。
 *	  これまでは、エラーは全て即停止していましたが、無視しても動作可能なエラーも有る事が判ったので、特定のエラーは無視出来るようにしました。
 *	  今回、無視するようにしたエラーはE2010052705だけですが、今後、無視するエラーを追加する事も可能です。
 *	  無視するエラーを追加する場合は、TBL_IgnoreErrid[]に無視するエラーのエラーIDを追加して下さい。
 *	- E2010052705を無視するようにしました。
 *	  理由は、以下の通りです。
 *	  これまで長い間、「ビットレートの高いムービーを複数同時に再生すると、E2010052705が通知されてプログラムが停止する問題」に悩まされていました。
 *	  criAtomExDbas_Create()のパラメータを調整しても解決せず、どうやらSofdec2内部のバッファサイズの制約みたいだという結論に達していました。
 *	  そして、E2010052705はエラーだから、再生しようとしたムービーは再生開始していないと思っていたので、プログラムを停止していました。
 *	  □(C:\cri\documentation\CRI_Error_Manual_pc_j.chmより引用)
 *	  │E2010052705
 *	  │エラーID		E2010052705
 *	  │エラーメッセージ	"too high bit-rate. (Decrease max_bps of CriAtomDbasConfig under %u.)"
 *	  │発生ライブラリ	CRI Base
 *	  │発生状況		ライブラリ内部で制御しているストリーミングバッファのサイズ上限が超えてしまった場合に発生するエラーです。
 *	  │解決方法		ストリーミングの同時再生本数を減らすか、総ビットレートのピークを下げるようにデータ側を調整してください。
 *	  │			例えばSofdec2ムービについてはエンコード時のビットレート、解像度を落として拡大表示するなどの対応を行って下さい。
 *	  しかし、history_crimana_pc_j.txtを読んでいた所、E2010052705は無視しても大丈夫そうだという事に気が付きました。
 *	  □(C:\cri\pc\docs\jpn\history_crimana_pc_j.txtより引用)
 *	  │[2014-09-26 CRI Mana Ver.2.01.00]------------------------------------------
 *	  │(1) 仕様変更
 *	  │    (a) ビットレート上限を突破しても、ストリーミング再生を却下しないようにように変更しました。
 *	  │        ただし、理論的にはストリーミングバッファが足りない状態になるため、動画再生がカクつく可能性があります。
 *	  具体的には書かれていないのですが、ビットレート上限を突破したエラー(E2010052705)を無視しても、再生は行われると言っているように取れます。
 *	  そこで、E2010052705を無視して見たところ、再生しようとしたムービーは再生開始されている事が判りました。
 *	  場合によっては多少カクつくという、見た目の問題しか発生しません。
 *	  実際の所、ビットレート上限を突破する主なケースとしては、ムービーの切り替え時に前のムービーと次のムービーが一瞬同時に再生されて突破するケースが多いです。(本当にたくさん同時再生し過ぎて突破する事も無い訳では無いが)
 *	  そのようなケースで一瞬カクついても、全体の見た目にはさほど支障は有りません。
 *	  従って、E2010052705は無視する事にしました。
 *	  今回の変更により、「ビットレートの高いムービーを複数同時に再生すると、E2010052705が通知されてプログラムが停止する問題」が解決しました。
 *	* Mon Aug 08 21:51:29 JST 2016 Naoyuki Sawa
 *	- 無視する警告やエラーのメッセージ出力先を、デバッグ端末⇒標準エラー出力に変更しました。
 *	  これまではデバッグ端末に出力していましたが、Visual Studioのアウトプットウインドウを注意して見ていないと見落とす危険が高いので、より気づき易い標準エラー出力に出力する事にしました。
 *	* Mon Aug 08 21:53:54 JST 2016 Naoyuki Sawa
 *	- 無視する警告やエラーのメッセージを標準エラー出力に出力する際、同じ内容を連続して出力しないようにしました。
 *	  たとえば、E2010052705はほぼ毎フレーム(?)通知されるようなので、全て標準エラー出力に表示すると他のメッセージが読み辛くなる問題が有ります。
 *	  そこで、同じ内容のメッセージが連続して通知された場合は、後続の通知は標準エラー出力に表示しない事にしました。
 *	  ただし、別のメッセージを挟めばまた表示されますので、特殊なケースとして二種類の無視する通知が交互に発生した場合、これまで通り全て表示していまう事になります。
 *	  現在の所そのようなケースは生じていませんが、もし発生した場合は、メッセージの標準エラー出力への出力をオフにする機能などを追加して対応する事を検討して下さい。
 *	- [プリプロセッサの定義]で、_CRT_NONSTDC_NO_WARNINGSを追加しました。
 *	  上記の対応のためにstrdup()を使用した所、コンパイラの警告C4996が発生したからです。
 *	  strdup()の代わりに_strdup()を使う事でも回避出来るのですが、今後も同様の警告が発生する可能性が有るので、_CRT_NONSTDC_NO_WARNINGSを定義しておく事にしました。
 *	  ちなみに_CRT_SECURE_NO_WARNINGSは当プロジェクトの作成時に定義を追加していたので、今回新たに定義を追加したのは_CRT_NONSTDC_NO_WARNINGSだけです。
 *	  今後も、Visual Studio 2015でプロジェクトを作成したら、すぐに_CRT_SECURE_NO_WARNINGSと_CRT_NONSTDC_NO_WARNINGSを定義しておくのが良いと思います。
 *	* Tue Sep 06 21:06:46 JST 2016 Naoyuki Sawa
 *	- これまでの32ビット版に加えて、64ビット版のDLLもビルド出来るようにするために、以下の変更を行いました。
 *	- 32ビット用のDLLと、64ビットのDLLを共存出来るようにするために、64ビット版のDLLには、'_x64'のサフィックスを付与する事にしました。
 *	  □「プロジェクト」⇒「プロパティ」⇒「構成=すべての構成、プラットフォーム=x64」⇒「構成プロパティ」⇒「全般」⇒「ターゲット名」:
 *	  │【変更前】$(ProjectName)
 *	  │【変更後】$(ProjectName)_x64
 *	- _findfirst()が返した検索ハンドルを格納する変数の型を、(long)⇒(intptr_t)に変更しました。
 *	  VC++6.0の頃は(long)だったのですが、VS2015までの間のどこかで(intptr_t)に仕様変更になっていたようです。
 *	* Fri Sep 16 21:30:10 JST 2016 Naoyuki Sawa
 *	- 使用するCriFsLoaderの数を、厳密な同時再生数ぴったりでなく、1多い数に変更しました。
 *	  詳細は、SofdecHelper_Init()内のコメントを参照して下さい。
 *	- 同時再生するムービーが全てアルファムービーであってもデコーダハンドルが足りるように、最大消費デコーダハンドル数の設定を二倍にしました。
 *	  マニュアルから推測するに、アルファムービーの場合に二倍必要なのはデコーダハンドルだけであり、使用するCriFsLoaderの数(config_fs.num_loaders)や最大同時オープンファイル数(config_fs.max_files)は二倍にしなくても良いみたいです。(※要検証)
 *	- Visual Studio 2015をバージョンアップした事に伴い、当モジュールもリビルドしました。
 *	  当モジュールのソースコードには変更有りません。
 *	- 使用したコンパイラとライブラリは、以下の通りです。
 *	  │Microsoft Visual Studio Community 2015 Version 14.0.25431.01 Update 3
 *	  │CRI ADX2 SDK 2.09 (cri_adx2_sdk_pc_v2_09_j.zip)				←前回と同じ
 *	  │CRI Sofdec2 SDK 2.09 (cri_sofdec2_pc_v2_09_j.zip)				←前回と同じ
 *	* Sat Sep 17 21:14:33 JST 2016 Naoyuki Sawa
 *	- SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
 *	  これまでは、SofdecHelperのアルゴリズムは、AdxHelperのアルゴリズムと違って、再生の都度、プレイヤーの作成と削除を繰り返していましたが、今後は上記の通り、AdxHelperのアルゴリズムと同様になります。
 *	  このように変更した理由の一つは、プレイヤーの作成と削除のオーバーヘッド低減のためであり、アプリケーションから意識する必要は有りません。
 *	  もう一つの理由は、再生開始時の遅延を低減するために、ムービーの再生準備を対応したいからです。(←この対応はまだ行っていません。まずは準備として上記の変更を行いました。)	⇒{{2016/09/17コメント追記:ムービーの再生準備を対応しました。}}
 *	* Sat Sep 17 21:29:14 JST 2016 Naoyuki Sawa
 *	- ムービーの再生準備を対応しました。
 *	  当モジュールの処理の変更点は多かったのですが、アプリケーションの観点からの変更点は以下の二点のみです。
 *	  ・近い将来に再生する可能性が高いムービーを、SofdecHelper_Prepare()を呼び出して再生準備しておき、再生開始時の遅延を低減する事が出来ます。
 *	    空きエントリが無ければ再生準備は行われませんし、その後に別の再生準備が多く行われると古い再生準備はキャンセルされるので、必ずしも効果が有るとは限らず、実際に再生準備されているかどうかをアプリケーションから確認する方法は有りません。(一般的なキャッシュと同様です)
 *	  ・SofdecHelper_Free()で停止したムービーは、すぐに、次回の再生に備えて暗黙的に再生準備が行われます。(一般的なLRUと同様の動作です。最近停止したムービーはまたすぐに再生される可能性が高いという想定に基づきます。)
 *	    この動作については、アプリケーションが明示的にSofdecHelper_Prepare()を呼び出す必要は無く、自動的に行われます。
 *	    実際に再生開始まで再生準備が維持されているかどうかは、その後の動作次第であり、実際に再生準備されているかどうかをアプリケーションから確認する方法は有りません。(一般的なキャッシュと同様です)
 *	    尚、この動作はUSE_FREE_PREPシンボルを定義した場合のみ有効となります。2016/09/17現在はUSE_FREE_PREPシンボルを定義して、この動作を有効としてあります。
 *	* Sun Sep 18 21:28:04 JST 2016 Naoyuki Sawa
 *	- サウンドの再生準備を対応しました。
 *	  具体的には、以下の通りです。
 *	- ムービーと違って、サウンドはアプリケーション側で再生開始までブロックしないので、遅延がさほど目立たないのですが、実際に測ってみると、サウンドも50～100ミリ秒ぐらい再生開始時の遅延が有るようでした。
 *	  50～100ミリ秒も遅延が有ると、やはり、ループの繋がり等が不自然なのが聞いて判ってしまいます。
 *	  そこで、昨日のムービーの再生準備と同様に、サウンドも再生準備を対応する事にしました。
 *	- ムービーの再生準備のアルゴリズムと、サウンドの再生準備のアルゴリズムは、少し異なるものとなりました。
 *	  なぜならば、ムービーの再生チャネルは当モジュールが割り当ててアプリケーションに返すのに対し、サウンドの再生チャネルはアプリケーション側から指定する仕組みになっているからです。(そう決めたのは自分ですが…)
 *	  ムービーでは、再生チャネルがそのまま再生準備のプールとして使えたのですが、サウンドでは、プールを表す新たな構造体(ST_AtomExPlayerPool)を設けました。
 *	* Sun Sep 18 22:15:08 JST 2016 Naoyuki Sawa
 *	- サウンドの同時再生数が多い時に「E09022032B:Increase max_streams of CriAtomDbasConfig.」が出ていた問題を修正しました。
 *	  2016/09/18現時点ではまだ、AdxHelper_GetTotalTime()が消費する分を考慮出来ていないという問題が残っています。詳細はSofdecHelper_Init()内のコメントを参照して下さい。	(※TODO:2016/09/18)
 *	- サウンドの同時再生数が多い時に「W2010030305:Can not allocate playback info. (Increase max_virtual_voices of CriAtomExConfig.)」が出ていた問題を修正しました。
 *	  2016/09/18現時点ではまだ、AdxHelper_GetTotalTime()が消費する分を考慮出来ていないという問題が残っています。詳細はSofdecHelper_Init()内のコメントを参照して下さい。	(※TODO:2016/09/18)
 *	* Sun Sep 18 22:20:10 JST 2016 Naoyuki Sawa
 *	- SofdecHelper_Stop()を廃止しました。
 *	  既に、2016/08/01にシームレス連続再生に対応するための変更を行った時点で、SofdecHelper_Stop()は使えなくなっていました。
 *	  また、2016/09/17にムービーの再生準備に対応するための変更を行った時点で、SofdecHelper_Stop()が再び使える見込みは無くなりました。
 *	  SofdecHelper_Stop()は一度セットしたデータを何度も使い回して再生する目的で用意したのですが、上記二点の変更によって一度セットしたデータを何度も使い回して再生する事は出来なくなっているからです。
 *	  SofdecHelper_Stop()を廃止した事による、アプリケーションへの影響は有りません。
 *	  便利かなと思ってSofdecHelper_Stop()を追加していたものの、実際には/clip/DxlibHelper.cppからもアプリケーションから直接にも、一度も使用していなかったからです。
 *	* Sun Sep 18 25:18:38 JST 2016 Naoyuki Sawa
 *	- AdxHelper_Stop()が呼び出されなくても、再生が終了したら自動的に次回の再生準備を行うようにしました。
 *	  具体的には、以下の通りです。
 *	- これまでは、例えば5秒のサウンドを、10秒毎に再生する場合、5秒経過時点では次回の再生準備が行われず、10秒時点でAdxHelper_Play()経由で呼び出されるAdxHelper_Stop()で再生準備が行われ、このケースでは、再生準備による遅延回避が有効に働いていませんでした。
 *	  実際には、2016/09/18現在の/clip/DxlibHelper.cppでは、サウンドの再生時間のタイミングに合わせてfnEndハンドラがAdxHelper_Stop()を呼び出すので、5秒時点でAdxHelper_Stop()が呼び出されて次回の再生準備が行われるのですが、DxlibHelper.cppの実装に依存してたまたま遅延回避出来ていただけで、当モジュールの動作としては望ましくないものでした。
 *	  そこで改善策として、サウンドが再生終了(=CRIATOMEXPLAYER_STATUS_PLAYEND)しているかを監視し、再生終了していたら自動的に次回の再生準備を行うようにしました。(明示的にAdxHelper_Stop()が呼び出されなくても)
 *	  こうする事で、例えば5秒のサウンドを、10秒毎に再生する場合、明示的なAdxHelper_Stop()が呼び出されなくても、5秒時点で次回の再生準備が行われ、10秒時点の再生での再生遅延が回避出来ます。
 *	- 尚、上記のアルゴリズムは、サウンドに対してのみ適用可能なアルゴリズムであり、ムービーに対して同様の変更を行ってはいけない事に注意して下さい。	←←←←←←←←←←←←←←←←←←←←■■■■■■重要■■■■■
 *	  サウンドは再生チャネルの状態がアプリケーションから不可視であり、再生チャネルが自動的に終了して、使用している再生プールが勝手に開放されて次回の再生準備状態へ移行しても問題無いから、適用出来るアルゴリズムです。
 *	  ムービーの場合は、エントリの状態がアプリケーションから可視であり、ムービーが最後まで到達しても、そのエントリは引き続きアプリケーションからのSofdecHelper_CopyFrame()やSofdecHelper_CopyFrameYUV()に応答しなければいけないので、勝手に終了する事は出来ません。
 *	* Mon Sep 19 21:47:11 JST 2016 Naoyuki Sawa
 *	- AdxHelper_GetTotalTime()を高速化しました。
 *	- これまでは、再生処理とは関係無く一時的なプレーヤを作成して、新たにサウンドをロードしてサウンドの情報を取得していました。
 *	  cliptaps.cは、再生開始時に再生時間を取得し、DxlibHelper.cppでの再生時間のキャッシュにヒットしなければ、再生時間を取得するために60～70ミリ秒ぐらいの時間がかかっており、再生遅延の問題が生じていました。
 *	  その上、再生時間を取得するために準備したサウンドは、実際の再生の準備としては使用せずに破棄してしまい、無駄が生じていました。
 *	- 今回の変更によって、再生中の再生プールや再生準備中の再生プールから再生時間が取得出来るならば、それらから再生時間を取得するようにして、AdxHelper_GetTotalTime()を高速化しました。
 *	  上記が失敗した場合は、これまで通り、新たにサウンドをロードしてサウンドの情報を取得するので遅延が生じるのですが、その場合でも、再生プールが開いているならばそのまま再生準備中の再生プールとして残すようにしました。
 *	  再生時間を取得した後にすぐ再生を開始する場合に、これまでは二倍の遅延が生じていたのが、一倍の遅延で済む可能性が高くなりました。(再生プールが開いていなかった場合はこれまで通り二倍の遅延が生じます。)
 *	- 詳細は、AdxHelper_GetTotalTime()のコメントを参照して下さい。
 *	* Mon Sep 19 22:17:04 JST 2016 Naoyuki Sawa
 *	- SofdecHelper_Prepare()が戻り値を返すように変更しました。
 *	  戻り値の仕様は、SofdecHelper_Prepare()のコメントを参照して下さい。
 *	- AdxHelper_Prepare()が戻り値を返すように変更しました。
 *	  戻り値の仕様は、AdxHelper_Prepare()のコメントを参照して下さい。
 *	* Tue Sep 20 21:19:56 JST 2016 Naoyuki Sawa
 *	- 非ループのムービーが誤って二周ループしてしまう事が有った問題を修正しました。
 *	  詳細は以下の通りです。
 *	- 例えば、以下のケースで、問題が発生する事が有りました。
 *	  │1. エントリxでループムービーの再生準備を行う。
 *	  │2. エントリxでループムービーの再生を開始する。
 *	  │3. エントリxを停止する。
 *	  │4. エントリxで非ループムービーの再生準備を行う。
 *	  │5. エントリxの非ループムービーの再生を開始する。
 *	  │6. 非ループムービーなのに、二周ループしてしまう。
 *	  必ず発生する訳では無く、手順4,5で使用する非ループムービーが、短いムービーである時に発生し易いです。
 *	- この問題の原因は、Manaライブラリの特性に依存するものでした。
 *	  それは、短いムービーの場合、再生を開始する前の、再生準備中に、初回のループの有無を判断してしまう場合が有るというものです。
 *	  上記の例の場合、Manaライブラリの内部では、以下のような現象が発生していました。
 *	  │1.   エントリxでループムービーの再生準備を行う。
 *	  │2.   エントリxでループムービーの再生を開始する。当モジュールがcriManaPlayer_Loop(player,1)を実行し、エントリxのプレイヤーは'ループOn'の状態となる。
 *	  │3.   エントリxを停止する。
 *	  │4.   エントリxで非ループムービーの再生準備を行う。この時、エントリxのプレイヤーは'ループOn'の状態のままである。
 *	  │4.1. 再生準備中にムービーの終端に到達し、Manaライブラリは次のループの判断を行う。
 *	  │     エントリxのプレイヤーは'ループOn'の状態であるので、Manaライブラリはuser_movie_request_callback_funcを呼び出さずに、ループすると決定する。
 *	  │5.   エントリxの非ループムービーの再生を開始する。当モジュールがcriManaPlayer_Loop(player,0)を実行し、エントリxのプレイヤーは'ループOff'の状態となる。
 *	  │     しかし、もう既に、二周目のループを行う事は決定してしまっている。
 *	  │6.   非ループムービーなのに、二周ループしてしまう。
 *	  │6.1. Manaライブラリが二周目のループの終端に到達し、エントリxのプレイヤーは'ループOff'の状態なので、user_movie_request_callback_funcを呼び出す。
 *	  │     user_movie_request_callback_funcが次のムービーをセットしなければ、再生が終了する。
 *	  どうやらManaプレイヤーは、CRIMANAPLAYER_STATUS_PREP以上の状態になると、次のループの有無を判断してしまう可能性が有るようです。
 *	  (ヘッダのデコードだけならばそこまで進まないようです。再生準備に対応する前(2016/09/17以前)に問題が出ていなかった理由は、ヘッダのデコードだけでCRIMANAPLAYER_STATUS_WAIT_PREPまでしか進めていなかったからです。)
 *	  尚、上記は、ループ⇒非ループのケースでしたが、逆も然りです。
 *	  ループすべきムービーが、準備中に終端に到達し、criManaPlayer_Loop(player,1)を呼び出す前に非ループと判断され、ループしない問題も発生し得ます。(検証済み)
 *	- この問題を解決するために、当モジュール内にて、以下の二点の対応を行いました。
 *	  │①SofdecHelper_Prepare_subr()にて、criManaPlayer_Prepare()を呼んで再生準備を開始する前に、常に、criManaPlayer_Loop(player,0)を呼んで、一旦、Manaプレイヤーの状態を非ループに戻す。
 *	  │②user_movie_request_callback_funcでは、次に再生するムービーのパス名が無ければ(=初回以外の本当の終端時,又は,再生準備中)、以下の処理を行う。
 *	  │　・プレイヤーの状態がCRIMANAPLAYER_STATUS_PLAYING以上ならば、何もせずに処理を返す。
 *	  │　  これは、これまで通りの、本当の終端のケースに相当します。
 *	  │　・プレイヤーの状態がCRIMANAPLAYER_STATUS_PLAYING未満ならば、criManaPlayer_DeferCallback()を呼び出す。
 *	  │　  これが、再生準備中に終端に到達してしまったケースに相当します。
 *	  │　  まだ、SofdecHelper_Play()が呼び出されておらず、ループの有無が決定していないので、Manaプレイヤーがループの有無を決定するのを遅延させるためです。
 *	  上記の対応により、SofdecHelper_Play()が、criManaPlayer_Loop()を呼んだ後にcriManaPlayer_Start()を呼ぶまで、Manaプレイヤーがループの有無を決定するのが遅延されます。
 *	  SofdecHelper_Play()が、criManaPlayer_Loop(player,0)⇒criManaPlayer_Start()としたならば、次回にuser_movie_request_callback_funcが呼ばれた時にCRIMANAPLAYER_STATUS_PLAYING以上なので、これまで通り処理されます。
 *	  SofdecHelper_Play()が、criManaPlayer_Loop(player,1)⇒criManaPlayer_Start()としたならば、もう、user_movie_request_callback_funcが呼ばれる事は無く、ループ再生されます。
 *	- 上記の通り、かなり複雑なのですが、Manaライブラリの内部を推測すれば、何となく理解出来るかな、と思いました。
 *	  マニュアルにはさらっと書かれているので、実際に問題が発生して検証するまで気付かずに、かなり悩みました・・・
 *	  □\cri\documentation\CRI_Sofdec2_Manual_pc_j.chmより引用:
 *	　│criManaPlayer_Loop
 *	　│ループ再生OFFに設定した場合は、そのとき読み込んでいたムービの終端まで再生すると、ハンドル状態がCRIMANAPLAYER_STATUS_PLAYENDに遷移します。
 *	　│再生中にループOFFにした場合、タイミングによっては、再生中のムービ終端で終わらず、次の繰り返し再生まで実行されます。
 *	  '再生中にループOFFにした場合、～'と書いてありますが、実際には、再生準備中も含まれていた、という訳です。
 *	* Wed Sep 21 21:20:16 JST 2016 Naoyuki Sawa
 *	- 無視するエラーに「E10092103B」を暫定的に追加しました。本当に無視して良いのかどうかは問い合わせ中です。
 *	  詳細は、以下の通りです。
 *	- ビットレートオーバー時に発生する、無視して強行可能なエラーや警告として、以下のものは既に無視するように処理していました。
 *	  ・E2010052705:too high bit-rate. (Decrease max_bps of CriAtomDbasConfig under ～.)
 *	  ・W2014080102:Too high streaming bit-rate. (Limit total BPS is ～)
 *	  ・W2014080103:Start streaming BPS is ～. Total BPS before playback begins is ～.
 *	  しかし今回、新たに、以下のエラーが発生する事が有る事が判りました。
 *	  ・E10092103B:too high streaming bit-rate. (limit total bps is ～. cannot start streaming)
 *	  2016/09/17以降に、再生準備の処理を追加した事により、さらにビットレートの負荷が高まって、このエラーが発生するようになったのだと思います。
 *	- 「E10092103B」は、CRIエラーマニュアル(C:\cri\documentation\CRI_Error_Manual_pc_j.chm)には載っておらず、詳細が不明です。
 *	  しかし、類似のエラーコードが内部エラーとして記載されているので、おそらく「E10092103B」も内部エラーの一種なのだと思います。
 *	  「E10092103B」を無視して強行して良いのかどうかも不明であり、現在、CRIサポートに問い合わせ中です。(本日質問したばかりなので、まだ回答は頂けていません。)	⇒{{2016/09/22コメント追記:「E10092103B」について、CRIサポートから回答を頂きました。}}
 *	  エラーメッセージに'cannot start streaming'とあるので、強行出来ないような気もするのですが、試しに強行してみた所、(動作を見る限りでは)問題無く動作しているように見えました。
 *	  そこで、CRIサポートから回答が得られるまでは、とりあえず、「E10092103B」も無視して強行しておく事にしました。
 *	* Thu Sep 22 10:30:07 JST 2016 Naoyuki Sawa
 *	- 「E10092103B」について、CRIサポートから回答を頂きました。
 *	  要点は、以下の通りです。
 *	  ・通常のビットレートオーバーが原因で、「E10092103B」発生する事は有り得る。
 *	  ・ビットレートオーバー判定の計算には、再生準備状態で待機中のムービーやサウンドも含まれる。
 *	  ・「E10092103B」を無視して再生を強行する事は可能だが、音声ノイズやムービーコマ落ち等が発生する恐れが有る。
 *	  ・「E10092103B」を回避するには、待機中のプレイヤーを減らしたり、データのエンコードを調整してビットレートのピークを下げる。
 *	  結論としては、「E10092103B」の扱いは「E2010052705」と同じで良い、という事になります。
 *	  昨日は推測で、無視するエラーに「E10092103B」を暫定的に追加していたのですが、それで正解だったという訳です。
 *	* Sat Sep 24 21:37:23 JST 2016 Naoyuki Sawa
 *	- やっぱり、num_loadersもアルファムービーを考慮して、二倍にしました。
 *	  経緯は、以下の通りです。
 *	- これまで、/clip/test.win/Define.hにて、SOFDEC_NUM_MOVIES=8,ADX_NUM_SOUNDS=8の設定で使っている限りでは、エラーは発生していませんでした。
 *	  しかし、SOFDEC_NUM_MOVIES=9,ADX_NUM_SOUNDS=7や、SOFDEC_NUM_MOVIES=8,ADX_NUM_SOUNDS=0にしたところ、「E2008070931:Can not allocate loader handle. (Increase num_loaders of CriFsConfig.)」エラーが発生しました。
 *	  どうやら、ADX_NUM_SOUNDSを8未満にすると、エラーが発生するようです。
 *	  エラーが発生する箇所を調べた所、SofdecHelper_Init()内のcriManaPlayer_Create()を何個目か作成した時点で、エラーが発生していました。
 *	  ムービーやサウンドの再生準備や再生開始しようとした時点ではなく、プレイヤーを作成した時点でエラーが発生するというのが謎です。
 *	  はっきりした原因は判っていないのですが、やはり、Manaライブラリがプレイヤーを作成する時点で、アルファムービーを想定して二倍のローダを確保しようとしているのではないかと推測しました。
 *	  これまでエラーが出ていなかった理由は、SOFDEC_NUM_MOVIESの二倍がたまたまデフォルトのローダ数に収まっていたからではないかと思います。
 *	  そこで、やっぱり、num_loadersもアルファムービーを考慮して、二倍にしてみた所、エラーは出なくなりました。(max_filesは二倍にする必要は無いかも知れないのですが、現在の所max_filesも二倍にしています。)
 *	  本当に二倍必要かどうかははっきりせず、もしかすると多少の無駄が生じているかも知れないのですが、PCではさほど問題にならないでしょう。
 *	* Tue Dec 20 21:28:56 JST 2016 Naoyuki Sawa
 *	- ライブラリをバージョンアップした事に伴い、当モジュールもリビルドしました。
 *	  当モジュールのソースコードには変更有りません。
 *	- 使用したコンパイラとライブラリは、以下の通りです。
 *	  │Microsoft Visual Studio Community 2015 Version 14.0.25431.01 Update 3	←前回と同じ
 *	  │CRI ADX2 SDK 2.10 (cri_adx2_sdk_pc_v2_10_j.zip)				←バージョンアップ
 *	  │CRI Sofdec2 SDK 2.10 (cri_sofdec2_pc_v2_10_j.zip)				←バージョンアップ
 *	* Thu Dec 22 09:49:41 JST 2016 Naoyuki Sawa
 *	- ライブラリをバージョンアップした事に伴い、当モジュールもリビルドしました。
 *	  当モジュールのソースコードには変更有りません。
 *	- 使用したコンパイラとライブラリは、以下の通りです。
 *	  │Microsoft Visual Studio Community 2015 Version 14.0.25431.01 Update 3	←前回と同じ
 *	  │CRI ADX2 SDK 2.11 (cri_adx2_sdk_pc_v2_11_j.zip)				←バージョンアップ
 *	  │CRI Sofdec2 SDK 2.10 (cri_sofdec2_pc_v2_10_j.zip)				←前回と同じ
 *	* Wed Mar 22 22:14:37 JST 2017 Naoyuki Sawa
 *	- AdxHelper_GetFormatInfo()を追加しました。
 *	  これまでも最低限の情報はAdxHelper_GetTotalTime()で取得出来ていましたが、もっと多くの情報を取得するためにcriAtomExPlayback_GetFormatInfo()が返す情報を全て取得出来るようにしました。
 *	  AdxHelper_GetFormatInfo()を追加した事に伴い、既存のAdxHelper_GetTotalTime()がAdxHelper_GetFormatInfo()を呼び出すように実装を変更しました。
 *	  AdxHelper_GetTotalTime()の結果は変わらないので、既存のアプリケーションの修正は不要です。
 *	* Mon Apr 03 23:36:16 JST 2017 Naoyuki Sawa
 *	- 暗号化されたデータの再生や情報取得に対応しました。
 *	  ST_SofdecHelperInfo構造体のフィールドが増えたので、当dllを使用している既存のアプリケーションもリビルドが必要です。
 *	- データを暗号化した時のキーと、ST_SofdecHelperInfo構造体に指定した復号キーが一致しない場合、ムービーと音声とで、挙動が違う事に注意して下さい。
 *	  ・ムービーは、再生エラーになります。(参考資料:/cri/pc/include/cri_mana.h「（暗号化されていないムービデータや、暗号キーの異なるムービデータは全て再生エラーとなります。）」)
 *	    復号キーが合っていないと、情報を取得しようとしただけでもエラーになるので、/clip/tool/SofdecProbe/での復号キー指定対応は必須です。
 *	  ・音声は、エラーは発生せず、ノイズが再生されます。(参考資料:/cri/pc/include/cri_atom_ex.h「（暗号化されていない音声データや、暗号キーの異なる音声データは全てノイズとなります。）」)
 *	    復号キーが合っていなくても、情報は取得出来るようなので、/clip/tool/AdxProbe/の復号キー対応は必須では有りませんが、対応しておく方が良いと思うので対応する事にしました。
 *	- 暗号化の方法については、PC用のCRIのSDKよりも、Unity用のCRIのSDKの方が、判り易いと思います。
 *	  主に、「C:\criware_sdk_unity_smartphone_v2_88_j\cri\documentation\CRIWARE_Unity_Plugin_Manual_smartphone_j.chm」の、「データの暗号化」のページを参照して下さい。
 *	  上記のページには主に、GUIツールを使って暗号化する方法が書かれていますが、全てコマンドラインツールでも暗号化出来ます。
 *	  コマンドラインツールの説明ページのオプションも、合わせて参照して下さい。
 *	  音声データの暗号化は、criatomencd.exeの"-keycode="オプションです。
 *	  ムービーデータの暗号化は、sofdec2enc.exeは暗号化にかかわらず、独立した暗号化ツールであるusmbreak.exeで暗号化します。
 *	  Unityで再生する場合は、上記に加えて、GenerateAuthenticationFile.exeも使います。
 *	  尚、APIの仕様自体はムービーと音声にそれぞれ別の復号キーを指定して別々のデクリプタを作成出来ますが、Unityで再生する場合はCriWareLibraryInitializerの復号キーの指定が一つなので、両方のキーを同じにせざるを得ません。
 *	* Sun Apr 09 22:05:18 JST 2017 Naoyuki Sawa
 *	- コンパイラを、VS2015⇒VS2017に更新しました。
 *	  当モジュールのソースコードには変更有りません。
 *	- 使用したコンパイラとライブラリは、以下の通りです。
 *	  │Microsoft Visual Studio Community 2017 Version 15.1 (26403.0) Release	←バージョンアップ
 *	  │CRI ADX2 SDK 2.11 (cri_adx2_sdk_pc_v2_11_j.zip)				←前回と同じ
 *	  │CRI Sofdec2 SDK 2.10 (cri_sofdec2_pc_v2_10_j.zip)				←前回と同じ
 *	* Tue Apr 18 21:46:46 JST 2017 Naoyuki Sawa
 *	- Visual Studio 2017をバージョンアップした事に伴い、当モジュールもリビルドしました。
 *	  当モジュールのソースコードには変更有りません。
 *	- 使用したコンパイラとライブラリは、以下の通りです。
 *	  │Microsoft Visual Studio Community 2017 Version 15.1 (26403.7) Release	←バージョンアップ
 *	  │CRI ADX2 SDK 2.11 (cri_adx2_sdk_pc_v2_11_j.zip)				←前回と同じ
 *	  │CRI Sofdec2 SDK 2.10 (cri_sofdec2_pc_v2_10_j.zip)				←前回と同じ
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//{{2016/05/16変更:カレントフォルダに有る全ての'*.cpk'(有れば)にバインドするように変更しました。これまではカレントフォルダに有る'default.cpk'(有れば)だけにバインドしていました。変更後も'default.cpk'は自動的にバインド対象になりますので、既存のアプリケーションに対する互換性の問題は生じません。
//#include <sys/types.h>
//#include <sys/stat.h>
//↓2016/05/16変更:カレントフォルダに有る全ての'*.cpk'(有れば)にバインドするように変更しました。これまではカレントフォルダに有る'default.cpk'(有れば)だけにバインドしていました。変更後も'default.cpk'は自動的にバインド対象になりますので、既存のアプリケーションに対する互換性の問題は生じません。
#include <io.h>		//_findfirst(),_findnext(),_findclose()
//}}2016/05/16変更:カレントフォルダに有る全ての'*.cpk'(有れば)にバインドするように変更しました。これまではカレントフォルダに有る'default.cpk'(有れば)だけにバインドしていました。変更後も'default.cpk'は自動的にバインド対象になりますので、既存のアプリケーションに対する互換性の問題は生じません。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#define STRICT
#include <windows.h>
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#define SOFDECHELPER_BUILD
#include "SofdecHelper.h"
/****************************************************************************
 *	
 ****************************************************************************/
	#define USE_ADX		//ADX(オーディオトラック)を使用する場合はこのシンボルを定義して下さい。		2016/04/06現在は、定義していません。	⇒2016/04/07:USE_ADXを定義有りに変更しました。定義無しだと'CRI Serve,'スレッドの中でアクセス違反で落ちる事が有ったからです。確かにサンプルプログラムは全てcriAtomEx_Initialize_WASAPI()を使用しており、criAtomEx_Initialize()を使用しているサンプルプログラムは有りません。これが原因と確定した訳では有りませんが、定義有りにすると今のところ落ちないので、何か関連は有りそうです。現在の所サウンドはDxLibで行うから、ADXは無効にしたいのですが仕方がありません。まだサウンド付きムービーを使用していないので、もしDxLibのサウンド処理と衝突した時にどうなるか未検証です。(ADXはOnでCRIMANA_AUDIO_TRACK_OFFだけ設定すれば良いのかな?)
	#define USE_CPK		//CPK(パッキングファイル)を使用する場合はこのシンボルを定義して下さい。		2016/04/06現在は、定義しています。実行時に'default.cpk'の有無によって自動的に動作を変えるようにしていますので、常に定義しておいて良いと思います。もし定義を外す必要が有るとすれば、万一criFsBinder_BindDirectory()の挙動が怪しいと思った場合のみです。(現時点ではそのような事は有りません。)
/*--------------------------------------------------------------------------*/
//{{2016/09/17追加:ムービーの再生準備を対応しました。
	#define USE_FREE_PREP	//USE_FREE_PREPシンボルを定義すると、SofdecHelper_Free()が呼び出されたら即SofdecHelper_Prepare()を呼び出して次回の再生準備を行う。LRUキャッシュのイメージです。
//}}2016/09/17追加:ムービーの再生準備を対応しました。
/*--------------------------------------------------------------------------*/
#ifndef USE_ADX
  #include <cri_atom_ex.h>	//オーディオトラックを使用しない場合
#else //USE_ADX
  #include <cri_atom_wasapi.h>	//オーディオトラックを使用する場合
#endif//USE_ADX
#include <cri_mana.h>
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//2016/04/04現在の所x64用を使う予定は有りませんが、今後のためにx64用のリンク設定も書いておく事にしました。	⇒{{2016/09/06コメント追記:x64用も使うようになりました。}}
//シンボル'_WIN64'が定義されているか定義されていないかによって、プラットフォームがx64かx86かを区別します。
//シンボル'_WIN32'はx64でもx86でも定義されているので、シンボル'_WIN32'では区別出来ない事に注意して下さい。
#ifndef _WIN64
  #ifndef _DEBUG
    #pragma comment(lib, "cri_atom_pcx86.lib")
    #pragma comment(lib, "cri_base_pcx86.lib")
    #pragma comment(lib, "cri_file_system_pcx86.lib")
  #else //_DEBUG
    #pragma comment(lib, "cri_atom_pcx86D.lib")
    #pragma comment(lib, "cri_base_pcx86D.lib")
    #pragma comment(lib, "cri_file_system_pcx86D.lib")
  #endif//_DEBUG
  #pragma comment(lib, "cri_mana_pcx86.lib")
#else //_WIN64
  #ifndef _DEBUG
    #pragma comment(lib, "cri_atom_pcx64.lib")
    #pragma comment(lib, "cri_base_pcx64.lib")
    #pragma comment(lib, "cri_file_system_pcx64.lib")
  #else //_DEBUG
    #pragma comment(lib, "cri_atom_pcx64D.lib")
    #pragma comment(lib, "cri_base_pcx64D.lib")
    #pragma comment(lib, "cri_file_system_pcx64D.lib")
  #endif//_DEBUG
  #pragma comment(lib, "cri_mana_pcx64.lib")
#endif//_WIN64
#pragma comment(lib, "legacy_stdio_definitions.lib")
/****************************************************************************
 *	
 ****************************************************************************/
//{{2016/08/01追加:シームレス連続再生に対応しました。
static ST_SofdecHelperInfo	stSofdecHelperInfo;			//初期化情報
//}}2016/08/01追加:シームレス連続再生に対応しました。
#ifdef  USE_CPK
CriFsBinderHn			binder;					//バインダ
#endif//USE_CPK
static CriManaDecrypterHn	mana_decrypter;				//暗号化されたムービーデータのデクリプタ	ムービーデータのデクリプタを作成しなかった場合はNULL	//{{2017/04/03追加:暗号化されたデータの再生や情報取得に対応しました。}}
//{{2016/09/17追加:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//ST_ManaPlayer.Flag
#define ManaPlayer_Flag_InUse	(1<<0)		//使用中		//2016/09/17現在はこのフラグしか無いので変数全体を一つのフラグと見なしても構わなかったのですが、もし今後フラグを増やす場合のためにビットフラグとしておく事にしました。
//}}2016/09/17追加:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
typedef struct _ST_ManaPlayer {
//{{2016/09/17追加:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
	int			Flag;					//(ManaPlayer_Flag_*)の0個以上の組み合わせ
//}}2016/09/17追加:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//{{2016/09/17コメント変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//	CriManaPlayerHn		player;					//Manaプレーヤハンドル		NULLならば空きエントリ
//↓2016/09/17コメント変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
	CriManaPlayerHn		player;					//Manaプレーヤハンドル		最初にプレイヤーを全て作成しておいて最後まで使い回します。
//}}2016/09/17コメント変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
	CriManaMovieInfo	movie_info;				//ムービーヘッダ情報
//{{2016/08/01追加:シームレス連続再生に対応しました。
	char			sNextMoviePath[_MAX_PATH];		//次に再生するムービーのパス名	空文字列ならば次に再生するムービーが無い事を示す。
//}}2016/08/01追加:シームレス連続再生に対応しました。
//{{2016/09/17追加:ムービーの再生準備を対応しました。
	char			sPrepMoviePath[_MAX_PATH];		//再生準備中のムービーのパス名	空文字列ならば再生準備中のムービーが無い事を示す。	再生中もクリアせずに維持する。USE_FREE_PREPシンボルを定義すると、SofdecHelper_Free()が呼び出されたら即SofdecHelper_Prepare()を呼び出して次回の再生準備を行う。LRUキャッシュのイメージです。
	int			PrepAge;				//再生準備の古さ。sPrepMoviePath[]が空文字でない(=再生準備中である)場合のみ有効です。この値が大きい程優先的に再生準備のキャンセル対象となります。
//}}2016/09/17追加:ムービーの再生準備を対応しました。
} ST_ManaPlayer;
//{{2016/08/01削除:シームレス連続再生に対応しました。
//static int			NumMovies;				//同時再生するムービーの数	実際にはこれよりも少ない数になります。詳細はCriFsConfig.num_loaders,max_filesのコメントを参照して下さい。
//}}2016/08/01削除:シームレス連続再生に対応しました。
static ST_ManaPlayer*		TBL_ManaPlayer/*[NumMovies]*/;
#ifdef  USE_ADX
//{{--- サウンド ---
static CriAtomExDbasId		dbas_id;				//D-BAS
static CriAtomExDecrypterHn	atom_ex_decrypter;			//暗号化された音声データのデクリプタ	音声データのデクリプタを作成しなかった場合はNULL	//{{2017/04/03追加:暗号化されたデータの再生や情報取得に対応しました。}}
static CriAtomExVoicePoolHn	voice_pool;				//ボイスプール
//{{2016/08/01削除:シームレス連続再生に対応しました。
//static int			NumSounds;				//同時再生するサウンドの数
//}}2016/08/01削除:シームレス連続再生に対応しました。
//{{2016/08/01変更:シームレス連続再生に対応しました。
//static CriAtomExPlayerHn*	TBL_AtomExPlayer/*[NumSounds]*/;
//↓2016/08/01変更:シームレス連続再生に対応しました。
//typedef struct _ST_AtomExPlayer {
//	CriAtomExPlayerHn	player;					//Manaプレーヤハンドル		NULLならば空きエントリ
////不要	CriAtomExFormatInfo	format_info;				//音声データフォーマット情報								2016/08/01現在の所不要なのでコメントアウトした。もし必要になったら有効にせよ。
//	char			sNextSoundPath[_MAX_PATH];		//次に再生するサウンドのパス名	空文字列ならば次に再生するサウンドが無い事を示す。
//} ST_AtomExPlayer;
//static ST_AtomExPlayer*		TBL_AtomExPlayer/*[NumSounds]*/;
//↓2016/09/18変更:サウンドの再生準備を対応しました。
//ST_AtomExPlayerPool.Flag
#define AtomExPlayerPool_Flag_InUse	(1<<0)	//使用中		//2016/09/18現在はこのフラグしか無いので変数全体を一つのフラグと見なしても構わなかったのですが、もし今後フラグを増やす場合のためにビットフラグとしておく事にしました。
typedef struct _ST_AtomExPlayerPool {	//再生プール
	int			Flag;					//(AtomExPlayerPool_Flag_*)の0個以上の組み合わせ
	CriAtomExPlayerHn	player;					//Manaプレーヤハンドル		NULLならば空きエントリ
	char			sNextSoundPath[_MAX_PATH];		//次に再生するサウンドのパス名	空文字列ならば次に再生するサウンドが無い事を示す。
	char			sPrepSoundPath[_MAX_PATH];		//再生準備中のサウンドのパス名	空文字列ならば再生準備中のサウンドが無い事を示す。	再生中もクリアせずに維持する。USE_FREE_PREPシンボルを定義すると、AdxHelper_Stop()が呼び出されたら即SofdecHelper_Prepare()を呼び出して次回の再生準備を行う。LRUキャッシュのイメージです。
	int			PrepAge;				//再生準備の古さ。sPrepSoundPath[]が空文字でない(=再生準備中である)場合のみ有効です。この値が大きい程優先的に再生準備のキャンセル対象となります。
//{{2016/09/19追加:AdxHelper_GetTotalTime()を高速化しました。
	CriAtomExPlaybackId	playback_id;				//sPrepSoundPath[]の再生準備を開始する時に取得した再生ID。	シームレス連続再生を使用した場合も、sNextSoundPath[]ではなく、sPrepSoundPath[]に対応する再生IDである事に注意して下さい。再生が終了していたり、シームレス連続再生で次のフレーズの再生が開始していた場合は、この再生IDはもう無効になっているので、criAtomExPlayback_GetFormatInfo()は失敗し、別の方法で情報を取得する必要が有ります。(AdxHelper_GetTotalTime()内で行っています)
//}}2016/09/19追加:AdxHelper_GetTotalTime()を高速化しました。
} ST_AtomExPlayerPool;
static ST_AtomExPlayerPool*	TBL_AtomExPlayerPool/*[NumSounds]*/;
typedef struct _ST_AtomExPlayer {	//再生チャネル
	ST_AtomExPlayerPool*	pAtomExPlayerPool;			//この再生チャネルが使用している再生プールへのポインタ。	NULLならば再生中でない(=再生プールを使用していない)事を示す。
} ST_AtomExPlayer;
static ST_AtomExPlayer*		TBL_AtomExPlayer/*[NumSounds]*/;
//}}2016/09/18変更:サウンドの再生準備を対応しました。
//}}--- サウンド ---
#endif//USE_ADX
/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
//{{2016/09/18追加:AdxHelper_Stop()が呼び出されなくても、再生が終了したら自動的に次回の再生準備を行うようにしました。
#ifdef  USE_ADX
static void AdxHelper_Exec();
#endif//USE_ADX
//}}2016/09/18追加:AdxHelper_Stop()が呼び出されなくても、再生が終了したら自動的に次回の再生準備を行うようにしました。
/****************************************************************************
 *	
 ****************************************************************************/
#define DIE() die("###%s(%d): DIE\n",__FILE__,__LINE__)
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
__declspec(noreturn) static void die(const char* fmt, ...) {
#ifdef  _DEBUG
	va_list ap;
	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
	abort();
#else //_DEBUG
	char buf[1024];
	va_list ap;
	va_start(ap, fmt);
	vsnprintf(buf, sizeof buf, fmt, ap);
	va_end(ap);
	MessageBox(NULL, buf, "SofdecHelper.dll", MB_OK|MB_ICONERROR|MB_TASKMODAL);	//MB_SYSTEMMODALではなくMB_TASKMODALが適切です。名前とは裏腹にアプリケーションのスレッドに属する全てのウインドウにフォーカスを移せなくするのはMB_SYSTEMMODALではなくMB_TASKMODALの方です。
	exit(EXIT_FAILURE);
#endif//_DEBUG
}
/*--------------------------------------------------------------------------*/
//{{2016/08/07追加:無視する警告やエラーのメッセージを標準エラー出力に出力する際、同じ内容を連続して出力しないようにしました。
static void output_ignored_message(const CriChar8* errid, CriUint32 p1, CriUint32 p2, CriUint32* parray) {
	static char* old_msg; /* 前回のメッセージ、ＳＴＡＴＩＣです！ */
	const char* new_msg = criErr_ConvertIdToMessage(errid, p1, p2);		//今回のメッセージを取得する。(一時的な文字列)
	if(!old_msg || strcmp(old_msg, new_msg)) {				//初回か,又は,前回のメッセージと今回のメッセージが異なるならば…
		free(old_msg/*NULL可*/);					//前回のメッセージを開放する。	//忘れないで!!
		if(!(old_msg = strdup(new_msg))) { DIE(); }			//今回のメッセージを複製する。
		fprintf(stderr, "%s: %s\n", "output_ignored_message", old_msg);	//今回のメッセージを出力する。	//単純に無視しても良いだろうが、一応標準エラー出力にメッセージを出力しておく事にした。	※TODO:エラーも警告もアプリケーション側へコールバックで通知する方が良いと思う。既に/clip/tool/ssbpLib/ssbpLib.cppではそうしているので当モジュールも同じように変更しろ。
	}
}
//}}2016/08/07追加:無視する警告やエラーのメッセージを標準エラー出力に出力する際、同じ内容を連続して出力しないようにしました。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void user_error_callback_func(const CriChar8* errid, CriUint32 p1, CriUint32 p2, CriUint32* parray) {
//{{2016/05/19追加:警告は無視するようにしました。
	//┌─────────────────────────────────────────────────────────────────────────
	//│(C:\cri\documentation\CRI_Error_Manual_pc_j.chmより引用)
	//│
	//│■エラー情報の取得について
	//│
	//│ライブラリ内で発生したエラーの情報は、エラーコールバック関数に返されます。
	//│エラー情報を取得するには、criErr_SetCallback関数を使用し、エラーコールバック関数を登録した状態でアプリケーションを実行する必要があります。
	//│
	//│エラーコールバック関数を登録した状態でエラーが発生すると、登録済みのコールバック関数が呼び出されます。
	//│エラー情報の詳細は、エラーコールバック関数の引数に格納されています。
	//│
	//│第一引数のerridには、エラーIDと説明文章が「:」で連結された文字列で格納されています。
	//│
	//│<<エラー文字列の例>>
	//│E2010021506:CRI AtomEx library has been initialized twice.
	//│~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//│エラーID    エラー内容
	//│
	//│エラーIDは、エラーに割り当てられた一意のIDです。
	//│エラーIDの先頭文字が「E」で始まるものがエラー、「W」で始まるものが警告です。
	//│「W」で始まる警告については、場合によっては無視しても問題のないエラーが含まれています。	←←←←←←←←←←←←←←←←←←←←←←←←←←←■■重要■■
	//│
	//│エラー内容は、エラーの情報を示す簡易な文章です。
	//│(文字列中にプログラム依存の内容が含まれるため、文字列を出力する際には、一旦criErr_ConvertIdToMessage関数で変換を行ってください。)
	//│より詳細なエラー情報については、本ドキュメントのエラー一覧ページで確認可能です。
	//│(エラーIDを本ドキュメントのエラー一覧ページで検索することで、詳細なエラー情報を確認可能です。)
	//└─────────────────────────────────────────────────────────────────────────
	if(errid[0] == 'W') {	//警告ならば…
	//{{2016/08/07変更:無視する警告やエラーのメッセージを標準エラー出力に出力する際、同じ内容を連続して出力しないようにしました。
	//  //{{2016/08/07変更:無視する警告やエラーのメッセージ出力先を、デバッグ端末⇒標準エラー出力に変更しました。
	//  //	OutputDebugString(criErr_ConvertIdToMessage(errid, p1, p2));	//単純に無視しても良いだろうが、一応デバッグ端末にメッセージを出力しておく事にした。	※TODO:エラーも警告もアプリケーション側へコールバックで通知する方が良いと思う。既に/clip/tool/ssbpLib/ssbpLib.cppではそうしているので当モジュールも同じように変更しろ。
	//  //↓2016/08/07変更:無視する警告やエラーのメッセージ出力先を、デバッグ端末⇒標準エラー出力に変更しました。
	//	fprintf(stderr, "%s: %s\n", "user_error_callback_func", criErr_ConvertIdToMessage(errid, p1, p2));	//単純に無視しても良いだろうが、一応標準エラー出力にメッセージを出力しておく事にした。	※TODO:エラーも警告もアプリケーション側へコールバックで通知する方が良いと思う。既に/clip/tool/ssbpLib/ssbpLib.cppではそうしているので当モジュールも同じように変更しろ。
	//  //}}2016/08/07変更:無視する警告やエラーのメッセージ出力先を、デバッグ端末⇒標準エラー出力に変更しました。
	//↓2016/08/07変更:無視する警告やエラーのメッセージを標準エラー出力に出力する際、同じ内容を連続して出力しないようにしました。
		output_ignored_message(errid, p1, p2, parray);
	//}}2016/08/07変更:無視する警告やエラーのメッセージを標準エラー出力に出力する際、同じ内容を連続して出力しないようにしました。
		return;	//ここまで
	}
//}}2016/05/19追加:警告は無視するようにしました。
//{{2016/08/07追加:特定のエラーを無視出来るようにしました。
	// * Mon Aug 08 21:14:30 JST 2016 Naoyuki Sawa
	// - 特定のエラーを無視出来るようにしました。
	//   これまでは、エラーは全て即停止していましたが、無視しても動作可能なエラーも有る事が判ったので、特定のエラーは無視出来るようにしました。
	//   今回、無視するようにしたエラーはE2010052705だけですが、今後、無視するエラーを追加する事も可能です。
	//   無視するエラーを追加する場合は、TBL_IgnoreErrid[]に無視するエラーのエラーIDを追加して下さい。
	// - E2010052705を無視するようにしました。
	//   理由は、以下の通りです。
	//   これまで長い間、「ビットレートの高いムービーを複数同時に再生すると、E2010052705が通知されてプログラムが停止する問題」に悩まされていました。
	//   criAtomExDbas_Create()のパラメータを調整しても解決せず、どうやらSofdec2内部のバッファサイズの制約みたいだという結論に達していました。
	//   そして、E2010052705はエラーだから、再生しようとしたムービーは再生開始していないと思っていたので、プログラムを停止していました。
	//   □(C:\cri\documentation\CRI_Error_Manual_pc_j.chmより引用)
	//   │E2010052705
	//   │エラーID		E2010052705
	//   │エラーメッセージ	"too high bit-rate. (Decrease max_bps of CriAtomDbasConfig under %u.)"
	//   │発生ライブラリ	CRI Base
	//   │発生状況		ライブラリ内部で制御しているストリーミングバッファのサイズ上限が超えてしまった場合に発生するエラーです。
	//   │解決方法		ストリーミングの同時再生本数を減らすか、総ビットレートのピークを下げるようにデータ側を調整してください。
	//   │			例えばSofdec2ムービについてはエンコード時のビットレート、解像度を落として拡大表示するなどの対応を行って下さい。
	//   しかし、history_crimana_pc_j.txtを読んでいた所、E2010052705は無視しても大丈夫そうだという事に気が付きました。
	//   □(C:\cri\pc\docs\jpn\history_crimana_pc_j.txtより引用)
	//   │[2014-09-26 CRI Mana Ver.2.01.00]------------------------------------------
	//   │(1) 仕様変更
	//   │    (a) ビットレート上限を突破しても、ストリーミング再生を却下しないようにように変更しました。
	//   │        ただし、理論的にはストリーミングバッファが足りない状態になるため、動画再生がカクつく可能性があります。
	//   具体的には書かれていないのですが、ビットレート上限を突破したエラー(E2010052705)を無視しても、再生は行われると言っているように取れます。
	//   そこで、E2010052705を無視して見たところ、再生しようとしたムービーは再生開始されている事が判りました。
	//   場合によっては多少カクつくという、見た目の問題しか発生しません。
	//   実際の所、ビットレート上限を突破する主なケースとしては、ムービーの切り替え時に前のムービーと次のムービーが一瞬同時に再生されて突破するケースが多いです。(本当にたくさん同時再生し過ぎて突破する事も無い訳では無いが)
	//   そのようなケースで一瞬カクついても、全体の見た目にはさほど支障は有りません。
	//   従って、E2010052705は無視する事にしました。
	//   今回の変更により、「ビットレートの高いムービーを複数同時に再生すると、E2010052705が通知されてプログラムが停止する問題」が解決しました。
	// * Wed Sep 21 21:20:16 JST 2016 Naoyuki Sawa
	// - 無視するエラーに「E10092103B」を暫定的に追加しました。本当に無視して良いのかどうかは問い合わせ中です。
	//   詳細は、以下の通りです。
	// - ビットレートオーバー時に発生する、無視して強行可能なエラーや警告として、以下のものは既に無視するように処理していました。
	//   ・E2010052705:too high bit-rate. (Decrease max_bps of CriAtomDbasConfig under ～.)
	//   ・W2014080102:Too high streaming bit-rate. (Limit total BPS is ～)
	//   ・W2014080103:Start streaming BPS is ～. Total BPS before playback begins is ～.
	//   しかし今回、新たに、以下のエラーが発生する事が有る事が判りました。
	//   ・E10092103B:too high streaming bit-rate. (limit total bps is ～. cannot start streaming)
	//   2016/09/17以降に、再生準備の処理を追加した事により、さらにビットレートの負荷が高まって、このエラーが発生するようになったのだと思います。
	// - 「E10092103B」は、CRIエラーマニュアル(C:\cri\documentation\CRI_Error_Manual_pc_j.chm)には載っておらず、詳細が不明です。
	//   しかし、類似のエラーコードが内部エラーとして記載されているので、おそらく「E10092103B」も内部エラーの一種なのだと思います。
	//   「E10092103B」を無視して強行して良いのかどうかも不明であり、現在、CRIサポートに問い合わせ中です。(本日質問したばかりなので、まだ回答は頂けていません。)	⇒{{2016/09/22コメント追記:「E10092103B」について、CRIサポートから回答を頂きました。}}
	//   エラーメッセージに'cannot start streaming'とあるので、強行出来ないような気もするのですが、試しに強行してみた所、(動作を見る限りでは)問題無く動作しているように見えました。
	//   そこで、CRIサポートから回答が得られるまでは、とりあえず、「E10092103B」も無視して強行しておく事にしました。
	// * Thu Sep 22 10:30:07 JST 2016 Naoyuki Sawa
	// - 「E10092103B」について、CRIサポートから回答を頂きました。
	//   要点は、以下の通りです。
	//   ・通常のビットレートオーバーが原因で、「E10092103B」発生する事は有り得る。
	//   ・ビットレートオーバー判定の計算には、再生準備状態で待機中のムービーやサウンドも含まれる。
	//   ・「E10092103B」を無視して再生を強行する事は可能だが、音声ノイズやムービーコマ落ち等が発生する恐れが有る。
	//   ・「E10092103B」を回避するには、待機中のプレイヤーを減らしたり、データのエンコードを調整してビットレートのピークを下げる。
	//   結論としては、「E10092103B」の扱いは「E2010052705」と同じで良い、という事になります。
	//   昨日は推測で、無視するエラーに「E10092103B」を暫定的に追加していたのですが、それで正解だったという訳です。
	{
		static const char* const TBL_IgnoreErrid[]={	//無視するエラーIDの一覧
			"E2010052705",
			"E10092103B",	//{{2016/09/21追加:無視するエラーに「E10092103B」を暫定的に追加しました。本当に無視して良いのかどうかは問い合わせ中です。}}	⇒{{2016/09/22コメント追記:「E10092103B」について、CRIサポートから回答を頂きました。}}
		//	"E～～～～～",	//※TODO:今後、無視するエラーが増えたら、ここに追加して行け。
		};
		int i, len = (int)strcspn(errid, ":");	//errid="エラーID:エラー内容"の、"エラーID"の部分の文字数を求める。
		for(i = 0; i < _countof(TBL_IgnoreErrid); i++) {
			if((strlen(TBL_IgnoreErrid[i]) ==     len) &&	//無視するエラーIDの文字数が、上で求めた"エラーID"の部分の文字数と同じであり…
			  !strncmp(TBL_IgnoreErrid[i], errid, len)) {	//無視するエラーIDの文字列と、上で求めた"エラーID"の部分の文字列が同じならば…
			//{{2016/08/07変更:無視する警告やエラーのメッセージを標準エラー出力に出力する際、同じ内容を連続して出力しないようにしました。
			//  //{{2016/08/07変更:無視する警告やエラーのメッセージ出力先を、デバッグ端末⇒標準エラー出力に変更しました。
			//  //	OutputDebugString(criErr_ConvertIdToMessage(errid, p1, p2));	//単純に無視しても良いだろうが、一応デバッグ端末にメッセージを出力しておく事にした。	※TODO:エラーも警告もアプリケーション側へコールバックで通知する方が良いと思う。既に/clip/tool/ssbpLib/ssbpLib.cppではそうしているので当モジュールも同じように変更しろ。
			//  //↓2016/08/07変更:無視する警告やエラーのメッセージ出力先を、デバッグ端末⇒標準エラー出力に変更しました。
			//	fprintf(stderr, "%s: %s\n", "user_error_callback_func", criErr_ConvertIdToMessage(errid, p1, p2));	//単純に無視しても良いだろうが、一応標準エラー出力にメッセージを出力しておく事にした。	※TODO:エラーも警告もアプリケーション側へコールバックで通知する方が良いと思う。既に/clip/tool/ssbpLib/ssbpLib.cppではそうしているので当モジュールも同じように変更しろ。
			//  //}}2016/08/07変更:無視する警告やエラーのメッセージ出力先を、デバッグ端末⇒標準エラー出力に変更しました。
			//↓2016/08/07変更:無視する警告やエラーのメッセージを標準エラー出力に出力する際、同じ内容を連続して出力しないようにしました。
				output_ignored_message(errid, p1, p2, parray);
			//}}2016/08/07変更:無視する警告やエラーのメッセージを標準エラー出力に出力する際、同じ内容を連続して出力しないようにしました。
				return;	//ここまで
			}
		}
	}
//}}2016/08/07追加:特定のエラーを無視出来るようにしました。
	die("%s: %s\n", "user_error_callback_func", criErr_ConvertIdToMessage(errid, p1, p2));
}
/*--------------------------------------------------------------------------*/
static void* user_malloc(void* obj, CriUint32 size) {
	//ユーザアロケータが確保して返すメモリは、アライメントされていなくても構わない。
	//例えば奇数アドレスから開始するメモリを返しても、問題無く動作する事を確認した。
	return malloc(size);
}
/*--------------------------------------------------------------------------*/
static void user_free(void* obj, void* mem) {
	free(mem);
}
/****************************************************************************
 *	SofdecHelper
 ****************************************************************************/
static ST_ManaPlayer* SofdecHelper_GetManaPlayer(int iManaPlayer) {
	ST_ManaPlayer* pManaPlayer;
	iManaPlayer--;							//プレイヤーid⇒管理用構造体のインデクスに変換する。	//プレイヤーid=(管理用構造体のインデクス+1)
//{{2016/08/01変更:シームレス連続再生に対応しました。
//	if((unsigned)iManaPlayer >= (unsigned)NumMovies) { DIE(); }	//管理用構造体のインデクスが不正。
//↓2016/08/01変更:シームレス連続再生に対応しました。
	if((unsigned)iManaPlayer >= (unsigned)stSofdecHelperInfo.NumMovies) { DIE(); }	//管理用構造体のインデクスが不正。
//{{2016/08/01変更:シームレス連続再生に対応しました。
	pManaPlayer = &TBL_ManaPlayer[iManaPlayer];
//{{2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//	if(!pManaPlayer->player) { DIE(); }				//空きエントリを参照しようとした。
//↓2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
	if(!(pManaPlayer->Flag & ManaPlayer_Flag_InUse)) { DIE(); }	//使用中でないエントリを参照しようとした。
//}}2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
	return pManaPlayer;
}
/*--------------------------------------------------------------------------*/
//{{2016/08/01追加:シームレス連続再生に対応しました。
//連結再生ファイル要求コールバック定義
// * Mon Aug 01 21:12:56 JST 2016 Naoyuki Sawa
// - 2016/08/01現時点では、criManaPlayer_Loop()とシームレス連続再生を併用した時に、直観と違う挙動となる。
//   詳細は以下の通りです。
// - 「criManaPlayer_Loop(pManaPlayer->player,1)」を指定してループ再生を行うと、このコールバックは呼び出されない。(実験より)
//   従って、criManaPlayer_Loop()とシームレス連続再生を併用すると、二つ目以降のムービーへ移行しない。
//   いずれ、単純なループもcriManaPlayer_Loop()を使わずシームレス連続再生を使って行うように変更しようと思っているが、それまでは注意して下さい。
static void user_movie_request_callback_func(void* obj, CriManaPlayerHn player) {
	ST_ManaPlayer* pManaPlayer = obj;
	//今回再生するムービーのパス名が有れば…
	if(pManaPlayer->sNextMoviePath[0]) {
		//今回再生するムービーファイルを設定する。
		// - pManaPlayer->playerと、コールバック引数のplayerが、同じものであるかどうかが、Sofdec2のマニュアルには書かれていないようだ。
		//   しかし、ADX2のマニュアルのシームレス連結再生の頁には、
		//   │例えば、複数のオンメモリデータを連結再生する場合、初回のデータ設定はAtomExプレーヤに対してcriAtomExPlayer_SetData関数を実行しますが、
		//   │コールバック関数内で後続データを指定する際には、コールバック関数の引数で渡って来るAtomプレーヤハンドルに対しcriAtomPlayer_SetData関数を使用してデータを指定します。
		//   と書いてある。おそらく、Sofdec2も同様なのではないかと思う。
		//   従って以下の処理では、criManaPlayer_SetFile()の第一引数に、pManaPlayer->playerではなく、コールバック関数の引数playerを指定するのが安全だろう。
#ifndef USE_CPK
		criManaPlayer_SetFile(player, NULL,   pManaPlayer->sNextMoviePath);
#else //USE_CPK
		criManaPlayer_SetFile(player, binder, pManaPlayer->sNextMoviePath);
#endif//USE_CPK
		//次に再生するムービーのパス名を取得する。
		if(stSofdecHelperInfo.fnGetNextMovie) {
			char sCurrentMoviePath[_MAX_PATH];
			strcpy(sCurrentMoviePath, pManaPlayer->sNextMoviePath);
			pManaPlayer->sNextMoviePath[0] = '\0';		//まず空文字にしておく。
			(*stSofdecHelperInfo.fnGetNextMovie)(sCurrentMoviePath, pManaPlayer->sNextMoviePath);
		}
//{{2016/09/20追加:非ループのムービーが誤って二周ループしてしまう事が有った問題を修正しました。
	//今回再生するムービーのパス名が無ければ…
	} else {
		//プレイヤーの状態がCRIMANAPLAYER_STATUS_PLAYING以上ならば、何もせずに処理を返す。
		//プレイヤーの状態がCRIMANAPLAYER_STATUS_PLAYING未満ならば、criManaPlayer_DeferCallback()を呼び出す。
		int Status = criManaPlayer_GetStatus(player);
		if(Status < CRIMANAPLAYER_STATUS_PLAYING) {
			criManaPlayer_DeferCallback(player);
		}
//}}2016/09/20追加:非ループのムービーが誤って二周ループしてしまう事が有った問題を修正しました。
	}
}
//}}2016/08/01追加:シームレス連続再生に対応しました。
/*--------------------------------------------------------------------------*/
//{{2016/08/01変更:シームレス連続再生に対応しました。
//void SofdecHelper_Init(int _NumMovies_NumSounds) {
//↓2016/08/01変更:シームレス連続再生に対応しました。
static void app_data_request_callback(void* obj, CriAtomExPlaybackId id, CriAtomPlayerHn player);	//前方参照
SOFDECHELPER_DLLAPI void SofdecHelper_Init(const ST_SofdecHelperInfo* pInfo) {
	//初期化情報を格納する。
	stSofdecHelperInfo = *pInfo;
//}}2016/08/01変更:シームレス連続再生に対応しました。
	//--- メモリ確保 ---
	{
//{{2016/08/01削除:シームレス連続再生に対応しました。
//		int _NumMovies = (unsigned short)(_NumMovies_NumSounds >>  0);
//		int _NumSounds = (unsigned short)(_NumMovies_NumSounds >> 16);
//}}2016/08/01削除:シームレス連続再生に対応しました。
		//--- ムービー ---
//{{2016/08/01削除:シームレス連続再生に対応しました。
//		//同時再生するムービーの数を格納する。
//		NumMovies = _NumMovies;	//0可
//}}2016/08/01削除:シームレス連続再生に対応しました。
		//管理用構造体の配列を確保する。
//{{2016/08/01変更:シームレス連続再生に対応しました。
//		TBL_ManaPlayer = calloc(NumMovies, sizeof(ST_ManaPlayer));	//TBL_ManaPlayer[].playerの初期値をNULLとするために、malloc()でなくcalloc()が必須です。
//↓2016/08/01変更:シームレス連続再生に対応しました。
		TBL_ManaPlayer = calloc(stSofdecHelperInfo.NumMovies, sizeof(ST_ManaPlayer));	//TBL_ManaPlayer[].playerの初期値をNULLとするために、malloc()でなくcalloc()が必須です。
//}}2016/08/01変更:シームレス連続再生に対応しました。
		if(!TBL_ManaPlayer) { DIE(); }
		//'致命的なエラー'が発生した時に呼び出される、エラーコールバック関数を登録する。
		// - エラーコールバック関数を登録せずに実行を開始する事も出来るが、'致命的なエラー'が発生した時に何も通知せずに実行を継続してしまいバグの発見が遅れる。
		//   全ての'致命的なエラー'の通知を確実に受け取れるようにするためには、初期化処理の一番最初(ここ)でエラーコールバック関数を登録しておくのが確実である。
		criErr_SetCallback(user_error_callback_func);
		//--- サウンド ---
#ifndef USE_ADX
//{{2016/08/01変更:シームレス連続再生に対応しました。
//		if(_NumSounds) { DIE(); }
//↓2016/08/01変更:シームレス連続再生に対応しました。
		if(stSofdecHelperInfo.NumSounds) { DIE(); }
//}}2016/08/01変更:シームレス連続再生に対応しました。
#else //USE_ADX
//{{2016/08/01削除:シームレス連続再生に対応しました。
//		//同時再生するサウンドの数を格納する。
//		NumSounds = _NumSounds;	//0可
//}}2016/08/01削除:シームレス連続再生に対応しました。
		//プレーヤハンドルの配列を確保する。
//{{2016/08/01変更:シームレス連続再生に対応しました。
//		TBL_AtomExPlayer = calloc(NumSounds, sizeof(CriAtomExPlayerHn*));
//↓2016/08/01変更:シームレス連続再生に対応しました。
		TBL_AtomExPlayer = calloc(stSofdecHelperInfo.NumSounds, sizeof(ST_AtomExPlayer));
//}}2016/08/01変更:シームレス連続再生に対応しました。
		if(!TBL_AtomExPlayer) { DIE(); }
//{{2016/09/18追加:サウンドの再生準備を対応しました。
		//再生プールの配列を確保する。
		TBL_AtomExPlayerPool = calloc(stSofdecHelperInfo.NumSounds, sizeof(ST_AtomExPlayerPool));
		if(!TBL_AtomExPlayerPool) { DIE(); }
//}}2016/09/18追加:サウンドの再生準備を対応しました。
#endif//USE_ADX
	}
	//--- Atom Exライブラリの初期化処理 ---
	{
#ifndef USE_ADX
//{{2016/08/01変更:シームレス連続再生に対応しました。
//		int NumLoaders = NumMovies;
//↓2016/08/01変更:シームレス連続再生に対応しました。
//		int NumLoaders = stSofdecHelperInfo.NumMovies;
//↓2016/09/16変更:使用するCriFsLoaderの数を、厳密な同時再生数ぴったりでなく、1多い数に変更しました。
		// * Fri Sep 16 21:30:10 JST 2016 Naoyuki Sawa
		// - 使用するCriFsLoaderの数を、厳密な同時再生数ぴったりでなく、1多い数に変更しました。
		//   そうした理由は、パッキングファイルに対するバインダでなく、カレントフォルダに対するバインダを経由して、ムービーを限界数まで再生した時に、
		//   │E2008070931:Can not allocate loader handle. (Increase num_loaders of CriFsConfig.)
		//   というエラーが出る事が有ったからです。(パッキングファイルに対するバインダを経由すると何故かエラーが出ませんでしたが、理由は判りません。)
		// - 上記のエラーが出る原因は、まだはっきりとは判っていないのですが、おそらく以下の点が関係しているのではないかと思っています。
		//   □C:\cri\documentation\CRI_ADX2_Manual_pc_j.chm
		//   │ADX2とSofdec2を併用する際の注意点は？
		//   │ライブラリ初期化時の注意点
		//   │（３）ローダ
		//   │CRIAtomライブラリやCRIManaライブラリは、CRIFileSystemライブラリのローダを使用してストリーム再生を行います。
		//   │ストリーム再生を行う場合には、ファイルの読み込みに使用するローダの数に加え、ボイスプール内のボイス数や、Manaプレーヤ数分のローダを余分に確保しておく必要があります。
		//   │以下のAPIは、関数実行時に一時的にローダ（CriFsLoaderHn）を使用します。
		//   │（関数実行開始時にcriFsLoader_Create関数でローダが確保されます。）
		//   │criAtomEx_RegisterAcfFile
		//   │criAtomEx_RegisterAcfFileById
		//   │criAtomEx_GetAcfVersionFromFile
		//   │criAtomEx_GetAcfVersionFromFileById
		//   │criAtomExAcb_LoadAcbFile
		//   │criAtomExAcb_LoadAcbFileById
		//   │criAtomExStreamingCache_LoadWaveformByNameAsync
		//   │criAtomExStreamingCache_LoadWaveformByIdAsync
		//   │上記関数を実行する際には、空きローダハンドルが１つ以上ある状態になるよう、ローダ数を調整してください。
		//   │［注意］
		//   │上記APIのうち、criAtomExStreamingCache_LoadWaveformByNameAsync関数とcriAtomExStreamingCache_LoadWaveformByIdAsync関数については、関数が返すローダハンドルを別途ユーザが破棄する必要があります。
		//   │その他のAPIについては、関数終了時点でcriFsLoader_Destroy関数でローダが破棄されます。
		//   当プログラムは、上記のAPIを明示的に呼び出していませんが、間接的に呼び出している可能性が有るのではないかと思いました。
		//   要するに、config_fs.num_loadersには、厳密な同時再生数ぴったりでなく、少なくとも1多い数を設定する必要が有ると言う事です。
		//   1多い数で充分かどうかは未検証なのですが、とりあえず1多い数にするとエラーが出なくなったので、1多い数に設定する事にしました。
		// - ここでNumLoadersを1多い数に設定すると、config_fs.num_loadersだけでなく、config_fs.max_filesも1多い数になってしまいます。
		//   実験した限りでは、1多い数にする必要が有るのはconfig_fs.num_loadersだけみたいで、config_fs.max_filesは同時再生数ぴったりでもエラーは出ませんでした。
		//   しかし、安全のためと、処理を簡単にするために、config_fs.max_filesも1多い数にしておく事にしました。(無駄かも知れませんが、さほど違いは無いでしょう。)
//		int NumLoaders = stSofdecHelperInfo.NumMovies + 1/*※要検証、調整可*/;
//↓2016/09/24変更:やっぱり、num_loadersもアルファムービーを考慮して、二倍にしました。
		// * Sat Sep 24 21:37:23 JST 2016 Naoyuki Sawa
		// - やっぱり、num_loadersもアルファムービーを考慮して、二倍にしました。
		//   経緯は、以下の通りです。
		// - これまで、/clip/test.win/Define.hにて、SOFDEC_NUM_MOVIES=8,ADX_NUM_SOUNDS=8の設定で使っている限りでは、エラーは発生していませんでした。
		//   しかし、SOFDEC_NUM_MOVIES=9,ADX_NUM_SOUNDS=7や、SOFDEC_NUM_MOVIES=8,ADX_NUM_SOUNDS=0にしたところ、「E2008070931:Can not allocate loader handle. (Increase num_loaders of CriFsConfig.)」エラーが発生しました。
		//   どうやら、ADX_NUM_SOUNDSを8未満にすると、エラーが発生するようです。
		//   エラーが発生する箇所を調べた所、SofdecHelper_Init()内のcriManaPlayer_Create()を何個目か作成した時点で、エラーが発生していました。
		//   ムービーやサウンドの再生準備や再生開始しようとした時点ではなく、プレイヤーを作成した時点でエラーが発生するというのが謎です。
		//   はっきりした原因は判っていないのですが、やはり、Manaライブラリがプレイヤーを作成する時点で、アルファムービーを想定して二倍のローダを確保しようとしているのではないかと推測しました。
		//   これまでエラーが出ていなかった理由は、SOFDEC_NUM_MOVIESの二倍がたまたまデフォルトのローダ数に収まっていたからではないかと思います。
		//   そこで、やっぱり、num_loadersもアルファムービーを考慮して、二倍にしてみた所、エラーは出なくなりました。(max_filesは二倍にする必要は無いかも知れないのですが、現在の所max_filesも二倍にしています。)
		//   本当に二倍必要かどうかははっきりせず、もしかすると多少の無駄が生じているかも知れないのですが、PCではさほど問題にならないでしょう。
		int NumLoaders = (stSofdecHelperInfo.NumMovies * 2) + 1/*※要検証、調整可*/;
//}}2016/09/24変更:やっぱり、num_loadersもアルファムービーを考慮して、二倍にしました。
		CriFsConfig config_fs;
		CriAtomExConfig config_atom_ex;
		//Atom Exライブラリの、ユーザアロケータを登録する。
		// - Atom Exライブラリがワーク領域を確保する方法には、User Allocator方式とFixed Memory方式が有る。
		//   User Allocator方式の方が簡単なので、User Allocator方式を使用する事にした。
		criAtomEx_SetUserAllocator(user_malloc, user_free, NULL);
		//File Systemライブラリの初期化用コンフィグ構造体にデフォルト値をセットする。
		criFs_SetDefaultConfig(&config_fs);
		//Atom Exライブラリの初期化用コンフィグ構造体にデフォルト値をセットする。
		criAtomEx_SetDefaultConfig(&config_atom_ex);
		//初期化用コンフィグ構造体を調整する。
		// - 初期化用コンフィグ構造体の値をデフォルト値よりも減らすと初期化エラーになる事が有ったので、デフォルト値よりも減らさないようにした。
		if(config_fs.num_loaders < NumLoaders) { config_fs.num_loaders = NumLoaders; }	//使用するCriFsLoaderの数	┬max_files数は、USMファイルを単独で再生する場合は、同時再生するUSM個数分指定します。USMファイルをCPKファイルにパッキングして再生する場合は、CPKの個数を設定します。
		if(config_fs.max_files   < NumLoaders) { config_fs.max_files   = NumLoaders; }	//最大同時オープンファイル数	┘動画の他にADX2による音声再生を別途同時に行う場合は、さらに音声分も調整して下さい。ストリーミング再生ではなくメモリ再生を行う場合は、num_loadersやmax_filesの数値を増やす必要はありません。
		config_atom_ex.fs_config = &config_fs;			//調整したCriFsConfigをCriAtomExConfigに設定する。
//{{2016/09/18追加:サウンドの同時再生数が多い時に「W2010030305:Can not allocate playback info. (Increase max_virtual_voices of CriAtomExConfig.)」が出ていた問題を修正しました。
		// * Sun Sep 18 22:15:08 JST 2016 Naoyuki Sawa
		// - max_virtual_voicesを、最大同時発音数以上に設定する必要が有ります。
		//   ただし、max_virtual_voicesの設定を最大同時発音数に設定しても、回避出来ないエラーが発生する事が有ります。
		//   1V当たりの発音リクエスト数はプログラム作成時に予測する事は難しく、どこまでmax_virtual_voicesを増やせば回避出来るか判断出来ないので、このエラーを無視して回避する必要が有ります。(※TODO:2016/09/18現在はまだエラーを無視していません。)
		//   また、AdxHelper_GetTotalTime()が1ストリーム消費する分も考慮するのを忘れていました。AdxHelper_GetTotalTime()は失敗を無視出来ないので少々厄介です。(※TODO:2016/09/18現在は未解決です。)
		//   2016/09/18現在は、とりあえず+1だけ余裕を持たせてあります。実験した限りこれでエラーが出なくなったのですが、下記の説明を読むとこれだけでは足りないはずです。(※TODO:要検討)
		//   □C:\cri\documentation\CRI_ADX2_Manual_pc_j.chm
		//   │構造体CriAtomExConfigTag
		//   │CriSint32 max_virtual_voices
		//   │最大バーチャルボイス数
		//   │説明:
		//   │アプリケーションで同時に発音制御を行うボイスの数です。
		//   │Atomライブラリは、初期化時にmax_virtual_voicesで指定された数分だけ発音管理に必要なリソースを確保します。
		//   │備考:
		//   │max_virtual_voicesで指定された数以上の音声を同時に発音することはできません。
		//   │また、max_virtual_voices分の発音リクエストを行ったとしても、実際に発音される音声の数は、必ずしもmax_virtual_voicesに一致するとは限りません。
		//   │実際に発音可能な音声の数は、ボイスプールで確保されたボイス数や、ターゲット機で利用可能なハードウェアボイスの数に依存します。
		//   │バーチャルボイス数の目安は、「最大同時発音数＋1V当たりの発音リクエスト数」です。													←←←■■■注目!!■■■
		//   │バーチャルボイス数が最大同時発音数より少ない場合や、発音数とリクエスト数の合計が最大バーチャルボイスを超える場合、エラーコールバック関数に警告が返される可能性があります。	←←←■■■注目!!■■■
		//   │CRIATOMEX_RETRY_VOICE_ALLOCATIONを指定してAtomExプレーヤを作成する場合、上記よりもさらに多くのバーチャルボイスを必要とする可能性があります。
		if(config_atom_ex.max_virtual_voices < (stSofdecHelperInfo.NumSounds + 1/*※要検討*/)) { config_atom_ex.max_virtual_voices = (stSofdecHelperInfo.NumSounds + 1/*※要検討*/); }
//}}2016/09/18追加:サウンドの同時再生数が多い時に「W2010030305:Can not allocate playback info. (Increase max_virtual_voices of CriAtomExConfig.)」が出ていた問題を修正しました。
		//Atom Exライブラリを初期化する。
		criAtomEx_Initialize(&config_atom_ex, NULL, 0);		//オーディオトラックを使用しない場合
#else //USE_ADX
//{{2016/08/01変更:シームレス連続再生に対応しました。
//		int NumLoaders = NumMovies + NumSounds;
//↓2016/08/01変更:シームレス連続再生に対応しました。
//		int NumLoaders = stSofdecHelperInfo.NumMovies + stSofdecHelperInfo.NumSounds;
//↓2016/09/16変更:使用するCriFsLoaderの数を、厳密な同時再生数ぴったりでなく、1多い数に変更しました。
		// * Fri Sep 16 21:30:10 JST 2016 Naoyuki Sawa
		// - 使用するCriFsLoaderの数を、厳密な同時再生数ぴったりでなく、1多い数に変更しました。
		//   そうした理由は、パッキングファイルに対するバインダでなく、カレントフォルダに対するバインダを経由して、ムービーを限界数まで再生した時に、
		//   │E2008070931:Can not allocate loader handle. (Increase num_loaders of CriFsConfig.)
		//   というエラーが出る事が有ったからです。(パッキングファイルに対するバインダを経由すると何故かエラーが出ませんでしたが、理由は判りません。)
		// - 上記のエラーが出る原因は、まだはっきりとは判っていないのですが、おそらく以下の点が関係しているのではないかと思っています。
		//   □C:\cri\documentation\CRI_ADX2_Manual_pc_j.chm
		//   │ADX2とSofdec2を併用する際の注意点は？
		//   │ライブラリ初期化時の注意点
		//   │（３）ローダ
		//   │CRIAtomライブラリやCRIManaライブラリは、CRIFileSystemライブラリのローダを使用してストリーム再生を行います。
		//   │ストリーム再生を行う場合には、ファイルの読み込みに使用するローダの数に加え、ボイスプール内のボイス数や、Manaプレーヤ数分のローダを余分に確保しておく必要があります。
		//   │以下のAPIは、関数実行時に一時的にローダ（CriFsLoaderHn）を使用します。
		//   │（関数実行開始時にcriFsLoader_Create関数でローダが確保されます。）
		//   │criAtomEx_RegisterAcfFile
		//   │criAtomEx_RegisterAcfFileById
		//   │criAtomEx_GetAcfVersionFromFile
		//   │criAtomEx_GetAcfVersionFromFileById
		//   │criAtomExAcb_LoadAcbFile
		//   │criAtomExAcb_LoadAcbFileById
		//   │criAtomExStreamingCache_LoadWaveformByNameAsync
		//   │criAtomExStreamingCache_LoadWaveformByIdAsync
		//   │上記関数を実行する際には、空きローダハンドルが１つ以上ある状態になるよう、ローダ数を調整してください。
		//   │［注意］
		//   │上記APIのうち、criAtomExStreamingCache_LoadWaveformByNameAsync関数とcriAtomExStreamingCache_LoadWaveformByIdAsync関数については、関数が返すローダハンドルを別途ユーザが破棄する必要があります。
		//   │その他のAPIについては、関数終了時点でcriFsLoader_Destroy関数でローダが破棄されます。
		//   当プログラムは、上記のAPIを明示的に呼び出していませんが、間接的に呼び出している可能性が有るのではないかと思いました。
		//   要するに、config_fs.num_loadersには、厳密な同時再生数ぴったりでなく、少なくとも1多い数を設定する必要が有ると言う事です。
		//   1多い数で充分かどうかは未検証なのですが、とりあえず1多い数にするとエラーが出なくなったので、1多い数に設定する事にしました。
		// - ここでNumLoadersを1多い数に設定すると、config_fs.num_loadersだけでなく、config_fs.max_filesも1多い数になってしまいます。
		//   実験した限りでは、1多い数にする必要が有るのはconfig_fs.num_loadersだけみたいで、config_fs.max_filesは同時再生数ぴったりでもエラーは出ませんでした。
		//   しかし、安全のためと、処理を簡単にするために、config_fs.max_filesも1多い数にしておく事にしました。(無駄かも知れませんが、さほど違いは無いでしょう。)
//		int NumLoaders = stSofdecHelperInfo.NumMovies + stSofdecHelperInfo.NumSounds + 1/*※要検証、調整可*/;
//↓2016/09/24変更:やっぱり、num_loadersもアルファムービーを考慮して、二倍にしました。
		// * Sat Sep 24 21:37:23 JST 2016 Naoyuki Sawa
		// - やっぱり、num_loadersもアルファムービーを考慮して、二倍にしました。
		//   経緯は、以下の通りです。
		// - これまで、/clip/test.win/Define.hにて、SOFDEC_NUM_MOVIES=8,ADX_NUM_SOUNDS=8の設定で使っている限りでは、エラーは発生していませんでした。
		//   しかし、SOFDEC_NUM_MOVIES=9,ADX_NUM_SOUNDS=7や、SOFDEC_NUM_MOVIES=8,ADX_NUM_SOUNDS=0にしたところ、「E2008070931:Can not allocate loader handle. (Increase num_loaders of CriFsConfig.)」エラーが発生しました。
		//   どうやら、ADX_NUM_SOUNDSを8未満にすると、エラーが発生するようです。
		//   エラーが発生する箇所を調べた所、SofdecHelper_Init()内のcriManaPlayer_Create()を何個目か作成した時点で、エラーが発生していました。
		//   ムービーやサウンドの再生準備や再生開始しようとした時点ではなく、プレイヤーを作成した時点でエラーが発生するというのが謎です。
		//   はっきりした原因は判っていないのですが、やはり、Manaライブラリがプレイヤーを作成する時点で、アルファムービーを想定して二倍のローダを確保しようとしているのではないかと推測しました。
		//   これまでエラーが出ていなかった理由は、SOFDEC_NUM_MOVIESの二倍がたまたまデフォルトのローダ数に収まっていたからではないかと思います。
		//   そこで、やっぱり、num_loadersもアルファムービーを考慮して、二倍にしてみた所、エラーは出なくなりました。(max_filesは二倍にする必要は無いかも知れないのですが、現在の所max_filesも二倍にしています。)
		//   本当に二倍必要かどうかははっきりせず、もしかすると多少の無駄が生じているかも知れないのですが、PCではさほど問題にならないでしょう。
		int NumLoaders = (stSofdecHelperInfo.NumMovies * 2) + stSofdecHelperInfo.NumSounds + 1/*※要検証、調整可*/;
//}}2016/09/24変更:やっぱり、num_loadersもアルファムービーを考慮して、二倍にしました。
		CriFsConfig config_fs;
		CriAtomExConfig_WASAPI config_pc;
		//Atom Exライブラリの、ユーザアロケータを登録する。
		// - Atom Exライブラリがワーク領域を確保する方法には、User Allocator方式とFixed Memory方式が有る。
		//   User Allocator方式の方が簡単なので、User Allocator方式を使用する事にした。
		criAtomEx_SetUserAllocator(user_malloc, user_free, NULL);
		//File Systemライブラリの初期化用コンフィグ構造体にデフォルト値をセットする。
		criFs_SetDefaultConfig(&config_fs);
		//Atom Exライブラリの初期化用コンフィグ構造体にデフォルト値をセットする。
		criAtomEx_SetDefaultConfig_WASAPI(&config_pc);
		//初期化用コンフィグ構造体を調整する。
		// - 初期化用コンフィグ構造体の値をデフォルト値よりも減らすと初期化エラーになる事が有ったので、デフォルト値よりも減らさないようにした。
		if(config_fs.num_loaders < NumLoaders) { config_fs.num_loaders = NumLoaders; }	//使用するCriFsLoaderの数	┬max_files数は、USMファイルを単独で再生する場合は、同時再生するUSM個数分指定します。USMファイルをCPK(パッキングファイル)にパッキングして再生する場合は、CPKの個数を設定します。
		if(config_fs.max_files   < NumLoaders) { config_fs.max_files   = NumLoaders; }	//最大同時オープンファイル数	┘動画の他にADX2による音声再生を別途同時に行う場合は、さらに音声分も調整して下さい。ストリーミング再生ではなくメモリ再生を行う場合は、num_loadersやmax_filesの数値を増やす必要はありません。
		config_pc.atom_ex.fs_config = &config_fs;		//調整したCriFsConfigをCriAtomExConfigに設定する。
//{{2016/09/18追加:サウンドの同時再生数が多い時に「W2010030305:Can not allocate playback info. (Increase max_virtual_voices of CriAtomExConfig.)」が出ていた問題を修正しました。
		// * Sun Sep 18 22:15:08 JST 2016 Naoyuki Sawa
		// - max_virtual_voicesを、最大同時発音数以上に設定する必要が有ります。
		//   ただし、max_virtual_voicesの設定を最大同時発音数に設定しても、回避出来ないエラーが発生する事が有ります。
		//   1V当たりの発音リクエスト数はプログラム作成時に予測する事は難しく、どこまでmax_virtual_voicesを増やせば回避出来るか判断出来ないので、このエラーを無視して回避する必要が有ります。(※TODO:2016/09/18現在はまだエラーを無視していません。)
		//   また、AdxHelper_GetTotalTime()が1ストリーム消費する分も考慮するのを忘れていました。AdxHelper_GetTotalTime()は失敗を無視出来ないので少々厄介です。(※TODO:2016/09/18現在は未解決です。)
		//   2016/09/18現在は、とりあえず+1だけ余裕を持たせてあります。実験した限りこれでエラーが出なくなったのですが、下記の説明を読むとこれだけでは足りないはずです。(※TODO:要検討)
		//   □C:\cri\documentation\CRI_ADX2_Manual_pc_j.chm
		//   │構造体CriAtomExConfigTag
		//   │CriSint32 max_virtual_voices
		//   │最大バーチャルボイス数
		//   │説明:
		//   │アプリケーションで同時に発音制御を行うボイスの数です。
		//   │Atomライブラリは、初期化時にmax_virtual_voicesで指定された数分だけ発音管理に必要なリソースを確保します。
		//   │備考:
		//   │max_virtual_voicesで指定された数以上の音声を同時に発音することはできません。
		//   │また、max_virtual_voices分の発音リクエストを行ったとしても、実際に発音される音声の数は、必ずしもmax_virtual_voicesに一致するとは限りません。
		//   │実際に発音可能な音声の数は、ボイスプールで確保されたボイス数や、ターゲット機で利用可能なハードウェアボイスの数に依存します。
		//   │バーチャルボイス数の目安は、「最大同時発音数＋1V当たりの発音リクエスト数」です。													←←←■■■注目!!■■■
		//   │バーチャルボイス数が最大同時発音数より少ない場合や、発音数とリクエスト数の合計が最大バーチャルボイスを超える場合、エラーコールバック関数に警告が返される可能性があります。	←←←■■■注目!!■■■
		//   │CRIATOMEX_RETRY_VOICE_ALLOCATIONを指定してAtomExプレーヤを作成する場合、上記よりもさらに多くのバーチャルボイスを必要とする可能性があります。
		if(config_pc.atom_ex.max_virtual_voices < (stSofdecHelperInfo.NumSounds + 1/*※要検討*/)) { config_pc.atom_ex.max_virtual_voices = (stSofdecHelperInfo.NumSounds + 1/*※要検討*/); }
//}}2016/09/18追加:サウンドの同時再生数が多い時に「W2010030305:Can not allocate playback info. (Increase max_virtual_voices of CriAtomExConfig.)」が出ていた問題を修正しました。
		//Atom Exライブラリを初期化する。
		criAtomEx_Initialize_WASAPI(&config_pc, NULL, 0);	//オーディオトラックを使用する場合
#endif//USE_ADX
	}
#ifdef  USE_CPK
	//--- バインダの初期化処理 ---
	{
		//File Systemライブラリの、バインダモジュール関数で使用されるメモリ管理関数を登録する。
		// - パッキングファイルを使用する場合、File Systemライブラリの、バインダモジュール関数(criFsBinder_BindCpk()等)を呼び出す必要が生じる。
		//   File Systemライブラリを初期化するためのcriFs_InitializeLibrary()は、criAtomEx_Initialize(),又は,criAtomEx_Initialize_WASAPI()の内部で呼び出されるが、criFsBinder_SetUserHeapFunc()は呼び出されないようだ。
		//   criFsBinder_SetUserHeapFunc()を呼び出さずに、criFsBinder_BindCpk()をワークエリアを指定せずに呼び出すと、エラーとなる。
		//   criFsBinder_BindCpk()等のワークエリアを明示的に指定するのは手間が掛かるので、ここでcriFsBinder_SetUserHeapFunc()を呼び出しておき、criFsBinder_BindCpk()等にはワークエリアを指定せずに(=User Allocator方式で)使用するのが良いだろう。
		//   尚、criFsBinder_SetUserHeapFunc()は、criAtomEx_SetUserAllocator()と違って、ライブラリの初期化前ではなく、ライブラリの初期化後に呼び出さなければいけない事に注意せよ。
		//   criFs_InitializeLibrary()は、criAtomEx_Initialize(),又は,criAtomEx_Initialize_WASAPI()によって呼び出されるので、その直後であるここでcriFsBinder_SetUserHeapFunc()を呼び出すのが適切だ。
		criFsBinder_SetUserHeapFunc(user_malloc, user_free, NULL);
		//バインダを生成する。
		criFsBinder_Create(&binder);
		//カレントフォルダに'default.cpk'が有るか無いかによって、下記①②いずれかのバインドを行う。
		//①カレントフォルダに'default.cpk'が有る場合
		//│優先度1:default.cpk,優先度2:カレントフォルダ の順でマルチバインドを行う。
		//│この場合、default.cpk内でファイルが検索され、default.cpk内にファイルが無ければ、カレントフォルダ及びそれ以下のフォルダ内でファイルが検索される。
		//│DxLibに例えると、DXアーカイブファイルを使用し,かつ,'SetDXArchivePriority(0/*=DXアーカイブファイルを優先(デフォルト)*/)'に設定したのと、同様の動作になる。
		//②カレントフォルダに'default.cpk'が無い場合
		//│カレントフォルダだけにバインドを行う。
		//│この場合、カレントフォルダ及びそれ以下のフォルダ内だけで、ファイルが検索される。
		//│DxLibに例えると、DXアーカイブファイルを使用しないのと、同様の動作になる。
		// - パッキングファイル名は、'default.cpk'固定とした。
		//   このファイル名は、「CPK File Builder」(/cri/tools/crifilesystem/CpkFileBuilder.exe)のデフォルト出力ファイル名から取りました。
		//   現在の所は、一つのアプリケーション内で複数のパッキングファイルを使わない予定なので、ファイル名は'default.cpk'固定で問題無いと思います。
		//   もし今後、一つのアプリケーション内で複数のパッキングファイルを使う事になったら、複数のパッキングファイル名を指定する方法を検討しようと思います。
		// - パッキングファイルを作成するツールについて、注意点が有ります。
		//   パッキングファイルを作成するツールは、CpkFileBuilder.exe,CriPackedFileMaker.exe,cpkmakec.exeの三種類有ります。
		//   フォルダをドラッグ&ドロップ(cpkmakec.exeの場合はフォルダ名を指定)してパッキングファイルを作成した場合、各ツールによって、ファイルのコンテンツパスが異なるようです。
		//   具体的には、以下の通りです。
		//   ・CpkFileBuilder.exe
		//     ドラッグ&ドロップしたフォルダも、コンテンツパスに含めます。
		//     マルチバインドで使用する場合を考慮すると、このツールの挙動が望ましいと思います。
		//     例えば、appフォルダの下のgrpフォルダをドラッグ&ドロップしてパッキングファイルを作成し、grp/mov/test.usmにアクセスする場合、マルチバンイドのどちらでも'grp/mov/test.usm'というパスになるからです。
		//   ・CriPackedFileMaker.exe
		//     フォルダ名は全て取り除かれるようです。
		//     ドラッグ&ドロップしたフォルダも途中の階層のフォルダも全て取り除いて、フラットな構造のコンテンツパスになるようです。
		//     アプリケーションでは使い辛いので、CriPackedFileMaker.exeでパッキングファイルを作るのは避ける方が良いと思います。
		//     ただし、既に作成したパッキングファイルの内容を確認出来るのはCriPackedFileMaker.exeだけ(?)みたいなので、その用途としては使えると思います。	⇒訂正:CpkFileBuilder.exeの[ツール]→[CPKファイルビューア]でも見れました。どうやらCriPackedFileMaker.exeは用無しになりそうです。
		//   ・cpkmakec.exe
		//     指定した最上位のフォルダは取り除かれて、それ以下のフォルダはコンテンツパスに含まれるようです。
		//     例えば、'app/grp/mov/test.usm'というフォルダ階層が有って、grpフォルダを指定すると、コンテンツパスは'mov/test.usm'になります。
		//     この挙動自体は、DxLibのDXAファイル簡易作成ソフト(DxaEncode.exe)の挙動と似ているのですが、使用時の挙動が違うので注意が必要です。
		//     DxLibは、DXアーカイブを使用する時に、パスの先頭のフォルダをDXアーカイブ名として消費し、それ以降のパスを使ってDXアーカイブ内を検索します。
		//     一方、パッキングファイルの場合は、criFsBinder_BindCpk()がパスの先頭のフォルダを消費したりはしないので、cpkmakec.exeを利用する場合はアプリケーションが明示的にパスの先頭のフォルダを削除してcriFsBinder_BindCpk()を呼び出す必要が生じます。
		//     そうしないと、マルチバインドの時にパッキングファイルから検索するかフォルダから検索するかによって、コンテンツパスが変わってしまうからです。
		//     現実的には、そのような処理を行うのはバグの元だと思うので、望ましくありません。
		//     cpkmakec.exeでなくCpkFileBuilder.exeを使うようにするか、又は、cpkmakec.exeを使う場合はダミーのフォルダを作って目的のフォルダをその中に入れ、ダミーのフォルダを指定してパッキングファイルを作成するのが良いと思います。
		//     そうすれば、cpkmakec.exeは、コンテンツパスにダミーのフォルダを含めず目的のフォルダ以降を残すので、結果としてCpkFileBuilder.exeが生成するのと同じコンテンツパスが得られます。
		//   まとめると、CpkFileBuilder.exeを使うのが一番安全です。							←←←←←←←←←←┬■■■■■結論■■■■■
		//   どうしてもコマンドラインで処理する必要が有りcpkmakec.exeを使う場合は、上述のような工夫が必要です。		←←←←←←←←←←┘
		// - この下の処理で取得するバインダID(binder_id)は、記憶しておく必要はありません。
		//   SofdecHelper_Free()の中でcriFsBinder_Destroy()を呼ぶと、そのバインダにバインドされているバインダIDも、自動的に破棄されるからです。
		//   従って、現在のアプリケーション構造では、明示的にcriFsBinder_Unbind()に呼び出す必要は有りません。
		//   もし今後、実行中にバインド/アンバインドを繰り返すような構造に変更する場合は、バインダIDを記憶しておいて、明示的にcriFsBinder_Unbind()を呼び出すようにして下さい。
		// * Wed Apr 06 21:53:01 JST 2016 Naoyuki Sawa
		// - カレントフォルダに'default.cpk'が有るかどうかを調べるために、stat()ではなくGetFileAttributes()を使うように変更しました。
		//   変更した理由は、VC++2015のランタイムのバグのために、WinXpではstat()が常に-1を返すからです。
		//   詳細は、下記の資料を参照して下さい。(下記の資料は_stat()について述べていますが、stat()でも同じです。
		//   尚、2016/04/06現在のVC++2015 Update2でもまだ直っていませんでした。
		//   □「Stack Overflow - Visual C++ 2015 express: _stat not working on Windows XP」
		//   │(http://stackoverflow.com/questions/32452777/visual-c-2015-express-stat-not-working-on-windows-xp)
		//   │As pointed out in the comments, it is a bug in the runtime.
		//   │Right now (2015-09-09) the fix is not yet available in an update, but probably will be soon.
		//   │A workaround is to use GetFileAttributesEx instead.
		CriFsBindId binder_id;	//バインダIDを記憶しておく必要は無いので、ローカル変数として良い。詳細は、上述の説明を参照して下さい。
		CriFsBinderStatus Status;
//{{2016/05/16変更:カレントフォルダに有る全ての'*.cpk'(有れば)にバインドするように変更しました。これまではカレントフォルダに有る'default.cpk'(有れば)だけにバインドしていました。変更後も'default.cpk'は自動的にバインド対象になりますので、既存のアプリケーションに対する互換性の問題は生じません。
//	//{{2016/04/06修正:カレントフォルダに'default.cpk'が有るかどうかを調べるために、stat()ではなくGetFileAttributes()を使うように変更しました。
//	//	struct stat buf;
//	//↓2016/04/06修正:カレントフォルダに'default.cpk'が有るかどうかを調べるために、stat()ではなくGetFileAttributes()を使うように変更しました。
//		int attr;
//	//}}2016/04/06修正:カレントフォルダに'default.cpk'が有るかどうかを調べるために、stat()ではなくGetFileAttributes()を使うように変更しました。
//		//カレントフォルダに'default.cpk'が有り,かつ,通常のファイルならば…
//	//{{2016/04/06修正:カレントフォルダに'default.cpk'が有るかどうかを調べるために、stat()ではなくGetFileAttributes()を使うように変更しました。
//	//	if(!stat("default.cpk", &buf) && (buf.st_mode & S_IFREG)) {	//VC++にはS_ISREG()マクロが無いので、直接S_IFREGを使って判定する必要が有ります。
//	//↓2016/04/06修正:カレントフォルダに'default.cpk'が有るかどうかを調べるために、stat()ではなくGetFileAttributes()を使うように変更しました。
//		attr = GetFileAttributes("default.cpk");
//		if((attr != -1) && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {	//(attr&FILE_ATTRIBUTE_NORMAL)で判定しないように注意せよ。FILE_ATTRIBUTE_NORMALは通常ファイルかどうかを表すフラグではない。
//	//}}2016/04/06修正:カレントフォルダに'default.cpk'が有るかどうかを調べるために、stat()ではなくGetFileAttributes()を使うように変更しました。
//			//パッキングファイルにバインドする。
//			criFsBinder_BindCpk(binder, NULL, "default.cpk", NULL, 0, &binder_id);
//			if(binder_id == CRIFSBINDER_BID_NULL) { DIE(); }
//			//バインドが完了するまで待つ。
//			for(;;) {
//				criFsBinder_GetStatus(binder_id, &Status);
//				if(Status == CRIFSBINDER_STATUS_ERROR) { DIE(); }
//				if(Status == CRIFSBINDER_STATUS_COMPLETE) { break; }
//				criFs_ExecuteMain();	//アプリケーション実行中は、criAtomEx_ExecuteMain()の中でcriFs_ExecuteMain()も呼び出されるので、criFs_ExecuteMain()は不要である。ここでは、criFs_ExecuteMain()だけが必要なので、直接criFs_ExecuteMain()を呼び出すことにした。
//				Sleep(0);	//マニュアルによると、明示的なスリープが必要だそうです。サンプルでは10ミリ秒スリープしていますが、0ミリ秒でも大丈夫でした。現在はパッキングファイルに圧縮形式を使っていないので、スレッドの優先度に影響されていないからだと思います。もし今後圧縮形式を使う場合は、ここで止まってしまわないか注意が必要です。
//			}
//		}
//↓2016/05/16変更:カレントフォルダに有る全ての'*.cpk'(有れば)にバインドするように変更しました。これまではカレントフォルダに有る'default.cpk'(有れば)だけにバインドしていました。変更後も'default.cpk'は自動的にバインド対象になりますので、既存のアプリケーションに対する互換性の問題は生じません。
	//{{2016/09/06変更:_findfirst()が返した検索ハンドルを格納する変数の型を、(long)⇒(intptr_t)に変更しました。
	//	long hfind;
	//↓2016/09/06変更:_findfirst()が返した検索ハンドルを格納する変数の型を、(long)⇒(intptr_t)に変更しました。
		intptr_t hfind;
	//}}2016/09/06変更:_findfirst()が返した検索ハンドルを格納する変数の型を、(long)⇒(intptr_t)に変更しました。
		struct _finddata_t fileinfo;
		//カレントフォルダ内の最初の.cpkファイル(又はディレクトリ)を探す。
		hfind = _findfirst("*.cpk", &fileinfo);
		//カレントフォルダ内に.cpkファイル(又はディレクトリ)が有れば…
		if(hfind != -1) {
			do {
				//ディレクトリでなければ…
				if(!(fileinfo.attrib & _A_SUBDIR)) {	//'if(fileinfo.attrib&_A_NORMAL)'で判定してはいけません。_A_NORMALの値は0x00です。
					//パッキングファイルにバインドする。
					criFsBinder_BindCpk(binder, NULL, fileinfo.name, NULL, 0, &binder_id);
					if(binder_id == CRIFSBINDER_BID_NULL) { DIE(); }
					//バインドが完了するまで待つ。
					for(;;) {
						criFsBinder_GetStatus(binder_id, &Status);
						if(Status == CRIFSBINDER_STATUS_ERROR) { DIE(); }
						if(Status == CRIFSBINDER_STATUS_COMPLETE) { break; }
						criFs_ExecuteMain();	//アプリケーション実行中は、criAtomEx_ExecuteMain()の中でcriFs_ExecuteMain()も呼び出されるので、criFs_ExecuteMain()は不要である。ここでは、criFs_ExecuteMain()だけが必要なので、直接criFs_ExecuteMain()を呼び出すことにした。
						Sleep(0);	//マニュアルによると、明示的なスリープが必要だそうです。サンプルでは10ミリ秒スリープしていますが、0ミリ秒でも大丈夫でした。現在はパッキングファイルに圧縮形式を使っていないので、スレッドの優先度に影響されていないからだと思います。もし今後圧縮形式を使う場合は、ここで止まってしまわないか注意が必要です。
					}
				}
			} while(!_findnext(hfind, &fileinfo));	//次の.cpkファイル(又はディレクトリ)が有る間、繰り返す。
			//検索ハンドルを閉じる。
			if(_findclose(hfind)) { DIE(); }
		}
//}}2016/05/16変更:カレントフォルダに有る全ての'*.cpk'(有れば)にバインドするように変更しました。これまではカレントフォルダに有る'default.cpk'(有れば)だけにバインドしていました。変更後も'default.cpk'は自動的にバインド対象になりますので、既存のアプリケーションに対する互換性の問題は生じません。
		//カレントフォルダに'default.cpk'が有るか無いかに関係無く、カレントフォルダにバインドする。
		// - マニュアルには'バインドするディレクトリ名は絶対パスで指定してください。'と書いてありますが、相対パスでも大丈夫でした。
		//   サンプルプログラムも相対パスで指定しているので、多分相対パスで大丈夫なのだと思います。
		criFsBinder_BindDirectory(binder, NULL, ".", NULL, 0, &binder_id);
		if(binder_id == CRIFSBINDER_BID_NULL) { DIE(); }
		//バインドが完了するまで待つ。
		for(;;) {
			criFsBinder_GetStatus(binder_id, &Status);
			if(Status == CRIFSBINDER_STATUS_ERROR) { DIE(); }
			if(Status == CRIFSBINDER_STATUS_COMPLETE) { break; }
			criFs_ExecuteMain();	//アプリケーション実行中は、criAtomEx_ExecuteMain()の中でcriFs_ExecuteMain()も呼び出されるので、criFs_ExecuteMain()は不要である。ここでは、criFs_ExecuteMain()だけが必要なので、直接criFs_ExecuteMain()を呼び出すことにした。
			Sleep(0);	//マニュアルによると、明示的なスリープが必要だそうです。サンプルでは10ミリ秒スリープしていますが、0ミリ秒でも大丈夫でした。現在はパッキングファイルに圧縮形式を使っていないので、スレッドの優先度に影響されていないからだと思います。もし今後圧縮形式を使う場合は、ここで止まってしまわないか注意が必要です。
		}
	}
#endif//USE_CPK
#ifdef  USE_ADX
	//--- サウンドの初期化処理 ---
	{
		CriAtomExStandardVoicePoolConfig voice_pool_config;
		CriAtomExPlayerConfig player_config;
		int iAtomExPlayer;
		//D-BASの作成。
		// - ストリーミング再生を行う場合はD-BASを作成する必要が有ります。
//{{2016/09/18変更:サウンドの同時再生数が多い時に「E09022032B:Increase max_streams of CriAtomDbasConfig.」が出ていた問題を修正しました。
//	    //{{2016/05/19コメント追記:SofdecHelper_Init()のサウンドの初期化処理の箇所に、CriAtomExDbasConfigを明示的な指定についてコメントを追記しました。要検討であり、まだコードは変更していません。
//		// - ※要検討:
//		//   ADXとSofdec2を併用する場合は、下記のように設定するべきかも知れない。(\cri\pc\samples\crimana\playback_with_adx2\playback_with_adx2.c参照)
//		//   │CriAtomExDbasConfig config_dbas;
//		//   │criAtomExDbas_SetDefaultConfig(&config_dbas);
//		//   │config_dbas.max_bps     = ???;	//フルHDのアルファムービー1本で約40Mbpsなので、×本数分を加算するか?
//		//   │config_dbas.max_streams = ???;	//(NumMovies+NumSounds)か?
//		//   │dbas_id = criAtomExDbas_Create(&config_dbas, NULL, 0);
//		//   しかし実際に試した所、デフォルト設定「criAtomExDbas_Create(NULL,NULL,0)」のままでもADXとSofdec2は併用出来たし、CriAtomExDbasConfigを明示的に指定しても(今の所)利点が見付からなかった。
//		//   具体的には、デフォルト設定だとフルHDのSofdec2ムービーを三本ぐらい同時に再生するとE2010052705(too high bit-rate.)が発生するのだが、明示的に設定してもこのエラーを消す事は出来なかった。
//		//   設定パラメータを調整すれば何とかなるのかも知れないが…
//		//   ともかく、はっきりするまで、デフォルト設定のままにしておく事にした。
//	    //}}2016/05/19コメント追記:SofdecHelper_Init()のサウンドの初期化処理の箇所に、CriAtomExDbasConfigを明示的な指定についてコメントを追記しました。要検討であり、まだコードは変更していません。
//		dbas_id = criAtomExDbas_Create(NULL, NULL, 0);
//↓2016/09/18変更:サウンドの同時再生数が多い時に「E09022032B:Increase max_streams of CriAtomDbasConfig.」が出ていた問題を修正しました。
		CriAtomExDbasConfig config_dbas;
		criAtomExDbas_SetDefaultConfig(&config_dbas);
	    //	if(config_dbas.max_bps     < ???                         ) { config_dbas.max_bps     = ???;                          }	//フルHDのアルファムービー1本で約40Mbpsなので、×本数分を加算するか?	⇒とりあえずデフォルト値のままとする事にした。
		if(config_dbas.max_streams < stSofdecHelperInfo.NumSounds) { config_dbas.max_streams = stSofdecHelperInfo.NumSounds; }	//(NumMovies+NumSounds)か?						⇒試した所ムービーの数は含めなくて良いみたい。		※TODO:2016/09/18現在、AdxHelper_GetTotalTime()が1ストリーム消費する分が考慮出来ていません。全チャネル同時再生とAdxHelper_GetTotalTime()が同時に起きなければ大丈夫だと思いますが、厳密にはこのままでは駄目なので考慮する必要が有ります。
		dbas_id = criAtomExDbas_Create(&config_dbas, NULL, 0);
//}}2016/09/18変更:サウンドの同時再生数が多い時に「E09022032B:Increase max_streams of CriAtomDbasConfig.」が出ていた問題を修正しました。
//{{2017/04/03追加:暗号化されたデータの再生や情報取得に対応しました。
		//暗号化された音声データの復号キーが指定されていたら…
		if(stSofdecHelperInfo.SoundKey) {
			CriAtomExDecrypterConfig decrypter_config;
			//デクリプタ作成用コンフィグ構造体をゼロクリアする。
			//□参考資料:/cri/pc/include/cri_atom.h
			//│将来的にメンバが増える可能性があるため、使用前に必ず構造体をゼロクリアしてください。
			//│（構造体のメンバに不定値が入らないようご注意ください。）
			memset(&decrypter_config, 0, sizeof decrypter_config);
			//暗号化キーを設定する。
			decrypter_config.key = stSofdecHelperInfo.SoundKey;
			//デクリプタを作成する。
			atom_ex_decrypter = criAtomExDecrypter_Create(&decrypter_config, NULL, 0);
			if(!atom_ex_decrypter) { DIE(); }
			//□参考資料:/cri/pc/samples/criatomex/playback_with_decryption/playback_with_decryption.c
			//│※デクリプタの作成はボイスプール作成前にしておく必要があります。
			//│Note that create a decrypter before creating a voice pool
		}
//}}2017/04/03追加:暗号化されたデータの再生や情報取得に対応しました。
		//ボイスプールの作成パラメータをデフォルト値で初期化。
		criAtomExVoicePool_SetDefaultConfigForStandardVoicePool(&voice_pool_config);
		//ファイル名指定の再生はストリーミング再生なので、ストリーミング再生可能に設定。
		voice_pool_config.player_config.streaming_flag = 1;
		//初期化用コンフィグ構造体を調整する。
		// - 初期化用コンフィグ構造体の値をデフォルト値よりも減らすと初期化エラーになる事が有ったので、デフォルト値よりも減らさないようにした。
//{{2016/08/01変更:シームレス連続再生に対応しました。
//		if(voice_pool_config.num_voices < NumSounds) { voice_pool_config.num_voices = NumSounds; }
//↓2016/08/01変更:シームレス連続再生に対応しました。
		if(voice_pool_config.num_voices < stSofdecHelperInfo.NumSounds) { voice_pool_config.num_voices = stSofdecHelperInfo.NumSounds; }
//}}2016/08/01変更:シームレス連続再生に対応しました。
		//ストリーミング再生可能なボイスプールの作成。
		// - 標準ボイスは、ADXデータとHCAデータの両方の再生に対応したボイスです。
		//   Waveファイルも再生する場合は、Waveボイスプールも作成する必要が有ります。
		voice_pool = criAtomExVoicePool_AllocateStandardVoicePool(&voice_pool_config, NULL, 0);
		//プレーヤの作成パラメータをデフォルト値で初期化。
		criAtomExPlayer_SetDefaultConfig(&player_config);
		//再生するファイル名を覚えておくためのパス保存領域サイズを指定。
		// - 1つのプレーヤに対して複数のパス文字列を同時にセットする場合、max_path_stringsの値を増やす必要が有ります。	⇒当モジュールの使用方法では1つのプレーヤに対して複数のパス文字列を同時にセットしないので必要有りません。
		player_config.max_path = _MAX_PATH;
		//プレーヤの作成。
//{{2016/08/01変更:シームレス連続再生に対応しました。
//		for(iAtomExPlayer = 0; iAtomExPlayer < NumSounds; iAtomExPlayer++) {
//			TBL_AtomExPlayer[iAtomExPlayer] = criAtomExPlayer_Create(&player_config, NULL, 0);
//		}
//↓2016/08/01変更:シームレス連続再生に対応しました。
//		for(iAtomExPlayer = 0; iAtomExPlayer < stSofdecHelperInfo.NumSounds; iAtomExPlayer++) {
//			TBL_AtomExPlayer[iAtomExPlayer].player = criAtomExPlayer_Create(&player_config, NULL, 0);
//			criAtomExPlayer_SetDataRequestCallback(TBL_AtomExPlayer[iAtomExPlayer].player, app_data_request_callback, &TBL_AtomExPlayer[iAtomExPlayer]);	//シームレス連結再生用にコールバック関数を登録する。
//		}
//↓2016/09/18変更:サウンドの再生準備を対応しました。
		for(iAtomExPlayer = 0; iAtomExPlayer < stSofdecHelperInfo.NumSounds; iAtomExPlayer++) {
			ST_AtomExPlayerPool* pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayer];
			pAtomExPlayerPool->player = criAtomExPlayer_Create(&player_config, NULL, 0);
			criAtomExPlayer_SetDataRequestCallback(pAtomExPlayerPool->player, app_data_request_callback, pAtomExPlayerPool);	//シームレス連結再生用にコールバック関数を登録する。
		}
//}}2016/09/18変更:サウンドの再生準備を対応しました。
	}
#endif//USE_ADX
	//--- Manaライブラリの初期化処理 ---
	{
		CriManaLibConfig config_mana;
		//Manaライブラリの、ユーザアロケータを登録する。
		// - Manaライブラリがワーク領域を確保する方法には、User Allocator方式とFixed Memory方式が有る。
		//   User Allocator方式の方が簡単なので、User Allocator方式を使用する事にした。
		criMana_SetUserAllocator(user_malloc, user_free, NULL);
		//Manaライブラリの初期化用コンフィグ構造体にデフォルト値をセットする。
		criMana_SetDefaultLibConfig(&config_mana);
		//最大消費デコーダハンドル数を変更する。
		// - 最大消費デコーダハンドル数のデフォルト値は4なので、もし(MAX_DECODER_HANDLES=4)の場合はダミー処理となるが、行っても問題は無い。
//{{2016/08/01変更:シームレス連続再生に対応しました。
//		config_mana.max_decoder_handles = NumMovies;	//最大消費デコーダハンドル数。アルファムービーを再生する場合はハンドル資源を二つ消費します。
//↓2016/08/01変更:シームレス連続再生に対応しました。
	//{{2016/09/16変更:同時再生するムービーが全てアルファムービーであってもデコーダハンドルが足りるように、最大消費デコーダハンドル数の設定を二倍にしました。マニュアルから推測するに、アルファムービーの場合に二倍必要なのはデコーダハンドルだけであり、使用するCriFsLoaderの数(config_fs.num_loaders)や最大同時オープンファイル数(config_fs.max_files)は二倍にしなくても良いみたいです。(※要検証)	⇒{{2016/09/24コメント追記:やっぱりconfig_fs.num_loadersも二倍にする必要が有るみたいです。詳細は'Sat Sep 24 21:37:23 JST 2016'のコメントを参照して下さい。}}
	//	config_mana.max_decoder_handles = stSofdecHelperInfo.NumMovies;	//最大消費デコーダハンドル数。アルファムービーを再生する場合はハンドル資源を二つ消費します。
	//↓2016/09/16変更:同時再生するムービーが全てアルファムービーであってもデコーダハンドルが足りるように、最大消費デコーダハンドル数の設定を二倍にしました。マニュアルから推測するに、アルファムービーの場合に二倍必要なのはデコーダハンドルだけであり、使用するCriFsLoaderの数(config_fs.num_loaders)や最大同時オープンファイル数(config_fs.max_files)は二倍にしなくても良いみたいです。(※要検証)	⇒{{2016/09/24コメント追記:やっぱりconfig_fs.num_loadersも二倍にする必要が有るみたいです。詳細は'Sat Sep 24 21:37:23 JST 2016'のコメントを参照して下さい。}}
		config_mana.max_decoder_handles = stSofdecHelperInfo.NumMovies * 2;	//最大消費デコーダハンドル数。アルファムービーを再生する場合はハンドル資源を二つ消費します。
	//}}2016/09/16変更:同時再生するムービーが全てアルファムービーであってもデコーダハンドルが足りるように、最大消費デコーダハンドル数の設定を二倍にしました。マニュアルから推測するに、アルファムービーの場合に二倍必要なのはデコーダハンドルだけであり、使用するCriFsLoaderの数(config_fs.num_loaders)や最大同時オープンファイル数(config_fs.max_files)は二倍にしなくても良いみたいです。(※要検証)	⇒{{2016/09/24コメント追記:やっぱりconfig_fs.num_loadersも二倍にする必要が有るみたいです。詳細は'Sat Sep 24 21:37:23 JST 2016'のコメントを参照して下さい。}}
//}}2016/08/01変更:シームレス連続再生に対応しました。
		//Manaライブラリを初期化する。
		criMana_Initialize(&config_mana, NULL, 0);
		//32bit ARGB8888 フォーマットでフレームを取得するために必要な初期化処理を行う。
		criMana_InitializeFrameARGB32();
//{{2017/04/03追加:暗号化されたデータの再生や情報取得に対応しました。
		//暗号化されたムービーデータの復号キーが指定されていたら…
		if(stSofdecHelperInfo.MovieKey) {
			CriManaDecrypterConfig decrypter_config;
			//デクリプタ作成用コンフィグ構造体をゼロクリアする。
			//□参考資料:/cri/pc/include/cri_mana.h
			//│将来的にメンバが増える可能性があるため、使用前に必ず構造体をゼロクリアしてください。
			//│（構造体のメンバに不定値が入らないようご注意ください。）
			memset(&decrypter_config, 0, sizeof decrypter_config);
			//暗号化キーを設定する。
			decrypter_config.key = stSofdecHelperInfo.MovieKey;
			//デクリプタを作成する。
			mana_decrypter = criManaDecrypter_Create(&decrypter_config, NULL, 0);
			if(!mana_decrypter) { DIE(); }
			//□参考資料:/cri/pc/samples/criatomex/playback_with_decryption/playback_with_decryption.c
			//│※デクリプタの作成はボイスプール作成前にしておく必要があります。
			//│Note that create a decrypter before creating a voice pool
			//上記は暗号化されたムービーデータの再生サンプルではなく、暗号化された音声データの再生サンプルですが、(暗号化されたムービーデータの再生サンプルは無いようです…)
			//暗号化された音声データも暗号化されたムービーデータも、多分同じルールだと思うので、ムービーのデクリプタ作成もムービーのプレイヤー作成前にしておく事にしました。
			//尚、サンプルプロジェクトではありませんが、/cri/pc/include/cri_mana.hのcriManaDecrypter_Create()のコメントの「例:」のコードでも、
			//ムービーのデクリプタ作成の後にムービーのプレイヤー作成を行っているので、多分これで間違いないと思います。
		}
//}}2017/04/03追加:暗号化されたデータの再生や情報取得に対応しました。
//{{2016/09/17追加:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
		{
			int iManaPlayer;
			ST_ManaPlayer* pManaPlayer;
			//Manaプレーヤを全て作成する。
			for(iManaPlayer = 0; iManaPlayer < stSofdecHelperInfo.NumMovies; iManaPlayer++) {
				pManaPlayer = &TBL_ManaPlayer[iManaPlayer];
				//Manaプレーヤを作成する。
				pManaPlayer->player = criManaPlayer_Create(NULL, 0);
				if(!pManaPlayer->player) { DIE(); }
				//内部ビデオバッファ(フレームプール)数を指定する。
				// - デフォルトのフレームプール数(1)のままで良ければ、criManaPlayer_SetNumberOfFramePools()を呼び出す必要は無い。
				//   しかしスムーズな再生を行うためには、フレームプール数を2以上にするのが適切である。
				criManaPlayer_SetNumberOfFramePools(pManaPlayer->player, 2/*調整可*/);
				//オーディオトラックを使用しない場合、例えムービーにオーディオトラックが含まれていたとしてもオーディオトラックを再生しないように設定する。
				// - 初期化時にcriAtomEx_Initialize_WASAPI()ではなくcriAtomEx_Initialize()を呼び出していた場合、そのままでは、オーディオトラックが含まれているムービーを再生しようとすると'致命的なエラー'が発生する。
				//   CRIMANA_AUDIO_TRACK_OFFの設定を行っておけば、オーディオトラックが含まれているムービーも、オーディオトラックが含まれていないムービーと見なして再生され、正常に動作する。
				//   尚、オーディオトラックが含まれていないムービーに対してCRIMANA_AUDIO_TRACK_OFFの設定を行っても、単に無視されるだけであり安全である。
				// - criManaPlayer_SetAudioTrack()を呼び出せるのは、criManaPlayer_Prepare()を呼び出す前までである。
				//   もしムービーヘッダ情報を見て、criManaPlayer_SetAudioTrack()の設定を変えたい場合は、この後の処理を変える必要が有る。
				//   具体的には、現在はcriManaPlayer_Prepare()で準備状態まで進めているが、criManaPlayer_DecodeHeader()⇒criManaPlayer_SetAudioTrack()⇒criManaPlayer_Prepare()という手順に変える必要が生じる。
				//   現在の所はムービーヘッダ情報を見てcriManaPlayer_SetAudioTrack()の設定を変える必要が無いので、単にcriManaPlayer_Prepare()よりも前にcriManaPlayer_SetAudioTrack()を呼べば充分であるが、
				//   もし今後、ムービーヘッダ情報を見てcriManaPlayer_SetAudioTrack()の設定を変える場合は、手順も変更する必要が有る事に注意せよ。
#ifndef USE_ADX
				criManaPlayer_SetAudioTrack(pManaPlayer->player, CRIMANA_AUDIO_TRACK_OFF);
#endif//USE_ADX
				//連結再生ファイル要求コールバックを登録する。
				criManaPlayer_SetDataRequestCallback(pManaPlayer->player, user_movie_request_callback_func, pManaPlayer);
			}
		}
//}}2016/09/17追加:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
	}
}
/*--------------------------------------------------------------------------*/
void SofdecHelper_Exit() {
	//--- Manaライブラリの終了処理 ---
	{
//{{2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//		int iManaPlayer;
//		//残っているManaプレーヤを全て削除する。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////		for(iManaPlayer = 0; iManaPlayer < NumMovies; iManaPlayer++) {
////↓2016/08/01変更:シームレス連続再生に対応しました。
//		for(iManaPlayer = 0; iManaPlayer < stSofdecHelperInfo.NumMovies; iManaPlayer++) {
////}}2016/08/01変更:シームレス連続再生に対応しました。
//			if(TBL_ManaPlayer[iManaPlayer].player) {
//				SofdecHelper_Free(iManaPlayer + 1);	//プレイヤーid=(管理用構造体のインデクス+1)
//			}
//		}
//↓2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
		int iManaPlayer;
		ST_ManaPlayer* pManaPlayer;
		//Manaプレーヤを全て破棄する。
		for(iManaPlayer = 0; iManaPlayer < stSofdecHelperInfo.NumMovies; iManaPlayer++) {
			pManaPlayer = &TBL_ManaPlayer[iManaPlayer];
			//ムービー再生を確実に停止して、停止が完了するまで待機する。
			// - 実験したところ、criManaPlayer_StopAndWaitCompletion()は任意のタイミングで呼び出せるようだ。
			//   再生開始前や、準備中や、既に停止している時に再度呼び出しても、エラーは発生しなかった。
			criManaPlayer_StopAndWaitCompletion(pManaPlayer->player);
			//Manaプレーヤを破棄する。
			criManaPlayer_Destroy(pManaPlayer->player);
		}
//}}2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//{{2017/04/03追加:暗号化されたデータの再生や情報取得に対応しました。
		//デクリプタが作成されていたら…
		if(mana_decrypter) {
			//デクリプタを破棄する。
			criManaDecrypter_Destroy(mana_decrypter);
			mana_decrypter = NULL;
		}
//}}2017/04/03追加:暗号化されたデータの再生や情報取得に対応しました。
		//Manaライブラリの終了処理を行う。
		criMana_Finalize();
	}
#ifdef  USE_ADX
	//--- サウンドの終了処理 ---
	{
		int iAtomExPlayer;
		//プレーヤハンドルの破棄。
//{{2016/08/01変更:シームレス連続再生に対応しました。
//		for(iAtomExPlayer = 0; iAtomExPlayer < NumSounds; iAtomExPlayer++) {
//		//不要	AdxHelper_Stop(iAtomExPlayer + 1);	//プレイヤーid=プレーヤハンドルの配列のインデクス+1)
//			criAtomExPlayer_Destroy(TBL_AtomExPlayer[iAtomExPlayer]);	//本関数は完了復帰型の関数です。音声再生中のAtomExプレーヤを破棄しようとした場合、本関数内で再生停止を待ってからリソースの解放が行われます。
//		}
//↓2016/08/01変更:シームレス連続再生に対応しました。
//		for(iAtomExPlayer = 0; iAtomExPlayer < stSofdecHelperInfo.NumSounds; iAtomExPlayer++) {
//		//不要	AdxHelper_Stop(iAtomExPlayer + 1);	//プレイヤーid=プレーヤハンドルの配列のインデクス+1)
//			criAtomExPlayer_Destroy(TBL_AtomExPlayer[iAtomExPlayer].player);	//本関数は完了復帰型の関数です。音声再生中のAtomExプレーヤを破棄しようとした場合、本関数内で再生停止を待ってからリソースの解放が行われます。
//		}
//↓2016/09/18変更:サウンドの再生準備を対応しました。
		for(iAtomExPlayer = 0; iAtomExPlayer < stSofdecHelperInfo.NumSounds; iAtomExPlayer++) {
		//不要	AdxHelper_Stop(iAtomExPlayer + 1);	//プレイヤーid=プレーヤハンドルの配列のインデクス+1)
			criAtomExPlayer_Destroy(TBL_AtomExPlayerPool[iAtomExPlayer].player);	//本関数は完了復帰型の関数です。音声再生中のAtomExプレーヤを破棄しようとした場合、本関数内で再生停止を待ってからリソースの解放が行われます。
		}
//}}2016/09/18変更:サウンドの再生準備を対応しました。
		//ボイスプールの破棄。
		criAtomExVoicePool_Free(voice_pool);
		voice_pool = NULL;
//{{2017/04/03追加:暗号化されたデータの再生や情報取得に対応しました。
		//デクリプタが作成されていたら…
		if(atom_ex_decrypter) {
			//デクリプタを破棄する。
			criAtomExDecrypter_Destroy(atom_ex_decrypter);
			atom_ex_decrypter = NULL;
		}
//}}2017/04/03追加:暗号化されたデータの再生や情報取得に対応しました。
		//D-BASの破棄。
		criAtomExDbas_Destroy(dbas_id);
		dbas_id = 0;	//CriAtomExDbasIdはポインタではないのでNULLではなく0でクリアする。
	}
#endif//USE_ADX
#ifdef  USE_CPK
	//--- バインダの終了処理 ---
	{
		//バインダを破棄する。
		criFsBinder_Destroy(binder);
		binder = NULL;
	}
#endif//USE_CPK
	//--- Atom Exライブラリの終了処理 ---
	{
		//Atom Exライブラリの終了処理を行う。
#ifndef USE_ADX
		criAtomEx_Finalize();				//オーディオトラックを使用しない場合
#else //USE_ADX
		criAtomEx_Finalize_WASAPI();			//オーディオトラックを使用する場合
#endif//USE_ADX
	}
	//--- メモリ開放 ---
	{
#ifdef  USE_ADX
		//--- サウンド ---
//{{2016/09/18追加:サウンドの再生準備を対応しました。
		//再生プールの配列を開放する。
		free(TBL_AtomExPlayerPool);
		TBL_AtomExPlayerPool = NULL;
//}}2016/09/18追加:サウンドの再生準備を対応しました。
		//プレーヤハンドルの配列を開放する。
		free(TBL_AtomExPlayer);
		TBL_AtomExPlayer = NULL;
//{{2016/08/01削除:シームレス連続再生に対応しました。
//		//同時再生するサウンドの数をクリアする。
//		NumSounds = 0;
//}}2016/08/01削除:シームレス連続再生に対応しました。
#endif//USE_ADX
		//--- ムービー ---
		//管理用構造体の配列を開放する。
		free(TBL_ManaPlayer);
		TBL_ManaPlayer = NULL;
//{{2016/08/01削除:シームレス連続再生に対応しました。
//		//同時再生するムービーの数をクリアする。
//		NumMovies = 0;
//}}2016/08/01削除:シームレス連続再生に対応しました。
	}
//{{2016/08/01追加:シームレス連続再生に対応しました。
	//初期化情報をクリアする。
	memset(&stSofdecHelperInfo, 0, sizeof stSofdecHelperInfo);
//}}2016/08/01追加:シームレス連続再生に対応しました。
}
/*--------------------------------------------------------------------------*/
void SofdecHelper_Exec() {
	criAtomEx_ExecuteMain();	//スレッドモデルに関係無く(たとえCRIATOMEX_THREAD_MODEL_MULTIであっても)、この関数はメインスレッドから周期的に呼び出す必要が有る。
	criMana_ExecuteMain();		//┬スレッドモデルに関係無く(たとえCRIMANA_THREAD_MODEL_MULTIであっても)、この関数はメインスレッドから周期的に呼び出す必要が有る。
	criMana_SyncMasterTimer();	//┘
//{{2016/09/18追加:AdxHelper_Stop()が呼び出されなくても、再生が終了したら自動的に次回の再生準備を行うようにしました。
#ifdef  USE_ADX
	AdxHelper_Exec();
#endif//USE_ADX
//}}2016/09/18追加:AdxHelper_Stop()が呼び出されなくても、再生が終了したら自動的に次回の再生準備を行うようにしました。
}
/*--------------------------------------------------------------------------*/
//{{2016/09/17追加:ムービーの再生準備を対応しました。
//再生準備を行い、そのエントリのインデクス(0～(NumMovies-1))を返す。
//ただし、再生準備を行えなかった場合は、-1を返す。
static int SofdecHelper_Prepare_subr(const char* path) {
	int iManaPlayer;
	ST_ManaPlayer* pManaPlayer;
	if(!path[0] || (strlen(path) >= _MAX_PATH)) { DIE(); }	//パス名は1文字以上,かつ,_MAX_PATH文字未満でなくてはならない。
	//手順①:既に再生準備中のエントリが有れば何もしない。
	{
		for(iManaPlayer = 0; iManaPlayer < stSofdecHelperInfo.NumMovies; iManaPlayer++) {
			pManaPlayer = &TBL_ManaPlayer[iManaPlayer];
			//使用中でなく…
			if(!(pManaPlayer->Flag & ManaPlayer_Flag_InUse)) {
				//再生準備中のムービーのパス名が一致したら…
				if(!strcmp(pManaPlayer->sPrepMoviePath, path)) {
					//再生準備開始処理は行わずに、世代管理処理だけを行って、このエントリのインデクス(0～(NumMovies-1))を返す。
					// - 同じムービーを重複して準備する事は行わない事にしました。
					//   その方が、アプリケーション側で無駄に重複した準備を避けるための処理が不要になり、アプリケーションの実装が簡単になると思ったからです。
					goto L_AGING;	//世代管理処理へ移行する。
				}
			}
		}
	}
	//手順②:使用中でなく、再生準備中でないエントリを探して、再生準備を開始する。
	{
		for(iManaPlayer = 0; iManaPlayer < stSofdecHelperInfo.NumMovies; iManaPlayer++) {
			pManaPlayer = &TBL_ManaPlayer[iManaPlayer];
			//使用中でなく…
			if(!(pManaPlayer->Flag & ManaPlayer_Flag_InUse)) {
				//再生準備中でなければ…
				if(!pManaPlayer->sPrepMoviePath[0]) {
					//再生準備開始処理へ移行する。
					goto L_PREP;
				}
			}
		}
	}
	//手順③:使用中でなく、再生準備中であるエントリのうち、一番古い再生準備中のエントリの、既存の再生準備を停止し、新たな再生準備を開始する。
	{
		int Oldest_PrepAge     = -1;
		int Oldest_iManaPlayer = -1;
		for(iManaPlayer = 0; iManaPlayer < stSofdecHelperInfo.NumMovies; iManaPlayer++) {
			pManaPlayer = &TBL_ManaPlayer[iManaPlayer];
			//使用中でなく…
			if(!(pManaPlayer->Flag & ManaPlayer_Flag_InUse)) {
				//再生準備中ならば…
				if(!pManaPlayer->sPrepMoviePath[0]) { DIE(); }	//手順②にヒットしなかったと言うことは、使用中でないエントリは全て再生準備中であるはずです。もしここで停止したら、当関数のバグです。
				//再生準備中のエントリのうち、一番古いエントリを検索する。
				if(pManaPlayer->PrepAge > Oldest_PrepAge) {
					Oldest_PrepAge     = pManaPlayer->PrepAge;
					Oldest_iManaPlayer = iManaPlayer;
				}
			}
		}
		//使用中でなく、再生準備中のエントリのうち、一番古いエントリが見つかったら…
		if(Oldest_iManaPlayer != -1) {
			iManaPlayer = Oldest_iManaPlayer;
			pManaPlayer = &TBL_ManaPlayer[iManaPlayer];
			//既存の再生準備を停止し、停止が完了するまで待機する。
			// - 実験したところ、criManaPlayer_StopAndWaitCompletion()は任意のタイミングで呼び出せるようだ。
			//   再生開始前や、準備中や、既に停止している時に再度呼び出しても、エラーは発生しなかった。
			criManaPlayer_StopAndWaitCompletion(pManaPlayer->player);
			//再生準備開始処理へ移行する。
			goto L_PREP;
		}
	}
	//手順④:使用中でないエントリが無ければ、再生準備を開始せずに、-1を返す。
	return -1;
	//--- 再生準備開始処理 ---
L_PREP:	{
		pManaPlayer = &TBL_ManaPlayer[iManaPlayer];
	    #ifdef  _DEBUG
		fprintf(stderr, "SofdecHelper_Prepare_subr: %d: %s\n", iManaPlayer, path);
	    #endif//_DEBUG
		//再生準備中のムービーのパス名を格納する。
		strcpy(pManaPlayer->sPrepMoviePath, path);
		//最初に再生するムービーのパス名を格納する。
		// - sNextMoviePath[]は、直後のcriManaPlayer_DecodeHeader()から呼び出されるuser_movie_request_callback_func()によって書き換えられます。
		strcpy(pManaPlayer->sNextMoviePath, path);
//{{2016/09/20追加:非ループのムービーが誤って二周ループしてしまう事が有った問題を修正しました。
		//再生準備を開始する前に、一旦、Manaプレイヤーの状態を非ループに戻す。
		criManaPlayer_Loop(pManaPlayer->player, 0);
//}}2016/09/20追加:非ループのムービーが誤って二周ループしてしまう事が有った問題を修正しました。
		//ヘッダ解析を開始する。
		// - criManaPlayer_Prepare()は不可です!!ここで呼び出すのはcriManaPlayer_DecodeHeader()でなくてはなりません。				┐
		//   もしcriManaPlayer_Prepare()を呼び出すと、ヘッダ解析完了後にSofdec2内のスレッドは引き続き再生準備を継続するのですが、		│
		//   再生準備が完了してCRIMANAPLAYER_STATUS_READYへ移行する前に、SofdecHelper_Play()が呼び出されてcriManaPlayer_Start()が実行されると、	│
		//   そのcriManaPlayer_Start()は *エラーも発生せずに* 単に無視されてしまうようです(実験より)。						├→左記のコメントは2016/04/04時点の待機方法に依存するものです。
		//   結果として、いつまで経っても再生状態へ移行しない問題が生じます。									│　2016/09/17ムービーの再生準備対応に伴い待機方法を変えたので、criManaPlayer_DecodeHeader()ではなくcriManaPlayer_Prepare()で再生準備を開始するように変更しました。
		//   問題を防ぐため、ここで呼び出すのはcriManaPlayer_DecodeHeader()とし、CRIMANAPLAYER_STATUS_WAIT_PREPで待機させておく必要が有ります。	│　criManaPlayer_DecodeHeader()よりもcriManaPlayer_Prepare()の方がより先まで準備が進むので、最初のフレームのデコードの遅延が少なくなって見た目にも(少しですが)引っかかりが少なくなる事を目視で確認しました。
		//   CRIMANAPLAYER_STATUS_WAIT_PREPの状態でSofdecHelper_Play()が呼び出されれば、正常に動作して、再生準備中を経て再生中へ移行します。	│
		//   状態移行中にSofdecHelper_Play()が呼び出されると無視される事は、マニュアルには書いてありませんが、どうやらそういう挙動のようです。	┘
	    //	criManaPlayer_DecodeHeader(pManaPlayer->player);	//←2016/09/17以前の処理方法
		criManaPlayer_Prepare(pManaPlayer->player);		//←2016/09/17以降の処理方法・・・SofdecHelper_Load()の最後あたりの待機方法を変更した事に依存しています。SofdecHelper_Load()の最後あたりの2016/09/17の変更点を参照して下さい。
		//世代管理処理へ移行する。
		/* FALLTHRU */
	}
	//--- 世代管理処理 ---
	//手順④で処理を返した場合以外(=既にこのムービーを再生準備中のエントリを見つけたか、又は、いずれかのエントリを新たに再生準備中へ移行した場合)は、以下の世代管理処理を行う。
L_AGING:{
		int i;
		//当該エントリの再生準備の古さをクリアする。
		pManaPlayer = &TBL_ManaPlayer[iManaPlayer];
		pManaPlayer->PrepAge = 0;
		//当該エントリ以外で、使用中でなく、再生準備中で、再生準備の古さが0のエントリが有るかを調べる。
		for(i = 0; i < stSofdecHelperInfo.NumMovies; i++) {
			pManaPlayer = &TBL_ManaPlayer[i];
			if(i != iManaPlayer) {						//当該エントリ以外で、
				if(!(pManaPlayer->Flag & ManaPlayer_Flag_InUse)) {	//使用中でなく、
					if(pManaPlayer->sPrepMoviePath[0]) {		//再生準備中で、
						if(!pManaPlayer->PrepAge) { break; }	//再生準備の古さが0のエントリが有れば、ループを抜ける。
					}
				}
			}
		}
		//当該エントリ以外で、使用中でなく、再生準備中で、再生準備の古さが0のエントリが有れば…
		if(i < stSofdecHelperInfo.NumMovies) {
			//当該エントリ以外で、使用中でなく、再生準備中のエントリの、再生準備の古さを増やす。
			for(i = 0; i < stSofdecHelperInfo.NumMovies; i++) {
				pManaPlayer = &TBL_ManaPlayer[i];
				if(i != iManaPlayer) {						//当該エントリ以外で、
					if(!(pManaPlayer->Flag & ManaPlayer_Flag_InUse)) {	//使用中でなく、
						if(pManaPlayer->sPrepMoviePath[0]) {		//再生準備中ならば…
							pManaPlayer->PrepAge++;			//再生準備の古さを増やす。	このアルゴリズムならば、(PrepAge≧NumMovies)にはならないはずなので、飽和の判断は不要です。
						}
					}
				}
			}
		}
	}
	return iManaPlayer;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2016/09/19変更:SofdecHelper_Prepare()が戻り値を返すように変更しました。
////アプリケーションに対して再生準備の成否を意識させないために、グローバル関数は戻り値無しとした。
//void SofdecHelper_Prepare(const char* path) {
//	SofdecHelper_Prepare_subr(path);
//}
//↓2016/09/19変更:SofdecHelper_Prepare()が戻り値を返すように変更しました。
//SofdecHelper_Prepare()は、以下のいずれかの値を返す。
//・再生準備中のエントリが無く、新たな再生準備を開始出来なければ、-1を返す。
//・再生準備中のエントリが有り、再生準備が完了していなければ、1を返す。
//・再生準備中のエントリが有り、再生準備が完了していたら、0を返す。
//アプリケーションの観点からは、SofdecHelper_Prepare()の戻り値が0ならば、遅延無しで再生を開始出来る。
int SofdecHelper_Prepare(const char* path) {
	int iManaPlayer, Status;
	ST_ManaPlayer* pManaPlayer;
	//指定されたムービーの、再生準備中のエントリを取得する。
	iManaPlayer = SofdecHelper_Prepare_subr(path);
	//再生準備中のエントリが無く、新たな再生準備を開始出来なければ、-1を返す。
	if(iManaPlayer == -1) { return -1; }
	//Manaプレーヤのステータスを取得する。
	pManaPlayer = &TBL_ManaPlayer[iManaPlayer];
	Status = criManaPlayer_GetStatus(pManaPlayer->player);
	//再生準備中のエントリが有り、再生準備が完了していなければ、1を返す。
	if((Status == CRIMANAPLAYER_STATUS_DECHDR) ||
	   (Status == CRIMANAPLAYER_STATUS_WAIT_PREP) ||	//SofdecHelper_Prepare_subr()にて、criManaPlayer_DecodeHeader()ではなくcriManaPlayer_Prepare()で再生準備を開始しているので、CRIMANAPLAYER_STATUS_DECHDR⇒CRIMANAPLAYER_STATUS_PREPへ遷移するはずであり、CRIMANAPLAYER_STATUS_WAIT_PREPに長時間留まる事はないはずではあるが、CRI Manaライブラリの内部処理がCRIMANAPLAYER_STATUS_WAIT_PREPを一瞬経由するのか飛ばすのかが不明だったため、一応CRIMANAPLAYER_STATUS_WAIT_PREPも許容する事にした。
	   (Status == CRIMANAPLAYER_STATUS_PREP)) { return 1; }
	//再生準備中のエントリが有り、再生準備が完了していたら、0を返す。
	if(Status != CRIMANAPLAYER_STATUS_READY) { DIE(); }	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
	return 0;
}
//}}2016/09/19変更:SofdecHelper_Prepare()が戻り値を返すように変更しました。
//}}2016/09/17追加:ムービーの再生準備を対応しました。
/*--------------------------------------------------------------------------*/
int SofdecHelper_Load(const char* path) {
	int iManaPlayer;
	ST_ManaPlayer* pManaPlayer;
//{{2016/09/17変更:ムービーの再生準備を対応しました。
//	//管理用構造体の空きエントリを検索する。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////	for(iManaPlayer = 0; iManaPlayer < NumMovies; iManaPlayer++) {
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	for(iManaPlayer = 0; iManaPlayer < stSofdecHelperInfo.NumMovies; iManaPlayer++) {
////}}2016/08/01変更:シームレス連続再生に対応しました。
////{{2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
////		if(!TBL_ManaPlayer[iManaPlayer].player) { break; }
////↓2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//		if(!(TBL_ManaPlayer[iManaPlayer].Flag & ManaPlayer_Flag_InUse)) { break; }	//使用中でないエントリを見つけたら抜ける。
////}}2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//	}
////{{2016/08/01変更:シームレス連続再生に対応しました。
////	if(iManaPlayer >= NumMovies) { DIE(); }
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	if(iManaPlayer >= stSofdecHelperInfo.NumMovies) { DIE(); }
////}}2016/08/01変更:シームレス連続再生に対応しました。
//↓2016/09/17変更:ムービーの再生準備を対応しました。
	//指定されたムービーの、再生準備中のエントリを取得する。
	iManaPlayer = SofdecHelper_Prepare_subr(path);
	if(iManaPlayer == -1) { DIE(); }	//使用中でないエントリが無く、再生準備を開始出来なかった。ここでエラー停止した場合は、アプリケーションが指定するstSofdecHelperInfo.NumMoviesを増やせ。
//}}2016/09/17変更:ムービーの再生準備を対応しました。
	pManaPlayer = &TBL_ManaPlayer[iManaPlayer];
//{{2016/09/17削除:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//	//Manaプレーヤを作成する。
//	pManaPlayer->player = criManaPlayer_Create(NULL, 0);
//	if(!pManaPlayer->player) { DIE(); }
//	//内部ビデオバッファ(フレームプール)数を指定する。
//	// - デフォルトのフレームプール数(1)のままで良ければ、criManaPlayer_SetNumberOfFramePools()を呼び出す必要は無い。
//	//   しかしスムーズな再生を行うためには、フレームプール数を2以上にするのが適切である。
//	criManaPlayer_SetNumberOfFramePools(pManaPlayer->player, 2/*調整可*/);
//	//オーディオトラックを使用しない場合、例えムービーにオーディオトラックが含まれていたとしてもオーディオトラックを再生しないように設定する。
//	// - 初期化時にcriAtomEx_Initialize_WASAPI()ではなくcriAtomEx_Initialize()を呼び出していた場合、そのままでは、オーディオトラックが含まれているムービーを再生しようとすると'致命的なエラー'が発生する。
//	//   CRIMANA_AUDIO_TRACK_OFFの設定を行っておけば、オーディオトラックが含まれているムービーも、オーディオトラックが含まれていないムービーと見なして再生され、正常に動作する。
//	//   尚、オーディオトラックが含まれていないムービーに対してCRIMANA_AUDIO_TRACK_OFFの設定を行っても、単に無視されるだけであり安全である。
//	// - criManaPlayer_SetAudioTrack()を呼び出せるのは、criManaPlayer_Prepare()を呼び出す前までである。
//	//   もしムービーヘッダ情報を見て、criManaPlayer_SetAudioTrack()の設定を変えたい場合は、この後の処理を変える必要が有る。
//	//   具体的には、現在はcriManaPlayer_Prepare()で準備状態まで進めているが、criManaPlayer_DecodeHeader()⇒criManaPlayer_SetAudioTrack()⇒criManaPlayer_Prepare()という手順に変える必要が生じる。
//	//   現在の所はムービーヘッダ情報を見てcriManaPlayer_SetAudioTrack()の設定を変える必要が無いので、単にcriManaPlayer_Prepare()よりも前にcriManaPlayer_SetAudioTrack()を呼べば充分であるが、
//	//   もし今後、ムービーヘッダ情報を見てcriManaPlayer_SetAudioTrack()の設定を変える場合は、手順も変更する必要が有る事に注意せよ。
//#ifndef USE_ADX
//	criManaPlayer_SetAudioTrack(pManaPlayer->player, CRIMANA_AUDIO_TRACK_OFF);
//#endif//USE_ADX
//↓2016/09/17削除:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
	//使用中フラグをセットする。
	pManaPlayer->Flag |= ManaPlayer_Flag_InUse;
//}}2016/09/17削除:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//{{2016/09/17削除:ムービーの再生準備を対応しました。
//	//ムービーファイルを設定する。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////#ifndef USE_CPK
////	criManaPlayer_SetFile(pManaPlayer->player, NULL,   path);	//パッキングファイルを使用しない場合
////#else //USE_CPK
////	criManaPlayer_SetFile(pManaPlayer->player, binder, path);	//パッキングファイルを使用する場合
////#endif//USE_CPK
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	//□要注意
//	//│Sofdec2とADX2とで、シームレス連続再生の方法に、以下のような違いが有る事に注意せよ。
//	//│・Sofdec2のシームレス連続再生は、最初の再生も後続の再生も、コールバック内で行う。
//	//│・ADX2のシームレス連続再生は、最初の再生は明示的に行い、後続の再生はコールバック内で行う。
//	//何故こんな歪な仕様になっているのか、不明である。
//	if(!path[0] || (strlen(path) >= _MAX_PATH)) { DIE(); }	//パス名は1文字以上,かつ,_MAX_PATH文字未満でなくてはならない。
//	strcpy(pManaPlayer->sNextMoviePath, path);		//最初に再生するムービーのパス名を格納する。
////{{2016/09/17削除:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
////	criManaPlayer_SetDataRequestCallback(pManaPlayer->player, user_movie_request_callback_func, pManaPlayer);	//連結再生ファイル要求コールバックを登録する。
////}}2016/09/17削除:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//	//上述の通り、Sofdec2のシームレス連続再生は、最初の再生も後続の再生もコールバック内で行うので、ここで最初の再生指示を行ってはいけない。
////}}2016/08/01変更:シームレス連続再生に対応しました。
////{{2016/04/04修正:SofdecHelper_Load()が呼び出す関数を、criManaPlayer_Prepare()⇒criManaPlayer_DecodeHeader()に変更しました。
////	//ヘッダ解析と再生準備を開始する。
////	// - この後に行う処理はムービーヘッダ情報の取得であるから、criManaPlayer_Prepare()ではなくcriManaPlayer_DecodeHeader()でも構わない。
////	//   しかしその後すぐに再生処理へ移行させる事がわかっているのだから、criManaPlayer_Prepare()で再生準備まで進めておくのが良いだろう。	⇒{{2016/04/04追加:これは間違いでした。criManaPlayer_Prepare()は不可です!!ここで呼び出すのはcriManaPlayer_DecodeHeader()でなくてはなりません。詳細は下記のブロックのコメントを参照して下さい。}}
////	//   尚、もしここで呼び出す関数をcriManaPlayer_Prepare()⇒criManaPlayer_DecodeHeader()に変更した場合も、ここ以外の変更は不要である。
////	//   すぐに再生準備を行わない分の読み込み負荷が下がる代わりに、後でcriManaPlayer_Start()を呼び出した時の再生開始遅延が増えるだろう。
////	criManaPlayer_Prepare(pManaPlayer->player);
////	//少なくともヘッダ解析が完了するまで待機する。
////	// - ヘッダ解析が完了(≧CRIMANAPLAYER_STATUS_WAIT_PREP)したら当関数は処理を返すが、Sofdec2内のスレッドは引き続き再生準備を継続する。
////	//   再生準備が完了(≧CRIMANAPLAYER_STATUS_READY)していなくても、任意のタイミングでcriManaPlayer_Start()を呼び出す事が出来る。		⇒{{2016/04/04追加:これは間違いでした。再生準備が完了してCRIMANAPLAYER_STATUS_READYへ移行する前に、SofdecHelper_Play()が呼び出されてcriManaPlayer_Start()が実行されると、そのcriManaPlayer_Start()は *エラーも発生せずに* 単に無視されてしまうようです(実験より)。詳細は下記のブロックのコメントを参照して下さい。}}
////	for(;;) {
////		int Status = criManaPlayer_GetStatus(pManaPlayer->player);
////		if(Status == CRIMANAPLAYER_STATUS_ERROR) { DIE(); }
////	//不要	if(Status >= CRIMANAPLAYER_STATUS_WAIT_PREP) { break; }		//criManaPlayer_GetMovieInfo()が成功するかどうかで判断出来るので、この行の判断は不要である。
////		if(criManaPlayer_GetMovieInfo(pManaPlayer->player, &pManaPlayer->movie_info)) { break; }
////		SofdecHelper_Exec();	//必要
////	}
////↓2016/04/04修正:SofdecHelper_Load()が呼び出す関数を、criManaPlayer_Prepare()⇒criManaPlayer_DecodeHeader()に変更しました。
//	//ヘッダ解析を開始する。
//	// - criManaPlayer_Prepare()は不可です!!ここで呼び出すのはcriManaPlayer_DecodeHeader()でなくてはなりません。
//	//   もしcriManaPlayer_Prepare()を呼び出すと、ヘッダ解析完了後にSofdec2内のスレッドは引き続き再生準備を継続するのですが、
//	//   再生準備が完了してCRIMANAPLAYER_STATUS_READYへ移行する前に、SofdecHelper_Play()が呼び出されてcriManaPlayer_Start()が実行されると、
//	//   そのcriManaPlayer_Start()は *エラーも発生せずに* 単に無視されてしまうようです(実験より)。
//	//   結果として、いつまで経っても再生状態へ移行しない問題が生じます。
//	//   問題を防ぐため、ここで呼び出すのはcriManaPlayer_DecodeHeader()とし、CRIMANAPLAYER_STATUS_WAIT_PREPで待機させておく必要が有ります。
//	//   CRIMANAPLAYER_STATUS_WAIT_PREPの状態でSofdecHelper_Play()が呼び出されれば、正常に動作して、再生準備中を経て再生中へ移行します。
//	//   状態移行中にSofdecHelper_Play()が呼び出されると無視される事は、マニュアルには書いてありませんが、どうやらそういう挙動のようです。
//	criManaPlayer_DecodeHeader(pManaPlayer->player);
//}}2016/09/17削除:ムービーの再生準備を対応しました。
	//少なくともヘッダ解析が完了するまで待機する。
	// - ヘッダ解析が完了したら、バッファリング開始停止中(CRIMANAPLAYER_STATUS_WAIT_PREP)の状態で待機する。
	for(;;) {
		int Status = criManaPlayer_GetStatus(pManaPlayer->player);
		if(Status == CRIMANAPLAYER_STATUS_ERROR) { DIE(); }
	//不要	if(Status >= CRIMANAPLAYER_STATUS_WAIT_PREP) {	//criManaPlayer_GetMovieInfo()が成功するかどうかで判断出来るので、この行の判断は不要である。
			if(criManaPlayer_GetMovieInfo(pManaPlayer->player, &pManaPlayer->movie_info)) { break; }
	//不要	}
		SofdecHelper_Exec();	//必要
	}
//{{2016/09/17変更:ムービーの再生準備を対応しました。
//    //{{この範囲の処理はSofdec2の挙動を確認するためだけです。動作が安定したら削除して構いません。
//	//criManaPlayer_GetMovieInfo()が成功したら、バッファリング開始停止中(CRIMANAPLAYER_STATUS_WAIT_PREP)の状態で待機している事を確認する。
//	if(criManaPlayer_GetStatus(pManaPlayer->player) != CRIMANAPLAYER_STATUS_WAIT_PREP) { DIE(); }
//    //}}この範囲の処理はSofdec2の挙動を確認するためだけです。動作が安定したら削除して構いません。
//↓2016/09/17変更:ムービーの再生準備を対応しました。
	//SofdecHelper_Prepare_subr()にて、criManaPlayer_DecodeHeader()ではなくcriManaPlayer_Prepare()で再生準備を開始するように変更した事に伴い、SofdecHelper_Load()での待機方法を変更しました。
	//詳細は、SofdecHelper_Prepare_subr()のコメントを参照して下さい。
	for(;;) {
		int Status = criManaPlayer_GetStatus(pManaPlayer->player);
		switch(Status) {
		default:DIE();
	//	case CRIMANAPLAYER_STATUS_STOP:		//停止中
	//	case CRIMANAPLAYER_STATUS_DECHDR:	//ヘッダ解析中			ヘッダ解析中は上のヘッダ解析完了待ちのforループ内でブロックしているので、ここでまだヘッダ解析中である事は起こり得ないはず。
		case CRIMANAPLAYER_STATUS_WAIT_PREP:	//バッファリング開始停止中	SofdecHelper_Prepare_subr()にて、criManaPlayer_DecodeHeader()ではなくcriManaPlayer_Prepare()で再生準備を開始しているので、CRIMANAPLAYER_STATUS_DECHDR⇒CRIMANAPLAYER_STATUS_PREPへ遷移するはずであり、CRIMANAPLAYER_STATUS_WAIT_PREPに長時間留まる事はないはずではあるが、CRI Manaライブラリの内部処理がCRIMANAPLAYER_STATUS_WAIT_PREPを一瞬経由するのか飛ばすのかが不明だったため、一応CRIMANAPLAYER_STATUS_WAIT_PREPも許容する事にした。
		case CRIMANAPLAYER_STATUS_PREP:		//再生準備中
		case CRIMANAPLAYER_STATUS_READY:	//再生待機中
			/** no job **/
			break;
	//	case CRIMANAPLAYER_STATUS_PLAYING:	//再生中
	//	case CRIMANAPLAYER_STATUS_PLAYEND:	//再生終了
	//	case CRIMANAPLAYER_STATUS_ERROR:	//エラー
		}
		if(Status == CRIMANAPLAYER_STATUS_READY) { break; }	//再生待機中になったらループを抜ける。
		SofdecHelper_Exec();	//必要
	}
//}}2016/09/17変更:ムービーの再生準備を対応しました。
//}}2016/04/04修正:SofdecHelper_Load()が呼び出す関数を、criManaPlayer_Prepare()⇒criManaPlayer_DecodeHeader()に変更しました。
	//管理用構造体のインデクス⇒プレイヤーidに変換する。
	iManaPlayer++;	//プレイヤーid=(管理用構造体のインデクス+1)
	//プレイヤーidを返す。
	return iManaPlayer;
}
/*--------------------------------------------------------------------------*/
void SofdecHelper_Play(int iManaPlayer, int loop) {
	ST_ManaPlayer* pManaPlayer;
	//管理用構造体のポインタを取得する。
	pManaPlayer = SofdecHelper_GetManaPlayer(iManaPlayer);
	//現在の状態によって…
	switch(criManaPlayer_GetStatus(pManaPlayer->player)) {
	default:DIE();
	case CRIMANAPLAYER_STATUS_STOP:		//停止中
//	case CRIMANAPLAYER_STATUS_DECHDR:	//ヘッダ解析中			ヘッダ解析中はSofdecHelper_Load()内でブロックしているので、ここでヘッダ解析中である事は起こり得ないはず。
	case CRIMANAPLAYER_STATUS_WAIT_PREP:	//バッファリング開始停止中
	case CRIMANAPLAYER_STATUS_PREP:		//再生準備中
	case CRIMANAPLAYER_STATUS_READY:	//再生待機中
		/** no job **/
		break;
	case CRIMANAPLAYER_STATUS_PLAYING:	//再生中
		criManaPlayer_StopAndWaitCompletion(pManaPlayer->player);	//再生中に呼び出された場合は、一旦停止する。もし競合状態が発生して、criManaPlayer_StopAndWaitCompletion()を呼び出す直前に自然終了したとしても、安全である。criManaPlayer_StopAndWaitCompletion()は任意の状態で呼び出せるようだ。
		break;
	case CRIMANAPLAYER_STATUS_PLAYEND:	//再生終了
		/** no job **/
		break;
//	case CRIMANAPLAYER_STATUS_ERROR:	//エラー
	}
	//ループ再生の有無を設定する。
	// - criManaPlayer_Loop()は任意のタイミングで呼び出しが可能であり、任意のタイミングでループ再生の有無を切り替えられる。
	//   criManaPlayer_SetFile()の前やcriManaPlayer_Start()の後で下記の処理を行っても構わないが、ここで行うのが自然だろう。
	//   尚、criManaPlayer_Start()の後で行うのは危険です。
	//   極端に短いムービーをループ再生する場合、criManaPlayer_Loop()を呼ぶ前にムービーが終了してしまう危険が有るからです。
	criManaPlayer_Loop(pManaPlayer->player, loop);	//引数loopは、仕様上は(0 or 1)だが、実験したところ(0 or 0以外)で大丈夫なようだ。従って、'!!loop'ではなく'loop'と指定して構わない。
	//ムービーの再生を開始する。
	criManaPlayer_Start(pManaPlayer->player);
	//再生中,又は,(極端に短いムービーを非ループ再生した場合に)再生終了になるまで待つ。
	// - ここで待っておかないと、短い間隔でSofdecHelper_Play()を連続複数回呼び出された場合に、問題が生じる恐れが有る。
	//   Load後のCRIMANAPLAYER_STATUS_PREPと、直前のPlayの要求が完了する前のCRIMANAPLAYER_STATUS_PREPが、区別出来ない。
	for(;;) {
		int Status = criManaPlayer_GetStatus(pManaPlayer->player);
		if(Status == CRIMANAPLAYER_STATUS_ERROR) { DIE(); }
		if(Status >= CRIMANAPLAYER_STATUS_PLAYING) { break; }
		SofdecHelper_Exec();	//必要
	}
}
/*--------------------------------------------------------------------------*/
//{{2016/09/18削除:SofdecHelper_Stop()を廃止しました。
//void SofdecHelper_Stop(int iManaPlayer) {
//	ST_ManaPlayer* pManaPlayer;
//	//管理用構造体のポインタを取得する。
//	pManaPlayer = SofdecHelper_GetManaPlayer(iManaPlayer);
//	//ムービー再生を停止し、停止が完了するまで待機する。
//	// - 実験したところ、criManaPlayer_StopAndWaitCompletion()は任意のタイミングで呼び出せるようだ。
//	//   再生開始前や、準備中や、既に停止している時に再度呼び出しても、エラーは発生しなかった。
//	criManaPlayer_StopAndWaitCompletion(pManaPlayer->player);
//}
//}}2016/09/18削除:SofdecHelper_Stop()を廃止しました。
/*--------------------------------------------------------------------------*/
void SofdecHelper_Free(int iManaPlayer) {
	ST_ManaPlayer* pManaPlayer;
	//管理用構造体のポインタを取得する。
	pManaPlayer = SofdecHelper_GetManaPlayer(iManaPlayer);
	//ムービー再生を確実に停止する。
	// - 既に停止している時に、再度criManaPlayer_StopAndWaitCompletion()を呼び出しても安全なようだ。
	//   SofdecHelper_Stop()内のコメントを参照せよ。
	criManaPlayer_StopAndWaitCompletion(pManaPlayer->player);	//{{2016/09/17コメント追記:ここをcriManaPlayer_Stop()に変更して非同期で終了待ちする改善案も有るのだが、実際に試してみると見た目にほとんど違いが無かった。おそらくcriManaPlayer_StopAndWaitCompletion()は待ってもほぼ一瞬で終わっているのだろう。criManaPlayer_Stop()よりもcriManaPlayer_StopAndWaitCompletion()を使う方が当モジュールの実装が単純で安全なので、これまで通りcriManaPlayer_StopAndWaitCompletion()のままとする事にした。}}
//{{2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//	//Manaプレーヤを破棄する。
//	criManaPlayer_Destroy(pManaPlayer->player);
//	//管理用構造体を空きエントリに戻す。
//	pManaPlayer->player = NULL;
//↓2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
	//使用中フラグをクリアする。
	pManaPlayer->Flag &= ~ManaPlayer_Flag_InUse;
//}}2016/09/17変更:SofdecHelperのアルゴリズムも、AdxHelperのアルゴリズムと同様に、最初にプレイヤーを全て作成しておいて最後まで使い回すように変更しました。
//{{2016/09/17追加:ムービーの再生準備を対応しました。
#ifdef  USE_FREE_PREP
	//即SofdecHelper_Prepare()を呼び出して次回の再生準備を行う。LRUキャッシュのイメージです。
	// - 再生準備の対象とするムービーは、このSofdecHelper_Free()に対応するSofdecHelper_Load()の際に指定されたムービーです。
	//   シームレス連続再生が行われた場合も、一番最初のムービーの再生準備を行います。
	if(!pManaPlayer->sPrepMoviePath[0]) { DIE(); }	//再生中もsPrepMoviePath[]はクリアしてはいけない。ここで停止したら当モジュールのバグです。
	//SofdecHelper_Prepare_subr()の処理のために、このエントリを一時的に準備中でない状態に戻す。
	{
		char path[_MAX_PATH];
		strcpy(path, pManaPlayer->sPrepMoviePath);
		pManaPlayer->sPrepMoviePath[0] = '\0';	//必要です。忘れないで!!
		//次回の再生準備を行う。
		// - 必ずしもこのエントリを使って再生準備が行われるとは限りません。別のエントリを使って再生準備が行われる可能性も有ります。
		//   少なくともこのエントリが空きなので、再生準備は必ず行われるはずです。(ここで再生準備が必ず行われる動作には依存していないので、その事を検査する必要は有りません。)
		SofdecHelper_Prepare(path);
	}
#else //USE_FREE_PREP
	//再生準備中のムービーのパス名をクリアする。
	pManaPlayer->sPrepMoviePath[0] = '\0';
#endif//USE_FREE_PREP
//}}2016/09/17追加:ムービーの再生準備を対応しました。
}
/*--------------------------------------------------------------------------*/
int SofdecHelper_CopyFrame(int iManaPlayer, void* imagebuf, int bufsize, int pitch) {
	ST_ManaPlayer* pManaPlayer;
	CriManaFrameInfo frame_info;
	CriManaTextureBuffer frame_buf;
	//管理用構造体のポインタを取得する。
	pManaPlayer = SofdecHelper_GetManaPlayer(iManaPlayer);
	//デコード済みのフレーム情報の参照する。
	// - Manaプレーヤのフレームプールにデコード済みのフレームが有れば、次のフレームの情報を取得出来る。
	//   次のフレームの情報を取得出来なければ、何もせずに帰る。
	//   criManaPlayer_DiscardFrame()を呼ばない限り、criManaPlayer_ReferFrame()は次回も同じフレーム情報を取得する。
	if(!criManaPlayer_ReferFrame(pManaPlayer->player, &frame_info)) { return 0; }		//フレームを取得出来なかった事を示す、0を返す。
	//フレームの時刻判定を行う。
	// - 入力したフレーム情報がまだ表示時刻になっていなければ、何もせずに帰る。
	if(!criManaPlayer_IsFrameOnTime(pManaPlayer->player, &frame_info)) { return 0; }	//フレームを取得出来なかった事を示す、0を返す。
	//32bit ARGB8888 フォーマットで、ビデオフレームを取得する。
	frame_buf.imagebuf = (CriUint8*)imagebuf;
	frame_buf.bufsize  =            bufsize;
	frame_buf.pitch    =            pitch;
	criManaPlayer_CopyFrameToBufferARGB32(pManaPlayer->player, &frame_info, &frame_buf);
	//フレームを解放する。
	// - criManaPlayer_DiscardFrame()を呼ばない限り、criManaPlayer_ReferFrame()は次回も同じフレーム情報を取得する。
	criManaPlayer_DiscardFrame(pManaPlayer->player, &frame_info);
	//フレームを取得出来た事を示す、1を返す。
	return 1;
}
/*--------------------------------------------------------------------------*/
int SofdecHelper_CopyFrameYUV(int iManaPlayer,
    void* y_imagebuf, int y_bufsize, int y_pitch,
    void* u_imagebuf, int u_bufsize, int u_pitch,
    void* v_imagebuf, int v_bufsize, int v_pitch,
    void* a_imagebuf/*NULL可*/, int a_bufsize/*0可*/, int a_pitch/*0可*/) {
	ST_ManaPlayer* pManaPlayer;
	CriManaFrameInfo frame_info;
	CriManaTextureBuffersYUV frame_buf;
	//管理用構造体のポインタを取得する。
	pManaPlayer = SofdecHelper_GetManaPlayer(iManaPlayer);
	//デコード済みのフレーム情報の参照する。
	// - Manaプレーヤのフレームプールにデコード済みのフレームが有れば、次のフレームの情報を取得出来る。
	//   次のフレームの情報を取得出来なければ、何もせずに帰る。
	//   criManaPlayer_DiscardFrame()を呼ばない限り、criManaPlayer_ReferFrame()は次回も同じフレーム情報を取得する。
	if(!criManaPlayer_ReferFrame(pManaPlayer->player, &frame_info)) { return 0; }		//フレームを取得出来なかった事を示す、0を返す。
	//フレームの時刻判定を行う。
	// - 入力したフレーム情報がまだ表示時刻になっていなければ、何もせずに帰る。
	if(!criManaPlayer_IsFrameOnTime(pManaPlayer->player, &frame_info)) { return 0; }	//フレームを取得出来なかった事を示す、0を返す。
	//YUVフォーマットで、ビデオフレームを取得する。
	// * Fri Jul 29 21:34:34 JST 2016 Naoyuki Sawa
	// - YUVの種類がどれであるかが、Sofdec2のマニュアル(\cri\documentation\CRI_Sofdec2_Manual_pc_j.chm)内に見当たりませんでした。
	//   しかし、サンプルプログラム(\cri\pc\samples\crimana\simple_playback\simple_playback.c)から推測するに、YUV420のようです。
	//   simple_playback.cのsmpvideo_texture_create_YUVA()の「テクスチャリソースの作成」の処理において、U,V用のテクスチャは、Y,A用のテクスチャの縦横半分のサイズで作成されているからです。
	// - YUV420の構造は、以下の通りです。
	//   (参照資料:「お前が言うなと思われつつ勝手にレビューしてみる腕コン」(http://st6uc.blog119.fc2.com/)さんの「色の話・その２」(http://st6uc.blog119.fc2.com/blog-entry-27.html)の記事)
	//   ┌───┬───┐
	//   │Y:8bit│Y:8bit│
	//   │  ┌─┴─┐  │
	//   │  │U:8bit│  │
	//   ├─┤      ├─┤
	//   │  │V:8bit│  │
	//   │  └─┬─┘  │
	//   │Y:8bit│Y:8bit│
	//   └───┴───┘
	// - 2016/07/29現在、以下の点が不明であり、要調査です。		⇒{{2016/07/30コメント追記:調査しました。詳細はSat Jul 30 20:01:40 JST 2016のコメントを参照して下さい。}}
	//   │アルファ無しムービーの場合に、criManaPlayer_DiscardFrame()は、frame_buf.a_planeをどのように扱うのか?
	//   │・criManaPlayer_DiscardFrame()がframe_buf.a_planeを255でフィルしてくれるのか? → もしそうならば、criManaPlayer_CopyFrameToBufferARGB32()の場合と同様に、当関数はムービーがアルファ付きかを気にする必要は無い。
	//   │・criManaPlayer_DiscardFrame()はframe_buf.a_planeを無視するのか? → もしそうならば、(frame_info.alpha_type==CRIMANA_COMPO_OPAQ) の場合、当関数内で明示的にframe_buf.a_planeを255でフィルする必要が有るだろう。
	// * Sat Jul 30 20:01:40 JST 2016 Naoyuki Sawa
	// - criManaPlayer_DiscardFrame()がframe_buf.a_plane,及び,frame_buf.y_plane,frame_buf.u_plane,frame_buf.v_planeをどのように扱うのかを、調査しました。
	//   □Sofdec2のAPI「criManaPlayer_DiscardFrame()」の挙動について:
	//   │・frame_buf.y_plane,frame_buf.u_plane,frame_buf.v_planeの指定は必須です。(アルファ付きムービーでも、アルファ無しムービーでも)
	//   │  これらのフィールドのimagebufがNULLだったり、bufsizeやpitchが正しく指定されていないと、criManaPlayer_DiscardFrame()を呼び出した瞬間に、異常終了します。
	//   │・frame_buf.a_planeの指定は、アルファ付きムービーの場合は任意であり、アルファ無しムービーの場合は無視されます。
	//   │  具体的には、以下の通りです。
	//   │  1-1. アルファ付きムービーで、frame_buf.a_planeが指定されていると、criManaPlayer_DiscardFrame()がアルファ値を格納します。
	//   │  1-2. アルファ付きムービーで、frame_buf.a_planeが指定されていなければ、criManaPlayer_DiscardFrame()がアルファ値を格納しません。
	//   │       例えば、アルファ付きムービーであるがアルファ値が不要である場合は、(frame_buf.a_plane.imagebuf=NULL)にしてcriManaPlayer_DiscardFrame()を呼び出せば良いという事です。
	//   │  2-1. アルファ無しムービーで、frame_buf.a_planeが指定されていると、criManaPlayer_DiscardFrame()はそれを無視して、何も格納しません。
	//   │       frame_buf.a_plane.imagebufが指しているバッファの値は、書き換えられません。
	//   │  2-2. アルファ無しムービーで、frame_buf.a_planeが指定されていなければ、criManaPlayer_DiscardFrame()がアルファ値を格納しません。
	//   □当関数(SofdecHelper_CopyFrameYUV())の動作について:
	//   │当関数を呼び出すアプリケーションが、アルファ付きムービーであるかアルファ無しムービーであるかを意識しないで済むようにするために、以下のように処理する事にしました。
	//   │・アルファ無しムービーで、a_imagebuf引数にNULLポインタ以外が指定された場合は、a_imagebufが指すバッファをを明示的に255でフィルする。
	//   │ただし、フィル動作の分、少し性能低下が発生しますので、呼び出し側でアルファ無しムービーで有る事が判っている場合は、a_imagebuf引数にNULLポインタを指定する方が望ましいです。
	//   │アプリケーションがアルファ付きムービーであるかアルファ無しムービーであるかを区別するには、SofdecHelper_GetAlpha()を使用して下さい。
	frame_buf.y_plane.imagebuf = (CriUint8*)y_imagebuf;
	frame_buf.y_plane.bufsize  =            y_bufsize;
	frame_buf.y_plane.pitch    =            y_pitch;
	frame_buf.u_plane.imagebuf = (CriUint8*)u_imagebuf;
	frame_buf.u_plane.bufsize  =            u_bufsize;
	frame_buf.u_plane.pitch    =            u_pitch;
	frame_buf.v_plane.imagebuf = (CriUint8*)v_imagebuf;
	frame_buf.v_plane.bufsize  =            v_bufsize;
	frame_buf.v_plane.pitch    =            v_pitch;
	frame_buf.a_plane.imagebuf = (CriUint8*)a_imagebuf;
	frame_buf.a_plane.bufsize  =            a_bufsize;
	frame_buf.a_plane.pitch    =            a_pitch;
	criManaPlayer_CopyFrameToBuffersYUV(pManaPlayer->player, &frame_info, &frame_buf);
	//フレームを解放する。
	// - criManaPlayer_DiscardFrame()を呼ばない限り、criManaPlayer_ReferFrame()は次回も同じフレーム情報を取得する。
	criManaPlayer_DiscardFrame(pManaPlayer->player, &frame_info);
//{{2016/07/30追加:詳細はSat Jul 30 20:01:40 JST 2016のコメントを参照して下さい。
	//アルファ無しムービーで、a_imagebuf引数にNULLポインタ以外が指定された場合は、a_imagebufが指すバッファをを明示的に255でフィルする。
	if((frame_info.alpha_type == CRIMANA_COMPO_OPAQ) && (a_imagebuf)) {
		char* p = a_imagebuf;
		int i = frame_info.height;
		while(--i >= 0) {
			memset(p, -1, frame_info.width);
			p += a_pitch;
		}
	}
//}}2016/07/30追加:詳細はSat Jul 30 20:01:40 JST 2016のコメントを参照して下さい。
	//フレームを取得出来た事を示す、1を返す。
	return 1;
}
/*--------------------------------------------------------------------------*/
int SofdecHelper_GetFrameYUV(int iManaPlayer,
    void (*fn)(void* y_imagebuf, int y_bufsize, int y_pitch,
               void* u_imagebuf, int u_bufsize, int u_pitch,
               void* v_imagebuf, int v_bufsize, int v_pitch,
               void* a_imagebuf, int a_bufsize, int a_pitch,
               void* user_data), void* user_data) {
	ST_ManaPlayer* pManaPlayer;
	CriManaFrameInfo frame_info;
	//管理用構造体のポインタを取得する。
	pManaPlayer = SofdecHelper_GetManaPlayer(iManaPlayer);
	//デコード済みのフレーム情報の参照する。
	// - Manaプレーヤのフレームプールにデコード済みのフレームが有れば、次のフレームの情報を取得出来る。
	//   次のフレームの情報を取得出来なければ、何もせずに帰る。
	//   criManaPlayer_DiscardFrame()を呼ばない限り、criManaPlayer_ReferFrame()は次回も同じフレーム情報を取得する。
	if(!criManaPlayer_ReferFrame(pManaPlayer->player, &frame_info)) { return 0; }		//フレームを取得出来なかった事を示す、0を返す。
	//フレームの時刻判定を行う。
	// - 入力したフレーム情報がまだ表示時刻になっていなければ、何もせずに帰る。
	if(!criManaPlayer_IsFrameOnTime(pManaPlayer->player, &frame_info)) { return 0; }	//フレームを取得出来なかった事を示す、0を返す。
	//アプリケーション定義のコールバック関数を呼び出す。
	(*fn)(frame_info.image_info[0].imageptr, frame_info.image_info[0].bufsize, frame_info.image_info[0].line_pitch,	//Y
	      frame_info.image_info[1].imageptr, frame_info.image_info[1].bufsize, frame_info.image_info[1].line_pitch,	//U
	      frame_info.image_info[2].imageptr, frame_info.image_info[2].bufsize, frame_info.image_info[2].line_pitch,	//V
	      frame_info.image_info[3].imageptr, frame_info.image_info[3].bufsize, frame_info.image_info[3].line_pitch,	//A
	      user_data);
	//フレームを解放する。
	// - criManaPlayer_DiscardFrame()を呼ばない限り、criManaPlayer_ReferFrame()は次回も同じフレーム情報を取得する。
	criManaPlayer_DiscardFrame(pManaPlayer->player, &frame_info);
	//フレームを取得出来た事を示す、1を返す。
	return 1;
}
/*--------------------------------------------------------------------------*/
const char* SofdecHelper_GetStatus(int iManaPlayer) {
	static const char* const TBL_StatusStr[]={
		"STOP",		//0 CRIMANAPLAYER_STATUS_STOP      停止中
		"DECHDR",	//1 CRIMANAPLAYER_STATUS_DECHDR    ヘッダ解析中
		"WAIT_PREP",	//2 CRIMANAPLAYER_STATUS_WAIT_PREP バッファリング開始停止中
		"PREP",		//3 CRIMANAPLAYER_STATUS_PREP      再生準備中
		"READY",	//4 CRIMANAPLAYER_STATUS_READY     再生待機中
		"PLAYING",	//5 CRIMANAPLAYER_STATUS_PLAYING   再生中
		"PLAYEND",	//6 CRIMANAPLAYER_STATUS_PLAYEND   再生終了
		"ERROR",	//7 CRIMANAPLAYER_STATUS_ERROR     エラー
	};
	ST_ManaPlayer* pManaPlayer = SofdecHelper_GetManaPlayer(iManaPlayer);
	int Status = criManaPlayer_GetStatus(pManaPlayer->player);
	if((unsigned)Status >= (unsigned)_countof(TBL_StatusStr)) { DIE(); }
	return TBL_StatusStr[Status];
}
/*--------------------------------------------------------------------------*/
int SofdecHelper_GetWidth(int iManaPlayer)		{	return SofdecHelper_GetManaPlayer(iManaPlayer)->movie_info.video_prm[0].width;			}	//ムービー最大幅(8の倍数)
int SofdecHelper_GetHeight(int iManaPlayer)		{	return SofdecHelper_GetManaPlayer(iManaPlayer)->movie_info.video_prm[0].height;			}	//ムービー最大高さ(8の倍数)
int SofdecHelper_GetDispWidth(int iManaPlayer)		{	return SofdecHelper_GetManaPlayer(iManaPlayer)->movie_info.video_prm[0].disp_width;		}	//表示したい映像の横ピクセル数(左端から)
int SofdecHelper_GetDispHeight(int iManaPlayer)		{	return SofdecHelper_GetManaPlayer(iManaPlayer)->movie_info.video_prm[0].disp_height;		}	//表示したい映像の縦ピクセル数(上端から)
int SofdecHelper_GetFramerate(int iManaPlayer)		{	return SofdecHelper_GetManaPlayer(iManaPlayer)->movie_info.video_prm[0].framerate;		}	//フレームレート[x1000]
int SofdecHelper_GetFramerateN(int iManaPlayer)		{	return SofdecHelper_GetManaPlayer(iManaPlayer)->movie_info.video_prm[0].framerate_n;		}	//有理数形式フレームレート(分子) framerate [x1000] = framerate_n / framerate_d
int SofdecHelper_GetFramerateD(int iManaPlayer)		{	return SofdecHelper_GetManaPlayer(iManaPlayer)->movie_info.video_prm[0].framerate_d;		}	//有理数形式フレームレート(分母) framerate [x1000] = framerate_n / framerate_d
int SofdecHelper_GetTotalFrames(int iManaPlayer)	{	return SofdecHelper_GetManaPlayer(iManaPlayer)->movie_info.video_prm[0].total_frames;		}	//総フレーム数
int SofdecHelper_GetMaterialWidth(int iManaPlayer)	{	return SofdecHelper_GetManaPlayer(iManaPlayer)->movie_info.video_prm[0].material_width;		}	//エンコード前のビデオ素材の横ピクセル数
int SofdecHelper_GetMaterialHeight(int iManaPlayer)	{	return SofdecHelper_GetManaPlayer(iManaPlayer)->movie_info.video_prm[0].material_height;	}	//エンコード前のビデオ素材の縦ピクセル数
int SofdecHelper_GetAlpha(int iManaPlayer)		{	return SofdecHelper_GetManaPlayer(iManaPlayer)->movie_info.num_alpha_streams;			}	//アルファストリームの数(0,又は,1)
/****************************************************************************
 *	AdxHelper
 ****************************************************************************/
#ifdef  USE_ADX
//{{2016/08/01変更:シームレス連続再生に対応しました。
//static CriAtomExPlayerHn SofdecHelper_GetAtomExPlayer(int iAtomExPlayer) {
//	CriAtomExPlayerHn player;
//	iAtomExPlayer--;						//プレイヤーid⇒プレーヤハンドルの配列のインデクスに変換する。	//プレイヤーid=プレーヤハンドルの配列のインデクス+1)
//	if((unsigned)iAtomExPlayer >= (unsigned)NumSounds) { DIE(); }	//プレーヤハンドルの配列のインデクスが不正。
//	player = TBL_AtomExPlayer[iAtomExPlayer];
//	return player;
//}
//↓2016/08/01変更:シームレス連続再生に対応しました。
static ST_AtomExPlayer* SofdecHelper_GetAtomExPlayer(int iAtomExPlayer) {
	ST_AtomExPlayer* pAtomExPlayer;
	iAtomExPlayer--;						//プレイヤーid⇒プレーヤハンドルの配列のインデクスに変換する。	//プレイヤーid=プレーヤハンドルの配列のインデクス+1)
	if((unsigned)iAtomExPlayer >= (unsigned)stSofdecHelperInfo.NumSounds) { DIE(); }	//プレーヤハンドルの配列のインデクスが不正。
	pAtomExPlayer = &TBL_AtomExPlayer[iAtomExPlayer];
//不要	if(!pAtomExPlayer->player) { DIE(); }				//空きエントリを取得しようとした。	⇒	AdxHelperのアルゴリズムは、SofdecHelperのアルゴリズムと違って、最初にプレイヤーを全て作成しておいて最後まで使い回すので、空きエントリの概念は無い。この判断は不要である。
	return pAtomExPlayer;
}
//}}2016/08/01変更:シームレス連続再生に対応しました。
/*--------------------------------------------------------------------------*/
//{{2016/08/01追加:シームレス連続再生に対応しました。
//static void app_data_request_callback(void* obj, CriAtomExPlaybackId id, CriAtomPlayerHn player) {
//	ST_AtomExPlayer* pAtomExPlayer = obj;
//	//今回再生するサウンドのパス名が有れば…
//	if(pAtomExPlayer->sNextSoundPath[0]) {
//		//今回再生するサウンドファイルを設定する。
//		// - □注意点(ADX2のマニュアルのシームレス連結再生の頁より引用):
//		//   │尚、コールバックの設定や初回のデータ設定はAtomExプレーヤに対して行いますが、
//		//   │コールバック内でのデータの追加については、下位レイヤ(Atomレイヤ)の「Atomプレーヤ」のAPIを使用する必要があります。
//		//   │例えば、複数のオンメモリデータを連結再生する場合、初回のデータ設定はAtomExプレーヤに対してcriAtomExPlayer_SetData関数を実行しますが、
//		//   │コールバック関数内で後続データを指定する際には、コールバック関数の引数で渡って来るAtomプレーヤハンドルに対しcriAtomPlayer_SetData関数を使用してデータを指定します。
//#ifndef USE_CPK
//		criAtomPlayer_SetFile(player, NULL,   pAtomExPlayer->sNextSoundPath);
//#else //USE_CPK
//		criAtomPlayer_SetFile(player, binder, pAtomExPlayer->sNextSoundPath);
//#endif//USE_CPK
//		//次に再生するサウンドのパス名を取得する。
//		if(stSofdecHelperInfo.fnGetNextSound) {
//			char sCurrentSoundPath[_MAX_PATH];
//			strcpy(sCurrentSoundPath, pAtomExPlayer->sNextSoundPath);
//			pAtomExPlayer->sNextSoundPath[0] = '\0';	//まず空文字にしておく。
//			(*stSofdecHelperInfo.fnGetNextSound)(sCurrentSoundPath, pAtomExPlayer->sNextSoundPath);
//		}
//	}
//}
//↓2016/09/18変更:サウンドの再生準備を対応しました。
static void app_data_request_callback(void* obj, CriAtomExPlaybackId id, CriAtomPlayerHn player) {
	ST_AtomExPlayerPool* pAtomExPlayerPool = obj;
	//今回再生するサウンドのパス名が有れば…
	if(pAtomExPlayerPool->sNextSoundPath[0]) {
		//今回再生するサウンドファイルを設定する。
		// - □注意点(ADX2のマニュアルのシームレス連結再生の頁より引用):
		//   │尚、コールバックの設定や初回のデータ設定はAtomExプレーヤに対して行いますが、
		//   │コールバック内でのデータの追加については、下位レイヤ(Atomレイヤ)の「Atomプレーヤ」のAPIを使用する必要があります。
		//   │例えば、複数のオンメモリデータを連結再生する場合、初回のデータ設定はAtomExプレーヤに対してcriAtomExPlayer_SetData関数を実行しますが、
		//   │コールバック関数内で後続データを指定する際には、コールバック関数の引数で渡って来るAtomプレーヤハンドルに対しcriAtomPlayer_SetData関数を使用してデータを指定します。
#ifndef USE_CPK
		criAtomPlayer_SetFile(player, NULL,   pAtomExPlayerPool->sNextSoundPath);
#else //USE_CPK
		criAtomPlayer_SetFile(player, binder, pAtomExPlayerPool->sNextSoundPath);
#endif//USE_CPK
		//次に再生するサウンドのパス名を取得する。
		if(stSofdecHelperInfo.fnGetNextSound) {
			char sCurrentSoundPath[_MAX_PATH];
			strcpy(sCurrentSoundPath, pAtomExPlayerPool->sNextSoundPath);
			pAtomExPlayerPool->sNextSoundPath[0] = '\0';	//まず空文字にしておく。
			(*stSofdecHelperInfo.fnGetNextSound)(sCurrentSoundPath, pAtomExPlayerPool->sNextSoundPath);
		}
	}
}
//}}2016/09/18変更:サウンドの再生準備を対応しました。
/*--------------------------------------------------------------------------*/
//{{2016/09/18追加:サウンドの再生準備を対応しました。
//再生準備を行い、その再生プールのインデクス(0～(NumSounds-1))を返す。
//ただし、再生準備を行えなかった場合は、-1を返す。
static int AdxHelper_Prepare_subr(const char* path) {
	int iAtomExPlayerPool;
	ST_AtomExPlayerPool* pAtomExPlayerPool;
	if(!path[0] || (strlen(path) >= _MAX_PATH)) { DIE(); }	//パス名は1文字以上,かつ,_MAX_PATH文字未満でなくてはならない。
	//手順①:既に再生準備中の再生プールが有れば何もしない。
	{
		for(iAtomExPlayerPool = 0; iAtomExPlayerPool < stSofdecHelperInfo.NumSounds; iAtomExPlayerPool++) {
			pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
			//使用中でなく…
			if(!(pAtomExPlayerPool->Flag & AtomExPlayerPool_Flag_InUse)) {
				//再生準備中のサウンドのパス名が一致したら…
				if(!strcmp(pAtomExPlayerPool->sPrepSoundPath, path)) {
					//再生準備開始処理は行わずに、世代管理処理だけを行って、この再生プールのインデクス(0～(NumSounds-1))を返す。
					// - 同じサウンドを重複して準備する事は行わない事にしました。
					//   その方が、アプリケーション側で無駄に重複した準備を避けるための処理が不要になり、アプリケーションの実装が簡単になると思ったからです。
					goto L_AGING;	//世代管理処理へ移行する。
				}
			}
		}
	}
	//手順②:使用中でなく、再生準備中でない再生プールを探して、再生準備を開始する。
	{
		for(iAtomExPlayerPool = 0; iAtomExPlayerPool < stSofdecHelperInfo.NumSounds; iAtomExPlayerPool++) {
			pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
			//使用中でなく…
			if(!(pAtomExPlayerPool->Flag & AtomExPlayerPool_Flag_InUse)) {
				//再生準備中でなければ…
				if(!pAtomExPlayerPool->sPrepSoundPath[0]) {
					//再生準備開始処理へ移行する。
					goto L_PREP;
				}
			}
		}
	}
	//手順③:使用中でなく、再生準備中である再生プールのうち、一番古い再生準備中の再生プールの、既存の再生準備を停止し、新たな再生準備を開始する。
	{
		int Oldest_PrepAge       = -1;
		int Oldest_iAtomExPlayer = -1;
		for(iAtomExPlayerPool = 0; iAtomExPlayerPool < stSofdecHelperInfo.NumSounds; iAtomExPlayerPool++) {
			pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
			//使用中でなく…
			if(!(pAtomExPlayerPool->Flag & AtomExPlayerPool_Flag_InUse)) {
				//再生準備中ならば…
				if(!pAtomExPlayerPool->sPrepSoundPath[0]) { DIE(); }	//手順②にヒットしなかったと言うことは、使用中でない再生プールは全て再生準備中であるはずです。もしここで停止したら、当関数のバグです。
				//再生準備中の再生プールのうち、一番古い再生プールを検索する。
				if(pAtomExPlayerPool->PrepAge > Oldest_PrepAge) {
					Oldest_PrepAge       = pAtomExPlayerPool->PrepAge;
					Oldest_iAtomExPlayer = iAtomExPlayerPool;
				}
			}
		}
		//使用中でなく、再生準備中の再生プールのうち、一番古い再生プールが見つかったら…
		if(Oldest_iAtomExPlayer != -1) {
			iAtomExPlayerPool = Oldest_iAtomExPlayer;
			pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
			//既存の再生準備を停止し、停止が完了するまで待機する。
			switch(criAtomExPlayer_GetStatus(pAtomExPlayerPool->player)) {
			default:DIE();
			case CRIATOMEXPLAYER_STATUS_STOP:	//停止中
				/** no job **/
				break;
			case CRIATOMEXPLAYER_STATUS_PREP:	//再生準備中
			case CRIATOMEXPLAYER_STATUS_PLAYING:	//再生中
			case CRIATOMEXPLAYER_STATUS_PLAYEND:	//再生完了	←←←※要検討:PLAYENDはSTOPと同じと見なしてそのままでも良いかも知れないのだが、2016/05/04現在は念のためにPLAYENDもSTOPへ移行しておく事にした。
				//再生の停止(リリースタイム無視)。
				criAtomExPlayer_StopWithoutReleaseTime(pAtomExPlayerPool->player);
				//AtomExプレーヤのステータスが停止状態になるまで待つ。	//{{2016/09/18コメント追記:ここで停止状態になるまで待たずに非同期で終了待ちする改善案も有るのだが、実際に試してみると現在の処理でもここでの待ち時間は一瞬(計測誤差を考慮してもせいぜい一桁ミリ秒)であり、現在の処理のままでも問題無いと思った。ここで停止状態になるまで待つ方が当モジュールの実装が単純で安全なので、これまで通りここで停止状態になるまで待つ事にした。}}
				for(;;) {
					int Status = criAtomExPlayer_GetStatus(pAtomExPlayerPool->player);
					if(Status == CRIATOMEXPLAYER_STATUS_ERROR) { DIE(); }
					if(Status == CRIATOMEXPLAYER_STATUS_STOP) { break; }
					criAtomEx_ExecuteMain();
					Sleep(0);	//不要かも?
				}
				break;
		//	case CRIATOMEXPLAYER_STATUS_ERROR:	//エラーが発生
			}
			//再生準備開始処理へ移行する。
			goto L_PREP;
		}
	}
	//手順④:使用中でない再生プールが無ければ、再生準備を開始せずに、-1を返す。
	return -1;
	//--- 再生準備開始処理 ---
L_PREP:	{
		pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
	    #ifdef  _DEBUG
		fprintf(stderr, "AdxHelper_Prepare_subr: %d: %s\n", iAtomExPlayerPool, path);
	    #endif//_DEBUG
		//再生準備中のサウンドのパス名を格納する。
		strcpy(pAtomExPlayerPool->sPrepSoundPath, path);
		//□要注意
		//│Sofdec2とADX2とで、シームレス連続再生の方法に、以下のような違いが有る事に注意せよ。
		//│・Sofdec2のシームレス連続再生は、最初の再生も後続の再生も、コールバック内で行う。
		//│・ADX2のシームレス連続再生は、最初の再生は明示的に行い、後続の再生はコールバック内で行う。
		//何故こんな歪な仕様になっているのか、不明である。
		pAtomExPlayerPool->sNextSoundPath[0] = '\0';		//まず空文字にしておく。
		if(stSofdecHelperInfo.fnGetNextSound) {
			(*stSofdecHelperInfo.fnGetNextSound)(path, pAtomExPlayerPool->sNextSoundPath);	//次に再生するサウンドのパス名を取得する。	'最初に再生するサウンドのパス名'ではない事に注意せよ。最初の再生はこの下で明示的に行う。
		}
		//上述の通り、ADX2のシームレス連続再生は、最初の再生は明示的に行う必要が有るので、ここで最初の再生指示を行う。
	    #ifndef USE_CPK
		criAtomExPlayer_SetFile(pAtomExPlayerPool->player, NULL,   path);	//パッキングファイルを使用しない場合
	    #else //USE_CPK
		criAtomExPlayer_SetFile(pAtomExPlayerPool->player, binder, path);	//パッキングファイルを使用する場合
	    #endif//USE_CPK
	    #if 0
		//再生する音声データのフォーマットを指定。	⇒この処理は不要みたいです。この処理を行わなくても、ADX形式とHCA形式が自動判別されて、問題無く再生される事を動作確認しました。もし今後何か問題が生じたら、このブロックを有効にする事を検討して下さい。
		{
			char ext[_MAX_EXT];
			_splitpath(path, NULL, NULL, NULL, ext);
			if(!_stricmp(ext, ".adx")) {
				criAtomExPlayer_SetFormat(player, CRIATOMEX_FORMAT_ADX);
			} else if(!_stricmp(ext, ".hca")) {
				criAtomExPlayer_SetFormat(player, CRIATOMEX_FORMAT_HCA);
			} else {
				DIE();
			}
		}
	    #endif
		//ヘッダ解析を開始する。
//{{2016/09/19変更:AdxHelper_GetTotalTime()を高速化しました。
//		criAtomExPlayer_Prepare(pAtomExPlayerPool->player);
//↓2016/09/19変更:AdxHelper_GetTotalTime()を高速化しました。
		pAtomExPlayerPool->playback_id = criAtomExPlayer_Prepare(pAtomExPlayerPool->player);
		if(pAtomExPlayerPool->playback_id == CRIATOMEX_INVALID_PLAYBACK_ID) { DIE(); }	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
//}}2016/09/19変更:AdxHelper_GetTotalTime()を高速化しました。
		//世代管理処理へ移行する。
		/* FALLTHRU */
	}
	//--- 世代管理処理 ---
	//手順④で処理を返した場合以外(=既にこのサウンドを再生準備中の再生プールを見つけたか、又は、いずれかの再生プールを新たに再生準備中へ移行した場合)は、以下の世代管理処理を行う。
L_AGING:{
		int i;
		//当該再生プールの再生準備の古さをクリアする。
		pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
		pAtomExPlayerPool->PrepAge = 0;
		//当該再生プール以外で、使用中でなく、再生準備中で、再生準備の古さが0の再生プールが有るかを調べる。
		for(i = 0; i < stSofdecHelperInfo.NumSounds; i++) {
			pAtomExPlayerPool = &TBL_AtomExPlayerPool[i];
			if(i != iAtomExPlayerPool) {						//当該再生プール以外で、
				if(!(pAtomExPlayerPool->Flag & AtomExPlayerPool_Flag_InUse)) {	//使用中でなく、
					if(pAtomExPlayerPool->sPrepSoundPath[0]) {		//再生準備中で、
						if(!pAtomExPlayerPool->PrepAge) { break; }	//再生準備の古さが0の再生プールが有れば、ループを抜ける。
					}
				}
			}
		}
		//当該再生プール以外で、使用中でなく、再生準備中で、再生準備の古さが0の再生プールが有れば…
		if(i < stSofdecHelperInfo.NumSounds) {
			//当該再生プール以外で、使用中でなく、再生準備中の再生プールの、再生準備の古さを増やす。
			for(i = 0; i < stSofdecHelperInfo.NumSounds; i++) {
				pAtomExPlayerPool = &TBL_AtomExPlayerPool[i];
				if(i != iAtomExPlayerPool) {						//当該再生プール以外で、
					if(!(pAtomExPlayerPool->Flag & AtomExPlayerPool_Flag_InUse)) {	//使用中でなく、
						if(pAtomExPlayerPool->sPrepSoundPath[0]) {		//再生準備中ならば…
							pAtomExPlayerPool->PrepAge++;			//再生準備の古さを増やす。	このアルゴリズムならば、(PrepAge≧NumSounds)にはならないはずなので、飽和の判断は不要です。
						}
					}
				}
			}
		}
	}
	return iAtomExPlayerPool;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2016/09/19変更:AdxHelper_Prepare()が戻り値を返すように変更しました。
////アプリケーションに対して再生準備の成否を意識させないために、グローバル関数は戻り値無しとした。
//void AdxHelper_Prepare(const char* path) {
//	AdxHelper_Prepare_subr(path);
//}
//↓2016/09/19変更:AdxHelper_Prepare()が戻り値を返すように変更しました。
//AdxHelper_Prepare()は、以下のいずれかの値を返す。
//・再生準備中の再生プールが無く、新たな再生準備を開始出来なければ、-1を返す。
//・再生準備中の再生プールが有り、再生準備が完了していなければ、1を返す。
//・再生準備中の再生プールが有り、再生準備が完了していたら、0を返す。
//アプリケーションの観点からは、AdxHelper_Prepare()の戻り値が0ならば、遅延無しで再生を開始出来る。
int AdxHelper_Prepare(const char* path) {
	int iAtomExPlayerPool, Status;
	ST_AtomExPlayerPool* pAtomExPlayerPool;
	//指定されたサウンドの、再生準備中の再生プールを取得する。
	iAtomExPlayerPool = AdxHelper_Prepare_subr(path);
	//再生準備中の再生プールが無く、新たな再生準備を開始出来なければ、-1を返す。
	if(iAtomExPlayerPool == -1) { return -1; }
	//AtomExプレーヤのステータスを取得する。
	pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
	Status = criAtomExPlayer_GetStatus(pAtomExPlayerPool->player);
	//再生準備中の再生プールが有り、再生準備が完了していなければ、1を返す。
	if(Status == CRIATOMEXPLAYER_STATUS_PREP) { return 1; }
	//再生準備中の再生プールが有り、再生準備が完了していたら、0を返す。
	if(Status != CRIATOMEXPLAYER_STATUS_PLAYING) { DIE(); }	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
	return 0;
}
//}}2016/09/19変更:AdxHelper_Prepare()が戻り値を返すように変更しました。
//}}2016/09/18追加:サウンドの再生準備を対応しました。
/*--------------------------------------------------------------------------*/
//{{2016/09/18変更:サウンドの再生準備を対応しました。
//void AdxHelper_Play(int iAtomExPlayer, const char* path) {
//	//プレーヤハンドルを取得する。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////	CriAtomExPlayerHn player = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	ST_AtomExPlayer* pAtomExPlayer = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
////}}2016/08/01変更:シームレス連続再生に対応しました。
//	//まず確実に停止しておく。
//	AdxHelper_Stop(iAtomExPlayer);
//	//再生ファイルの指定。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////#ifndef USE_CPK
////	criAtomExPlayer_SetFile(player, NULL,   path);	//パッキングファイルを使用しない場合
////#else //USE_CPK
////	criAtomExPlayer_SetFile(player, binder, path);	//パッキングファイルを使用する場合
////#endif//USE_CPK
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	//□要注意
//	//│Sofdec2とADX2とで、シームレス連続再生の方法に、以下のような違いが有る事に注意せよ。
//	//│・Sofdec2のシームレス連続再生は、最初の再生も後続の再生も、コールバック内で行う。
//	//│・ADX2のシームレス連続再生は、最初の再生は明示的に行い、後続の再生はコールバック内で行う。
//	//何故こんな歪な仕様になっているのか、不明である。
//	if(!path[0] || (strlen(path) >= _MAX_PATH)) { DIE(); }	//パス名は1文字以上,かつ,_MAX_PATH文字未満でなくてはならない。
//	pAtomExPlayer->sNextSoundPath[0] = '\0';		//まず空文字にしておく。
//	if(stSofdecHelperInfo.fnGetNextSound) {
//		(*stSofdecHelperInfo.fnGetNextSound)(path, pAtomExPlayer->sNextSoundPath);	//次に再生するサウンドのパス名を取得する。	'最初に再生するサウンドのパス名'ではない事に注意せよ。最初の再生はこの下で明示的に行う。
//	}
//	//上述の通り、ADX2のシームレス連続再生は、最初の再生は明示的に行う必要が有るので、ここで最初の再生指示を行う。
//#ifndef USE_CPK
//	criAtomExPlayer_SetFile(pAtomExPlayer->player, NULL,   path);	//パッキングファイルを使用しない場合
//#else //USE_CPK
//	criAtomExPlayer_SetFile(pAtomExPlayer->player, binder, path);	//パッキングファイルを使用する場合
//#endif//USE_CPK
////}}2016/08/01変更:シームレス連続再生に対応しました。
//#if 0
//	//再生する音声データのフォーマットを指定。	⇒この処理は不要みたいです。この処理を行わなくても、ADX形式とHCA形式が自動判別されて、問題無く再生される事を動作確認しました。もし今後何か問題が生じたら、このブロックを有効にする事を検討して下さい。
//	{
//		char ext[_MAX_EXT];
//		_splitpath(path, NULL, NULL, NULL, ext);
//		if(!_stricmp(ext, ".adx")) {
//			criAtomExPlayer_SetFormat(player, CRIATOMEX_FORMAT_ADX);
//		} else if(!_stricmp(ext, ".hca")) {
//			criAtomExPlayer_SetFormat(player, CRIATOMEX_FORMAT_HCA);
//		} else {
//			DIE();
//		}
//	}
//#endif
//	//再生の開始。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////	criAtomExPlayer_Start(player);
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	criAtomExPlayer_Start(pAtomExPlayer->player);
////}}2016/08/01変更:シームレス連続再生に対応しました。
//}
//↓2016/09/18変更:サウンドの再生準備を対応しました。
void AdxHelper_Play(int iAtomExPlayer, const char* path) {
	int iAtomExPlayerPool;
	ST_AtomExPlayerPool* pAtomExPlayerPool;
	//再生チャネルを取得する。
	ST_AtomExPlayer* pAtomExPlayer = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
	//まず確実に停止しておく。
	AdxHelper_Stop(iAtomExPlayer);
	//指定されたサウンドの、再生準備中の再生プールを取得する。
	iAtomExPlayerPool = AdxHelper_Prepare_subr(path);
	if(iAtomExPlayerPool == -1) { DIE(); }	//使用中でない再生プールが無く、再生準備を開始出来なかった。再生チャネル数と再生プール数は同じなので、ここでエラー停止する事は無いはず。もしここでエラー停止した場合は、当モジュールのバグです。
	pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
	//使用中フラグをセットする。
	pAtomExPlayerPool->Flag |= AtomExPlayerPool_Flag_InUse;
	//この再生チャネルが使用している再生プールを格納する。
	pAtomExPlayer->pAtomExPlayerPool = pAtomExPlayerPool;
	//再生準備が完了したら、再生を開始するようにする。
	// - 既に再生準備が完了していた場合(CRIATOMEXPLAYER_STATUS_PLAYING)、即、再生が開始する。
	// - まだ再生準備が完了していなかった場合(CRIATOMEXPLAYER_STATUS_PREP)、再生準備が完了した後に自動的に再生が開始する。
	//   ※要検証:ここでCRIATOMEXPLAYER_STATUS_PLAYINGになるまで待つ必要は無く、CRIATOMEXPLAYER_STATUS_PREPのままでcriAtomExPlayer_Pause()を呼び出して大丈夫みたいです。ただしマニュアルに明記されていないので少し心配です。しばらく使ってみてもし鳴るはずの音が鳴らない場合はこの点が原因である可能性が有ります。
	criAtomExPlayer_Pause(pAtomExPlayerPool->player, 0);
}
//}}2016/09/18変更:サウンドの再生準備を対応しました。
/*--------------------------------------------------------------------------*/
//{{2016/09/18変更:サウンドの再生準備を対応しました。
//void AdxHelper_Stop(int iAtomExPlayer) {
//	//プレーヤハンドルを取得する。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////	CriAtomExPlayerHn player = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	ST_AtomExPlayer* pAtomExPlayer = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
////}}2016/08/01変更:シームレス連続再生に対応しました。
//	//現在の状態によって…
////{{2016/08/01変更:シームレス連続再生に対応しました。
////	switch(criAtomExPlayer_GetStatus(player)) {
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	switch(criAtomExPlayer_GetStatus(pAtomExPlayer->player)) {
////}}2016/08/01変更:シームレス連続再生に対応しました。
//	default:DIE();
//	case CRIATOMEXPLAYER_STATUS_STOP:	//停止中
//		/** no job **/
//		break;
//	case CRIATOMEXPLAYER_STATUS_PREP:	//再生準備中
//	case CRIATOMEXPLAYER_STATUS_PLAYING:	//再生中
//	case CRIATOMEXPLAYER_STATUS_PLAYEND:	//再生完了	←←←※要検討:PLAYENDはSTOPと同じと見なしてそのままでも良いかも知れないのだが、2016/05/04現在は念のためにPLAYENDもSTOPへ移行しておく事にした。
//		//再生の停止(リリースタイム無視)。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////		criAtomExPlayer_StopWithoutReleaseTime(player);
////↓2016/08/01変更:シームレス連続再生に対応しました。
//		criAtomExPlayer_StopWithoutReleaseTime(pAtomExPlayer->player);
////}}2016/08/01変更:シームレス連続再生に対応しました。
//		//AtomExプレーヤのステータスが停止状態になるまで待つ。
//		for(;;) {
////{{2016/08/01変更:シームレス連続再生に対応しました。
////			int Status = criAtomExPlayer_GetStatus(player);
////↓2016/08/01変更:シームレス連続再生に対応しました。
//			int Status = criAtomExPlayer_GetStatus(pAtomExPlayer->player);
////}}2016/08/01変更:シームレス連続再生に対応しました。
//			if(Status == CRIATOMEXPLAYER_STATUS_ERROR) { DIE(); }
//			if(Status == CRIATOMEXPLAYER_STATUS_STOP) { break; }
//			criAtomEx_ExecuteMain();
//			Sleep(0);	//不要かも?
//		}
//		break;
////	case CRIATOMEXPLAYER_STATUS_ERROR:	//エラーが発生
//	}
//}
//↓2016/09/18変更:サウンドの再生準備を対応しました。
void AdxHelper_Stop(int iAtomExPlayer) {
	ST_AtomExPlayerPool* pAtomExPlayerPool;
	//再生チャネルを取得する。
	ST_AtomExPlayer* pAtomExPlayer = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
	//この再生チャネルが使用している再生プールを取得する。
	pAtomExPlayerPool = pAtomExPlayer->pAtomExPlayerPool;
	//再生中ならば…(この再生チャネルが使用している再生プールへのポインタがNULLでなければ…)
	if(pAtomExPlayerPool) {
		//使用中フラグがセットされている事を確認する。
		if(!(pAtomExPlayerPool->Flag & AtomExPlayerPool_Flag_InUse)) { DIE(); }	//この再生チャネルが使用しているはずの再生プールが使用中でない事は有り得ないはず。もしここで停止したら当モジュールのバグです。
		//現在の状態によって…
		switch(criAtomExPlayer_GetStatus(pAtomExPlayerPool->player)) {
		default:DIE();
		case CRIATOMEXPLAYER_STATUS_STOP:	//停止中
			/** no job **/
			break;
		case CRIATOMEXPLAYER_STATUS_PREP:	//再生準備中
		case CRIATOMEXPLAYER_STATUS_PLAYING:	//再生中
		case CRIATOMEXPLAYER_STATUS_PLAYEND:	//再生完了	←←←※要検討:PLAYENDはSTOPと同じと見なしてそのままでも良いかも知れないのだが、2016/05/04現在は念のためにPLAYENDもSTOPへ移行しておく事にした。
			//再生の停止(リリースタイム無視)。
			criAtomExPlayer_StopWithoutReleaseTime(pAtomExPlayerPool->player);
			//AtomExプレーヤのステータスが停止状態になるまで待つ。	//{{2016/09/18コメント追記:ここで停止状態になるまで待たずに非同期で終了待ちする改善案も有るのだが、実際に試してみると現在の処理でもここでの待ち時間は一瞬(計測誤差を考慮してもせいぜい一桁ミリ秒)であり、現在の処理のままでも問題無いと思った。ここで停止状態になるまで待つ方が当モジュールの実装が単純で安全なので、これまで通りここで停止状態になるまで待つ事にした。}}
			for(;;) {
				int Status = criAtomExPlayer_GetStatus(pAtomExPlayerPool->player);
				if(Status == CRIATOMEXPLAYER_STATUS_ERROR) { DIE(); }
				if(Status == CRIATOMEXPLAYER_STATUS_STOP) { break; }
				criAtomEx_ExecuteMain();
				Sleep(0);	//不要かも?
			}
			break;
	//	case CRIATOMEXPLAYER_STATUS_ERROR:	//エラーが発生
		}
//{{2016/09/18変更:AdxHelper_Stop()が呼び出されなくても、再生が終了したら自動的に次回の再生準備を行うようにしました。
//		//この再生チャネルが使用している再生プールをクリアする。
//		pAtomExPlayer->pAtomExPlayerPool = NULL;
//		//使用中フラグをクリアする。
//		pAtomExPlayerPool->Flag &= ~AtomExPlayerPool_Flag_InUse;
//#ifdef  USE_FREE_PREP
//		//即AdxHelper_Prepare()を呼び出して次回の再生準備を行う。LRUキャッシュのイメージです。
//		// - 再生準備の対象とするサウンドは、このAdxHelper_Stop()に対応するAdxHelper_Play()の際に指定されたサウンドです。
//		//   シームレス連続再生が行われた場合も、一番最初のサウンドの再生準備を行います。
//		if(!pAtomExPlayerPool->sPrepSoundPath[0]) { DIE(); }	//再生中もsPrepSoundPath[]はクリアしてはいけない。ここで停止したら当モジュールのバグです。
//		//SofdecHelper_Prepare_subr()の処理のために、このエントリを一時的に準備中でない状態に戻す。
//		{
//			char path[_MAX_PATH];
//			strcpy(path, pAtomExPlayerPool->sPrepSoundPath);
//			pAtomExPlayerPool->sPrepSoundPath[0] = '\0';	//必要です。忘れないで!!
//			//次回の再生準備を行う。
//			// - 必ずしもこの再生プールを使って再生準備が行われるとは限りません。別の再生プールを使って再生準備が行われる可能性も有ります。
//			//   少なくともこの再生プールが空きなので、再生準備は必ず行われるはずです。(ここで再生準備が必ず行われる動作には依存していないので、その事を検査する必要は有りません。)
//			AdxHelper_Prepare(path);
//		}
//#else //USE_FREE_PREP
//		//再生準備中のサウンドのパス名をクリアする。
//		pAtomExPlayerPool->sPrepSoundPath[0] = '\0';
//#endif//USE_FREE_PREP
//↓2016/09/18変更:AdxHelper_Stop()が呼び出されなくても、再生が終了したら自動的に次回の再生準備を行うようにしました。
		//明示的にAdxHelper_Exec()を呼び出して、この再生チャネルが使用している再生プールを開放し、USE_FREE_PREPシンボルが定義されているならば次回の再生準備を行う。
		// - ここでの明示的なAdxHelper_Exec()呼び出しは必要です。
		//   周期処理で呼び出されるAdxHelper_Exec()に任せる事は出来ません。
		//   当関数がAdxHelper_Play()から呼び出された場合、ここで再生プールを開放しておかないと、AdxHelper_Play()がpAtomExPlayer->pAtomExPlayerPoolを上書きしてしまい、再生プールが再生中のままリークしてしまうからです。
		AdxHelper_Exec();	//必要!!
		if(pAtomExPlayer->pAtomExPlayerPool) { DIE(); }	//この再生チャネルが使用している再生プールはクリアされたはず。もしここで停止したら当モジュールのバグです。
//}}2016/09/18変更:AdxHelper_Stop()が呼び出されなくても、再生が終了したら自動的に次回の再生準備を行うようにしました。
	}
}
//}}2016/09/18変更:サウンドの再生準備を対応しました。
/*--------------------------------------------------------------------------*/
//{{2016/09/18変更:サウンドの再生準備を対応しました。
//void AdxHelper_SetVolume(int iAtomExPlayer, double volume) {
//	//プレーヤハンドルを取得する。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////	CriAtomExPlayerHn player = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	ST_AtomExPlayer* pAtomExPlayer = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
////}}2016/08/01変更:シームレス連続再生に対応しました。
//	//ボリュームの設定。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////	criAtomExPlayer_SetVolume(player, (float)volume);
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	criAtomExPlayer_SetVolume(pAtomExPlayer->player, (float)volume);
////}}2016/08/01変更:シームレス連続再生に対応しました。
//	//再生パラメータの更新(再生中の音全て)。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////	criAtomExPlayer_UpdateAll(player);
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	criAtomExPlayer_UpdateAll(pAtomExPlayer->player);
////}}2016/08/01変更:シームレス連続再生に対応しました。
//}
//↓2016/09/18変更:サウンドの再生準備を対応しました。
void AdxHelper_SetVolume(int iAtomExPlayer, double volume) {
	ST_AtomExPlayerPool* pAtomExPlayerPool;
	//再生チャネルを取得する。
	ST_AtomExPlayer* pAtomExPlayer = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
	//この再生チャネルが使用している再生プールを取得する。
	pAtomExPlayerPool = pAtomExPlayer->pAtomExPlayerPool;
	//再生中ならば…(この再生チャネルが使用している再生プールへのポインタがNULLでなければ…)
	if(pAtomExPlayerPool) {
		//使用中フラグがセットされている事を確認する。
		if(!(pAtomExPlayerPool->Flag & AtomExPlayerPool_Flag_InUse)) { DIE(); }	//この再生チャネルが使用しているはずの再生プールが使用中でない事は有り得ないはず。もしここで停止したら当モジュールのバグです。
		//ボリュームの設定。
		criAtomExPlayer_SetVolume(pAtomExPlayerPool->player, (float)volume);
		//再生パラメータの更新(再生中の音全て)。
		criAtomExPlayer_UpdateAll(pAtomExPlayerPool->player);
	}
}
//}}2016/09/18変更:サウンドの再生準備を対応しました。
/*--------------------------------------------------------------------------*/
//{{2016/09/18変更:サウンドの再生準備を対応しました。
//void AdxHelper_SetPan(int iAtomExPlayer, double pan) {
////※未実装です。
//	//プレーヤハンドルを取得する。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////	CriAtomExPlayerHn player = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	ST_AtomExPlayer* pAtomExPlayer = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
////}}2016/08/01変更:シームレス連続再生に対応しました。
//	//パンの設定。
////※TODO:2Dパンを設定するADX2の関数がわかりません…
//	//再生パラメータの更新(再生中の音全て)。
////{{2016/08/01変更:シームレス連続再生に対応しました。
////	criAtomExPlayer_UpdateAll(player);
////↓2016/08/01変更:シームレス連続再生に対応しました。
//	criAtomExPlayer_UpdateAll(pAtomExPlayer->player);
////}}2016/08/01変更:シームレス連続再生に対応しました。
//}
//↓2016/09/18変更:サウンドの再生準備を対応しました。
void AdxHelper_SetPan(int iAtomExPlayer, double pan) {
//※未実装です。
	ST_AtomExPlayerPool* pAtomExPlayerPool;
	//プレーヤハンドルを取得する。
	ST_AtomExPlayer* pAtomExPlayer = SofdecHelper_GetAtomExPlayer(iAtomExPlayer);
	//この再生チャネルが使用している再生プールを取得する。
	pAtomExPlayerPool = pAtomExPlayer->pAtomExPlayerPool;
	//再生中ならば…(この再生チャネルが使用している再生プールへのポインタがNULLでなければ…)
	if(pAtomExPlayerPool) {
		//使用中フラグがセットされている事を確認する。
		if(!(pAtomExPlayerPool->Flag & AtomExPlayerPool_Flag_InUse)) { DIE(); }	//この再生チャネルが使用しているはずの再生プールが使用中でない事は有り得ないはず。もしここで停止したら当モジュールのバグです。
		//パンの設定。
//※TODO:2Dパンを設定するADX2の関数がわかりません…
		//再生パラメータの更新(再生中の音全て)。
		criAtomExPlayer_UpdateAll(pAtomExPlayerPool->player);
	}
}
//}}2016/09/18変更:サウンドの再生準備を対応しました。
/*--------------------------------------------------------------------------*/
//{{2016/09/19変更:AdxHelper_GetTotalTime()を高速化しました。
//int AdxHelper_GetTotalTime(const char* path) {
////※仮実装です。かなり効率が悪いのでアプリケーション側で結果をキャッシュする等の対策を行ってください。根本的には当関数をもっと効率の良い方法(まだ思い付きませんが)で実装し直すべきです。
//	CriAtomExPlayerConfig player_config;
//	CriAtomExPlayerHn player;
//	CriAtomExPlaybackId playback_id;
//	CriAtomExFormatInfo info;
//	//一時的なプレーヤを作成する。
//	criAtomExPlayer_SetDefaultConfig(&player_config);
//	player_config.max_path = _MAX_PATH;
//	player = criAtomExPlayer_Create(&player_config, NULL, 0);
//	//再生ファイルの指定。
//#ifndef USE_CPK
//	criAtomExPlayer_SetFile(player, NULL,   path);	//パッキングファイルを使用しない場合
//#else //USE_CPK
//	criAtomExPlayer_SetFile(player, binder, path);	//パッキングファイルを使用する場合
//#endif//USE_CPK
//#if 0
//	//再生する音声データのフォーマットを指定。	⇒この処理は不要みたいです。この処理を行わなくても、ADX形式とHCA形式が自動判別されて、問題無く再生される事を動作確認しました。もし今後何か問題が生じたら、このブロックを有効にする事を検討して下さい。
//	{
//		char ext[_MAX_EXT];
//		_splitpath(path, NULL, NULL, NULL, ext);
//		if(!_stricmp(ext, ".adx")) {
//			criAtomExPlayer_SetFormat(player, CRIATOMEX_FORMAT_ADX);
//		} else if(!_stricmp(ext, ".hca")) {
//			criAtomExPlayer_SetFormat(player, CRIATOMEX_FORMAT_HCA);
//		} else {
//			DIE();
//		}
//	}
//#endif
//	//再生の準備。
//	playback_id = criAtomExPlayer_Prepare(player);
//	//AtomExプレーヤのステータスが再生中になるまで待つ。
//	// - ポーズ状態もCRIATOMEXPLAYBACK_STATUS_PLAYINGに含まれています。
//	for (;;) {
//		int Status = criAtomExPlayback_GetStatus(playback_id);
//		if(Status == CRIATOMEXPLAYBACK_STATUS_PLAYING) { break; }
//		if(Status != CRIATOMEXPLAYBACK_STATUS_PREP) { DIE(); }
//		criAtomEx_ExecuteMain();
//		Sleep(0);	//不要かも?
//	}
//	//再生音声のフォーマット情報の取得。
//	criAtomExPlayback_GetFormatInfo(playback_id, &info);
//	//一時的なプレーヤを削除する。
//	criAtomExPlayer_Destroy(player);
//	//サウンドの音の総時間をミリ秒単位で返す。
//	return (int)((double)info.num_samples / (double)info.sampling_rate * 1000.0/*ミリ秒単位*/);
//}
//↓2016/09/19変更:AdxHelper_GetTotalTime()を高速化しました。
//int AdxHelper_GetTotalTime(const char* path) {
//	int iAtomExPlayerPool;
//	ST_AtomExPlayerPool* pAtomExPlayerPool;
//	CriAtomExFormatInfo info;
//	if(!path[0] || (strlen(path) >= _MAX_PATH)) { DIE(); }	//パス名は1文字以上,かつ,_MAX_PATH文字未満でなくてはならない。
//	//手順①:再生中の再生プールが有れば、そこから情報が取得出来るかを試みる。
//	{
//		for(iAtomExPlayerPool = 0; iAtomExPlayerPool < stSofdecHelperInfo.NumSounds; iAtomExPlayerPool++) {
//			pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
//			//使用中で…
//			if(pAtomExPlayerPool->Flag & AtomExPlayerPool_Flag_InUse) {
//				//再生準備の際に使用したサウンドのパス名が一致したら…
//				if(!strcmp(pAtomExPlayerPool->sPrepSoundPath, path)) {
//					//この再生IDから、情報が取得出来るかを試みる。
//					//□C:\cri\documentation\CRI_ADX2_Manual_pc_j.chm
//					//│CriBool CRIAPI criAtomExPlayback_GetFormatInfo(CriAtomExPlaybackId id, CriAtomExFormatInfo* info)
//					//│フォーマット情報が取得できた場合、本関数はCRI_TRUEを返します。						←←←■■■注目!!■■■
//					//│指定したボイスが既に消去されている場合等には、本関数はCRI_FALSEを返します。					←←←■■■注目!!■■■
//					//│注意:
//					//│本関数は、音声再生中のみフォーマット情報を取得可能です。							←←←■■■注目!!■■■
//					//│再生準備中や再生終了後、発音数制御によりボイスが消去された場合には、フォーマット情報の取得に失敗します。	←←←■■■注目!!■■■
//					if(criAtomExPlayback_GetFormatInfo(pAtomExPlayerPool->playback_id, &info)) {	//成功ならば…
//						goto L_RET;	//ここまで
//					}
//				}
//			}
//		}
//	}
//	//手順②:既に準備中の再生プールか、又は、再生プールに空きが有れば新たに準備中の再生プールを作成して、そこから情報を取得する。
//	{
//		//既に準備中の再生プールか、又は、再生プールに空きが有れば新たに準備中の再生プールを作成する。
//		iAtomExPlayerPool = AdxHelper_Prepare_subr(path);
//		if(iAtomExPlayerPool != -1) {	//既に準備中の再生プールが無く、かつ、使用中でない再生プールも無くて再生準備を開始出来なかった場合は、-1が返されるので、この手順を行わずに次の手順へ進む。
//			pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
//			//AtomExプレーヤのステータスが再生中になるまで待つ。
//			// - ポーズ状態もCRIATOMEXPLAYBACK_STATUS_PLAYINGに含まれています。
//			for (;;) {
//				int Status = criAtomExPlayback_GetStatus(pAtomExPlayerPool->playback_id);
//				if(Status == CRIATOMEXPLAYBACK_STATUS_PLAYING) { break; }
//				if(Status != CRIATOMEXPLAYBACK_STATUS_PREP) { DIE(); }	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
//				criAtomEx_ExecuteMain();
//				Sleep(0);	//不要かも?
//			}
//			//再生音声のフォーマット情報の取得。
//			if(!criAtomExPlayback_GetFormatInfo(pAtomExPlayerPool->playback_id, &info)) {	//失敗ならば…
//				DIE();	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
//			}
//			goto L_RET;	//ここまで
//		}
//	}
//	//手順③:一時的なプレーヤを作成して、一時的なサウンドをロードして、そこから情報を取得する。
//	{
//		CriAtomExPlayerConfig player_config;
//		CriAtomExPlayerHn player;
//		CriAtomExPlaybackId playback_id;
//		//一時的なプレーヤを作成する。
//		criAtomExPlayer_SetDefaultConfig(&player_config);
//		player_config.max_path = _MAX_PATH;
//		player = criAtomExPlayer_Create(&player_config, NULL, 0);
//		//再生ファイルの指定。
//	    #ifndef USE_CPK
//		criAtomExPlayer_SetFile(player, NULL,   path);	//パッキングファイルを使用しない場合
//	    #else //USE_CPK
//		criAtomExPlayer_SetFile(player, binder, path);	//パッキングファイルを使用する場合
//	    #endif//USE_CPK
//	    #if 0
//		//再生する音声データのフォーマットを指定。	⇒この処理は不要みたいです。この処理を行わなくても、ADX形式とHCA形式が自動判別されて、問題無く再生される事を動作確認しました。もし今後何か問題が生じたら、このブロックを有効にする事を検討して下さい。
//		{
//			char ext[_MAX_EXT];
//			_splitpath(path, NULL, NULL, NULL, ext);
//			if(!_stricmp(ext, ".adx")) {
//				criAtomExPlayer_SetFormat(player, CRIATOMEX_FORMAT_ADX);
//			} else if(!_stricmp(ext, ".hca")) {
//				criAtomExPlayer_SetFormat(player, CRIATOMEX_FORMAT_HCA);
//			} else {
//				DIE();
//			}
//		}
//	    #endif
//		//再生の準備。
//		playback_id = criAtomExPlayer_Prepare(player);
//		if(playback_id == CRIATOMEX_INVALID_PLAYBACK_ID) { DIE(); }	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
//		//AtomExプレーヤのステータスが再生中になるまで待つ。
//		// - ポーズ状態もCRIATOMEXPLAYBACK_STATUS_PLAYINGに含まれています。
//		for (;;) {
//			int Status = criAtomExPlayback_GetStatus(playback_id);
//			if(Status == CRIATOMEXPLAYBACK_STATUS_PLAYING) { break; }
//			if(Status != CRIATOMEXPLAYBACK_STATUS_PREP) { DIE(); }	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
//			criAtomEx_ExecuteMain();
//			Sleep(0);	//不要かも?
//		}
//		//再生音声のフォーマット情報の取得。
//		if(!criAtomExPlayback_GetFormatInfo(playback_id, &info)) {	//失敗ならば…
//			DIE();	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
//		}
//		//一時的なプレーヤを削除する。
//		criAtomExPlayer_Destroy(player);
//		/* FALLTHRU */
//	}
//L_RET:	//サウンドの音の総時間をミリ秒単位で返す。
//	return (int)((double)info.num_samples / (double)info.sampling_rate * 1000.0/*ミリ秒単位*/);
//}
//↓2017/03/22追加:AdxHelper_GetFormatInfo()を追加しました。
int AdxHelper_GetTotalTime(const char* path) {
	ST_AdxHelper_FormatInfo info;
	//フォーマット情報を取得する。
	AdxHelper_GetFormatInfo(path, &info);
	//サウンドの音の総時間をミリ秒単位で返す。
	return (int)((double)info.num_samples / (double)info.sampling_rate * 1000.0/*ミリ秒単位*/);
}
/*--------------------------------------------------------------------------*/
void AdxHelper_GetFormatInfo(const char* path, ST_AdxHelper_FormatInfo* pFormatInfo) {
	int iAtomExPlayerPool;
	ST_AtomExPlayerPool* pAtomExPlayerPool;
	CriAtomExFormatInfo info;
	if(!path[0] || (strlen(path) >= _MAX_PATH)) { DIE(); }	//パス名は1文字以上,かつ,_MAX_PATH文字未満でなくてはならない。
	//手順①:再生中の再生プールが有れば、そこから情報が取得出来るかを試みる。
	{
		for(iAtomExPlayerPool = 0; iAtomExPlayerPool < stSofdecHelperInfo.NumSounds; iAtomExPlayerPool++) {
			pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
			//使用中で…
			if(pAtomExPlayerPool->Flag & AtomExPlayerPool_Flag_InUse) {
				//再生準備の際に使用したサウンドのパス名が一致したら…
				if(!strcmp(pAtomExPlayerPool->sPrepSoundPath, path)) {
					//この再生IDから、情報が取得出来るかを試みる。
					//□C:\cri\documentation\CRI_ADX2_Manual_pc_j.chm
					//│CriBool CRIAPI criAtomExPlayback_GetFormatInfo(CriAtomExPlaybackId id, CriAtomExFormatInfo* info)
					//│フォーマット情報が取得できた場合、本関数はCRI_TRUEを返します。						←←←■■■注目!!■■■
					//│指定したボイスが既に消去されている場合等には、本関数はCRI_FALSEを返します。					←←←■■■注目!!■■■
					//│注意:
					//│本関数は、音声再生中のみフォーマット情報を取得可能です。							←←←■■■注目!!■■■
					//│再生準備中や再生終了後、発音数制御によりボイスが消去された場合には、フォーマット情報の取得に失敗します。	←←←■■■注目!!■■■
					if(criAtomExPlayback_GetFormatInfo(pAtomExPlayerPool->playback_id, &info)) {	//成功ならば…
						goto L_RET;	//ここまで
					}
				}
			}
		}
	}
	//手順②:既に準備中の再生プールか、又は、再生プールに空きが有れば新たに準備中の再生プールを作成して、そこから情報を取得する。
	{
		//既に準備中の再生プールか、又は、再生プールに空きが有れば新たに準備中の再生プールを作成する。
		iAtomExPlayerPool = AdxHelper_Prepare_subr(path);
		if(iAtomExPlayerPool != -1) {	//既に準備中の再生プールが無く、かつ、使用中でない再生プールも無くて再生準備を開始出来なかった場合は、-1が返されるので、この手順を行わずに次の手順へ進む。
			pAtomExPlayerPool = &TBL_AtomExPlayerPool[iAtomExPlayerPool];
			//AtomExプレーヤのステータスが再生中になるまで待つ。
			// - ポーズ状態もCRIATOMEXPLAYBACK_STATUS_PLAYINGに含まれています。
			for (;;) {
				int Status = criAtomExPlayback_GetStatus(pAtomExPlayerPool->playback_id);
				if(Status == CRIATOMEXPLAYBACK_STATUS_PLAYING) { break; }
				if(Status != CRIATOMEXPLAYBACK_STATUS_PREP) { DIE(); }	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
				criAtomEx_ExecuteMain();
				Sleep(0);	//不要かも?
			}
			//再生音声のフォーマット情報の取得。
			if(!criAtomExPlayback_GetFormatInfo(pAtomExPlayerPool->playback_id, &info)) {	//失敗ならば…
				DIE();	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
			}
			goto L_RET;	//ここまで
		}
	}
	//手順③:一時的なプレーヤを作成して、一時的なサウンドをロードして、そこから情報を取得する。
	{
		CriAtomExPlayerConfig player_config;
		CriAtomExPlayerHn player;
		CriAtomExPlaybackId playback_id;
		//一時的なプレーヤを作成する。
		criAtomExPlayer_SetDefaultConfig(&player_config);
		player_config.max_path = _MAX_PATH;
		player = criAtomExPlayer_Create(&player_config, NULL, 0);
		//再生ファイルの指定。
	    #ifndef USE_CPK
		criAtomExPlayer_SetFile(player, NULL,   path);	//パッキングファイルを使用しない場合
	    #else //USE_CPK
		criAtomExPlayer_SetFile(player, binder, path);	//パッキングファイルを使用する場合
	    #endif//USE_CPK
	    #if 0
		//再生する音声データのフォーマットを指定。	⇒この処理は不要みたいです。この処理を行わなくても、ADX形式とHCA形式が自動判別されて、問題無く再生される事を動作確認しました。もし今後何か問題が生じたら、このブロックを有効にする事を検討して下さい。
		{
			char ext[_MAX_EXT];
			_splitpath(path, NULL, NULL, NULL, ext);
			if(!_stricmp(ext, ".adx")) {
				criAtomExPlayer_SetFormat(player, CRIATOMEX_FORMAT_ADX);
			} else if(!_stricmp(ext, ".hca")) {
				criAtomExPlayer_SetFormat(player, CRIATOMEX_FORMAT_HCA);
			} else {
				DIE();
			}
		}
	    #endif
		//再生の準備。
		playback_id = criAtomExPlayer_Prepare(player);
		if(playback_id == CRIATOMEX_INVALID_PLAYBACK_ID) { DIE(); }	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
		//AtomExプレーヤのステータスが再生中になるまで待つ。
		// - ポーズ状態もCRIATOMEXPLAYBACK_STATUS_PLAYINGに含まれています。
		for (;;) {
			int Status = criAtomExPlayback_GetStatus(playback_id);
			if(Status == CRIATOMEXPLAYBACK_STATUS_PLAYING) { break; }
			if(Status != CRIATOMEXPLAYBACK_STATUS_PREP) { DIE(); }	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
			criAtomEx_ExecuteMain();
			Sleep(0);	//不要かも?
		}
		//再生音声のフォーマット情報の取得。
		if(!criAtomExPlayback_GetFormatInfo(playback_id, &info)) {	//失敗ならば…
			DIE();	//ここで失敗する事は無いはず。万一失敗したら初期化時のnum_loadersやmax_virtual_voicesの設定を再検討しろ。
		}
		//一時的なプレーヤを削除する。
		criAtomExPlayer_Destroy(player);
		/* FALLTHRU */
	}
L_RET:	//CriAtomExFormatInfo構造体を、ST_AdxHelper_FormatInfo構造体に変換して格納する。
	switch(info.format) {					//フォーマット種別
	default:                            pFormatInfo->format = "Unknown";      break;
	case CRIATOMEX_FORMAT_NONE:         pFormatInfo->format = "NONE";         break;	//なし
	case CRIATOMEX_FORMAT_ADX:          pFormatInfo->format = "ADX";          break;	//ADX
	case CRIATOMEX_FORMAT_HCA:          pFormatInfo->format = "HCA";          break;	//HCA
	case CRIATOMEX_FORMAT_HCA_MX:       pFormatInfo->format = "HCA_MX";       break;	//HCA-MX
	case CRIATOMEX_FORMAT_WAVE:         pFormatInfo->format = "WAVE";         break;	//Wave
	case CRIATOMEX_FORMAT_AIFF:         pFormatInfo->format = "AIFF";         break;	//AIFF
	case CRIATOMEX_FORMAT_RAW_PCM:      pFormatInfo->format = "RAW_PCM";      break;	//RawPCM
	case CRIATOMEX_FORMAT_VIBRATION:    pFormatInfo->format = "VIBRATION";    break;	//振動
	case CRIATOMEX_FORMAT_AUDIO_BUFFER: pFormatInfo->format = "AUDIO_BUFFER"; break;	//オーディオバッファ
	case CRIATOMEX_FORMAT_HW1:          pFormatInfo->format = "HW1";          break;	//ハードウェア固有1
	case CRIATOMEX_FORMAT_HW2:          pFormatInfo->format = "HW2";          break;	//ハードウェア固有2
	}
	pFormatInfo->sampling_rate = info.sampling_rate;	//サンプリング周波数
	pFormatInfo->num_samples   = (int)info.num_samples;	//総サンプル数
	pFormatInfo->loop_offset   = (int)info.loop_offset;	//ループ開始サンプル
	pFormatInfo->loop_length   = (int)info.loop_length;	//ループ区間サンプル数
	pFormatInfo->num_channels  = info.num_channels;		//チャンネル数
}
//}}2017/03/22追加:AdxHelper_GetFormatInfo()を追加しました。
/*--------------------------------------------------------------------------*/
//{{2016/09/18追加:AdxHelper_Stop()が呼び出されなくても、再生が終了したら自動的に次回の再生準備を行うようにしました。
static void AdxHelper_Exec() {
	int iAtomExPlayer;
	ST_AtomExPlayer* pAtomExPlayer;
	ST_AtomExPlayerPool* pAtomExPlayerPool;
	//各再生チャネルについて…
	for(iAtomExPlayer = 0; iAtomExPlayer < stSofdecHelperInfo.NumSounds; iAtomExPlayer++) {
		pAtomExPlayer = &TBL_AtomExPlayer[iAtomExPlayer];
		//この再生チャネルが使用している再生プールを取得する。
		pAtomExPlayerPool = pAtomExPlayer->pAtomExPlayerPool;
		//再生中ならば…(この再生チャネルが使用している再生プールへのポインタがNULLでなければ…)
		if(pAtomExPlayerPool) {
			//使用中フラグがセットされている事を確認する。
			if(!(pAtomExPlayerPool->Flag & AtomExPlayerPool_Flag_InUse)) { DIE(); }	//この再生チャネルが使用しているはずの再生プールが使用中でない事は有り得ないはず。もしここで停止したら当モジュールのバグです。
			//現在の状態によって…
			switch(criAtomExPlayer_GetStatus(pAtomExPlayerPool->player)) {
			default:DIE();
			case CRIATOMEXPLAYER_STATUS_PREP:	//再生準備中
			case CRIATOMEXPLAYER_STATUS_PLAYING:	//再生中
				/** no job **/
				break;
			case CRIATOMEXPLAYER_STATUS_STOP:	//停止中	CriManaPlayerHnが自動的にCRIATOMEXPLAYER_STATUS_STOPへ移行する事は有り得ませんが、AdxHelper_Stop()が明示的に停止後にSofdecHelper_Exec()経由で当関数を呼び出した場合に起こり得ます。
			case CRIATOMEXPLAYER_STATUS_PLAYEND:	//再生完了
				//この再生チャネルが使用している再生プールをクリアする。
				pAtomExPlayer->pAtomExPlayerPool = NULL;
				//使用中フラグをクリアする。
				pAtomExPlayerPool->Flag &= ~AtomExPlayerPool_Flag_InUse;
#ifdef  USE_FREE_PREP
				//即AdxHelper_Prepare()を呼び出して次回の再生準備を行う。LRUキャッシュのイメージです。
				// - 再生準備の対象とするサウンドは、このAdxHelper_Stop()に対応するAdxHelper_Play()の際に指定されたサウンドです。
				//   シームレス連続再生が行われた場合も、一番最初のサウンドの再生準備を行います。
				if(!pAtomExPlayerPool->sPrepSoundPath[0]) { DIE(); }	//再生中もsPrepSoundPath[]はクリアしてはいけない。ここで停止したら当モジュールのバグです。
				//SofdecHelper_Prepare_subr()の処理のために、このエントリを一時的に準備中でない状態に戻す。
				{
					char path[_MAX_PATH];
					strcpy(path, pAtomExPlayerPool->sPrepSoundPath);
					pAtomExPlayerPool->sPrepSoundPath[0] = '\0';	//必要です。忘れないで!!
					//次回の再生準備を行う。
					// - 必ずしもこの再生プールを使って再生準備が行われるとは限りません。別の再生プールを使って再生準備が行われる可能性も有ります。
					//   少なくともこの再生プールが空きなので、再生準備は必ず行われるはずです。(ここで再生準備が必ず行われる動作には依存していないので、その事を検査する必要は有りません。)
					AdxHelper_Prepare(path);
				}
#else //USE_FREE_PREP
				//再生準備中のサウンドのパス名をクリアする。
				pAtomExPlayerPool->sPrepSoundPath[0] = '\0';
#endif//USE_FREE_PREP
				break;
		//	case CRIATOMEXPLAYER_STATUS_ERROR:	//エラーが発生
			}
		}
	}
}
//}}2016/09/18追加:AdxHelper_Stop()が呼び出されなくても、再生が終了したら自動的に次回の再生準備を行うようにしました。
#endif//USE_ADX
