2012-09-20 12 views
37

Ich war wirklich aufgeregt zu sehen, dass iOS 6 die Web Audio API unterstützt, da wir HTML5-Spiele machen. Allerdings kann ich mit der Web Audio-API mit Beispielen, die in Desktop-Chrome gut funktionieren, kein iOS 6 für die Wiedergabe von Ton verwenden.Kein Ton auf iOS 6 Web Audio API

Hier sind ein HTML5-Spiel mit Touch-Steuerung und Wiedergabe von Audio über das Web Audio API (falls vorhanden - wenn es nicht wieder zu HTML5 Audio fallen):

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

Edit: @Srikumar vorgeschlagen einige Problemumgehungen. Ich habe sie in der folgenden Version angewendet. Es funktioniert immer noch nicht!

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

Alles spielt auf Desktop-Chrome ganz gut, aber iOS 6 emittiert kein gar Ton. Ich habe Probleme, es zu debuggen, weil ich nur Windows-Entwicklung mache, und iOS 6 ersetzt den Debug-Modus mit Remote-Web-Inspektor, der offenbar nicht für Safari für Windows verfügbar ist. Mit ein paar Warnungen habe ich festgestellt, dass es die Web Audio API richtig identifiziert, es verwendet, keine Vorbis-Unterstützung erkennt, also auf AAC-Audio zurückgreift, einen Puffer dekodiert und dann abspielt, und es werden keine Fehler geworfen, aber ich höre nichts. Und natürlich habe ich versucht, die Lautstärke auf max zu drehen :)

Es sollte nicht ein Codec-Problem sein, weil iOS 6 AAC ganz gut spielen kann - Sie können one of the .m4a's the game plays durchsuchen und es spielt gut besucht direkt von Safari.

Betrachten Sie die Web Audio API Beispiele hier auf iOS 6: http://chromium.googlecode.com/svn/trunk/samples/audio/samples.html - einige von ihnen funktionieren, andere nicht. Zum Beispiel funktioniert die Chrome Audio Visualizer, aber Javascript Drone nicht.

Es muss eine subtile Inkompatibilität zwischen Web Audio auf iOS 6 und Desktop Chrome bestehen. Was vermisse ich?

+0

Könnte mit Dateiformaten verwandt sein. Ich hatte Probleme mit einigen MP3s in Safari. –

+0

Möglicherweise, aber wie der Post sagt, konnte ich einen der .m4a-Sounds direkt aus Safari spielen. – AshleysBrain

+0

Mit welchen Problemen haben Sie speziell Probleme? –

Antwort

46

Bearbeiten (November 2015): iOS 9 nicht mehr erlaubt Audio startet in einem touchstart Ereignis, das die Lösung unten bricht. Es funktioniert jedoch in einem touchend Ereignis. Die ursprüngliche Antwort für iOS 6 bleibt unten intakt, aber für iOS 9-Unterstützung stellen Sie sicher, dass Sie touchend verwenden.

