FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »Unityプラグイン
このページの記事一覧

【Unity】【C#】Windows で mp3 をランタイムで再生する  


 なぜか 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 のみ)


関連記事
スポンサーサイト



category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityオープンソースライブラリ  Unityプラグイン  オーディオファイル 
tb: 0   cm: --

【Unity】UniRx 7.x にアップグレードすると UniTask 系のエラーが出る  



※(AssetStore) UniRx 7.0.0 からは UniTask (UniRx.Async) は入ってない


 どうやら Unity2019.2.x (2.21f1) が2月頃からアップデートが止まっている…これもコロナウィルスのせいなのか…(会社はロスみたいだし)?
だけど、Unity2019.3.x はずっとアップデートされ続けている…。

 しかたないので、そろそろ覚悟を決めて (Android では内部実装が変わるので、ネイティブプラグインが死ぬ可能性がある) Unity2019.3.x にアップグレードしなくてはいけないかな?と思ってやってみたら、大量のエラーが出た。

【Unity】Unity2019.3 で Android / iOS ネイティブの構成が変わるらしい

(※) Unity 2019.3.13f1 / Windows10(x64) / UniRx 7.1.0 / UniTask 1.3.1 で確認



 1つは、どうやらカメラに Unity5 時代にデフォルトで付いていた「GUI Layer」が完全に廃止されたっぽい。もしかしたら旧 GUI も完全に使えなくなったのかな…?

Component GUI Layer in Main Camera for Scene Assets/xxx.unity is no longer available.
It will be removed after you edit this GameObject and save the Scene.

これは一度シーンを保存すれば、自動的に消されるらしい


 もう1つは UniRx のエラーだった。その1つは「using UnityEngine.Experimental.LowLevel;」の namespace で、どうやら Unity2019.3 では「using UnityEngine.LowLevel;」になったっぽい。
※この修正は後述の UniRx 7.x にアップグレードする場合は書き換える必要はありません。

error CS0234: The type or namespace name 'LowLevel' does not exist in the namespace 'UnityEngine.Experimental' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'PlayerLoopSystem' could not be found (are you missing a using directive or an assembly reference?)


 そういえば、UniRx (6.x) は長らくアップデートしてないなぁと思ったので、AssetStore から再ダウンロード&インポート(UniRx 7.1.0)。そしたら今度は UniTask 関連のエラーが大量に出た。

UniRx - Reactive Extensions for Unity (AssetStore)

error CS0234: The type or namespace name 'Async' does not exist in the namespace 'UniRx' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'UniTask' could not be found (are you missing a using directive or an assembly reference?)

 「UniRx.Async の namespace が無い」みたいなエラーだったので、しばらく原因がわからなかったのだが(6.x では普通に使えたので)、github で何か issue でも立ってないかと見ていたら、もっと単純に「UniRx.Async is separated to https://github.com/Cysharp/UniTask」([Ver 7.0.0 で] UniRx.Async のパッケージが分離しました) というコメントを見つけた。ああ、これか、と(笑)。

 なので、UniRx.Async のパッケージをDLしようかと思ったら、Ver 2.x.x-preview (プレビュー版)となっている。しかし「This version is preview, if you want to use stable version, check 1.3.1.」(安定版が使いたいなら、1.3.1 を見てね)ともあるので、今回はそちらを使うことにした。


UniRx (github)
UniTask (github)


 インポートしてみると、UniRx.Async は以前のように「Plugins」フォルダ以下ではなくなったみたい。もしかしたら「Plugins」フォルダ以下に移動した方が良いのかもだが(ビルド順が変わるとか何とか)、まぁ、私は動けば良いので、とりあえずこれでよし(笑)。

特殊フォルダーとスクリプトのコンパイル順 (Unity Manual)





 後で試しに「Assets/Plugins/UniRx」以下に「UniRx.Async」を移動してみたが、これも問題ないようだ。まぁ、コンパイル順でエラーが出たことはないが、アップグレードするときにまとめておいた方が便利なので、Plugins 以下に移動しても良いかもね。





