Reputation: 170
Using the Web Audio API and createMediaElement method you can use a typed array to get frequency data from audio playback in an <audio>
element and it works in most browsers as long as the source URL is local (not streaming). See Codepen: http://codepen.io/soulwire/pen/Dscga
Actual Code:
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var audioElement = new Audio('http://crossorigin.me/http://87.230.103.9:80/top100station.mp3'); // example stream
audioElement.crossOrigin = 'anonymous';
audioElement.type = 'audio/mpeg';
var analyser = audioCtx.createAnalyser();
audioElement.addEventListener('canplay', function() {
var audioSrc = audioCtx.createMediaElementSource(audioElement);
// Bind our analyser to the media element source.
audioSrc.connect(analyser);
audioSrc.connect(audioCtx.destination);
});
var frequencyData = new Uint8Array(20);
var svgHeight = ($window.innerHeight / 2) - 20;
var svgWidth = $window.innerWidth - 20;
var barPadding = '2';
function createSvg(parent, height, width) {
return d3.select(parent).append('svg').attr('height', height).attr('width', width);
}
var svg = createSvg('.visualizer', svgHeight, svgWidth);
// Create our initial D3 chart.
svg.selectAll('rect')
.data(frequencyData)
.enter()
.append('rect')
.attr('x', function (d, i) {
return i * (svgWidth / frequencyData.length);
})
.attr('width', svgWidth / frequencyData.length - barPadding);
// Continuously loop and update chart with frequency data.
function renderChart() {
requestAnimationFrame(renderChart);
// Copy frequency data to frequencyData array.
analyser.getByteFrequencyData(frequencyData);
console.log(frequencyData);
// Update d3 chart with new data.
svg.selectAll('rect')
.data(frequencyData)
.attr('y', function(d) {
return svgHeight - d;
})
.attr('height', function(d) {
return d;
})
.style('opacity', function(d) {
return d / 255;
})
.attr('fill', function() {
return 'rgb(255, 255, 255)';
});
}
// Run the loop
renderChart();
Where .visualizer
is an empty <div>
I'm developing a hybrid app for a radio station using Ionic/Angular and the audio stream is through Icecast (http://dir.xiph.org/) and I've run into the following issue: local mp3s are analyzed and visualized no problem however if you use the streaming URL, analyser.getByteFrequencyData is all zeroes in iOS Safari but it plays fine.
So to recap:
I know there was a bug in earlier versions of Safari where createMediaElementSource() would fail but if that were still the case then it wouldn't work on the local file?
Any ideas?
Upvotes: 9
Views: 1984
Reputation: 1856
Still not working with Safari and iOS Chrome (which is using Apple WebKit?). Other browsers seem to be fine now. Audio plays fine, CORS is ok - yet Analyser is not working.
This fiddle (not mine) demonstrates the behavior well. Latter uri gets analysed, former not:
const url = useStream
? 'https://c2.radioboss.fm:18071/stream'
: 'https://twgljs.org/examples/sounds/DOCTOR%20VOX%20-%20Level%20Up.mp3';
Related question in Apple forum (no answers)
Upvotes: 4
Reputation: 2906
It just is not implemented according to the specs in Safari and will return an array of zeroes instead of frequencies for streams. Many people have observed this behaviour, e.g. http://isflashdeadyet.com/tests/web-audio-visualization/index-analyser.html and https://github.com/Okazari/Rythm.js/issues/7
It is supposted to work according to https://browsersupport.io/AnalyserNode.prototype.getByteFrequencyData
Here you will find that "Safari [is] seemingly reporting no signal (a value of 128) across the board when requesting byte data on an AnalyserNode": http://fourthof5.com/audio-visualisation-with-the-web-audio-api
Test the demo here to see the current status: http://fourthof5.com/assets/posts/audio-visualisation-with-the-web-audio-api/index.html
Upvotes: 2