【Unity】【C#】Windows で mp3 をランタイムで再生する 
2020/06/30 Tue [edit]
なぜか Windows ではランタイムでの mp3 再生(ストリーミング)に対応してない。Unity のバージョン上がったらいつか対応するのかと思ってたけど、いっこうに対応しない(エラーとなる)。Android や Mac, iOS などは普通に再生できるのにね。
VRM Live Viewer でも、音楽素材は wav/ogg でしか使えず、少し不便だな~といつも感じていた。
なのでちょっと調べてみたが、実装が面倒なのが多い。色々見てみたが結局、NAudio というプラグインライブラリで「mp3 → wav に変換して再生する」のが一番簡単なようだ。
あまり日本語での記事が見つからなかったので、せっかくなので実装例を載せておこう。VRM Live Viewer ではこれが実装されている(※実際には変換作業を非同期化[UniTask]にしている)。ここではわかりやすいように、中核となる部分を簡略化したコードを掲載しておこう。
・NAudio
・Microsoft Public License (Ms-PL)
(※) Unity 2019.2.21f1 / Windows10(x64) で確認
■NAudio (dll) の導入
まずはプラグイン本体の dll をダウンロードしよう。Release ページに移動すると、掲載時点では v1.10.0 が出ていたが、「▼Assets (2)」を開くとソースコードしか無かったので、自分でコンパイルしないのなら、そのすぐ下バージョンの v1.8.4 の「▼Assets (4)」を開いて、「NAudio-Release.zip」をダウンロードしよう(※要するに zip で落とせる最新版をダウンロードする)。
・(NAudio) Release ページ
zip を解凍したら、「NAudio.dll」と「NAudio.xml」とドキュメント(txt)が入っているので、「Plugins」フォルダを作って(Windows だけで良いのなら「Plugins/x86」を作る)プロジェクトにドラッグドロップしてインポートしよう(dll と xml だけでも良い)。ちなみに「Plugins」は全てのプラットフォーム or Fallback で、以下に「Android」「iOS」「x86_64」を作れば、それぞれのプラットフォームごとになると考えて良い。ここでは Windows でだけしか使わないので、以下のように配置している。

・Unityの特殊フォルダと各々の役割(追記版)
あとは dll をクリックして、Import Settings を設定しておこう。「x86_64」にチェックを入れておけば 64bit でも使えるようになる。CPU の種類もとりあえず全てにしておこう。

●CPU

■mp3 → wav に変換して再生する
NAudio のプラグインを導入できたら、次にサンプルコードを作成しよう。
先に簡単にコードの内容を解説しておくと、インスペクタで設定した mp3 のファイルパスを NAudio で一時フォルダに wav として書き出し、ストリーミング再生するサンプルになっている。
ちにみに、音声ファイルをストリーミング再生する方法は以前に紹介した方法と基本的には同じだ。
・動的にオーディオファイルの読み込みと再生をする
●mp3 → wav に変換して再生するサンプルコード
using System.Collections;
using System.IO;
using UnityEngine;
using NAudio.Wave; //← 見つからないとエラーが出たときは Import Settings を見直す
public class Mp3Play : MonoBehaviour //クラス名は任意
{
public AudioSource audioSource; //再生用 AudioSource をアタッチしておく
public string mp3Path; //再生する mp3 のパス
// Use this for initialization
private void Start()
{
if (File.Exists(mp3Path))
{
//wav 変換した一時ファイルを保存するパス
var wavPath = Application.temporaryCachePath + "/converted.wav";
ConvertMp3ToWav(mp3Path, wavPath);
StartCoroutine(PlayWav(wavPath));
}
else
{
Debug.Log($"File not found : {mp3Path}");
}
}
//mp3 → wav 変換して、保存したパスを返す
public void ConvertMp3ToWav(string mp3Path, string wavPath)
{
//ファイルを byte 配列で読み込み
var bytes = File.ReadAllBytes(mp3Path);
//wav を一時ファイルとして保存
using (var stream = new MemoryStream())
{
stream.Write(bytes, 0, bytes.Length);
stream.Position = 0;
using (var reader = new Mp3FileReader(stream))
{
WaveFileWriter.CreateWaveFile(wavPath, reader); //wav で書き出し
Debug.Log($"Convert to wav successfully : {wavPath}");
}
}
}
//ストリーミング再生する
IEnumerator PlayWav(string path)
{
using (var www = new WWW("file://" + path))
{
while (!www.isDone)
yield return null;
if (string.IsNullOrEmpty(www.error))
{
var clip = www.GetAudioClip(false, true);
audioSource.clip = clip;
audioSource.Play();
Debug.Log($"Play wav : {path}");
}
else
{
Debug.Log(www.error);
}
}
}
}
再生するには GameOjbect にこのスクリプトと AudioSource をアタッチし、その AudioSource をインスペクタでセットしておく必要がある。
プレイして、設定したパス(mp3)の音声が鳴ったなら成功だ。

エラーチェックは最低限しかしてないので、必要なら try-catch などを追加しておいた方が良いだろう。
また wav ファイルはサイズが大きいので、アプリ終了時(OnApplicationQuit)などで一時ファイルを削除しておいた方が良いかも知れない。その辺りは好きにやって欲しい。
ちなみに、上記の例で Mp3FileReader にパスを与えるオーバーロードもあるので、ConvertMp3ToWav() の中身を以下のように書くこともできる。
●Mp3FileReader(パス) のオーバーロードを使う
//mp3 → wav 変換して、保存したパスを返す
public void ConvertMp3ToWav(string mp3Path, string wavPath)
{
using (var reader = new Mp3FileReader(mp3Path))
{
WaveFileWriter.CreateWaveFile(wavPath, reader); //wav で書き出し
Debug.Log($"Convert to wav successfully : {wavPath}");
}
}
ただコードは短くて良いのだが、私が実行速度を計測してみた所、前述のコードからすると約 100~200ms ほど遅いようだ(※2~3分の曲の場合)。また Mp3FileReader → AudioFileReader に置き換えてみたら、更に 300~400ms 遅い。
なので、実行速度を気にしない場合であれば、こちらを使っても良いだろう。
また、再生には WaveOut を使う方法があるが(本来は WaveOut が正しい使い方だと思うが、Unity では AudioClip として扱いたかったため、変換だけ利用している)、興味があったら、以下の資料を覗いてみると良い。
(参考)
・C# プログラミング解説 NAudio
(関連記事)
【Unity】【C#】動的にオーディオファイルの読み込みと再生をする
【Unity】【C#】オーディオ再生でのエラー:An invalid seek position was passed to this function の対処法
【Unity】【C#】BMP をランタイムで読み込む
【Unity】【C#】TGAをランタイムでロードする
【Unity】【C#】ランタイム時にファイルをドラッグ&ドロップして取得する(Windows のみ)
- 関連記事
トラックバック
トラックバックURL
→http://fantom1x.blog130.fc2.com/tb.php/356-0401ff1c
この記事にトラックバックする(FC2ブログユーザー)
| h o m e |