(関連記事)
【Unity】【C#】UniRx で「1フレームごと待機して処理」してみる


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityオープンソースライブラリ  Unityプラグイン  Unityトラブルシューティング 
tb: 0   cm: --

【Unity】【C#】インスペクタの UnityEvent を並べ替え可能にする  


 前回「インスペクタの配列やリストを並べ替え」を紹介したが、UnityEvent も並べ替えたいときもよくあるだろう。これもとても導入・利用が簡単なものがあるので、ついでに紹介しておこう。

EasyEventEditorMIT License

 また、あまりオープンソースに馴染みの無い人のためにも、導入方法も詳しく書いておく。慣れている人なら自分の方法でも構わない。といっても導入しただけで使えるスグレモノなので、簡単な使い方だけ見ておけば、すぐに使えると思う。


(※) Unity 2019.2.21f1 / Windows10(x64) で確認



■ライブラリのインポート

 まずは github でライブラリをダウンロードしよう。「Clone or download」から zip をダウンロードしても良いが、Releases ページにある .unitypackage を使う方が簡単なので、ここではそのやり方で導入してみよう。


Releases ページ(.unitypackage がダウンロードできる)

 .unitypackage をダウンロードしたら、プロジェクトビューにドラッグドロップしよう。ファイルは1つしかないので(※掲載時点:v1.0.1)、そのままインポートすれば良い。これだけで導入は完了だ。





 尚、zip の方には asmdef(Assembly Definition Files)やパッケージマネージャーでの導入するための package.json などが入っている。こちらの方法はまた別の方法となるので(あと、Unity のバージョンによるので)、興味があったら参考資料だけ載せておこう。

【Unity】Assembly Definition Filesという神機能
プロジェクト管理は進化しています – Unity Package Manager の概要
UnityのPackage Managerを使って機能をインストールする



■インスペクタで並べ替えてみるためのサンプルコード

 ここからは簡単なコードを書いて試してみよう。内容はかなり適当なので、好きなように書き換えて欲しい。

 もしあまり UnityEvent を使ったことないから、色々なイベントコールバックをするサンプルコードが以下の記事にまとめてあるので、参照して欲しい。

【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録

●インスペクタで並べ替えてみるためのサンプルコード(※.NET 4.x)
using UnityEngine;
using UnityEngine.Events;

public class EasyEventEditorTest : MonoBehaviour
{
//これがインスペクタに表示される
public UnityEvent OnCallback;

//UnityEvent のコールバック用1
public void Hoge()
{
Debug.Log("Hoge");
}

//UnityEvent のコールバック用2
public void Fuga()
{
Debug.Log("Fuga");
}

//UnityEvent のコールバック用3
public void Piyo()
{
Debug.Log("Piyo");
}

//UI-Button などの OnClick などに登録する等
public void Click()
{
OnCallback?.Invoke(); //コールバック発火
}
}

 このスクリプトをヒエラルキーで何らかの GameObject にアタッチしよう。

 後はインスペクタで実際に確認して欲しい(表示が更新されてなかったら、一旦フォーカスを外して、再度スクリプトをアタッチした GameObject を見てみると反映されてたりする ← Unity はコンパイルが走ると表示がデフォに戻ることがある)。

●左端にハンドルが付く


●ドラッグで移動できる


●移動後


 Click() を UI の Button に登録して試してみればわかるが(面倒なら Start() から呼んでも良い)、コールバックの発火はインスペクタで登録した上→下の順になる。試してみよう。

●並べ替え前
Hoge
Fuga
Piyo

●並べ替え後(※並び替えた順)
Piyo
Hoge
Fuga


 また、機能をオフにしたり、表示方法を変更したりする設定は「Edit>Preference」で「Easy Event Editor」タブ(Unity2019以降)にある。自分の見やすい設定にしておくと良いだろう。







