I'm trying to create an audio visualization for a podcast network, using the Web Audio API with createMediaElementSource() very similarly to the model explained in this tutorial. So far I've gotten it to work fine in Chrome, and you can see it here (note: click on the red box to start it).
Update: Based on discussion in the comments, it’s now become clear that the problem happens because the request gets redirected to another URL, by way of a 302 redirect.
However, Safari refuses to work, outputting no sound and producing no visualization although it shows the track playing. I believe it has to do with the CORS policy of the server I'm requesting the audio from, because I've alternatively tried using this audio source and it works great in all browsers. My suspicion is it's an issue arising due to this standard of the web audio API.
The fact that it only happens in safari makes me pray that there's some easy syntactic solution either on my end or the server host's end in their CORS policy to get this to work. I'm hoping someone can point out exactly what's going wrong in the header requests/responses that's causing this problem. Let me know if there's any more information I need to provide. I've left a simplified version of my AudioContext code below in case a problem surfaces there.
//definitions
var url='https://rss.art19.com/episodes/72a3bc7e-118a-4171-8be4-125913860ef7.mp3';
//in safari it works with the link below, but not with any art19 link such as the one above.
//https://s3-us-west-2.amazonaws.com/s.cdpn.io/858/outfoxing.mp3
var audiotag=document.querySelector('audio');
var AudioContext = window.AudioContext || window.webkitAudioContext;
var context;
var statcontext;
var analyser;
var source;
var loopf;
//on load:
context=new AudioContext();
audiotag.crossOrigin="anonymous";
audiotag.preload="none";
audiotag.src=url;
source=context.createMediaElementSource(audiotag);
analyser=context.createAnalyser();
source.connect(analyser);
analyser.connect(context.destination);
analyser.smoothingTimeConstant=0.85
analyser.fftSize = 16384;
//later, on user input(clicking on red bar):
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
function updateDisplay() {
loopf=requestAnimationFrame(updateDisplay);
analyser.getByteFrequencyData(dataArray);
draw(dataArray.slice(100,150),-100,100);
}
context.resume();
audiotag.play();
updateDisplay();
Short answer: The maintainers of the service sending the 302
response to your request should update their backend config such that it adds the Access-Control-Allow-Origin
header to 302
responses (and any other 3xx
redirect responses) — not just to 200
OK responses.
If you can’t get them to do that, then basically you only have exactly two other options:
Explanation
Here’s what happens:
Your frontend code makes a request to a https://rss.art19.com/episodes/….mp3
URL.
The https://rss.art19.com
server replies to with a 302
redirect response that has a Location: https://content.production.cdn.art19.com/…episodes/….mp3
header.
The browser receives that 302
response and checks the response headers to see if there’s an Access-Control-Allow-Origin
header. If there isn’t, the browser blocks your code from accessing the response from the https://content.production.cdn.art19.com/….mp3
redirect URL. Instead the browser will stop and throw an exception.
You can sometimes fix this problem by taking the redirect URL and using it as the request URL in your frontend code. For example, rather than using https://rss.art19.com/episodes/….mp3
in your code, use https://content.production.cdn.art19.com/…episodes/….mp3
— since the 200 OK
response from that includes the Access-Control-Allow-Origin
header).
But in many or most cases in practice, that strategy won’t work — because it’s not feasible to preemptively identify what the redirect URL will be.
Note: by design, browsers by design don’t expose redirects to frontend code. So it’s impossible from frontend code to programatically get a redirect URL and do another request with it.