Web Audio API
+
AngularJS
@batemanchris
The Web Audio API
Why?
- Synthesize
- Modify and mix
- Analyze
Visualized: webaudioplayground.appspot.com
Audio Trackr
removing vowels is still cool, right?
Wanted to push the Web Audio API to its limits.
Loading and Playing Audio Files
Two Options
- Audio Buffer Source
- Media Element Source
Audio Buffer Source
Load and decode files manually
var request = new XMLHttpRequest();
request.open('GET', myURL, true);
request.responseType = 'arraybuffer';
request.onload = requestLoad;
request.send();
function requestLoad() {
// yay, finally done loading
_audioContext.decodeAudioData(request.response, doneDecoding);
}
function doneDecoding(buffer) {
// yay, finally done decoding
playBuffer(buffer);
}
function playBuffer(buffer) {
var bufferSourceNode = _audioContext.createBufferSource();
bufferSourceNode.buffer = buffer;
// connect to output
buffer.connect(_audioContext.destination);
//play
buffer.start(0);
}
Media Element Source
Use HTML Audio element - get buffering and decoding for free
Thank you WebRTC
// create an HTML Audio Element, or use an existing one var audio = new Audio(myURL);
// Audio Element will let us know when it's buffered enough audio.addEventListener('canplaythrough', function(e) {
var mediaSourceNode = _audioContext.createMediaElementSource(audio);
// connect to output mediaSourceNode.connect(_audioContext.destination);
// play audio.play(); });
createMediaElementSource()
Caveats
- Not supported in Firefox yet (don't worry, they'll get there)
- iOS will only play one Audio Element at a time
-
and there's no way to detect whether a browser will play more than
one Audio Element at a time - Connections per Hostname
- Most browsers will open up to 6 simultaneous
HTTP requests per hostname (reference) -
If you try to load 7+ Audio Elements at the same time, the first 6 HTTP requests
will pause after buffering a bit, waiting to see if you're going to play them or not.
Meanwhile, your remaining files aren't loading at all.
Angular - HTML
<body ng-app="Page" ng-controller="PageController">
<div class="songs">
<span ng-repeat="song in songs">
<input type="radio" ng-model="$parent.currentSong" ng-value="song" />
<label>{{song.name+' ('+song.band+')'}}</label>
</span>
</div>
<div class="controls" ng-show="currentSong">
<h2>{{currentSong.name}}</h2>
<button ng-show="!playing" ng-click="playTracks()">Play</button>
<button ng-show="playing" ng-click="stopTracks()">Stop</button>
</div>
<div class="track-container">
<div ng-controller="TrackController" class="track" ng-class="{loading:loading==true}" ng-repeat="(key, track) in currentSong.tracks">
<input type="range" ng-model="trackVolume" />
<canvas></canvas>
<span class="spinner" ng-show="loading"></span>
</div>
</div>
</body>
Angular - Main Controller
just a part of the controller
_page.controller('PageController', function($scope) { $scope.playTracks = function(tracks) { angular.forEach(tracks, function(track, key) { track.play(); }); $scope.playing = true; };
function tick() { angular.forEach($scope.currentSong.tracks, function(track, key) { if (track.analyser) { _drawStuff(track.cCtx, track.analyser); } }); window.requestAnimationFrame(tick); } });
Angular - Track Controller
just a part of the controller
_page.controller('TrackController', function($scope, $element) {
$scope.trackVolume = 100;
$scope.canvas = $element[0].getElementsByTagName('canvas')[0];
$scope.track.play = function() {
$scope.track.audio.play();
};
$scope.track.stop = function() {
$scope.track.audio.pause();
};
$scope.$watch('trackVolume', function(value) {
value = value / 100;
if ($scope.track.gainNode) {
$scope.track.gainNode.gain.value = value;
}
});
});
Audio Visualization
var analyser = _aCtx.createAnalyser(); analyser.smoothingTimeConstant = 0.6; analyser.fftSize = 256;
// Each frame:
var byteFreqArr = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(byteFreqArr); var timeDomainArr = new Uint8Array(analyser.frequencyBinCount); analyser.getByteTimeDomainData(timeDomainArr); canvasContext.clearRect(0, 0, _cWidth, _cHeight); canvasContext.beginPath(); for (var i=0,iLen=byteFreqArr.length; i<iLen; i++) { canvasContext.fillRect(i*_freqDrawWidth, _cHeight - (byteFreqArr[i] / 256 * _cHeight), (_freqDrawWidth - 2), _cHeight); var percent = timeDomainArr[i] / 256; var offset = _cHeight - (percent * _cHeight) - 1; canvasContext.lineTo(i*_timeDrawWidth, offset); } canvasContext.stroke();
Questions?
Web Audio API+AngularJS
By Chris Bateman
Web Audio API+AngularJS
- 10,528