(関連記事)
【Unity】【C#】インスペクタの配列やリストを並べ替え可能にする
【Unity】【C#】インスペクタで入力不可(Disable)な属性を作る
【Unity】【C#】インスペクタの値を保持したまま変数をリネームする
【Unity】【C#】インスペクタの表示項目を動的に変更する
【Unity】【C#】インスペクタでの UnityEvent のコールバック登録の有無を調べる
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityオープンソースライブラリ  Unityプラグイン  インスペクタ  エディタ拡張 
tb: 0   cm: --

【Unity】【C#】インスペクタの配列やリストを並べ替え可能にする  


 この手のエディタ拡張はググればいくつか出てくるが、今回は特に導入・利用が簡単な「Reorderable Inspector」というオープンソースライブラリを紹介しよう。また、コード上で属性を付けるだけで、既に作ってあるものに後から機能追加できることも、このライブラリの魅力だ。

Reorderable InspectorMIT License

 あまりオープンソースに馴染みの無い人のためにも、導入方法も詳しく書いておく。慣れている人なら自分の方法でも構わない。それではやってみよう。


(※) Unity 2019.2.21f1 / Windows10(x64) で確認



■ライブラリのインポート

 まずは github でライブラリをダウンロードしよう。「Clone or download」から zip をダウンロードできるので、落としたら zip を解凍する。


Reorderable InspectorMIT License

 解凍したらいくつかファイルが出てくるので、プロジェクトビューで「Scripts」(名前は任意)フォルダを用意し、「ReorderableAttribute.cs」「EditScriptableAttribute.cs」をプロジェクトビューにドラッグ&ドロップしてインポートする

 また「Editor」以下のファイルも必要なので、フォルダごとインポートするか、自分で「Editor」(場所はどこでも構わないが、名前は "Editor" 固定)フォルダを作り、「ReorderableArrayInspector.cs」「SerializedPropExtension.cs」をインポートする。これだけで導入は完了だ。





■インスペクタで並べ替え可能な配列やリストを表示する

 ここからは簡単な使用例となる。配列やリストの変数名などは自分の任意で構わない。既に何らかのプロジェクトがあるのなら、それを使っても良い。

●ReorderableInspector の使用例
using System.Collections.Generic;
using UnityEngine;
using SubjectNerd.Utilities;

public class ReorderableInspectorTest : MonoBehaviour
{
//配列の例(public フィールド例)
[Reorderable]
public string[] strArray = { "Apple", "Banana", "Candy" };

//リストの例(private フィールド例)
[SerializeField, Reorderable]
List<string> strList = new List<string>() { "xxx", "yyy", "zzz" };
}

 要素の内容は適当だが、このスクリプトをヒエラルキーで何らかの GameObject にアタッチしよう。

 コードの要点としては「using SubjectNerd.Utilities;」を入れることと、フィールドに "[Reorderable]" を追加するということだ。逆に元に戻したければ "[Reorderable]" をコメントアウトするか、削除すれば良い。

 後はインスペクタで実際に確認して欲しい(表示が更新されてなかったら、一旦フォーカスを外して、再度スクリプトをアタッチした GameObject を見てみると反映されてたりする ← Unity はコンパイルが走ると表示がデフォに戻ることがある)。



●各項目をドラッグして並べ替えられるようになる


 もし、並び替え機能を自作したいなら、以下の資料を参考にすると良いだろう。ただ、自作となると Reflection の知識が必要となるので、結構大変だと思う(実は私も独自に作ってたのがあるが、結局このライブラリの方が楽なので、最近はこっちしか使ってない(笑))。興味があったら調べてみるのも良いだろう。

エディタ拡張で配列の入れ替えが簡単に出来るReorderableListの使い方と全コールバック【Unity】【エディタ拡張】
.NET のリフレクション
System.Reflection





(関連記事)
【Unity】【C#】インスペクタの UnityEvent を並べ替え可能にする
【Unity】【C#】インスペクタで入力不可(Disable)な属性を作る
【Unity】【C#】インスペクタの値を保持したまま変数をリネームする
【Unity】【C#】インスペクタの表示項目を動的に変更する
【Unity】【C#】インスペクタでの UnityEvent のコールバック登録の有無を調べる
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityオープンソースライブラリ  Unityプラグイン  インスペクタ  エディタ拡張 
tb: 0   cm: --

