SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Apache Cordovaで本格スマホアプリに挑戦しよう

ハイブリットアプリ開発でオーディオファイルの再生を便利にするプラグインを使う

Apache Cordovaで本格スマホアプリに挑戦しよう 第8回

  • X ポスト
  • このエントリーをはてなブックマークに追加

音楽プレイヤーを作成する

 続いては、音楽を再生する時の利用シーンを想定し、再生、一時停止、停止、スキップ、ボリュームの変更やオーディオファイルの長さ、再生中の時間を表示する機能など一連の再生にかかわる機能を利用する図2のようなサンプルを作成します。

図2 音声再生にかかわる一連の機能を利用するサンプルアプリのキャプチャ
図2 音声再生にかかわる一連の機能を利用するサンプルアプリのキャプチャ

 このサンプルで利用するAPIを表3に示します。

表3 再生時に使用するAPI一覧
メソッド 説明
play() オーディオファイルを再生する
stop() 再生、もしくは録画を停止する
pause() 再生を一時中断する
release() メディアインスタンスを解放する
seekTo(millseconds) 再生する位置を指定した時間(ミリ秒)に移動する
setVolume(val) 指定(0.0から1.0まで)した値でボリュームを設定する
getDuration() オーディオファイルの長さ(時間)を取得する
getCurrentPosition(callback) 再生中の現在の再生時間を取得する。Callbackには秒で値が取得できる

 また、今回作成するサンプルでは、前回省略していたコンストラクタでの各コールバック関数をリスト5のように利用します。

リスト5 ステータスが変わったときのコールバック関数の利用(js/controllers/PlayerController.jsより抜粋)
function PlayerController($scope) {
    // : (省略)

    // (1) 再生が終了したとき
    this.mediaSuccess = function () {
        console.log("media success");
    };
    // (2) エラーが発生したとき
    this.mediaError = function (e) {
        window.alert("Error :" +  e.code);
    };
    // (3) ステータスが変わったとき
    this.mediaStatus = function (status) {
        if(status != Media.MEDIA_NONE){
            // (メディアの状態が初期状態でなくなったとき)
        }
        if(status == Media.MEDIA_RUNNING){
            // 再生が開始されたタイミング
            // (省略)
        }
        else if(status == Media.MEDIA_STOPPED){
            // 停止された時の処理
        }
    };
    // : (省略)
    var media = new Media(src, this.mediaSuccess, this.mediaError, this.mediaStatus);
    // : (省略)
};

 (1)は再生が終了したときのコールバック関数です。つまり、指定したオーディオファイルが最後まで再生された時、もしくは停止処理がされた場合に実行されるコールバック関数です。筆者は当初メディアオブジェクトの初期化が完了したときと勘違いしてしまいました。初期化が完了した、もしくは、再生可能な状態になったなどのようなタイミングを取得ことはできません。

 続いて、(2)はエラーが発生したときのコールバック関数です。エラー原因はコールバック関数(ここではmediaError関数)の引数(e)のプロパティから表4で示すエラー情報を取得可能です。

表4 エラー時に取得できるプロパティ
変数名 説明
code 数値 エラーコードを示す。
message エラー理由の説明メッセージ。ただし、必ずこのプロパティがあるわけではないので注意。

 また、エラーコードは表5で示す理由を示します。

表5 エラーコード一覧
変数名 説明
MediaError.MEDIA_ERR_ABORTED 1 何らかのエラーにより処理が停止した
MediaError.MEDIA_ERR_NETWORK 2 ネットワークエラーにより処理が停止した
MediaError.MEDIA_ERR_DECODE 3 デコード処理に関するよるエラー
MediaError.MEDIA_ERR_NONE_SUPPORTED 4 サポートされていない処理に関するエラー

 ただし、筆者が確認した限りでは、ほとんどのケースでMediaError.MEDIA_ERR_ABORTEDのエラーになり、messageにも何もメッセージがありませんでした。

 再生前にエラーになる場合には、オーディオファイル名の間違い、もしくは、プラットフォーム毎のパスの指定方法が主な原因と思われます。

 また、再生していないにもかかわらず、一時停止(pause())などをした場合にもエラーになります。エラーコードのみからエラー原因を把握することは難しいため、続いて紹介するステータスなども踏まえて判断する必要があるでしょう。

 (3)はステータスが変わったときのコールバック関数です。ステータスは表6のステータスがあります。

