2012-09-20 164 views
37

我很高興看到iOS 6支持Web Audio API,因爲我們製作了HTML5遊戲。但是,我無法讓iOS 6使用Web Audio API播放任何聲音,並且這些例子在桌面版Chrome中運行良好。iOS 6沒有聲音Web Audio API

這裏是通過網絡音頻API一個HTML5遊戲,觸摸控制和播放音頻(如果存在的話 - 如果不是它會回落到HTML5音頻):

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

編輯:@Srikumar建議一些解決方法。我在下面的版本中應用了它們。它仍然不起作用!

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

一切都在扮演着桌面版Chrome就好了,但iOS 6中發出任何聲音都沒有。我在調試時遇到了問題,因爲我只進行Windows開發,而iOS 6用遠程Web檢查器取代了調試模式,這顯然在Safari的Windows上不可用。使用一些警報,我發現它正確地識別Web Audio API,使用它,檢測到沒有Vorbis支持,因此退回到AAC音頻,解碼緩衝區然後播放它,並且沒有錯誤,但是我什麼都沒聽到。當然,我試着將音量調高到最大:)

不應該有編解碼器問題,因爲iOS 6可以播放AAC就好 - 您可以瀏覽到one of the .m4a's the game plays,它可以直接從Safari瀏覽器中直接播放。

查看iOS 6上的Web Audio API示例:http://chromium.googlecode.com/svn/trunk/samples/audio/samples.html - 其中一些可以工作,而其他則不可以。例如,Chrome Audio Visualizer工程,但Javascript Drone不。

iOS 6上的Web Audio和桌面版Chrome之間必定存在某些細微的不兼容性。我錯過了什麼?

+0

可能與文件格式有關。我在Safari中遇到了一些問題。 –

+0

可能,但是正如帖子所說,我可以直接從Safari中播放.m4a聲音中的一種。 – AshleysBrain

+0

你有哪些具體問題? –

回答

46

編輯(2015年11月): iOS 9不再允許音頻在touchstart事件中啓動,這打破了下面的解決方案。但它在touchend事件中起作用。 iOS 6的原始答案在下面保留不變,但對於iOS 9支持,請確保使用touchend

那麼,很抱歉回答我自己的賞金問題,但經過幾個小時的調試,我終於找到了答案。 iOS 6上的Safari有效地從Web Audio API靜音開始。直到您嘗試在用戶輸入事件(創建緩衝區來源,將其連接到目標並呼叫noteOn())中播放聲音時,它纔會取消將其取消抑制。在此之後,它會取消靜音並且音頻播放不受限制,並且應該如此。這是Web Audio API如何在iOS 6上工作的一個未記錄的方面(Apple's doc is here,希望他們很快就會更新它)!

用戶可以很多地觸摸屏幕,參與遊戲。但它將保持靜音。你有在用戶輸入事件內播放,如touchstart [編輯:touchend iOS 9+],一次,然後所有音頻取消靜音。之後,您可以隨時播放音頻(不必在用戶輸入事件中)。

注意:這與HTML5音頻的限制不同:通常您只能在用戶輸入事件中啓動音頻,並且一次只播放一個聲音; Web Audio API在第一次播放用戶輸入後完全取消靜音,以便隨時播放聲音,然後您可以將它們混音,處理酷炫效果等。

這意味着很多遊戲已經在網絡上使用Web Audio API將不會播放音頻,因爲它們不會在觸摸事件中發出noteOn。您必須調整它以等待第一個用戶輸入事件。

有幾種解決方法:在用戶觸摸屏幕前不要播放標題音樂;有一個初始的「觸摸啓用音頻」屏幕並播放聲音,然後在他們觸摸時開始遊戲;等等。希望這可以幫助其他有相同問題的人節省一些時間來嘗試調試它!

+0

既不是以前的答案狀態,需要用戶操作獲取音頻? –

+0

我讀過它們,要麼意味着你只能在那裏創建上下文(不完全正確),要麼在用戶動作中沒有觸發的音頻永遠不會播放(也不完全正確) – AshleysBrain

+0

_它將不會取消靜音,直到您嘗試播放聲音一個用戶輸入event_。 我有一個反例 - http://srikumarks.github.com/demos/tala。 (這是南印度塔拉斯的一種「節拍器」。)如果您加載在Safari/iOS6的/ iPhone,只是等待一段時間,節拍器將開始滴答地走着,雖然顯卡不起作用它在Chrome中。沒有用戶輸入需要(除了輸入URL?)。 – Srikumar

5

您可以嘗試在Mac上使用Safari 6上的Web Inspector進行調試。

  1. 在Mobile Safari settings/advanced中啓用「Webkit Inspector」。
  2. 使用USB電纜將設備連接到運行Safari 6的Mac。
  3. 裝入頁/遊戲
  4. 進入菜單開發 - > [設備名] - > [PAGEURL]

它沒有現成的工作對我來說,但試了幾次就可以有助於縮小問題的範圍。

顯然也有音頻只能由用戶操作觸發的事情。我不確定這是真的嗎?因爲iPhone4上的iOS6上的某些代碼不能在iPad(也是iOS6)上播放任何聲音。

更新:iPhone4 + iOS6上的網絡音頻取得了一些成功。發現在iOS6上創建一個新的音頻上下文後,「currentTime」一段時間會一直保持爲0。爲了讓它移動,首先需要執行一個虛擬API調用(如createGainNode()並放棄結果)。只有當currentTime開始運行時纔會播放聲音,但在currentTime處準確調度聲音似乎不起作用。他們需要一點點未來(例如:10毫秒)。您可以使用以下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)來代替。

