C#初心者の私がGUIアプリケーションに挑戦した際に参考にしたサイト等(サウンド関連)

音楽ファイルを再生する場合、複数の選択肢があるが、選択する基準や妥協点を決めないと泥沼になる恐れがある
 今回、音楽ファイルの再生についての優先順位は次のようにした
優先順位:高
BGM と SE が同時再生できる
再生を実行してから実際に音が鳴るまでの遅れが小さい
音楽ファイルを exe ファイルに埋め込める
(配布フォルダ内に音楽ファイルを置きたくない)
優先順位:中
VisualStudio c# に用意されている標準の機能のみで実現できる
(配布フォルダ内に dll 等を置きたくない)
(今回は c# の勉強のためという側面もあるため、Unity 等の使用も考えない)
優先順位:低
複数の SE を同時再生できる
(音の扱い方によっては SE 同時再生しないほうが耳に優しい場合も)
(最初は優先順位高かったが、音が重なりすぎても耳障りなので順位が低くなっていった)
exe ファイルに埋め込んだまま音楽ファイルを使用できる
(埋め込む方が優先)

今回、音楽再生で主に参考にしたサイト
サウンド再生
7種類の音楽ファイル再生方法が説明されている pdf
 言葉の言い回しが少し独特な感じではあるものの、詳しく書かれている印象
この pdf より他のサイトの方が詳しく書かれている項目もあるので相互補完するのが良いと思う

VB 向けと書かれてはいるものの、c# のコードも掲載されている

MIDI、MP3などの音楽ファイルを再生する
C#WinFormsでオーディオを再生する
C# でサウンドの多重再生とか
c#標準的な機能で音楽ファイルを再生する場合はここも参考になる

MCIコマンド解説
Visual Basic サンプル集
MCI については、こちらがさらに詳しい

MCIコマンドによる音楽ファイル再生
MCI コマンド
他のサイトでは文字列でコマンドを作成して MCI に渡す使い方が紹介されているが、一般的な関数と同じ使い方もできるらしく、このサイトでは関数として使用する方法が紹介されている

ループ再生
音楽プレイヤー的なループ再生であれば、再生コマンドにループするオプションを付けるだけでOKだが、リピート再生するときに曲の途中から始めたい、曲の途中でリピートしたい、といった場合の例を見つけられなかったので力業で解決
(再生終了なんかの場合はメッセージをキャッチすることもできるようだが、任意の位置でメッセージを発生させる方法が分からなかった)

RPGツクールVXでのOGGループとはどういうことか?
素材サイトなどで配布されている曲にはループ位置情報が埋め込まれていたり、テキストファイルに書かれていたりするので、それが分かっている前提

MCI はミリ秒単位で現在位置の取得と再生位置の指定ができるので、ゲームのループ処理の中で曲の現在位置を監視して、ループエンドに来たらループスタートにシーク

狙った通り任意の範囲でループ再生できたが、一瞬音が途切れてしまう
 フレーム処理の外のループで監視しているので、これ以上の頻度で監視はできない
 MCI の再生位置情報がミリ秒単位(1/1000 秒)で、リピート情報はサンプル数単位(44.1 kHz なら 1/44.1k 秒)なので、この分解能の違いが影響している?
今回の妥協点

MCI でリソースに埋め込んだファイルを再生する
埋め込まれたWAVEのリソースファイルを再生する
埋め込みリソースのファイルパスを取得するには?
配布時にフォルダ内のファイルをシンプルに済ませたくて実行ファイル内に埋め込もうとしたが、MCI の仕様により HDD 等に置かれている「ファイル」でなければ再生することができず、リソース(メモリ上に存在するデータ)では再生できない
 SoundPlay ならばリソースに埋め込んだファイルでも標準で利用することができるが、SoundPlay の 場合は wav しか再生できないという問題がある

リソース
一時フォルダ(テンポラリ・フォルダ)を取得するには?
一時ファイル(テンポラリ・ファイル)を作成するには?
MCI で再生するにはファイルとして存在していれば良いので、リソースとして埋め込んで配布しておき、実行時にファイル化すれば良い

Windows には一時的にファイルを作成するための一時フォルダが用意されているので、そこを利用することもできるし、任意に決めることもできる

ただし、保存場所によっては UAC に注意が必要
 一時ファイル用に用意されている一時フォルダを利用すれば UAC による制限は受けない?

任意の場所に一時ファイルを作成した場合でも、一時フォルダに一時ファイルを作成した場合でも、利用後に一時ファイルを削除するのが基本
(2回目以降のファイル利用のために一時ファイルを残しておきたい、てな場合もあるかもしれないけど、そうするならインストーラで展開したほうが良い気がする。 ファイル化したままにすすならば、わざわざリソースとして実行ファイルに含める意味が無くなってしまうと思う。 一般的にはどうなんだろう?)

一時ファイル化することでリソースとして埋め込んだ音楽ファイルを MCI で再生できるが、全ての音楽ファイルを一時ファイル化していてはプログラミングの手間がかかってしまうし、管理しなければならないファイルの数も増えて、各種エラーに対応するためのプログラムも必要になってしまう
 実行のたびにファイル化する必要があるため、起動に必要な時間が長くなるという懸念もある

DirectSound を使用する方法もあるようだが、今は DirectX 自体がサポートされておらず、古い方法らしいので避けるのが無難そう

「標準で使える機能」にこだわらなければ、NAudio や BASS Audio Library などがあるようだが、今回は c# の勉強のために標準で使える機能の範囲で開発を進めるという意図もあるため見送り

で、悩んだ末に BGM は MCI、SE は SoundPlay とした
 当初の理想は複数の SE 同時再生だったが、BGM が流れていて、さらに複数の SE が同時に鳴ると、音が重なりすぎて耳障りだった

SE に優先順位を付けて、同時に鳴る SE の数を制限する、優先順位の低い音が鳴っている途中で優先順位の高い音を鳴らそうとしたときに順位の低い音をストップさせる、などの制御を考えたものの、明らかにめんどくさくなるので却下

mciSendString のエラーメッセージ
mciSendString関数のエラーメッセージを取得する
MCI コマンド
リンクが重複するが、MCI のエラーについてはこちらのサイトが詳しい

mciSendString 関数は指定されたコマンドの動作が成功すると0を返し、失敗するとエラーの内容に応じたエラーコードを返す
 mciGetErrorString 関数を使用することで、エラーコードに応じたエラーメッセージを取得することができる

動作確認用にサブ機で実行した際に BGM が再生されない不具合が発生した
 SoundPlay を使用している SE の再生は問題なかったことから、MCI の実行で問題が起きていると考え、そのエラーメッセージを取得してみると open の段階で「mciの初期化で問題が発生しました」というエラーが発生していることが分かった

mp4の再生
再生しようとするファイルの形式がm4a でも ogg でも同様のエラーが発生し、mp3 で試してみると問題なく再生できたので、コーデックの影響を疑う

検証のために MCI で wav を再生できるかを確認しようとしたら、開発に使用している PC ですら再生できない問題が発生した
 思い返せば MCI で wav 再生しようとしたのはこの時が初めてだった

泥沼化したし、今回は MCI で wav 再生しないので見切りをつける

結局、m4a や ogg が再生できなかったのはサブ機にコーデックが入っていないためで、今回は mp3 形式のファイルを使用することで対応することに決めた

0 件のコメント:

コメントを投稿