I am trying to make an audio visualizer in html/javascript using the Web Audio API and I want it to work on mobile phones too.
I have my audio as a local file. Now I found two ways to import it, either using new Audio(src) or asynchronously using an XMLHttpRequest. The first method gets me a HTMLMediaElement that is way easier to work with and faster to load. As I want the visualizer to be controlled by the user, I need to be able to quickly change the audio source or the current seek position. The second one gives a buffer that needs to be decoded and is less suited for what I want.
Everything works fine on desktop, but for some reason I cannot make the analyser work on mobile using the first method. It works either without the analyser, either using XMLHttpRequest.
Here is my code setup (I'm using a local server to prevent CORS issues):
let audioURL = './audio.mp3'
let audioCtx, source, analyser
// Init the audio context after the first valid user interaction
window.addEventListener('click', initAudioContext) // Desktop support
window.addEventListener('touchend', initAudioContext) // Mobile support
function initAudioContext() {
// Create the audio context
audioCtx = new (window.AudioContext || window.webkitAudioContext)()
audioCtx.resume() // Needed for mobile/iOS support
// Create an AnalyserNode
analyser = audioCtx.createAnalyser()
// Create the source node element (this part has an issue)
connectSource()
// Remove the listeners as the audio context must be setup only once
window.removeEventListener('click', initAudioContext)
window.removeEventListener('touchend', initAudioContext)
}
Now here are 3 implementations of connectSource that gives me various results :
The first implementation is using the most basic method, and skipping entirely the analyser. This code works on both mobile and desktop and the audio plays after the first click/touch, but I don't get the audio data as there's no analyser.
function connectSourceWithoutAnalyser() {
// Create an audio HTMLMediaElement and play it
const audio = new Audio(audioURL)
audio.play()
}
The second implementation is the one I'm aiming for, I'm still using the Audio element however I create a source node to use with the analyser. This implementation works fine for Desktop, but not for mobile (no audio at all on Safari or Chrome for iOS).
function connectMediaElementSource() {
// Create an audio HTMLMediaElement
const audio = new Audio(audioURL)
// Use it to create a MediaElementAudioSourceNode
source = audioCtx.createMediaElementSource(audio)
// Connect the source to the analyser, then the analyser to the audio context's destination
source.connect(analyser)
analyser.connect(audioCtx.destination)
audio.play() // Play the audio
}
Note that even if I remove the analyser and connect this new source directly to the destination by replacing source.connect(analyser); analyser.connect(audioCtx.destination) with source.connect(audioCtx.destination), it doesn't work for mobile, so maybe the issue is more related to createMediaElementSource() than the AnalyserNode.
Finally the third implementation is using XMLHttpRequest. This one works fine for both mobile and desktop but as I stated earlier, I lose the reference to HTMLMediaElement and it's not something I want at all :
function connectBufferSource() {
// Setup an XML Request
const request = new XMLHttpRequest()
request.open('GET', audioURL, true)
request.responseType = 'arraybuffer'
request.onload = function() {
// Decode the response using the audio context
audioCtx.decodeAudioData(request.response, function(buffer) {
// Create an AudioBufferSourceNode
source = audioCtx.createBufferSource()
source.buffer = buffer // copy the buffer
// Connect the source to the analyser, then the analyser to the audio context's destination
source.connect(analyser)
analyser.connect(audioCtx.destination)
// Since there's no media element, we can't use play() and must use source.start()
source.start()
})
}
// Send the XML request
request.send()
}
Is there a way to make the second implementation using an AnalyserNode as well as an HTMLMediaElement work for both mobile and desktop ?
from WebAudioAPI AnalyserNode is not working on mobile with MediaElementAudioSourceNode
No comments:
Post a Comment