Nun, Entschuldigung, meine eigene Kopfgeldfrage zu beantworten, aber nach stundenlangem Debugging fand ich endlich die Antwort. Safari auf iOS 6 startet effektiv mit stummgeschaltetem Web Audio API. Die Stummschaltung von wird erst aufgehoben, wenn Sie versuchen, in einem Benutzereingabeereignis einen Ton wiederzugeben (erstellen Sie eine Pufferquelle, verbinden Sie sie mit dem Ziel und rufen Sie noteOn() an). Danach wird die Stummschaltung aufgehoben und die Audiowiedergabe uneingeschränkt und so wie es sollte. Dies ist ein undokumentierter Aspekt der Funktionsweise der Web Audio API auf iOS 6 (Apple's doc is here, hoffentlich werden sie bald darauf aktualisiert!)

Der Benutzer kann den Bildschirm viel berühren, sich am Spiel beteiligen. Aber es wird gedämpft bleiben. Sie haben in einem Benutzereingabeereignis wie touchstart [Bearbeiten: touchend für iOS 9+], einmal, dann alle Audio-Stummschaltung zu spielen. Danach können Sie jederzeit Audio wiedergeben (muss nicht in einem Benutzereingabeereignis sein).

Beachten Sie, dass sich dies von den Einschränkungen für HTML5-Audio unterscheidet: Normalerweise können Sie Audio nur bei einem Benutzereingabeereignis starten und nur jeweils einen Ton abspielen. das Web Audio API vollständig nach der Stummschaltung des ersten Play-in-user-Eingang, so dass Sie Töne jederzeit spielen können, und dann können Sie diese coolen Effekte polyphon, Prozess mischen etc.

Dieses bereits viele Spiele bedeuten Im Web mit der Web Audio API wird nie Audio wiedergegeben, da sie nicht zufällig ein NoteOn in einem Touch-Ereignis ausgeben. Sie müssen es anpassen, um auf das erste Benutzereingabeereignis zu warten.

Es gibt einige Möglichkeiten, dies zu umgehen: Spielen Sie Ihre Titelmusik erst ab, wenn der Benutzer den Bildschirm berührt. einen ersten "touch to enable audio" -Bildschirm haben und einen Ton abspielen und dann das Spiel beginnen, wenn sie sich berühren; usw. Hoffentlich hilft dies jedem anderen, der das gleiche Problem hat, etwas Zeit zu sparen, um es zu debuggen!

+0

Haben nicht beide vorherigen Antworten angegeben, dass eine Benutzeraktion erforderlich ist, um Audio zu erhalten? –

+0

Ich lese sie als entweder Sie können nur den Kontext dort erstellen (nicht ganz richtig) oder dass Audio nicht ausgelöst in einer Benutzeraktion wird nie spielen (auch nicht ganz richtig) – AshleysBrain

+0

_ Es wird nicht die Stummschaltung, bis Sie versuchen, einen Sound in ein Benutzereingabeereignis_. Ich habe ein Gegenbeispiel dafür - http://srikumarks.github.com/demos/tala. (Es ist eine Art "Metronom" für südindische talas.) Wenn Sie das in Safari/iOS6/iPhone laden und einfach eine Weile warten, beginnt das Metronom zu ticken, obwohl die Grafik nicht so funktioniert wie in Chrome. Keine Benutzereingabe erforderlich (außer Eingabe der URL?). – Srikumar

5

Sie können versuchen, es mit dem Web Inspector auf Safari 6 auf einem Mac zu debuggen.

  1. Aktivieren Sie "Webkit Inspector" in Mobile Safari Einstellungen/Erweitert.
  2. Schließen Sie das Gerät über ein USB-Kabel an einen Mac mit Safari 6 an.
  3. Laden Sie Ihre Seite/Spiel
  4. Zum Menü Entwickeln -> [Gerätename] -> [PAGEURL]

Es funktioniert nicht für mich aus der Box, aber mit ein paar Versuchen kann es helfen Sie, das Problem einzugrenzen.

Offenbar gibt es auch die Sache, dass Audio nur durch eine Benutzeraktion ausgelöst werden kann. Ich bin mir nicht sicher, ob das stimmt, weil ein Code, der auf iOS6 auf iPhone4 funktioniert, keinen Sound auf einem iPad (auch iOS6) spielt.

Update: Einige Erfolge mit Web Audio auf iPhone4 + iOS6. Gefunden, dass die "currentTime" für eine Weile bei 0 stecken bleibt, sobald Sie einen neuen Audio-Kontext auf iOS6 erstellen. Um es in Bewegung zu bringen, müssen Sie zuerst einen Dummy-API-Aufruf durchführen (wie createGainNode() und das Ergebnis verwerfen). Sounds werden nur abgespielt, wenn currentTime gestartet wird, aber das Scheduling von Sounds genau zu currentTime scheint nicht zu funktionieren. Sie müssen ein bisschen in die Zukunft (ex: 10ms) sein. Sie können die folgende createAudioContext Funktion verwenden, um zu warten, bis der Kontext bereit ist, Rauschen zu erzeugen. Benutzeraktion scheint auf dem iPhone nicht erforderlich zu sein, aber noch kein solcher Erfolg auf dem 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(); 
} 

Anschließend, wenn eine Note spielen, nicht nennen .noteOn(ac.currentTime), aber .noteOn(ac.currentTime + 0.01) stattdessen tun.

Bitte fragen Sie mich nicht warum müssen Sie all das tun. So ist es im Moment - also verrückt.

+0

Danke für die Antwort, sehr interessant. Leider scheint es nicht für mein erstes Beispiel zu gelten. Betrachten Sie diese Demo von Grund auf neu, die auf einem iPad 2 mit iOS 6 funktioniert: http://www.scirra.com/labs/webaudio-ios6-working/ - es verwendet nur noteOn (0) und verwendet nicht Ihre " create gain node beim Start "Umgehung. Hier muss noch etwas anderes am laufen sein ... – AshleysBrain

+0

Der Trick createGainNode() schien für mich zu funktionieren - stelle sicher, dass er in einem Callback von einem Touch-Event aufgerufen wird und die Beschränkung auf Sounds, die durch Berührung ausgelöst werden müssen, scheint verschwinden. – TomW

+0

Das ist die Antwort, nach der ich gesucht habe - großartige Lösung! – Clafou

3

Also, ich denke, ich habe es herausgefunden.

Es ist ein Problem von Apple, das eine Benutzeraktion erfordert, bevor der Ton wiedergegeben werden darf. Es stellt sich zumindest für mich heraus, dass Sie den Audio-Kontext überhaupt nicht erstellen sollten, außer wenn der Benutzer danach fragt. Es reicht nicht aus, den Kontext zu erstellen, wenn die Seite geladen wird, und dann createGainNode oder Ähnliches für eine Benutzeraktion zu verwenden.

In Ihrem Fall würde ich den Kontext erstellen, wenn der Benutzer auf die Schaltfläche "Touch to begin" klickt.

+0

Ich denke nicht, dass es das ist - dieses Beispiel erstellt den Audio-Kontext beim Start und es funktioniert OK: http://www.scirra.com/labs/webaudio-ios6-working/ Ich habe auch versucht, die Sounds selbst auf einem Timer zu spielen (wie in, nicht in einem Benutzereingabeereignis) und es hat funktioniert. – AshleysBrain

+0

Ja, sieht aus wie es funktioniert, ok. Das ursprüngliche Spiel scheint jedoch einige Fehler zu haben. Ich bekomme dies beim Betreten des iPad: "sbios6b: 9 - Viewport Argument Schlüssel" Ziel-Dichtedpi "nicht erkannt und ignoriert." "c2runtime.js: 5534 - SYNTAX_ERR: DOM Ausnahme 12: Eine ungültige oder ungültige Zeichenfolge wurde angegeben." –

+0

Sorry über die Formatierung @AshleysBrain –

1

Beantworten Sie die Frage Original Frage, ich kann einige Probleme mit Dateiformaten auf iPhone 4S/iOS 6 und MacOSX bestätigen. Wenn eine MP3-Datei für Safari "nicht gut" ist, wird die Decodierung fehlerhaft und der Aufruf von AudioContext.createBuffer (Array, Bool) gibt Ihnen einen Fehler.

Die seltsame Sache ist über den Fehler: "SYNTAX_ERR, DOM Exception 12", wie von anderen hingewiesen. Das lässt mich denken, dass es ein Fehler ist ....

Gleiches Verhalten auch auf MacOS, mit Safari 6.0 (7536.25).

0

Dies ist keine tatsächliche Antwort, nur eine Richtung zu sehen, wenn die Dinge immer noch nicht funktionieren. iOS6 hat Audio Probleme auf einige Geräte (vor allem die 64gb 4s in einer bestimmten Zeit hergestellt, obwohl ich andere gesehen habe, so dass es möglicherweise nicht Hardware-bezogene) und wird auf mysteriöse Weise aufhören zu spielen einige Arten von Sounds (keine Klingeltöne oder Stimme, aus irgendeinem Grund, aber viele andere Sounds), und es ist Volume Slider wird verschwinden. Ich fand es notorisch schwierig zu debuggen, da es normalerweise (denkt nicht immer, manchmal kann man es fangen) nur passiert, wenn es nicht mit einem Stromkabel verbunden ist.

In der Konsole für ASSERTION FAILURE Nachrichten aus dem VirtualAudio_Device und mit verschiedenen Codecs suchen. Dies kann mit Ihrem speziellen Problem nichts zu tun haben, aber andererseits kann ein Fehler in einem Bereich des Audiogeräts mit einem anderen zusammenhängen. Zumindest ist es ein Bereich zu untersuchen, ob nichts anderes hilft.

0

Die API scheint auf iOS 6.1 defekt zu sein, oder zumindest hat sie eine brechende Änderung, was bedeutet, dass derzeit keine Websites mit ihr arbeiten.

1

ich über die Audio-Einschränkungen mit HTML5 Audio auf iOS kommen und gearbeitet haben, um das Problem von:

1) Erstellen einer Audio-Element mit eine stumme Audiodatei und spielt sie zunächst mit einem Berührungsereignis (z. B. "Spiel beginnen") und hält sie dann sofort an.

2) Erstellen einer Sound-Switcher-Funktion, die Audio-Quelle umschaltet und nach kurzer Zeit das Audio-Element wiedergibt.

