Web Audio API XHR Response = null - javascript

I can seem to figure out why my XHR response is null. My code appears to be correct from the examples I have read. I am currently initializing the script on DOM load with a listener, calling to load the audio file asynchronously, and calling a play function on buffer decode. Examining the response header shows null.. Does anyone have a clue as to why?
//ON DOM LOAD INITIALIZE AUDIO
window.addEventListener('load', init, false); // On Window Load Initialize
//GLOBAL VARS
var context;
var myAudioBuffer = null;
var source = null;
//INITIALIZE
function init() {
try {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context = new AudioContext();
loadSound("https://s3.amazonaws.com/PersonalWebServerContent/Projects/PalmDesert/Audio/sunshine.mp3");//Load Audio Track
}
catch(e) {
alert('Sorry, your browser does not support the Web Audio API.');
}
}
//LOAD SOUND SOURCE
function loadSound(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
myAudioBuffer = buffer;
playSound(myAudioBuffer);//Play Audio Track
});
}
request.send();
}
//PLAY SOUND BUFFER
function playSound(buffer) {
source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.start();
}

If you test this in Chrome 33.0.1736.2 dev you get the following error:
XMLHttpRequest cannot load https://s3.amazonaws.com/PersonalWebServerContent/Projects/PalmDesert/Audio/sunshine.mp3. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://fiddle.jshell.net' is therefore not allowed access.
Here is the source: http://jsfiddle.net/dragulceo/84G7v/

I was not able to access the URL on my S3 bucket due to the same-origin policy. My solution was to load the file locally. Thank you #tavi for the error response, it helped my come to the solution.

Related

Javascript audio context on Safari / IOS 15 - 14 dont work anymore

Recent, IOS/Safari upgrade seems to broke web audio api ...
Look here simple code that work before on IOS and safari but since upgrade thats dont work anymore ..by the way it work on firefox, edge, chrome.
https://www.mylooper.com/debug.html
var url = 'https://www.mylooper.com/static/btf_10_loop1.aac';
var context = new AudioContext();
var source = context.createBufferSource();
//connect it to the destination so you can hear it.
source.connect(context.destination);
/* --- load buffer --- */
var request = new XMLHttpRequest();
//open the request
request.open('GET', url, true);
//webaudio paramaters
request.responseType = 'arraybuffer';
request.onload = function() {
context.decodeAudioData(request.response, function(response) {
/* --- play the sound AFTER the buffer loaded --- */
//set the buffer to the response we just received.
source.buffer = response;
source.start(0);
source.loop = true;
}, function () { console.error('The request failed.'); } );
}
//Now that the request has been defined, actually make the request. (send it)
request.send();
</script>
Lot of library is impacted like howler js, tuna js ... but i dont know if this "bug"/"feature" may be fix one day ...
By the way, for now how do a perfect audio loop with JS now on Safari / IOS ?
Thanks a lot, this thing driving me crazy...
What do you mean by "broken"?
If it means that audio does not start playing with this warning, you should call source.start(0) after user's gesture.
The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.
For Example, call request.send() in some button's onclick function.
Or, wrap all your code in some button's onclick function.
const button = document.getElementbyId("foo");
button.oncklick = () => {
var url = 'https://www.mylooper.com/static/btf_10_loop1.aac';
var context = new AudioContext();
var source = context.createBufferSource();
//connect it to the destination so you can hear it.
source.connect(context.destination);
/* --- load buffer --- */
var request = new XMLHttpRequest();
//open the request
request.open('GET', url, true);
//webaudio paramaters
request.responseType = 'arraybuffer';
request.onload = function() {
context.decodeAudioData(request.response, function(response) {
/* --- play the sound AFTER the buffer loaded --- */
//set the buffer to the response we just received.
source.buffer = response;
source.start(0);
source.loop = true;
}, function () { console.error('The request failed.'); } );
}
//Now that the request has been defined, actually make the request. (send it)
request.send();
}
This is feature described here. https://developer.chrome.com/blog/autoplay/#webaudio
So it will not be fixed.

Web Audio API - How to set gain of output but also specify the channel to output to?