表6 ステータス変更で取得できる値
変数名 説明
Media.MEDIA_NONE 0 初期状態
Media.MEDIA_STARTING 1 初期処理が開始された時
Media.MEDIA_RUNNING 2 再生が開始、もしくは録画が開始した時
Media.MEDIA_PAUSED 3 再生が一時停止された時
Media.MEDIA_STOPPED 4 再生、録画が終了した時

 ステータスの変更をトリガーにして処理するケースは多々あることと思います。後述する再生時間などを取得する場合には、MEDIA_RUNNINGステータスになったタイミングで処理を行っていますし、操作ボタンの表示・非表示をコンロールするなどの時にもこれらのステータス変更のタイミングを利用できます。

 続いて、先ほどのコードにオーディオファイルの操作と再生時間やトータル時間などの実装したものが、リスト6のようになります。

リスト6 オーディオファイルを操作するサンプルコード(js/controllers/PlayerController.jsより抜粋)
function PlayerController($scope) {
    // : (省略)
    this.mediaStatus = function(status) {
         // : (省略)
        if(status == Media.MEDIA_RUNNING) {
            // AngularJSの場合には$intervalの利用を推奨。下記では説明上 setIntervalを利用
            timer = setInterval(function() {
                if($scope.duration <= 0) {
                    //  (1) トータル時間の取得
                    var duration = media.getDuration();
                    if(duration > 0) {
                        $scope.$apply(function() {
                            $scope.duration = duration;
                        })
                    }
                }
                // (2) 現在の再生時間を取得
                media.getCurrentPosition(function(sec) {
                    if(sec > 0) {
                        $scope.$apply(function() {
                            $scope.current = sec;
                        });
                    }
                });
            }, 200);
        }
        else if(status == Media.MEDIA_STOPPED){
            if(timer){
                clearInterval(timer);
                // : (省略)
            }
        }
        // : (省略)
    };
    // : (省略)
    var media = new Media(src, this.mediaSuccess, this.mediaError, this.mediaStatus);
    $scope.current = 0;
    media.setVolume(vol);  //  (3) ボリュームの設定
    $scope.play = function() {
        media.play(); // (4) 再生
    };
    $scope.pause = function() {
        media.pause();  // (5) 一時停止
    };
    $scope.stop = function() {
        media.stop(); // (6) 再生停止
    };
    // : 省略
    $scope.skip = function() {
        // (7) 10秒ほど現在位置より、先にスキップする
        var millsec = ($scope.current * 1000) + (10 * 1000);
        media.seekTo(millsec);
    };
    // : 省略
}

 (1)では再生するオーディオファイルの長さを取得するためにgetDuration()メソッドを実行しています。この再生時間は取得できない状態の時には-1を返し、インスタンスを作成した直後では-1になります。

 そして、実際に再生などの処理をしてからしか値が取得できず、AndroidとiOSで取得できるタイミングが異なるようです。筆者が確認した限りでは、Androidでは再生して多少の時間がたったとき、iOSではMEDIA_RUNNINGのステータスに変わったときに取得可能でした。そのためステータスがMEDIA_RUNNINGになったタイミングの中でタイマー処理で取得しています。Android側のプラグイン内部のコードを見る限りプラグイン側のJSコード内ではトータル時間が取得できるステータスを管理していますが、プラグインの利用者側にはその状態の取得まではサポートしていないようでした。

 (2)では、現在再生している時間を取得します。この処理も再生が始まった時のタイマー処理内で処理をしています。そして、再生が止まったときにはそのタイマー処理を停止しています。

 続いてボリュームの設定ではgetVolume()のようなメソッドで現在のボリューム値を取得するAPIはなく、また、初期値に既定がないため(3)のように設定をしています。また、変更する際にも0.0から1.0までの値で必ず指定する必要がありますので注意してください。

 (4)、(5)、(6)は再生する処理、一時停止する処理、停止する処理になります。

 また、時間を指定して特定の時間まで移動するには、(7)のようにseekToにミリ秒単位で値を取得します。getCurrentPositionメソッドで取得する時間は秒単位での取得ですので注意が必要です。

最後に

 今回は、オーディオファイルの再生を紹介しました。ドキュメントを参照しても各環境の制限を含めたところにまで記載が至っていないため、実装依存の部分が多々あります。そして、その実装は各OSで提供しているSDKにも依存しているのだと思います。しかし、実際にそれらについてわかってしまえば、単純に再生するというのであればそれほど面倒ではなく利用できます。

 ただし、今回説明した制限も今後は同じであるかはわからない部分がありますので、実際に開発するに際しては注意が必要です。

 次回はこのプラグインの録音機能の紹介と別のプラグインで音声、画像、映像をキャプチャーができるプラグインがあります。次回はそのプラグインについても紹介しますので、録音が必要な場合には、どちらのプラグインが合っているのかを比べるとよいかと思います。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Apache Cordovaで本格スマホアプリに挑戦しよう連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 小林 昌弘(コバヤシ マサヒロ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/9593 2016/09/13 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング