iOS Safariのaudio要素はユーザーイベントで再生をしないと音が出ないようになっています。iOS6でWeb Audio APIがサポートされ、この制約を緩和するノウハウが色々なブログで公開されています。
今回、このノウハウを実際に使うことになったので、CoffeeScriptのクラスで実装してみました。
class WAudio @NO_SOURCE: 0 @LOADING: 1 @LOADED: 2 @context: if AudioContext? then new AudioContext() else if webkitAudioContext? then new webkitAudioContext() else null @unlock: -> # (iOS用) 何かのユーザーイベントの際に呼び出し、Web Audioを有効にする。 # 空のソースを再生 source = @context.createBufferSource() source.buffer = @context.createBuffer 1, 1, 22050 source.connect @context.destination source.noteOn 0 constructor: (@src) -> @forcePlay = false @state = WAudio.NO_SOURCE load: -> xhr = new XMLHttpRequest() xhr.open 'GET', @src xhr.responseType = 'arraybuffer' xhr.onload = => WAudio.context.decodeAudioData xhr.response, (buffer) => @buffer = buffer @state = WAudio.LOADED @play() if @forcePlay xhr.send() @state = WAudio.LOADING play: -> switch @state when WAudio.NO_SOURCE @forcePlay = true @load() when WAudio.LOADING @forcePlay = true when WAudio.LOADED if @source? @source.disconnect() @source = WAudio.context.createBufferSource() @source.buffer = @buffer @source.connect WAudio.context.destination @source.noteOn 0 pause: -> @source.noteOff 0
使い方は、
1. 音を鳴らす前にユーザーが起こすイベントを用意する。(Game Startボタンをクリックするとか)
2. 上記イベント処理でWAudio.unlock()を呼び出す。
3. 後はAudio要素をJavaScriptで扱うのと同様、URLを指定してインスタンスを作り、loadなりplayなりします。ユーザーイベントでなくても好きなタイミングで音を鳴らせます。
一点、注意事項。
iOS6では、サイトを「ホーム画面に追加」してウェブアプリ(ウェブクリップ)化した場合、Web Audioで音を鳴らすと、高確率でホームボタンと電源ボタンが効かなくなるという致命的なバグがあります。
ウェブアプリの場合には、Web Audioを使わないようにしましょう。
追記1: iOS7, iOS7.1でもこの不具合は続いています。
追記2: iOS7で音声合成APIが使えるようになりました。これも、一度ユーザーイベントで合成すれば、後は好きなタイミングで合成発声できるようになります。ただ、Web Audioとは独立のようで、Web Audioの上記unlockをしても、好きなタイミングで合成発声はできませんでした。あくまでユーザーイベントで一度合成発声させる必要があります。