Quantcast
Channel: 音楽方丈記
Viewing all articles
Browse latest Browse all 339

iPhoneを振ってMobile SafariからJavaScriptで加速度を検知してスレイベルを鳴らしてみる

$
0
0
時期ネタを何かやってみようということで、iPhone を振って鳴らせるスレイベル (Sleigh Bells) のプチ Web アプリ的なページを作ってみました。
iOS 6 以降搭載の iPhone/iPod touch の Mobile Safari で動きます。


スレイベル (Sleigh Bells) とは?

スレイベルは木製の棒に鈴がたくさんついたパーカッションの一種です。
(輪っかに鈴がついた小型のタイプもあります)
名前や形は知らなくても、クリスマスの鈴の音といえば分かる人も多いのでは?



Sleigh Bells for iPhone の使い方


     
←左の QR コードをタップするか、iPhone のカメラで QR コードを読み取って Sleigh Bells のページにジャンプしてください。


使い方はいたってシンプルです。
  • まず「Sound Preview」ボタンで鈴の音量を確認・調整してください。
  • 次に iPhone を握って手首のスナップをきかせて振ってみてください。
  • 反応が悪い場合は、振る速度を速くするか、感度を低くしてみてください。

  振るときに iPhone がすっぽ抜けないように十分注意してください。
  (万が一 iPhone を落として壊してもこちらは責任は負いません)

プログラムコードについて

短時間で作ったサンプルレベルの大雑把なプログラムではありますがついでに少しだけ説明しておきます。
プログラムに興味がない人はここから先はスルーしてください。

 UI 部分はお手軽な jQuery + jQuery UI の組み合わせで、ただしスライダーの動作は標準の jQuery UI ではタッチドラッグができないので、jquery-ui-touch-punch を利用しました。

↓JavaScript の部分だけ抜粋
var context;
var buffer = null;
var threshold = 0;
var easing = false;

try{
  context = new webkitAudioContext();
  var request = new XMLHttpRequest();
  request.open("GET", "sleigh-bells.mp3", true);
  request.responseType = "arraybuffer";
  request.onload = function() {
    context.decodeAudioData(request.response, function(data){
      buffer = data;
    });
  }
  request.send();
}catch (e){
  alert("iOS 6 以降の mobile Safari で試してください");
}

function playSound(){
  var bufSrc = context.createBufferSource();
  bufSrc.buffer = buffer;
  bufSrc.connect(context.destination);
  bufSrc.noteOn(0);

  if(!easing){
    easing = true;
    $("body").effect("highlight",{color:'#FFF',easing:'easeInOutQuad'}, 700, function(){
      easing = false;
    });
  }
}

function setThreshold(value){
  threshold = value;
  $("#threshold-value").text("Sensitivity: " + threshold);
}

$(function(){
  setThreshold(1500);

  window.addEventListener("devicemotion", function(e){
    if(Math.floor(e.acceleration.x * 100) > threshold){
      playSound();
    }
  });

  $("#preview").button().click(function(){
    playSound();
  });

  $("#threshold-slider").slider({
    min: 0,
    max: 3000,
    value: threshold,
    change: function(event, ui){
      setThreshold(ui.value);
    }
  }).draggable();
});

» 加速度センサーのイベント devicemotion
 iOS はバージョン 4.2 から加速度センサーの値を読み出すことができる devicemotion イベントが追加されていて、Safari の JavaScript からイベントハンドラを指定してリアルタイムに現在値を取得することができます。

 » Safari Developer Library - DeviceMotionEvent Class Reference

function deviceMotion(e){
// e.acceleration.x
// e.acceleration.y
// e.acceleration.z
// e.accelerationIncludingGravity.x
// e.accelerationIncludingGravity.y
// e.accelerationIncludingGravity.z
// e.rotationRate.alpha
// e.rotationRate.beta
// e.rotationRate.gamma
}

window.addEventListener("devicemotion", deviceMotion);

 Sleigh Bells では X 方向の加速度変化 (e.acceleration.x) だけを利用しています。X 方向は iPhone 本体正面から見て左右に動かしたときの移動で、今回の場合は握ったときに本体側面が対面になるので前後に振る動作で X 軸が変化します。

 指定した加速度に達したときにスレイベルの音を鳴らす関数 playSound() を呼び出します。スレイベルを振る演奏操作的には手前に強く押し出したときだけ鳴って、引きの操作のときはならないほうがいいので、プラス方向の加速のときだけ反応するようにしてあります。

» Mobile Safari の Web Audio API
 サウンドの再生は Mobile Safari で利用できる WebKit の Web Audio API を使っています。
 最初は HTML5 Audio オブジェクトを使おうと思っていたのですが、Mobile Safari は play() を呼び出すたびに音声ファイルを毎回律儀にサーバーからロードしてしまい、再生が開始されるのが遅すぎて当初想定したレスポンスが得られませんでした。

 なにか別の方法はないものかと調べたてみら、iOS 6 から Mobile Safari が Web Audio API に対応していることが分かって、試しにやってみたらうまいこと動いてくれました。

 Safari Developer Library - Playing Sounds with the Web Audio API

 鈴の音の mp3 ファイルを Ajax 経由でバッファに読み込んで、指定のタイミングで noteOn() するだけのシンプルな作りです。

try{
  context = new webkitAudioContext();
  var request = new XMLHttpRequest();
  request.open("GET", "sleigh-bells.mp3", true);
  request.responseType = "arraybuffer";
  request.onload = function() {
    context.decodeAudioData(request.response, function(data){
      buffer = data;
    });
  }
  request.send();
}catch (e){
}

function playSound(){
  var bufSrc = context.createBufferSource();
  bufSrc.buffer = buffer;
  bufSrc.connect(context.destination);
  bufSrc.noteOn(0);
}

 ただ、ページがロードされた直後は devicemotion 内で呼び出している再生が動かないというよく分からない現象がでてます。(まだ原因は分かっていません)
 ボタンで手動で1回再生した後だと反応してくれるので、とりあえず音量確認用ということにしてボタンを設けました。

一応自分の iPhone 5s (iOS 7.0.4) では動いてます。
他のパターンではテストしてないので、もしかしたらちゃんと動かないこともあるかもしれません。

あと、試すときはくれぐれも iPhone を落とさないように注意してくださいね。


[追記] 2013/12/19
コメント欄の aike さんから教えて頂いた情報によると、デベロッパーライブラリのドキュメントに以下の注意書きがありました。

Note: On iOS, the Web Audio API requires sounds to be triggered from an explicit user action, such as a tap. Calling noteOn() from an onload event will not play sound.

iOS の Web Audio API のサウンドは画面タップなどのようなユーザーアクションのトリガーによる再生が必要で、ページロード後に noteOn() を直接呼び出しても反応しないのは仕様なのだそうです。
deviceMotion イベント内で noteOn() を呼ぶのもこれに該当します。

[関連サイト]
 jQuery
 jQuery UI
 jquery-ui-touch-punch
 Web Audio API
 Safari Developer Library



Viewing all articles
Browse latest Browse all 339

Trending Articles