My code:
// Create an AudioContext instance for this sound
var audioContext = new (window.AudioContext || window.webkitAudioContext)();
var maxChannelCount = audioContext.destination.maxChannelCount;
var gainNode = audioContext.createGain()
audioContext.destination.channelCount = maxChannelCount;
var merger = audioContext.createChannelMerger(maxChannelCount);
merger.connect(audioContext.destination);
gainNode.gain.value = 0.1 // 10 %
gainNode.connect(audioContext.destination)
// Create a buffer for the incoming sound content
var source = audioContext.createBufferSource();
// Create the XHR which will grab the audio contents
var request = new XMLHttpRequest();
// Set the audio file src here
request.open('GET', 'phonemes/bad-bouyed/bad.mp3', true);
// Setting the responseType to arraybuffer sets up the audio decoding
request.responseType = 'arraybuffer';
request.onload = function() {
// Decode the audio once the require is complete
audioContext.decodeAudioData(request.response, function(buffer) {
source.buffer = buffer;
// Connect the audio to source (Can I also set the gain within this declaration?)
source.connect(merger, 0,10);
// Simple setting for the buffer
source.loop = false;
// Play the sound!
source.start(0);
}, function(e) {
console.log('Audio error! ', e);
});
}
// Send the request which kicks off
request.send();
The above code loads an mp3 file in and plays it using Web Audio API, this works great although I am just wondering if it is possible to set the gain of the source AND also set the output channel using my already existing code. You can see above I have already created a gain node and connected the audio context. What is the syntax for specifying both? Do I declare them in the order I wish for them to be executed?
For example
source.connect(merger, 0,10);
source.connect(gainNode);
Any help on this would be great.

Web Audio Api - Fail to set 'buffer' property

I got the following code for loading and then playing a .wavfile on a Chrome browser:
var songBuffer = null;
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
function loadSound(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
songBuffer = buffer;
});
}
request.send();
}
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
function playSound(buffer) {
var source = context.createBufferSource();
// creates a sound source
source.buffer = buffer;
// tell the source which sound to play
source.connect(context.destination);
// connect the source to the context's destination (the speakers)
source.start(0);
// play the source now
}
loading is being triggered by:
<button onclick="loadSound('audio/sound.wav')">load sound</button> and the file loads fine when I generate the event.
and playing handled by : <button onclick="playSound()">play sound</button>.
console.log tells me, however:
Uncaught TypeError: Failed to set the 'buffer' property on 'AudioBufferSourceNode': The provided value is not of type 'AudioBuffer'.
what is going on?
You are not passing anything to your playSound function, which means that the line
source.buffer = buffer;
is interpreted as
source.buffer = undefined;
Since you have your songBuffer variable in a scope that is accessible to the playSound function, you can simply remove the buffer parameter and just do
source.buffer = songBuffer;
So, to wrap it up - keep it as it is, but edit the playSound function to look like this:
function playSound() {
var source = context.createBufferSource();
// creates a sound source
source.buffer = songBuffer;
// tell the source which sound to play
source.connect(context.destination);
// connect the source to the context's destination (the speakers)
source.start(0);
// play the source now
}

Basic Web Audio API not playing a mp3 file?

