web-dev-qa-db-ja.com

iOS 6 Web Audio APIで音が出ない

HTML5ゲームを作成しているため、iOS 6がWeb Audio APIをサポートしていることに本当に興奮しました。ただし、Web Chrome APIを使用して、デスクトップChromeで正常に機能する例を使用して、iOS 6でサウンドを再生することはまったくできません。

以下に、タッチコントロールとWeb Audio APIを介したオーディオ再生を備えたHTML5ゲームを示します(存在しない場合は、HTML5オーディオにフォールバックします)。

http://www.scirra.com/labs/sbios6b/

編集:@Srikumarはいくつかの回避策を提案しました。以下のバージョンで適用しました。それでも動作しません!

http://www.scirra.com/labs/sbios6f/

デスクトップChromeではすべてが正常に再生されますが、iOS 6はまったく音を出しません。 Windowsの開発しかしていないので、デバッグに問題があります。iOS6はデバッグモードをリモートWebインスペクターに置き換えました。これはWindowsのSafariでは利用できないようです。いくつかのアラートを使用して、Web Audio APIを正しく識別し、それを使用し、Vorbisサポートを検出しないためAACオーディオにフォールバックし、バッファーをデコードしてから再生すると、エラーはスローされませんが、何も聞こえません。そして、もちろん、音量を最大に上げてみました:)

IOS 6はAACを問題なく再生できるため、コーデックの問題はないはずです。 。m4aのゲームの1つ を参照すると、Safariから直接アクセスして正常に再生できます。

IOS 6のWeb Audio APIの例をご覧ください。 http://chromium.googlecode.com/svn/trunk/samples/audio/samples.html -動作するものもあれば、動作しないものもあります。 t。たとえば、 Chrome Audio Visualizer は機能しますが、 Javascript Drone は機能しません。

IOS 6のWeb AudioとデスクトップChromeの間には、わずかな非互換性が必要です。私は何が欠けていますか?

38
AshleysBrain

編集(2015年11月): iOS 9では、touchstartイベントでオーディオを開始できなくなりました。これにより、以下のソリューションが壊れます。ただし、touchendイベントでは機能します。 iOS 6の元の回答は以下にそのまま残されていますが、iOS 9のサポートではtouchendを使用してください。

まあ、私自身の報奨金の質問に答えて申し訳ありませんが、デバッグの時間後に私は最終的に答えを見つけました。 iOS 6のSafariは、Web Audio APIがミュートされた状態で効果的に起動します。ミュートを解除しませんユーザー入力イベントでサウンドを再生するまで(バッファーソースを作成し、宛先に接続し、noteOn()を呼び出します)。この後、ミュートが解除され、オーディオは制限されず、本来どおりに再生されます。これは、iOS 6でWeb Audio APIがどのように動作するかについての文書化されていない側面です( Appleのドキュメントはこちら

ユーザーは画面に何度も触れて、ゲームに参加できます。ただし、ミュートのままになります。あなたはtouchstart [edit:touchend for iOS 9+]のようなユーザー入力イベント内で一度再生し、その後すべてのオーディオのミュートを解除します。その後、いつでもオーディオを再生できます(ユーザー入力イベントに参加する必要はありません)。

これは、HTML5オーディオの制限とは異なることに注意してください。通常、ユーザー入力イベントではオーディオのみを開始でき、一度に1つのサウンドしか再生できません。 Web Audio APIは、最初のユーザー入力の再生後に完全にミュート解除されるため、いつでもサウンドを再生でき、その後、ポリフォニックにミックスしたり、クールなエフェクトを処理したりできます。

つまり、Web Audio APIを使用して既にWeb上にある多くのゲームは、タッチイベントでnoteOnを発行しないため、オーディオを再生しません。最初のユーザー入力イベントを待つように調整する必要があります。

この問題を回避するにはいくつかの方法があります。ユーザーが画面に触れるまでタイトル音楽を再生しないでください。最初の「タッチしてオーディオを有効にする」画面を開き、サウンドを再生し、タッチしたときにゲームを開始します。うまくいけば、これは同じ問題を抱えている他の誰かがそれをデバッグしようとする時間を節約できることを願っています!

48
AshleysBrain

他の場所で文書化されているはずの単純な解決策を見つけることができました-しかし、時にはこれらのことを自分で考え出すのに何時間も費やさなければなりません...

そのため、多くのチュートリアル( html5rocks のこのチュートリアルなど)で次の手順を実行するように指示されているようです。

  • _window.AudioContext_のインスタンスを作成し、それが存在しない場合(iOSにはない)、_window.webkitAudioContext_を作成します。

  • XMLHttpRequestを作成して、サウンドファイルをロードします

  • loadイベントでcontext.decodeAudioData(....)を実行し、次にcreateBufferSource()を実行して、デコードされたデータを入力し、最後にsource.start(0)を実行してサウンドを再生します。

他の人が指摘したように、ユーザーの操作(クリックまたはタッチスタート)の結果として、AudioContext(偶然、ページの存続期間にわたって保存および使用する必要があります)を作成する必要があります。

HOWEVER:iOSのオーディオ機能を「ロック解除」するには、[〜#〜] must [〜#〜]AudioContextを作成するときに音声データを利用できるようにします。データを非同期でロードすると、再生するものは何もありません。 AudioContextイベント内にclickを作成するだけでは不十分です。

信頼性の高いiOS再生のための2つのソリューションを次に示します。

  • 1)AudioContextを初期化する前に、少なくとも1つのサウンドファイルをロードし、単一のユーザーインタラクション(クリックなど)内でそのサウンドファイルに対して上記のすべての手順をすぐに実行する必要があります。

  • または2)メモリ内で動的にサウンドを作成して再生します。

これは私が2番目のオプションをどのようにしたかです:

覚えておいてください-iOSのclick/touchイベント内にある必要があります:

_ window.AudioContext = window.AudioContext || window.webkitAudioContext;
 var context = new window.AudioContext();

 // you should null check window.AudioContext for old browsers to not blow up

 // create a dummy sound - and play it immediately in same 'thread'
 var oscillator = context.createOscillator();
 oscillator.frequency.value = 400;
 oscillator.connect(context.destination);
 oscillator.start(0);
 oscillator.stop(.5);    // you can set this to zero, but I left it here for testing.

 // audio context is now 'unlocked' and ready to load and play sounds asynchronously
 // YOU MUST STORE 'context' for future usage. DON'T recreate more AudioContexts
_

これはよくある間違いだと思います-3年後に誰もこれを指摘したり発見したりしていないことに驚いています:-/

5
Simon_Weaver

Mac上のSafari 6でWeb Inspectorを使用してデバッグを試みることができます。

  1. Mobile Safari設定/詳細設定で「Webkit Inspector」を有効にします。
  2. USBケーブルを使用して、Safari 6を実行しているMacにデバイスを接続します。
  3. ページ/ゲームを読み込む
  4. メニューDevelop-> [devicename]-> [pageurl]に移動します

すぐに使えるわけではありませんが、数回試してみると問題を絞り込むのに役立ちます。

どうやら、オーディオはユーザーのアクションによってのみトリガーできるということもあります。 iPhone4のiOS6で動作するコードがiPad(iOS6でも)でサウンドを再生しないため、これが本当かどうかはわかりません。

更新:iPhone4 + iOS6でのWebオーディオの成功。 iOS6で新しいオーディオコンテキストを作成するとすぐに、「currentTime」がしばらく0のままになることがわかりました。それを動かすには、最初にダミーのAPI呼び出しを実行する必要があります(createGainNode()など)、結果を破棄します。サウンドはcurrentTimeの実行が開始されたときにのみ再生されますが、currentTimeでサウンドを正確にスケジュールすることは機能していないようです。それらは、少し先の未来である必要があります(例:10ms)。次のcreateAudioContext関数を使用して、コンテキストがノイズを生成する準備ができるまで待機できます。 iPhoneではユーザーの操作は必要ないようですが、iPadではそのような成功はまだありません。

_function createAudioContext(callback, errback) {
    var ac = new webkitAudioContext();
    ac.createGainNode(); // .. and discard it. This gets 
                         // the clock running at some point.

    var count = 0;

    function wait() {
        if (ac.currentTime === 0) {
            // Not ready yet.
            ++count;
            if (count > 600) {
                errback('timeout');
            } else {
                setTimeout(wait, 100);
            }
        } else {
            // Ready. Pass on the valid audio context.
            callback(ac); 
        }
    }

    wait();
}
_

その後、ノートを演奏するときは、.noteOn(ac.currentTime)を呼び出さずに、代わりに.noteOn(ac.currentTime + 0.01)を呼び出します。

私に聞かないでくださいなぜあなたはそれをすべてしなければなりません。それは現時点での方法です-つまり、クレイジーです。

5
Srikumar

だから、私はそれを理解したと思う。

これはAppleの問題であり、サウンドの再生を許可する前にユーザーのアクションが必要です。少なくとも私にとっては、ユーザーがページが読み込まれたときにコンテキストを作成し、ユーザーアクションでcreateGainNodeなどを使用するだけでは不十分です。

あなたの場合、ユーザーが「タッチして開始」ボタンをクリックしたときにコンテキストを作成します。

3
Oskar Eriksson

IOSでHTML5 Audioを使用して音声の制限に遭遇し、次の方法で問題を回避しました。

1)サイレントオーディオファイルを使用してオーディオ要素を作成し、最初にタッチイベント(「ゲーム開始」ボタンなど)で再生してから、すぐに一時停止します。

2)Audio srcを切り替え、短いタイムアウト後にAudio要素を再生するサウンドスイッチャー機能を構築します。

3)イベントでサウンドスイッチャー機能を呼び出す(タッチイベントである必要はありません)。

これは、オーディオエレメントがサイレントオーディオファイルで最初のタッチでミュート解除され、ミュートされないため、ソースをオンザフライで切り替えることができるため機能します。

switchSound: (id) ->
        @soundSwitch.pause()
        @soundSwitch.src = @sounds[id]._src

        clearTimeout @switchSoundDelay
        @switchSoundDelay = setTimeout =>
            # @soundSwitch.volume = 1
            @soundSwitch.play()
        ,50 
1
Jack Wild

更新:iOSでは、サウンドを再生するためにユーザー入力が引き続き必要です( iOS 6 Web Audio APIでサウンドがありません

以前、iOS WebでWebオーディオにこだわっていました。さらに悪いことに、Androidおよびその他のデスクトッププラットフォームで動作する必要があります。

howler.js が見つかるまで。

これは、クロスプラットフォームWebオーディオソリューションのソリューションです。

<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.0.3/howler.min.js"></script>

<script>

  var sound = new Howl({
    src: ['yay3.mp3']
  });
  sound.play();


</script>
1
ytbryan

originalの質問に答えると、iPhone 4S/iOS 6およびMacOSXのファイル形式に関するいくつかの問題を確認できます。 MP3ファイルがSafariにとって「良くない」場合、デコードは悪くなり、AudioContext.createBuffer(array、bool)を呼び出すとエラーが発生します。

奇妙なのは、他の人が指摘した「SYNTAX_ERR、DOM例外12」というエラーについてです。これはバグだと思うようになります。

Safari 6.0(7536.25)を使用したMacOSでも同じ動作をします。

1
Enrico MRK

2015年のソリューションのために更新されました:ios6 +でWebオーディオの問題に取り組んでいるなら、これらのリンクが助けとして見つかりました。

-これはコードソリューションに関する優れた記事です。 http://matt-harrison.com/perfect-web-audio-on-ios-devices-with-the-web-audio-api/

-上記のソリューション記事が作成された後のapiの更新 https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext

-以下は、2番目の記事からの変更を使用した、最初の記事に対する更新されたソリューションです。私が抱えていた問題は、iOS 7のサファリが奇妙なnot-enough-argsエラーを投げていたことです。これはそれを修正しました:

define(function() {

  try {
    window.AudioContext = window.AudioContext || window.webkitAudioContext;
    window.audioContext = new window.AudioContext();
  } catch (e) {
    console.log("No Web Audio API support");
  }
/*
 * WebAudioAPISoundManager Constructor
 */
 var WebAudioAPISoundManager = function (context) {
  this.context = context;
  this.bufferList = {};
  this.playingSounds = {};
};

/*
 * WebAudioAPISoundManager Prototype
 */
 WebAudioAPISoundManager.prototype = {
   addSound: function (url) {
      // Load buffer asynchronously
      var request = new XMLHttpRequest();
      request.open("GET", url, true);
      request.responseType = "arraybuffer";

      var self = this;

      request.onload = function () {
        // Asynchronously decode the audio file data in request.response
        self.context.decodeAudioData(
          request.response,

          function (buffer) {
            if (!buffer) {
              alert('error decoding file data: ' + url);
              return;
            }
            self.bufferList[url] = buffer;
          });
      };

      request.onerror = function () {
        alert('BufferLoader: XHR error');
      };

      request.send();
    },
    stopSoundWithUrl: function(url) {
      if(this.playingSounds.hasOwnProperty(url)){
        for(var i in this.playingSounds[url]){
          if(this.playingSounds[url].hasOwnProperty(i)) {
            this.playingSounds[url][i].stop(0);
          }
        }
      }
    }
  };

/*
 * WebAudioAPISound Constructor
 */
 var WebAudioAPISound = function (url, options) {
  this.settings = {
    loop: false
  };

  for(var i in options){
    if(options.hasOwnProperty(i)) {
      this.settings[i] = options[i];
    }
  }

  this.url = '/src/www/assets/audio/' + url + '.mp3';
  this.volume = 1;
  window.webAudioAPISoundManager = window.webAudioAPISoundManager || new WebAudioAPISoundManager(window.audioContext);
  this.manager = window.webAudioAPISoundManager;
  this.manager.addSound(this.url);
    // this.buffer = this.manager.bufferList[this.url];
  };

/*
 * WebAudioAPISound Prototype
 */
 WebAudioAPISound.prototype = {
  play: function () {
    var buffer = this.manager.bufferList[this.url];
    //Only play if it's loaded yet
    if (typeof buffer !== "undefined") {
      var source = this.makeSource(buffer);
      source.loop = this.settings.loop;
        source.start(0);

        if(!this.manager.playingSounds.hasOwnProperty(this.url)) {
          this.manager.playingSounds[this.url] = [];
        }
        this.manager.playingSounds[this.url].Push(source);
      }
    },
    stop: function () {
      this.manager.stopSoundWithUrl(this.url);
    },
    getVolume: function () {
      return this.translateVolume(this.volume, true);
    },
    //Expect to receive in range 0-100
    setVolume: function (volume) {
      this.volume = this.translateVolume(volume);
    },
    translateVolume: function(volume, inverse){
      return inverse ? volume * 100 : volume / 100;
    },
    makeSource: function (buffer) {
      var source = this.manager.context.createBufferSource();
      var gainNode = this.manager.context.createGain();
      source.connect(gainNode);
      gainNode.gain.value = this.volume;
      source.buffer = buffer;
      // source.connect(gainNode);
      gainNode.connect(this.manager.context.destination);
      return source;
    }
  };

  return WebAudioAPISound;
});
1
mat

すべての単純なソリューションを使用するのに問題があります。特に、サウンドを複数回再生したい場合。

だから私はこのjsライブラリを使用しています: http://pupunzi.open-lab.com/2013/03/13/making-html5-audio-actually-work-on-mobile

0
Matthias M

これは実際の答えではなく、物事がまだ機能していないかどうかを確認するための指示です。 iOS6にはsomeデバイス(特に特定の期間に製造された64GB 4がありますが、実際にはハードウェアに関連していない可能性があるため、特定の期間に製造された64gb 4s)何らかの理由で着信音や音声ではありませんが、他の多くの音)、ボリュームスライダーは消えます。電源コードが接続されていない場合にのみ発生するので、デバッグが難しいことで有名です(いつもとは限りませんが、キャッチできることもあります)。

コンソールで、VirtualAudio_DeviceからのさまざまなコーデックのASSERTION FAILUREメッセージを探します。これは特定の問題とは何の関係もありませんが、サウンドデバイスのある領域のバグは別の領域に関連している可能性があります。少なくとも、他に何も役に立たないかどうかを調査する領域です。

0
Mason Cloud

APIはiOS 6.1で壊れているように見えます。または、少なくとも、現時点で動作するサイトがないことを意味する重大な変更があります。

0
Mark

はい、AshleysBrainの回答が好きです。問題を解決するのに役立ちました。しかし、私はもう少し一般的な解決策を見つけました。

ユーザーイベントからサウンドの再生を開始する前に、ユーザー入力イベントを介して強制的に実行するようになりました。

そう

  $('#start-lesson').click(function() {
  return startThisLesson();
});
startThisLesson = function() {
     var value;
     value = $('#key-pad-value')[0].value;
     playSoundFile(yourBuffer);
}

playSoundFileは、バッファソースの作成に使用するものです。

0
bobbdelsol