【Monaca】Mediaプラグインを使う時に気をつけたいこと【Cordova】

,

この間Monacaの無料プランで2作目をリリースしました。
今作ではBGMを鳴らしたかったのでMediaプラグイン(cordova-plugin-media)を使ったところ、何をするにも一発で動かなかったので、自分用に注意点をまとめようと思います。

公式の説明はこちら。

公式を見ると簡単に実装できそうですが、同じく詰まってしまった方はご参考にどうぞ!

そもそも音が鳴らない

もしMonaca クラウド IDE(開発画面)上のプレビューで確認しているなら、cordovaプラグインは実機じゃないと鳴りません。monacaデバッガー等を使い実機で確認する必要があります。

詳しくは別記事を書いてます。
【Monaca】cordovaプラグインの動作は実機で確認しよう!【初心者】

無限ループ再生

「media.play()」はオプションを指定しないと一度再生して終了します。ループしたい場合は、以下のように書くことで無限ループができます。

media.play({ numberOfLoops:"infinite"});

しかし、それでも鳴らなかった場合があったので、その時は以下のように書いています。

media = new Media (getPath() + srcFile , onSuccess, onError, mediaStatus);

function mediaStatus(e){
	if ( e == 4 ){ //4は再生終了のイベントコード
		media.play({numberOfLoops:"infinite"});
	}
}

ステータスの変化をウォッチできるコールバックmediaStatusを使って、音源が再生終了した際にまた再生させます。

ただ外部から音源を参照してる場合、iOSだとループしない場合がありました。
その場合、monaca.isIOSを使ってiOSだけMediaオブジェクトを再作成させるようにします。

media1 = new Media (getPath() + srcFile , onSuccess, onError, mediaStatus);

function mediaStatus(e){
	if ( e == 4 ){
		if( monaca.isIOS ){
			media = new Media (srcFile , onSuccess, onError, mediaStatus);
		}
		media.play({numberOfLoops:"infinite"});
	}
}

なかなか力づく
こうすると、再生まで少しタイムラグがありますが、ちゃんループするようになりました。
*if(monaca.isIOS){}という条件をいれないと、Androidでは多重で音声が再生されるので注意が必要です。

参考記事)

(iOSのみ)複数音源の同時再生

これが一番の難関でした。結論から言うと、このプラグイン(cordova-plugin-media)だと、複数の音源を同時再生することはできないようです。

cordorva-plugin-nativeaudio」というプラグインならできるそうですが、カスタムプラグイン(外部CordovaプラグインやユーザCordovaプラグイン)は有料プランじゃないとインポートできません。

しかしなんとか無料プランでやりたかったので、無理くりやってみました。
BGMを鳴らしながら、ボタンを押すと正解・不正解の音源が鳴るサンプルです。

//Javascript
var srcFile = "main.mp3";
var srcFile2 = "correct.mp3";
var srcFile3 = "wrong.mp3";

document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
    media = new Media (getPath() + srcFile , onSuccess, onError, mediaStatus);
    media2 = new Media (getPath() + srcFile2);
    media3 = new Media (getPath() + srcFile3);
    
    media.play({numberOfLoops: "infinite"});
}
function getPath() {
    var str = location.pathname;
    var i = str.lastIndexOf('/');
    return str.substring(0,i+1);
}
function playSound(i){
    media.pause();
    if(i === '1'){
        media2.play();
        console.log('正解');
    }else{
        media3.play();
        console.log('不正解');
    }
    setTimeout(function(){
        media.play({numberOfLoops: "infinite"});
    },1000);
}
function onSuccess(){
       //省略
}
function onError(error){
       //省略
}
function mediaStatus(e){
       //省略
}
//HTML

<h1>Playing Sound</h1>
<button onclick="playSound('1')">正解</button>
<button onclick="playSound('2')">不正解</button>

このように書くと、

  1. ボタンクリックでBGMが止まる(media.pause())
  2. 正解・不正解の音源が鳴る
  3. BGMが再開する

という挙動になります。
再生タイミングが被ると音が停止するのでsetTimeoutでBGM再開を遅らせています。

ただ不自然さが残るのと挙動に不安定さを感じたので、本番適用は見送りました_(._._)_
もしもうちょっと実践的な形にできたら追記します。

