Trying to learn the Audio API, but I get an Uncaught reference error for BufferLoader class. I'm on chrome and it's up to date. Shouldn't this class be working with no problems?
<html>
<head>
<script type=text/javascript>
window.onload = init;
var context;
var bufferLoader;
function init(){
context = new webkitAudioContext();
bufferLoader = new BufferLoader(
context,
[
' https://dl.dropboxusercontent.com/u/1957768/kdFFO3.wav',
' https://dl.dropboxusercontent.com/u/1957768/geniuse%20meodies.wav',
],
finishedLoading
);
bufferLoader.load();
}
function finishedLoading(bufferList){
//make two sources and play them
var source1 = context.createBufferSource();
var source2 = context.createBufferSource();
source1.buffer = bufferList[0];
source2.buffer = bufferList[1];
source1.connect(context.destination);
source2.connect(context.destination);
source1.start(0);
source2.start(0);
}
</script>
</head>
<body>
</body>
</html>
The BufferLoader "class" is a custom function created to abstract the use of the Web Audio API. It's not a built-in feature, and must be included in your page in order to be used; there is nothing special about Chrome having this. Here's an example of where it is explained: http://www.html5rocks.com/en/tutorials/webaudio/intro/#toc-abstract
To use, include this code before it is used:
function BufferLoader(context, urlList, callback) {
this.context = context;
this.urlList = urlList;
this.onload = callback;
this.bufferList = new Array();
this.loadCount = 0;
}
BufferLoader.prototype.loadBuffer = function(url, index) {
// Load buffer asynchronously
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = "arraybuffer";
var loader = this;
request.onload = function() {
// Asynchronously decode the audio file data in request.response
loader.context.decodeAudioData(
request.response,
function(buffer) {
if (!buffer) {
alert('error decoding file data: ' + url);
return;
}
loader.bufferList[index] = buffer;
if (++loader.loadCount == loader.urlList.length)
loader.onload(loader.bufferList);
},
function(error) {
console.error('decodeAudioData error', error);
}
);
}
request.onerror = function() {
alert('BufferLoader: XHR error');
}
request.send();
}
BufferLoader.prototype.load = function() {
for (var i = 0; i < this.urlList.length; ++i)
this.loadBuffer(this.urlList[i], i);
}
Related
I have written some code stream video over a websocket so a sourcebuffer which works in Chrome and Edge.
However, when I run this in Firefox, the video never plays back, just a spinning wheel animation is displayed. When I check the <video> statistics, It reads HAVE_METADATA as the ready state and NETWORK_LOADING as the network state.
The code looks follows:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<video controls></video>
<script>
var mime = 'video/mp4; codecs="avc1.4D401E,mp4a.40.2"';
var address = 'ws://localhost:54132'
/* Media Source */
var source = new MediaSource();
var video = document.querySelector('video');
video.src = URL.createObjectURL(source);
source.addEventListener('sourceopen', sourceOpen);
/* Buffer */
var buffer;
var socket;
var queue = [];
var offset = -1;
var timescale;
// When the media source opens:
function sourceOpen() {
buffer = source.addSourceBuffer(mime);
buffer.addEventListener('updateend', processQueue);
socket = new WebSocket(address);
socket.binaryType = 'arraybuffer';
socket.onmessage = onMessage;
}
// When more data is received.
function onMessage(event) {
queue.push(event.data);
processQueue();
}
// Process queue if possible.
function processQueue() {
if ((queue.length == 0) || (buffer.updating)) {
return;
}
var data = queue.shift();
if (offset === -1) {
var parsed = parseMP4(data);
if (parsed.hasOwnProperty('moov')) {
timescale = parsed.moov.mvhd.timescale;
} else if (parsed.hasOwnProperty('moof')) {
offset = 0 - (parsed.moof.traf[0].tfdt.baseMediaDecodeTime / this.timescale - 0.4);
buffer.timestampOffset = offset;
}
}
// console.log('appending ' + data.byteLength + ' bytes');
buffer.appendBuffer(data);
}
// Parse out the offset.
function parseMP4(data) {
// SNIP for brevity
}
</script>
</body>
</html>
Could not reproduce <video> element not playing at firefox 47.
Merged approaches at Mocking Websocket Message Events to create mock WebSocket events; bufferAll.html demo at Let's Make a Netflix
An Intro to Streaming Media on the Web for MediaSource usage pattern.
Included <progress> and progress event to notify user of media loading status.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<progress min="0" value="0"></progress><br><label></label><br>
<video controls></video>
<script>
// http://nickdesaulniers.github.io/netfix/demo/bufferAll.html
// http://jsfiddle.net/adamboduch/JVfkt/
// The global web socket.
var sock, sourceBuffer;
sock = new WebSocket( "ws://mock" );
sock.onerror = function(e) {
console.log("sock error", e)
}
// This is unchanging production code that doesn"t know
// we"re mocking the web socket.
sock.onmessage = function( e ) {
console.log("socket message", e.data);
sourceBuffer.appendBuffer(e.data);
};
var video = document.querySelector("video");
var progress = document.querySelector("progress");
var label = document.querySelector("label");
var assetURL = "http://nickdesaulniers.github.io/netfix/"
+ "demo/frag_bunny.mp4";
// Need to be specific for Blink regarding codecs
// ./mp4info frag_bunny.mp4 | grep Codec
var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
if ("MediaSource" in window
&& MediaSource.isTypeSupported(mimeCodec)) {
var mediaSource = new MediaSource;
//console.log(mediaSource.readyState); // closed
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener("sourceopen", sourceOpen);
} else {
console.error("Unsupported MIME type or codec: ", mimeCodec);
}
video.addEventListener("canplay", function() {
alert("video canplay")
})
function sourceOpen (_) {
//console.log(this.readyState); // open
var mediaSource = this;
sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
fetchAB(assetURL, function (buf) {
sourceBuffer.addEventListener("updateend", function (event) {
console.log("sourceBuffer updateend event;"
+ "mediaSource.readyState:"
, mediaSource.readyState);
// mediaSource.endOfStream();
// video.play();
// console.log(mediaSource.readyState); // ended
});
});
};
// mock `WebSocket` message
function fetchAB (url, cb) {
var xhr = new XMLHttpRequest;
xhr.open("get", url);
var file = url.split("/").pop();
xhr.responseType = "arraybuffer";
xhr.onload = function () {
// mock `WebSocket` message
sock.dispatchEvent( new MessageEvent( "message", {
data: xhr.response
}));
console.log("video sent to sock", sock);
cb();
};
xhr.onprogress = function(e) {
progress.max = e.total;
progress.value = e.loaded;
label.innerHTML = "loading " + file + " ...<br>"
+ e.loaded + " of "
+ e.total + " bytes loaded";
}
xhr.send();
};
</script>
</body>
</html>
plnkr http://plnkr.co/edit/RCIqDXTB2BL3lec9bhfz
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<progress min="0" value="0"></progress><br><label></label><br>
<video controls></video>
<script>
// http://nickdesaulniers.github.io/netfix/demo/bufferAll.html
// http://jsfiddle.net/adamboduch/JVfkt/
// The global web socket.
var sock, sourceBuffer;
sock = new WebSocket( "ws://mock" );
sock.onerror = function(e) {
console.log("sock error", e)
}
// This is unchanging production code that doesn"t know
// we"re mocking the web socket.
sock.onmessage = function( e ) {
console.log("socket message", e.data);
sourceBuffer.appendBuffer(e.data);
};
var video = document.querySelector("video");
var progress = document.querySelector("progress");
var label = document.querySelector("label");
var assetURL = "http://nickdesaulniers.github.io/netfix/"
+ "demo/frag_bunny.mp4";
// Need to be specific for Blink regarding codecs
// ./mp4info frag_bunny.mp4 | grep Codec
var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
if ("MediaSource" in window
&& MediaSource.isTypeSupported(mimeCodec)) {
var mediaSource = new MediaSource;
//console.log(mediaSource.readyState); // closed
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener("sourceopen", sourceOpen);
} else {
console.error("Unsupported MIME type or codec: ", mimeCodec);
}
video.addEventListener("canplay", function() {
alert("video canplay")
})
function sourceOpen (_) {
//console.log(this.readyState); // open
var mediaSource = this;
sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
fetchAB(assetURL, function (buf) {
sourceBuffer.addEventListener("updateend", function (event) {
console.log("sourceBuffer updateend event;"
+ "mediaSource.readyState:"
, mediaSource.readyState);
// mediaSource.endOfStream();
// video.play();
// console.log(mediaSource.readyState); // ended
});
});
};
// mock `WebSocket` message
function fetchAB (url, cb) {
var xhr = new XMLHttpRequest;
xhr.open("get", url);
var file = url.split("/").pop();
xhr.responseType = "arraybuffer";
xhr.onload = function () {
// mock `WebSocket` message
sock.dispatchEvent( new MessageEvent( "message", {
data: xhr.response
}));
console.log("video sent to sock", sock);
cb();
};
xhr.onprogress = function(e) {
progress.max = e.total;
progress.value = e.loaded;
label.innerHTML = "loading " + file + " ...<br>"
+ e.loaded + " of "
+ e.total + " bytes loaded";
}
xhr.send();
};
</script>
</body>
</html>
I am trying to load some data from my JSON file using AJAX. The file is called external-file.json. Here is the code, it includes other parts that haven't got to do with the data loading.The part I'm not sure of begins in the getViaAjax funtion. I can't seem to find my error.
function flip(){
if(vlib_front.style.transform){
el.children[1].style.transform = "";
el.children[0].style.transform = "";
} else {
el.children[1].style.transform = "perspective(600px) rotateY(-180deg)";
el.children[0].style.transform = "perspective(600px) rotateY(0deg)";
}
}
var vlib_front = document.getElementById('front');
var el = document.getElementById('flip3D');
el.addEventListener('click', flip);
var word = null; var explanation = null;
var i=0;
function updateDiv(id, content) {
document.getElementById(id).innerHTML = content;
document.getElementById(id).innerHTML = content;
}
updateDiv('the-h',word[i]);
updateDiv('the-p',explanation[i])
function counter (index, step){
if (word[index+step] !== undefined) {
index+=step;
i=index;
updateDiv('the-h',word[index]);
updateDiv('the-p',explanation[index]);
}
}
var decos = document.getElementById('deco');
decos.addEventListener('click', function() {
counter(i,-1);
}, false);
var incos = document.getElementById('inco');
incos.addEventListener('click', function() {
counter(i,+1);
}, false);
function getViaAjax("external-file.json", callback) { // url being the url to external File holding the json
var r = new XMLHttpRequest();
r.open("GET", "external-file.json", true);
r.onload = function() {
if(this.status < 400 && this.status > 199) {
if(typeof callback === "function")
callback(JSON.parse(this.response));
} else {
console.log("err");// server reached but gave shitty status code}
};
}
r.onerror = function(err) {console.log("error Ajax.get "+url);console.log(err);}
r.send();
}
function yourLoadingFunction(jsonData) {
word = jsonData.words;
explanation = jsonData.explanation;
updateDiv('the-h',word[i]);
updateDiv('the-p',explanation[i])
// then call whatever it is to trigger the update within the page
}
getViaAjax("external-file.json", yourLoadingFunction)
As #light said, this:
function getViaAjax("external-file.json", callback) { // url being the url to external File holding the json
var r = new XMLHttpRequest();
r.open("GET", "external-file.json", true);
Should be:
function getViaAjax(url, callback) { // url being the url to external File holding the json
var r = new XMLHttpRequest();
r.open("GET", url, true);
I built up a quick sample that I can share that might help you isolate your issue. Stand this up in a local http-server of your choice and you should see JSON.parse(xhr.response) return a javascript array containing two objects.
There are two files
data.json
index.html
data.json
[{
"id":1,
"value":"foo"
},
{
"id":2,
"value":"bar"
}]
index.html
<html>
<head>
</head>
<body onload="so.getJsonStuffs()">
<h1>so.json-with-ajax</h1>
<script type="application/javascript">
var so = (function(){
function loadData(data){
var list = document.createElement("ul");
list.id = "data-list";
data.forEach(function(element){
var item = document.createElement("li");
var content = document.createTextNode(JSON.stringify(element));
item.appendChild(content);
list.appendChild(item);
});
document.body.appendChild(list);
}
var load = function()
{
console.log("Initializing xhr");
var xhr = new XMLHttpRequest();
xhr.onload = function(e){
console.log("response has returned");
if(xhr.status > 200
&& xhr.status < 400) {
var payload = JSON.parse(xhr.response);
console.log(payload);
loadData(payload);
}
}
var uri = "data.json";
console.log("opening resource request");
xhr.open("GET", uri, true);
xhr.send();
}
return {
getJsonStuffs : load
}
})();
</script>
</body>
</html>
Running will log two Javascript objects to the Dev Tools console as well as add a ul to the DOM containing a list item for every object inside the data.json array
So at the moment two sounds are loaded in a buffer which are then connected to two sources. How can I name the two sounds in the BufferLoader as "kick" and "hihat" then play them using kick.start(0). I know it must be easy but I can't find anything through searching.
window.onload = init;
var context = new AudioContext();
var bufferLoader;
function init() {
bufferLoader = new BufferLoader(
context,
[
'kick.wav',
'hihat.wav',
],
finishedLoading
);
bufferLoader.load();
}
function finishedLoading(bufferList) {
var source1 = context.createBufferSource();
var source2 = context.createBufferSource();
source1.buffer = bufferList[0];
source2.buffer = bufferList[1];
source1.connect(context.destination);
source2.connect(context.destination);
source1.start(0);
source2.start(0);
}
You can use the abstraction I wrote for myself if you like:
function audioFileLoader(fileDirectory) {
var soundObj = {};
soundObj.fileDirectory = fileDirectory;
var getSound = new XMLHttpRequest();
getSound.open("GET", soundObj.fileDirectory, true);
getSound.responseType = "arraybuffer";
getSound.onload = function() {
audioContext.decodeAudioData(getSound.response, function(buffer) {
soundObj.soundToPlay = buffer;
});
}
getSound.send();
soundObj.play = function() {
var playSound = audioContext.createBufferSource();
playSound.buffer = soundObj.soundToPlay;
playSound.connect(audioContext.destination)
playSound.start(audioContext.currentTime)
}
return soundObj;
};
var snare = audioFileLoader("snare.mp3");
snare.play()
Hello Web Audio Developers,
I am receiving "decodeAudioData error null" in Chrome
and
"decodeAudioData error undefined" in Firebug.
Firebug also says
"The buffer passed to decodeAudioData contains an unknown content type."
Is there something wrong with this code or
is there something else that needs to be worked out with the
"Web Audio API"?
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>Together 2 </title>
</head>
<body>
<script type="text/javascript">
window.onload = init;
var context;
var bufferLoader;
function init() {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context = new AudioContext();
bufferLoader = new BufferLoader(context,
[ '../web-audio/path/chrono.mp3' ],
finishedLoading
);
bufferLoader.load();
}
function finishedLoading(bufferList) {
var source1 = context.createBufferSource();
source1.buffer = bufferList[0];
source1.connect(context.destination);
source1.start(0);
}
function BufferLoader(context, urlList, callback) {
this.context = context;
this.urlList = urlList;
this.onload = callback;
this.bufferList = new Array();
this.loadCount = 0;
}
BufferLoader.prototype.loadBuffer = function(url, index) { // Load buffer asynchronously
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = "arraybuffer";
var loader = this;
request.onload = function() {
// Asynchronously decode the audio file data in request.response
loader.context.decodeAudioData(
request.response,
function(buffer) {
if (!buffer) {
alert('error decoding file data: ' + url);
return;
}
loader.bufferList[index] = buffer;
if (++loader.loadCount == loader.urlList.length)
loader.onload(loader.bufferList);
},
function(error) {
console.error('decodeAudioData error', error);
}
);
}
request.onerror = function() {
alert('BufferLoader: XHR error');
}
request.send();
}
BufferLoader.prototype.load = function() {
for (var i = 0; i < this.urlList.length; ++i)
this.loadBuffer(this.urlList[i], i);
}
</script>
</body>
</html>
I had the same problem and it was down to the type of codec used on the wav file itself. Only PCM is supported whereas the files that were throwing me errors were MS-ADPCM. You can use QuickTime or VLC to get the codec information and convert them to PCM if necessary.
I'd imagine that it is a similar issue with your MP3.
Chrome v6, Safari v7 (works in v6)
if('AudioContext' in window) {
var myAudioContext = new AudioContext();
const PATH = 'Sounds/',
SOUNDS = ['Tock08'];
var soundBuffers = {}, myNodes = {};
function fetchSounds() {
var request = new XMLHttpRequest();
for (var i = 0, len = SOUNDS.length; i < len; i++) {
request = new XMLHttpRequest();
request._soundName = SOUNDS[i];
request.open('GET', PATH + request._soundName + '.aiff', true);
request.responseType = 'arraybuffer';
request.addEventListener('load', bufferSound, false);
request.send();
}
}
function bufferSound(event) {
var request = event.target;
var buffer = myAudioContext.createBuffer(request.response, false);
soundBuffers[request._soundName] = buffer;
}
}
function clickSound() {
if('AudioContext' in window ) { //Chrome doesn't work with this or above code
// create a new AudioBufferSourceNode
source = myAudioContext.createBufferSource();
source.buffer = soundBuffers['Tock08'];
source.loop = false;
source.connect(myNodes.volume);
myNodes.volume.gain.value = 1;
myNodes.volume.connect(myAudioContext.destination);
source.noteOn(0); // play right now (0 seconds from now)
}
}
In Safari, all is well. An array of sound buffers is created, and a call to clickSound results in a satisfying "click".
In Chrome, things are different.
The line:
var buffer = myAudioContext.createBuffer(request.response, false);
is flagged with
Uncaught SyntaxError: An invalid or illegal string was specified.
on loading.
Then if I call "clickSound" I get:
source.buffer = soundBuffers['Tock08'];
Uncaught TypeError: Value is not of type AudioBuffer.
Does anyone know why this problem occurs?
Chrome still uses an older webkitAudioContext. This is how I set up context in SoundJS, and it works everywhere:
if (window.webkitAudioContext) {
s.context = new webkitAudioContext();
} else if (window.AudioContext) {
s.context = new AudioContext();
}
Hope that helps.