I'm trying to follow a tutorial online by piecing together the examples. I feel like this should be playing the mp3 file. I'm using the Chrome browser and it's up to date. I don't get any errors on the console. I'm not sure what I need to change or add to make this work.
<script type="text/javascript">
//creating an audio context
window.addEventListener('load',init);
function init()
{
try
{
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context=new AudioContext();
}
catch(e)
{
alert("Your browser doesn't support Web Audio API");
}
loadSound();
playSound();
}
//loading sound into the created audio context
function loadSound()
{
//set the audio file's URL
var audioURL='audio files/song.mp3';
//creating a new request
var request = new XMLhttpRequest();
request.open("GET",audioURL,true);
request.responseType= 'arraybuffer';
request.onLoad funtion(){
//take the audio from http request and decode it in an audio buffer
var audioBuffer = null;
context.decodeAudioData(request.response, function(buffer){ audioBuffer= buffer;});
}
request.send();
}, onError);
//playing the audio file
function playSound(buffer) {
//creating source node
var source = audioContext.createBufferSource();
//passing in file
source.buffer = audioBuffer;
//start playing
source.start(0);
}
</script>
</head>
</html>
You are using async XMLHttpRequest (note that it should be spelled by capital "H"), so most probably playSound function is called before request.onLoad (note: missing =) completes.
Try to trace execution of your script using console.log or similar to find bugs like this and use JavaScript Console to catch syntax errors.
i got this thing fixed :) i made use of audio tag along with web audio API. here's the code :
var audio = new Audio();
audio.src = 'audio files/song.mp3';
audio.controls = true;
audio.autoplay = true;
document.body.appendChild(audio);
var context = new webkitAudioContext();
var analyser = context.createAnalyser();
window.addEventListener('load', function(e) {
// Our <audio> element will be the audio source.
var source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
}, false);
thnks for trying to help :))
Is your audioURL correct?
audio files/song.mp3 Why is there an empty space?
============Edit============
<script>
//creating an audio context
var context;
var audioBuffer;
window.addEventListener('load', init);
function init()
{
try
{
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context=new AudioContext();
}
catch(e)
{
alert("Your browser doesn't support Web Audio API");
}
loadSound();
// playSound(); // comment here
}
//loading sound into the created audio context
function loadSound()
{
// set the audio file's URL
var audioURL='AllofMe.mp3';
//creating a new request
var request = new XMLHttpRequest();
request.open("GET",audioURL,true);
request.responseType= 'arraybuffer';
request.onload = function(){
//take the audio from http request and decode it in an audio buffer
context.decodeAudioData(request.response, function(buffer){
audioBuffer = buffer;
console.log(audioBuffer);
if(audioBuffer){ // check here
playSound();
}
});
};
request.send();
}
//playing the audio file
function playSound() {
//creating source node
var source = context.createBufferSource();
//passing in file
source.buffer = audioBuffer;
//start playing
source.connect(context.destination); // added
source.start(0);
console.log('playing');
}
</script>
I was looking for the solution to play mp3 on a mobile device, and found this page, I've made provided example to work with a help from here. Providing working example below:
var context;
var saved;
try {
context = new (window.AudioContext || window.webkitAudioContext)();
}
catch (e) {
console.log("Your browser doesn't support Web Audio API");
}
if (saved) {
playSound(saved);
} else {
loadSound();
}
//loading sound into the created audio context
function loadSound() {
//set the audio file's URL
var audioURL = '/path/to/file.mp3';
//creating a new request
var request = new XMLHttpRequest();
request.open('GET', audioURL, true);
request.responseType = 'arraybuffer';
request.onload = function () {
//take the audio from http request and decode it in an audio buffer
context.decodeAudioData(request.response, function (buffer) {
// save buffer, to not load again
saved = buffer;
// play sound
playSound(buffer);
});
};
request.send();
}
//playing the audio file
function playSound(buffer) {
//creating source node
var source = context.createBufferSource();
//passing in data
source.buffer = buffer;
//giving the source which sound to play
source.connect(context.destination);
//start playing
source.start(0);
}
It looks like playing files works fine on Android device, but not iOS. For that you have to follow this guide: How to: Web Audio on iOS (but use touchend event and replace noteOn method to start).

IndexedDB - Storing and Retrieving Videos

