Lynx-EyEDの電音鍵盤 新館

制御とか数学とか駄文とか

PhoneGap 3.3.0でiOS開発する

PhoneGapとは

HTML5CSSJavaScriptのみでさまざまなスマートフォンプラットフォーム、例えばAndroidiOSWindows Phone7/8、BlackBerryに対応できるフレームワークです。

なんでこんなもん使おうと思ったのか

html5で本来ネイティブアプリケーションでないと操作が難しかった、ファイル、カメラ、GPS、近接センサ、電池残量検出といった機能を実装できる様になってきました。

もし、ブラウザで完結できるようなアプリであれば、わざわざXcodeをつかう開発しなくていいので、ブログやプレゼンでjsのコードだけ貼付けておけば手軽に試してもらえるかな、と思った訳です。

具体的にはマイクで音声信号を広い、デコードした内容に応じて結果をスピーカから音声で返すモデムの実装です。(一時期流行ったAudio Hi-Jackです)

でも、現実はそんなに甘くなく

ブラウザ経由でマイクからの音声を取得するのにgetUserMedia() APIを使いますが、これは2014年1月現在iOS Safariでは対応していないわけです。

というわけで、(Xcodeで開発しなきゃならないのは仕方ないですが)マイクの取得=ネイティブ or それに準ずる環境、それ以外=HTML5 + JSで実行する事になります。このような理由からPhoneGapを使いました。

導入まで

導入方法が書いてある書籍やブログに当たったんですが、3.3.0ではかなり変わっちゃったみたいで少し苦労しました。
以下はそのメモ。
まずPhoneGap本家サイト:
PhoneGap | Home


Xcodeの導入、iOSデバイスのプロビジョニング関連は完了しているものとする。またnode.jsは導入済みのものとする

本体のインストール

$  sudo npm install -g phonegap

作りたいアプリケーションをCompany Identifierとともに指定します。自分の場合はVoiceCoderと言う名前で、Identifierはcom.lynxeyed.voicecoderとしました。

$ phonegap create voicecoder com.lynxeyed.voicecoder "VoiceCoder"

ディレクトリに移動

$ cd voicecoder/

使用するプラグインを導入します。今回はMedia API、Media Capture APIを使うのでこちらを導入しました

$ phonegap plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-media.git

$ phonegap plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture.git

iOS用にビルドします。

$ phonegap build ios

すると、XCode用プロジェクトが、[アプリケーション名]/platform/ios/ 配下に作成されます。
f:id:Lynx-EyED:20140211230404p:plain

とりあえず、このプロジェクトファイルをダブルクリックして、Xcodeを起動します。
実行します。
f:id:Lynx-EyED:20140211230624p:plain

実機を指定している場合はiOSデバイス本体、そうでない場合はシミュレータが立ち上がって
こんな感じになれば動作確認は終了です
f:id:Lynx-EyED:20140211230810p:plain

とりあえずマイクから録音できるか確認する

Audio Hi-Jackネタではマイクが使えないといけないので、ここだけ確認します。
使うのはPhoneGapのMedia APIです。

以下のサイトのコードをほぼそのまま使っています。が、3.3.0ではiOS、AndroidでMedia APIの呼び出すメソッドの差異は無い模様なので修正しました。
ASCII.jp:JavaScriptで作れるiPhone用ボイスレコーダー (1/5)|古籏一浩のJavaScriptラボ
画面はこんな感じ。
f:id:Lynx-EyED:20140215120044p:plain

以下を/www配下のindex.htmlに記述します。iOS以外でも動作するはずですがwebkit系のブラウザ以外ではスタイルが崩れるはずなので各自修正を。

<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
        <meta http-equiv="Content-type" content="text/html; charset=utf-8">
            <style>
                .btn {
                    font-family: Arial, Helvetica, sans-serif;
                    font-size: 28px;
                    color: #fff;
                    padding: 12px 18px;
                    
                    background: #8893da;
                    background: -webkit-gradient(
                        linear, left top, left bottom,
                        from(#8893da),
                        to(#344c77)
                    );
                    
                    border-radius: 10px;
                    
                    -webkit-box-shadow:
                        12px 12px 12px rgba(000, 000, 000, 0.3),
                        inset 0px 0px 0px rgba(255, 255, 255, 0);
                    
                    text-shadow:
                    10px 10px 10px rgba(000, 000, 000, 1),
                    
                }
            </style>
            
            <script type="text/javascript" charset="utf-8" src="phonegap.js"></script>
            <script type="text/javascript" charset="utf-8">
               
               var rec = null;
               var src = null;
               
               document.addEventListener("deviceready", init(), false);
               
               function init(){
                    src = "test.wav";
                }
            
            function startRec(){
                rec = new Media(src,
                                    // success callback
                                    function() {
                                    console.log("Audio Success");
                                    },
                                    // error callback
                                    function(err) {
                                    console.log("Audio Error: "+ err.code);
                                    });
                // Record audio
                rec.startRecord();
                document.getElementById("stat").innerHTML = "recording...";
            }
            function stopRec(){
                rec.stopRecord();
                document.getElementById("stat").innerHTML = "stop recording";
            }
            function playRec(){
                rec.play();
                document.getElementById("stat").innerHTML = "play...";
            }
            function playStop(){
                rec.stop();
                document.getElementById("stat").innerHTML = "stop playing";
            }
            </script>
            </head>
    <body>
        <h1>Using Media APIs</h1>
        <div>
            <button class="btn" onclick="startRec()">REC</button>
            <button class="btn" onclick="stopRec()">STOP REC</button>
            <button class="btn" onclick="playRec()">PLY</button>
            <button class="btn" onclick="playStop()">STOP PLAY</button>
        </div>
       <div id="stat"></div>
    </body>
</html>