{"id":1437,"date":"2024-04-03T18:56:40","date_gmt":"2024-04-03T13:26:40","guid":{"rendered":"http:\/\/codetheory.in\/?p=1437---bb7be891-ea9a-4765-8a9b-cc093cb71242"},"modified":"2024-04-03T18:56:40","modified_gmt":"2024-04-03T13:26:40","slug":"solve-your-game-audio-problems-on-ios-and-android-with-web-audio-api","status":"publish","type":"post","link":"https:\/\/codetheory.in\/solve-your-game-audio-problems-on-ios-and-android-with-web-audio-api\/","title":{"rendered":"Solve Your Game Audio Problems on iOS and Android with Web Audio API"},"content":{"rendered":"
So you made your game or some other app that has multiple sound effects produced by several audio files. It works great on your desktop, laptop, etc. browsers but then you realize that you have some major issues<\/a> with multiple sounds (or even single) on mobile and tablet platforms like iOS and android browsers. Sad!<\/p>\n <\/p>\n You try various methods<\/a> to fix it but no luck! Don’t worry, there’s a really good way to solve it with – as the title says – Web Audio API.<\/p>\n The HTML5 HTML5 Rocks<\/a> has a very solid introduction on this very topic along with some helpful FAQs<\/a>. Do read them to get acquainted with this API.<\/p>\n If you’ve read that html5rocks tutorial, then you probably already know what to do next, although it would be nice to share what I tried in my recent game that worked like a charm on iOS 6<\/strong> browsers.<\/p>\n When the game loads, we create an audio context and load all the audio data.<\/p>\n We just loaded all our audio data and stored them in an object available on a global variable called Do you notice the Take a close look at this piece of code Hopefully, you get the entire idea of using the web audio API to play multiple audio in Webkit browsers now! This really works well in iOS safari\/chrome. But remember, it is only supported in iOS 6 (with safari 6), but not iOS 5. Supporting flawless audio playback in iOS 5 is still a pain. We simply decided to give up on it.<\/p>\n At the moment, Web Audio API is only supported in webkit browsers. So we need to gracefully fallback to html5 audio elements for others like Firefox, Opera and Internet Explorer 9+. Doing this is pretty straightforward, just check for Web Audio API support – something like When you need to play the sound, check for web audio API support again and when there’s no support, call That’s pretty much it. Now you’ll be able to start playing audio flawlessly in iOS 6 mobiles and ipads atleast. Let’s hope that the audio support across all platforms gets rock solid soon!<\/p>\n","protected":false},"excerpt":{"rendered":" So you made your game or some other app that has multiple sound effects produced by several audio files. It works great on your desktop, laptop, etc. browsers but then you realize that you have some major issues with multiple sounds (or even single) on mobile and tablet platforms like iOS and android browsers. Sad!<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13,12],"tags":[105,43,15,104,45],"_links":{"self":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/1437"}],"collection":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/comments?post=1437"}],"version-history":[{"count":10,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/1437\/revisions"}],"predecessor-version":[{"id":184788,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/1437\/revisions\/184788"}],"wp:attachment":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/media?parent=1437"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/categories?post=1437"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/tags?post=1437"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}Web Audio API<\/h2>\n
audio<\/code> element has some serious limitations making it pretty much unusable in various environments, especially mobile browsers. We discussed them in one of the previous posts<\/a>. This is one of the reasons why the Web Audio API has been introduced, to work around those restrictions and do low level processing and synthesizing of single or multiple audio streams.<\/p>\n
Implementation<\/h2>\n
\r\nif (typeof webkitAudioContext !== 'undefined') {\r\n var audio_ctx = new webkitAudioContext();\r\n}\r\n \r\nfunction loadMusic(url, cb) {\r\n var req = new XMLHttpRequest();\r\n req.open('GET', url, true);\r\n \/\/ XHR2\r\n req.responseType = 'arraybuffer';\r\n\r\n req.onload = function() {\r\n audio_ctx.decodeAudioData(req.response, cb);\r\n };\r\n\r\n req.send();\r\n}\r\n<\/pre>\n
loadMusic<\/code> is just a utility function that loads the audio via XHR. Let’s look at how we invoke it.<\/p>\n
\r\nGAME.audio = {};\r\n\r\nvar audio = {\r\n enemy: 'sfx\/enemy.mp3',\r\n normal_jump: 'sfx\/normal_jump.mp3',\r\n super_jump: 'sfx\/super_jump.mp3',\r\n space_belt: 'sfx\/space_belt.mp3',\r\n game_over: 'sfx\/game_over.mp3'\r\n}\r\n\r\nvar loadAudioData = function(name, url) {\r\n\r\n \/\/ Async\r\n loadMusic(url, function(buffer) {\r\n GAME.audio[name] = buffer;\r\n });\r\n\r\n};\r\n\r\nfor (var name in audio) {\r\n var url = audio[name];\r\n loadAudioData(name, url);\r\n}\r\n<\/pre>\n
GAME<\/code> that acts as a namespace. So how do we play the sound exactly ? Let’s write a few helper functions that can help us achieve this.<\/p>\n
\r\nfunction playSound (buffer, opt, cb) {\r\n if (!opt) cb = opt;\r\n opt = opt || {};\r\n\r\n var src = audio_ctx.createBufferSource();\r\n src.buffer = buffer;\r\n\r\n gain_node = audio_ctx.createGainNode();\r\n src.connect(gain_node);\r\n \r\n gain_node.connect(audio_ctx.destination);\r\n \/\/console.log(gain_node);\r\n\r\n if (typeof opt.sound !== 'undefined')\r\n gain_node.gain.value = opt.sound;\r\n else\r\n gain_node.gain.value = 1;\r\n\r\n \/\/ Options\r\n if (opt.loop)\r\n src.loop = true;\r\n\r\n src.noteOn(0);\r\n\r\n cb(src);\r\n}\r\n\r\nfunction stopSound (src) {\r\n src.noteOff(0);\r\n}\r\n<\/pre>\n
playSound<\/code> method has a callback parameter
cb<\/code> which is called with the buffer source object ? I’ll show you why I did that in a bit (basically so that we can stop\/pause the source later). So how do we go about calling
playSound<\/code> to play our sound at different actions in the game ?<\/p>\n
\r\nfunction playGameSound(name, opt) {\r\n opt = opt || {};\r\n\r\n var cb = function(src) {\r\n GAME.audio_src[name] = src;\r\n };\r\n\r\n playSound( GAME.audio[name], opt, cb );\r\n}\r\n\r\n\/\/ This his how we call it\r\nplayGameSound('enemy', {loop: true, sound: 0.5})\r\n<\/pre>\n
GAME.audio_src[name] = src;<\/code> – we’re doing this so that we can stop the sound, whenever we want to, pretty easily. We store the source buffer object in the
audio_src<\/code> object by the
name<\/code> and later when we want to stop it, we just call
stopSound<\/code> with the same object.<\/p>\n
\r\nstopSound(GAME.audio_src[name]);\r\n<\/pre>\n
Falling Back to HTML5 Audio<\/h2>\n
typeof webkitAudioContext !== 'undefined'<\/code> should be good enough for now – and when there’s no support start creating
audio<\/code> elements and append them to the DOM.<\/p>\n
\r\nfor (var name in audio) {\r\n var url = audio[name];\r\n\r\n \/\/ Create an audio node\r\n var audio_el = document.createElement('audio');\r\n\r\n \/\/ Source nodes for mp3 and ogg\r\n var src1_el = document.createElement('source');\r\n var src2_el = document.createElement('source');\r\n\r\n audio_el.id = name;\r\n\r\n src1_el.src = url;\r\n src1_el.type = 'audio\/mp3';\r\n\r\n src2_el.src = url.replace('.mp3', '.ogg');\r\n src2_el.type = 'audio\/ogg';\r\n \r\n \/\/ Append OGG first (else firefox cries)\r\n audio_el.appendChild(src2_el);\r\n \/\/ Append MP3 second\/next\r\n audio_el.appendChild(src1_el);\r\n\r\n $$('body').appendChild(audio_el);\r\n\r\n GAME.audio[name] = audio_el;\r\n}\r\n<\/pre>\n
play()<\/code> to start and
pause()<\/code> to stop the audio files (on
GAME.audio[name]<\/code>).<\/p>\n