I'm trying to make an application that stores and retrieves Video files to and from IndexedDB. However, I am having issues while retrieving in Firefox and while storing in Chrome. I'll post the code:
(function () {
// IndexedDB
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB,
IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.OIDBTransaction || window.msIDBTransaction,
dbVersion = 1.0;
// Create/open database
var request = indexedDB.open("videoFiles", dbVersion);
var db;
var createObjectStore = function (dataBase) {
// Create an objectStore
console.log("Creating objectStore")
dataBase.createObjectStore("earth");
},
getVideoFile = function () {
// Create XHR
var xhr = new XMLHttpRequest(),
blob;
xhr.open("GET", "day_the_earth_stood_still.ogv", true);
// Set the responseType to blob
xhr.responseType = "blob";
xhr.addEventListener("load", function () {
if (xhr.status === 200) {
console.log("Video retrieved");
// Blob as response
blob = xhr.response;
console.log("Blob:" + blob);
// Put the received blob into IndexedDB
putEarthInDb(blob);
}
}, false);
// Send XHR
xhr.send();
},
putEarthInDb = function (blob) {
console.log("Putting earth in IndexedDB");
// Open a transaction to the database
var transaction = db.transaction(["earth"], "readwrite");
// Put the blob into the dabase
var put = transaction.objectStore("earth").put(blob, "video");
// Retrieve the file that was just stored
transaction.objectStore("earth").get("video").onsuccess = function (event) {
var vidFile = event.target.result;
console.log("Got earth!" + vidFile);
console.log('File Type: ' + vidFile.type); /// THIS SHOWS : application/xml
// Get window.URL object
var URL = window.URL || window.webkitURL;
// Create and revoke ObjectURL
var vidURL = URL.createObjectURL(vidFile);
// Set vid src to ObjectURL
var vidEarth = document.getElementById("earth");
vidEarth.setAttribute("src", vidURL);
// Revoking ObjectURL
URL.revokeObjectURL(vidURL);
};
};
request.onerror = function (event) {
console.log("Error creating/accessing IndexedDB database");
};
request.onsuccess = function (event) {
console.log("Success creating/accessing IndexedDB database");
db = request.result;
db.onerror = function (event) {
console.log("Error creating/accessing IndexedDB database");
};
// Interim solution for Google Chrome to create an objectStore. Will be deprecated
if (db.setVersion) {
if (db.version != dbVersion) {
var setVersion = db.setVersion(dbVersion);
setVersion.onsuccess = function () {
createObjectStore(db);
getVideoFile();
};
}
else {
getVideoFile();
}
}
else {
getVideoFile();
}
}
// For future use. Currently only in latest Firefox versions
request.onupgradeneeded = function (event) {
createObjectStore(event.target.result);
};
})();
Problem 1(Firefox): In Firefox, the line console.log('File Type: ' + vidFile.type); above shows "application/xml" while GETTING a video file (mp4, ogv, webm) and so the Video tag says "video format or mime type is not supported".
However when I GET an image file like png it shows "image/png" and works well if the src of an img tag is set.
Problem 2(Chrome): In Chrome, both image and video are not even getting stored into the IndexedDB. At the following line:
var put = transaction.objectStore("earth").put(blob, "video");
Uncaught Error: DataCloneError: DOM IDBDatabase Exception 25 is thrown.
I am new to IndexedDB and have no clue on how to solve this. All I need to do is store video files into indexedDB, retrieve it and show in Video tag.
The HTML is shown below:
(mp4):
<div class="myVidDiv">
<video id="earth" type="video/mp4" codecs="avc1.42e01e, mp4a.40.2" controls> </video>
</div>
(ogv):
<div class="myVidDiv">
<video id="earth" type="video/ogg" codecs="theora, vorbis" controls></video>
</div>
Also tried without "codecs" attribute. Nothing works. I've been stuck with this for dayss together... Couldn't find any working example via google as well. Someone kindly help me with this.
Ok, I'll try to sum up what it came out from the comments.
1. Firefox
It seems that, originally, the Blob object you get from the AJAX request has content type application/xml, because that's what you get in response from the server. It can be a problem of misconfiguration.
If you have access to the HTTP server's configuration, it may be solved quite easily. If it's Apache, you can simply add this line:
AddType video/ogg .ogv
Save, restart Apache and you should be ok. If you can't change the server's configuration, you'll have to change the Blob's content type in order to match the desired one:
blob = xhr.response.slice(0, xhr.response.size, "video/ogg");
Note that this could be memory expensive because you're making a copy of a (probably) large file, but xhr.response should be sent to garbage after a couple of steps.
2. Chrome
It seems that Chrome still doesn't support Blob and File storing.
It also seems that they've already fixed the problem, but haven't deployed the fix yet. I wonder what they're waiting for :[
UPDATE: as of July 1st, 2014, Chrome dev supports storing blobs into IndexedDB. It's expected to land soon on the stable channel.

Categories