請不要問我爲什麼你必須這麼做。這就是目前的情況 - 即瘋狂。

+0

感謝您的回答,非常有趣。不幸的是,它似乎並不適用於我的第一個例子。考慮這個從頭開始的演示,它適用於iOS 6的iPad 2:http://www.scirra.com/labs/webaudio-ios6-working/ - 它只是使用noteOn(0)並且不使用您的「在啓動時創建增益節點「的解決方法。在這裏工作時一定還有其他的東西...... – AshleysBrain

+0

createGainNode()技巧似乎對我很有用 - 只要確保它在觸摸事件的回調中被調用,並且對於需要觸摸觸發的聲音的限制似乎消失。 – TomW

+0

這是我正在尋找的答案 - 偉大的解決方案! – Clafou

3

所以,我想我已經想通了。

這是蘋果在允許播放聲音之前需要用戶操作的問題。事實證明,至少對我而言,除了用戶要求的時候,你不應該創建音頻上下文。在頁面加載時創建上下文是不夠的,然後使用createGainNode或類似的用戶操作。

在你的情況下,當用戶點擊「觸摸開始」按鈕時,我會創建上下文。

+0

我不認爲這是 - 這個例子在啓動時創建音頻上下文,它工作正常:http://www.scirra.com/labs/webaudio-ios6-working/我也嘗試在定時器上播放聲音(如在,而不是在用戶輸入事件),它的工作。 – AshleysBrain

+0

是的,看起來好像工作正常。不過,原始遊戲看起來目前有一些錯誤。在iPad上輸入時出現這種情況: 「sbios6b:9 - 視口參數鍵」target-densitydpi「未被識別並被忽略。」 「c2runtime.js:5534 - SYNTAX_ERR:DOM例外12:指定了無效或非法的字符串。」 –

+0

對不起'格式化@AshleysBrain –

1

回答原創問題,我可以在iPhone 4S/iOS 6和MacOSX上確認一些文件格式的問題。如果一個MP3文件對於Safari來說「不好」,那麼解碼會變得很糟糕,並且調用AudioContext.createBuffer(array,bool)會導致錯誤。

奇怪的是關於錯誤:「SYNTAX_ERR,DOM異常12」,正如其他人指出的那樣。這使我認爲這是一個錯誤....