参考記事)

(iOSのみ)リリース後に音がでない

こちらは細かく検証されている記事がありました。

monacaでアプリをビルドするときに、iOSアプリ設定のアプリケーション名がファイルパスに含まれ、ここに日本語を使っているとファイル名がURLエンコードされて%などがふくまれてしまい、存在しない(解釈できない?)ファイルパスになります。

ただのデバックのときはそのファイルパスにはアプリケーション名は使われず、プロジェクトコードかIDかわかりませんが、monacaがあてたIDの英数字になるので、問題なく動いていたようです。

また、他の簡単なテストアプリは、アプリケーション名を半角英数にしていたのですが、単語の間にスペースを入れていたため、その部分の文字がエンコードされて、同じく存在しない(解釈できない?)パスになっていたようです。

ということで、

  • Monacaプロジェクト内に音源を置いて使用している
  • Monacaアプリ設定にあるアプリケーション名を日本語(または半角空白等を含む文字列)にしている

この条件だとリリースビルドのみファイルパスが参照できず、エラーになるとのこと。

実際に実機デバッガーをかけた際のgetPath()をみると、アプリ名にスペースが入っても日本語文字列になっていても以下のようなパスで参照されており、問題ありませんでした。

/var/mobile/Containers/Data/Application/xxx(英数字ハイフン)/Library/Caches/launch/000(数字)/xxx(英数字)/www/

これにリリース時にはアプリ名が含まれてしまうんですね。

これに対応するには、以下のどちらかの手段をとる必要があります。

  • 音源を内部ではなく外部に置いて、URLで参照する
  • Monacaアプリ設定のアプリケーション名を英語(半角空白なし)にする

*ちなみにファイルを相対パスで呼べばパス関係ないのでは、、と思いましたが、これも参照できませんでした。

//エラーメッセージ
Failed to initialize a media file. [ Error code: 1, Error message: Cannot use audio file from resource 'ファイルパス']

公式にはできると書いてあるのになぁ(ヽ´ω`)?

ファイルの検索順序: ファイル名のみまたは不完全なパス ( simple path ) を指定している場合、iOS では、最初に、www ディレクトリー内を検索して、見つからなければ、次に、アプリの documents/tmp ディレクトリーを検索します。

var myMedia = new Media("audio/beer.mp3");
myMedia.play() ; // first looks for file in www/audio/beer.mp3 then in <application>/documents/tmp/audio/beer.mp3

引用元:メディア操作 プラグイン >media.play>ファイルの検索順序

 

私はアプリ名を英語に変更することで無事音源が再生できました。
ですがここのアプリ名はアプリアイコン下に表示されるタイトルでもあるので、日本語にしたいという方は外部ファイルになると思います。

その際はループが上手く回らないことがあるので対策する必要があります

参考記事)

(Androidのみ)バックグラウンドでもBGMが鳴り続ける

MonacaのAndroidアプリ設定「バックグラウンド時もアプリを常に実行」を有効にしてる場合に起きます。
この場合、addEventListenernのpause(バッググラウンド時)とresume(復帰時)を使って制御します。

function onDeviceReady() {
	media = new Media (getPath() + srcFile , onSuccess, onError, mediaStatus);
	media.play();

	if( monaca.isAndroid ){
		document.addEventListener("pause", function(){
			//アプリがバッググラウンドになったとき
			media.pause();
		}, false);
		document.addEventListener("resume", function(){
			//アプリが再実行されたとき
			media.play();
		}, false);
	}
}

*上記コードだと復帰時に強制的に音源再生が再開します。もしバッググラウンド前にアプリ側で操作した音楽設定を引き継ぎたい場合は、それ用に処理をいれましょう。

参考記事)

最後に

現時点で分かったMediaプラグインの注意点・使い方まとめでした。

原因がMonacaの開発環境によるもの、Mediaプラグイン(cordova-plugin-media)によるものとぞれぞれあり、なかなか苦戦してしまいました。
音楽再生の微調整を行いたいのであれば、有料プランでさくっとカスタムプラグインを入れた方がいいかも知れませんね。特に複数音同時再生はニーズが高さそうなところ。

今の所私は音楽再生の微調整はそこまで必要としていないので、もう少し無料プランでこねくり回してみようと思います。