3) Aufruf der Sound-Switcher-Funktion bei beliebigen Ereignissen (muss kein Berührungsereignis sein).

Dies funktioniert, weil das Audioelement bei der ersten Berührung mit der Silent-Audiodatei nicht stummgeschaltet wird und nicht stumm geschaltet wird, so dass die Quelle sofort umgeschaltet werden kann.

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

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

Ich schaffte es eine einfache Lösung, um herauszufinden, was ich sicher bin, muss an anderer Stelle dokumentiert wurden - aber manchmal müssen wir Stunden damit verbringen, diese Dinge für uns selbst herauszufinden ...

So scheint es viele Tutorials (wie diese auf html5rocks) anweisen, die folgenden Schritte zu tun:

  • eine Instanz window.AudioContext erstellen und wenn das nicht vorhanden ist (was es nicht auf iOS der Fall ist), dann window.webkitAudioContext erstellen.

  • Neues XMLHttpRequest Ihre Tondatei

  • Auf dem load Ereignis laufen zu laden context.decodeAudioData(....) und dann createBufferSource(), es mit den entschlüsselten Daten füllen und schließlich source.start(0) den Ton zu spielen.

Wie andere haben darauf hingewiesen, müssen Sie die AudioContext erstellen, die als Ergebnis einer Interaktion des Benutzers (die übrigens müssen Sie für die gesamte Lebensdauer der Seite speichern und verwenden) (Klick oder Berührungsstart).

JEDOCH: für iOS zu 'entsperren' seine Audio-Funktionen, die Sie MUST Audiodaten zur Verfügung haben, wenn Sie die AudioContext erstellen. Wenn Sie die Daten asynchron laden, gibt es nichts zu spielen. Es reicht nicht aus, das AudioContext innerhalb eines click Ereignisses zu erstellen.

Hier zwei Lösungen für eine zuverlässige iOS-Wiedergabe:

  • 1) Sie müssen mindestens eine Audiodatei laden, bevor Sie sogar die AudioContext initialisieren, und dann für die Sounddatei alle oben genannten Schritte ausgeführt werden sofort in ein Einzelbenutzerinteraktion (zB Klick).

  • ODER 2) Erstellen Sie einen Sound dynamisch im Speicher und spielen Sie ihn.

Dies ist, wie ich das zweite Option hat:

ERINNERN - innerhalb click/touch Ereignis für iOS sind:

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 

Ich stelle mir dies ein häufiger Fehler ist - und Ich bin nach 3 Jahren überrascht, dass niemand darauf hingewiesen oder es entdeckt hat: -/

+0

Funktioniert auch, wenn es in eine Funktion eingefügt wird, die von einem Ereignishandler aufgerufen wird. Genial! –

1

aktualisiert für 2015 Lösung: hey alle, wenn Sie hier arbeiten an einem Web-Audio-Problem mit IOS6 + habe ich diese Links als Hilfe gefunden.

-Das ist ein guter Artikel mit Code-Lösung: http://matt-harrison.com/perfect-web-audio-on-ios-devices-with-the-web-audio-api/

-hier ist ein Update für die api, nachdem die oben^Lösung Artikel geschrieben wurde https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext

-below ist meine aktualisierte Lösung zum ersten Artikel , mit den Änderungen aus dem zweiten Artikel. Das Problem, das ich hatte, war iOS 7 Safari, die einen seltsamen Fehler nicht genug args warf. dies regelte es:

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

Update: iOS noch eine Benutzereingabe erfordert Ton (No sound on iOS 6 Web Audio API)

ich vorher mit Web Audio auf iOS Web festsaß zu spielen. Und um die Dinge noch schlimmer zu machen, muss es auf Android und anderen Desktop-Plattformen arbeiten. Dieser Beitrag ist einer dieser Beiträge, die ich gelesen habe und keine unmittelbaren Antworten gefunden habe.

Bis ich howler.js gefunden habe.

Dies ist die Lösung für plattformübergreifende Web-Audio-Lösung:

<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

Okay, ich AshleysBrain Antwort mag, es hat mir geholfen, das Problem zu lösen. Aber ich fand eine etwas allgemeinere Lösung.

Bevor Sie den Spielsound von einem Benutzerereignis auslösen mussten, erzwingen Sie jetzt, dass Sie es über ein Benutzereingabeereignis tun (klingt seltsam). Was ich getan habe, war ein Eingabefeld vor dem Abspielen des Sounds.

So

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

playSoundFile ist, was auch immer Sie die Pufferquelle erstellen verwenden.