同樣的行爲也在MacOS上,與Safari 6.0(7536.25)。

0

這不是一個實際的答案,只是一個方向來看,如果事情仍然無法正常工作。 iOS6有一些設備上的音頻問題(特別是在特定時期製造的64gb 4s,儘管我已經看到其他人,所以它可能實際上並不涉及硬件),並會神祕地停止播放某些種類的聲音(不是鈴聲或聲音,出於某種原因,但許多其他聲音),它的音量滑塊將消失。我發現它非常難以調試,因爲它通常會(只是在沒有連接電源線的情況下才會發生)(並不總是,有時可以捕捉它)。

在控制檯中查找來自VirtualAudio_Device和各種編解碼器的ASSERTION FAILURE消息。這可能與您的特定問題沒有任何關係,但是再次,聲音設備的一個區域中的錯誤可能與另一個區域有關。至少,這是一個調查區域,如果沒有別的幫助。

0

API似乎在iOS 6.1上破壞了,或者至少有一個突破性的變化,這意味着目前沒有網站使用它。

1

我已經在iOS上遇到的音頻限制與HTML5音頻和解決問題的工作:

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 
4

我設法找出一個簡單的解決方案,我相信一定是記錄在其他地方 - 但有時我們不得不花費時間搞清楚這些東西佔爲己有......

如此看來很多教程(如這一項上html5rocks)指導你做以下步驟:

  • 創建的window.AudioContext一個實例,如果不存在(它不會在iOS上)然後創建window.webkitAudioContext

  • 創建一個XMLHttpRequest加載您的聲音文件

  • load事件運行context.decodeAudioData(....),然後createBufferSource(),用解碼後的數據填充它,最後source.start(0)播放聲音。

正如其他人所指出的,你必須創建AudioContext(順帶你必須存儲和使用的頁面的壽命)作爲用戶交互的結果(點擊或touchstart)。

但是: 對於iOS,以「解鎖」它的音頻功能,您MUST有音頻數據可以在創建AudioContext。如果您異步加載數據,則無法播放。僅在click事件內創建AudioContext是不夠的。

這裏有一個可靠的iOS播放兩種解決方案:

  • 1)必須加載至少一個聲音文件之前,你甚至初始化AudioContext,然後立即運行中的所有聲音文件上述步驟單一用戶交互(例如點擊)。

  • 或2)在內存中動態創建聲音並播放它。

這是我怎麼做的第二個選項:

記住 - 必須在click/touch事件爲iOS:

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年後我很驚訝,似乎沒有人指出或發現它: -/

+0

也有效。真棒! –

1

更新爲2015年解決方案: 嘿所有,如果你在這裏與ios6 +工作的網絡音頻問題我已經找到這些鏈接作爲幫助。

- 這是一個很好的文章與代碼解決方案:http://matt-harrison.com/perfect-web-audio-on-ios-devices-with-the-web-audio-api/

,尤其是圓形是對API的更新上述3溶液文章寫後https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext

-below是我更新解決了第一篇文章,使用第二篇文章中的更改。我遇到的問題是iOS 7 safari拋出了一個奇怪的不足夠的參數錯誤。這個固定:

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

更新:iOS版仍然需要用戶輸入播放聲音(No sound on iOS 6 Web Audio API

我以前套牢的iOS網絡上的網絡音頻。更糟的是,它需要在Android和其他桌面平臺上工作。這篇文章是我閱讀過的那些文章之一,並且沒有立即回答。

直到我找到howler.js

這是跨平臺的網絡音頻解決方案,該解決方案:

<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> 
0

好吧,我喜歡AshleysBrain答案,它幫我解決這個問題。但我發現了一些更一般的解決方案。

在您必須從用戶事件發起播放聲音之前,現在他們強制您通過用戶輸入事件來做到這一點(聽起來很奇怪)我只是在播放聲音之前讀取了輸入字段。

所以

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

playSoundFile是你用什麼來創建緩衝區源。