【Unity】【Android】マルチウィンドウを禁止する  


 Android 7.0 以降からは画面を分割してアプリを起動できるマルチウィンドウがサポートされているが、Unity で作っている場合、絶対座標で配置されているものは「分割画面←→フルスクリーン画面」に変更したりすると、レイアウトがおかしくなってしまうことがある。もちろん自動でレイアウトを再計算するように作られていれば問題ないのだが、ゲーム画面などで縦または横の長さ固定で考えられている場合、画面分割で正方形に近くなってしまうので、全てのオブジェクトが中央に寄ってしまう、なんてこともある。そして困ったことに、マルチウィンドウのサポートは Android 7.0 以降、デフォルト状態でオンなのである。

マルチウィンドウのサポート

 なのでいっそのことマルチウィンドウを禁止した方が早い場合も多い(可変レイアウトを考えるのには結構手間がかかる)。まぁ、Android 開発に詳しい人ならググればすぐにわかると思うが、Unity のみで開発している人にとってはわかりずらい部分であり、「なぜかたまにレイアウトが崩れる」なんて困っている人もいるかもと思ったので、とりあえず記事にしておくことにした。

(※) Unity 2019.2.21f1 / Android 8.0 で確認



■AndroidManifest.xml でマルチウィンドウを禁止設定する

 マルチウィンドウのサポートを設定する AndroidManifest.xml のタグは application または activity タグである(アプリ全体なら application タグ、アクティビティごとなら activity タグだが、Unity の場合、複数アクティビティを使うことは少ないので、機械的に application タグでも良い)。
Unity の場合、「Assets/Plugins/Android/」フォルダ以下に AndroidManifest.xml が置かれるので、それを適当なテキストエディタで開き、application または activity タグを見つけて、「android:resizeableActivity="false"」(禁止) を入れておく(許可にしたいなら true)。

application タグ
activity タグ
アクティビティ


 もし、プラグインなど全く使ってないのなら、デフォルトでは AndroidManifest.xml は用意されていないので、フォルダを含めて自分で作る必要がある。テンプレは Windows の場合「(インストールした Unity のフォルダ)\Editor\Data\PlaybackEngines\AndroidPlayer\src\com\unity3d\player」にあり、Mac の場合「(アプリケーションフォルダ)/Unity.app/Contents/PlaybackEngines/AndroidPlayer/src/com/unity3d/player 」にあるので、コピーして手を加えれば良いだろう。詳細は公式マニュアルを参照して欲しい。

Android 用のプラグインをビルド

 ここでは簡略した例を書いておこう。

●AndroidManifest.xml に resizeableActivity="false" を入れ、マルチウィンドウを禁止する
<?xml version="1.0" encoding="utf-8"?>
<manifest ~(中略)~
<application ~(中略)~ android:resizeableActivity="false">
~(中略)~
</application>
</manifest>

 あとは Android ビルドをして、実機で試していると良い。

 簡単な手順としては、例えば Google Chrome を起動し、メニューボタンを長押し→画面が分割されるので、空白の方のウィンドウをタップして、マルチウィンドウを禁止したアプリを立ち上げてみる、なんて感じでできるだろう。
 上手くいったなら「マルチウィンドウ表示では、このアプリを使用できません」等のトースト(メッセージ)が出る。確認してみよう。










(関連記事)
【Unity】【Android】自動バックアップの対象/除外設定をする
【Unity】Android アプリでパーミッション(権限)要求をする
【Unity】【C#】プロジェクト内で Android(Java)プラグインをビルドする
【Unity】標準以外のセンサー(歩数計や心拍数など)を使う(Android)
【Unity】Androidで音声認識を使う
【Unity】Firebase のプッシュ通知と FantomPlugin の共存(Firebase と他のプラグインの共存方法)
【Unity】Unity2019.3 で Android / iOS ネイティブの構成が変わるらしい


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityトラブルシューティング  Unityプラグイン  Androidリファレンス 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR