How to play a sound continuously without break? - javascript

I am trying to play a vehicle driven sound in a browser based game (continuously without break).
My .wav file length is 1 second and has same frequency from beginning to end. But sound takes a little break before next iteration.
Here is code:
function playSound()
{
//alert("");
myAudio = new Audio('http://ithmbwp.com/feedback/SoundsTest/sounds/tank_driven.wav');
if (typeof myAudio.loop == 'boolean')
{
myAudio.loop = true;
}
else
{
myAudio.addEventListener('ended', function() {
this.currentTime = 0;
this.play();
}, false);
}
myAudio.volume = 0.3;
myAudio.play();
}
Can anyone help me to play the sound continuously?
Edition
You can visit my page here to observe the problem.
window.onload = function() {
playSound();
};
function playSound()
{
//alert("");
myAudio = new Audio('http://ithmbwp.com/feedback/SoundsTest/sounds/tank_driven.wav');
if (typeof myAudio.loop == 'boolean')
{
myAudio.loop = true;
}
else
{
myAudio.addEventListener('ended', function() {
this.currentTime = 0;
this.play();
}, false);
}
myAudio.volume = 0.3;
myAudio.play();
}
<h3 style="font-family:verdana;">Please listen the sound break.</h3>
<h3 style="font-family:verdana;">It should be continuous.</h3>

Use the AudioContext API and its bufferSourceNode interface, to have seamlessly looped sounds.
Note that you'll also need your audio to be correctly edited to avoid crackles and sound clips, but yours seems good.
const aCtx = new AudioContext();
let source = aCtx.createBufferSource();
let buf;
fetch('https://dl.dropboxusercontent.com/s/knpo4d2yooe2u4h/tank_driven.wav') // can be XHR as well
.then(resp => resp.arrayBuffer())
.then(buf => aCtx.decodeAudioData(buf)) // can be callback as well
.then(decoded => {
source.buffer = buf = decoded;
source.loop = true;
source.connect(aCtx.destination);
check.disabled = false;
});
check.onchange = e => {
if (check.checked) {
source.start(0); // start our bufferSource
} else {
source.stop(0); // this destroys the buffer source
source = aCtx.createBufferSource(); // so we need to create a new one
source.buffer = buf;
source.loop = true;
source.connect(aCtx.destination);
}
};
<label>play audioBuffer</label>
<input type="checkbox" id="check" disabled><br><br>
Just to compare <audio src="https://dl.dropboxusercontent.com/s/knpo4d2yooe2u4h/tank_driven.wav" loop controls>
Or with your snippet :
window.onload = function() {
playSound();
};
function playSound() {
if (AudioContext) {
out.textContent = "yeah now it's continuous !!!";
playAsAudioBuffer();
} else {
out.textContent = "you should consider updating your browser...";
playNormally();
}
}
function playAsAudioBuffer() {
var aCtx = new AudioContext();
// here is the real audioBuffer to sound part
function ondecoded(buf) {
var source = aCtx.createBufferSource();
source.buffer = buf;
source.loop = true;
var gainNode = aCtx.createGain();
gainNode.gain.value = .3; // here you set the volume
source.connect(gainNode);
gainNode.connect(aCtx.destination);
source.start(0);
}
var xhr = new XMLHttpRequest();
xhr.onload = function() {
aCtx.decodeAudioData(this.response, ondecoded);
};
xhr.onerror = playNormally;
xhr.responseType = 'arraybuffer';
xhr.open('get', 'https://dl.dropboxusercontent.com/s/knpo4d2yooe2u4h/tank_driven.wav');
xhr.send();
}
// An ugly workaround in case of old browsers
function playNormally() {
var myAudios = [new Audio('https://dl.dropboxusercontent.com/s/knpo4d2yooe2u4h/tank_driven.wav')];
myAudios.push(new Audio(myAudios[0].src));
myAudios.forEach(function(a){
a.addEventListener('timeupdate', checkTime);
a.volume = 0.3;
});
function checkTime(){
if(this.currentTime > this.duration - 0.4){
startNext(this);
}
}
var current = 0;
function startNext(el){
current = (current + 1) % 2;
myAudios[current].play();
el.currentTime = 0;
el.pause();
}
myAudios[0].play();
}
<h3 id="out"></h3>

Bee Cool, just use few lines code
window.onload = function() {
playSound();
};
function playSound()
{
var myAudio = new Audio('http://ithmbwp.com/feedback/SoundsTest/sounds/tank_driven.wav');
myAudio.volume = 0.3 ;
var tank_driven_sound = setInterval(function()
{
myAudio.currentTime = 0;
myAudio.play();
}, 800);
}
<h3 style="font-family:verdana;">Please listen, it's continuous.</h3>

Related

where to add my websocket code in javascript

I am very new to javaScript, I know some basics but have not yet completely understood the complete logics behind it (so far I have only worked with Python and a little bit of VBA)
For uni I have to build a browser interface to record audio and transfer it to a server where a Speech to text application runs. I found some opensource code here (https://github.com/mdn/dom-examples/blob/main/media/web-dictaphone/scripts/app.js) which I wanted to use, but is missing the websocket part. Now I don't know, where exactly to insert that. So far I have this:
code of the Webdictaphone:
// set up basic variables for app
const record = document.querySelector('.record');
const stop = document.querySelector('.stop');
const soundClips = document.querySelector('.sound-clips');
const canvas = document.querySelector('.visualizer');
const mainSection = document.querySelector('.main-controls');
// disable stop button while not recording
stop.disabled = true;
// visualiser setup - create web audio api context and canvas
let audioCtx;
const canvasCtx = canvas.getContext("2d");
//main block for doing the audio recording
if (navigator.mediaDevices.getUserMedia) {
console.log('getUserMedia supported.');
const constraints = { audio: true };
let chunks = [];
let onSuccess = function(stream) {
const mediaRecorder = new MediaRecorder(stream);
visualize(stream);
record.onclick = function() {
mediaRecorder.start();
console.log(mediaRecorder.state);
console.log("recorder started");
record.style.background = "red";
stop.disabled = false;
record.disabled = true;
}
stop.onclick = function() {
mediaRecorder.stop();
console.log(mediaRecorder.state);
console.log("recorder stopped");
record.style.background = "";
record.style.color = "";
// mediaRecorder.requestData();
stop.disabled = true;
record.disabled = false;
}
mediaRecorder.onstop = function(e) {
console.log("data available after MediaRecorder.stop() called.");
const clipName = prompt('Enter a name for your sound clip?','My unnamed clip');
const clipContainer = document.createElement('article');
const clipLabel = document.createElement('p');
const audio = document.createElement('audio');
const deleteButton = document.createElement('button');
clipContainer.classList.add('clip');
audio.setAttribute('controls', '');
deleteButton.textContent = 'Delete';
deleteButton.className = 'delete';
if(clipName === null) {
clipLabel.textContent = 'My unnamed clip';
} else {
clipLabel.textContent = clipName;
}
clipContainer.appendChild(audio);
clipContainer.appendChild(clipLabel);
clipContainer.appendChild(deleteButton);
soundClips.appendChild(clipContainer);
audio.controls = true;
const blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
chunks = [];
const audioURL = window.URL.createObjectURL(blob);
audio.src = audioURL;
console.log("recorder stopped");
deleteButton.onclick = function(e) {
e.target.closest(".clip").remove();
}
clipLabel.onclick = function() {
const existingName = clipLabel.textContent;
const newClipName = prompt('Enter a new name for your sound clip?');
if(newClipName === null) {
clipLabel.textContent = existingName;
} else {
clipLabel.textContent = newClipName;
}
}
}
mediaRecorder.ondataavailable = function(e) {
chunks.push(e.data);
}
}
let onError = function(err) {
console.log('The following error occured: ' + err);
}
navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
} else {
console.log('getUserMedia not supported on your browser!');
}
websocket part (client side):
window.addEventListener("DOMContentLoaded", () => {
// Open the WebSocket connection and register event handlers.
console.log('DOMContentLoaded done');
const ws = new WebSocket("ws://localhost:8001/"); // temp moved to mediarecorder.onstop
dataToBeSent = function (data) {
ws.send(data);
};
console.log('ws is defined');
})
Right now I just stacked both of the parts on top of each other, but this doesn't work, since, as I found out, you only can define and use variables (such as ws) within a block. This leads to an error that says that ws i not defined when I call the sending function within the if-statement.
I already tried to look for tutorials for hours but none that I found included this topic. I also tried moving the web socket part into the if statement, but that also did - unsurprisingly work, at least not in the way that I tried.
I feel like my problem lays in understanding how to define the websocket so I can call it within the if statement, or figure out a way to somehow get the audio somewhere where ws is considered to be defined. Unfortunately I just don't get behind it and already invested days which has become really frustrating.
I appreciate any help. If you have any ideas what I could change or move in the code or maybe just know any tutorial that could help, I'd be really grateful.
Thanks in advance!
You don't need that window.addEventListener("DOMContentLoaded", () => { part
const ws = new WebSocket("ws://localhost:8001/"); // temp moved to mediarecorder.onstop
dataToBeSent = function (data) {
ws.send(data);
};
const record = document.querySelector(".record");
const stop = document.querySelector(".stop");
const soundClips = document.querySelector(".sound-clips");
const canvas = document.querySelector(".visualizer");
const mainSection = document.querySelector(".main-controls");
// disable stop button while not recording
stop.disabled = true;
// visualiser setup - create web audio api context and canvas
let audioCtx;
const canvasCtx = canvas.getContext("2d");
//main block for doing the audio recording
if (navigator.mediaDevices.getUserMedia) {
console.log("getUserMedia supported.");
const constraints = { audio: true };
let chunks = [];
let onSuccess = function (stream) {
const mediaRecorder = new MediaRecorder(stream);
visualize(stream);
record.onclick = function () {
mediaRecorder.start();
console.log(mediaRecorder.state);
console.log("recorder started");
record.style.background = "red";
stop.disabled = false;
record.disabled = true;
};
stop.onclick = function () {
mediaRecorder.stop();
console.log(mediaRecorder.state);
console.log("recorder stopped");
record.style.background = "";
record.style.color = "";
// mediaRecorder.requestData();
stop.disabled = true;
record.disabled = false;
};
mediaRecorder.onstop = function (e) {
console.log("data available after MediaRecorder.stop() called.");
const clipName = prompt(
"Enter a name for your sound clip?",
"My unnamed clip"
);
const clipContainer = document.createElement("article");
const clipLabel = document.createElement("p");
const audio = document.createElement("audio");
const deleteButton = document.createElement("button");
clipContainer.classList.add("clip");
audio.setAttribute("controls", "");
deleteButton.textContent = "Delete";
deleteButton.className = "delete";
if (clipName === null) {
clipLabel.textContent = "My unnamed clip";
} else {
clipLabel.textContent = clipName;
}
clipContainer.appendChild(audio);
clipContainer.appendChild(clipLabel);
clipContainer.appendChild(deleteButton);
soundClips.appendChild(clipContainer);
audio.controls = true;
const blob = new Blob(chunks, { type: "audio/ogg; codecs=opus" });
chunks = [];
const audioURL = window.URL.createObjectURL(blob);
audio.src = audioURL;
console.log("recorder stopped");
deleteButton.onclick = function (e) {
e.target.closest(".clip").remove();
};
clipLabel.onclick = function () {
const existingName = clipLabel.textContent;
const newClipName = prompt("Enter a new name for your sound clip?");
if (newClipName === null) {
clipLabel.textContent = existingName;
} else {
clipLabel.textContent = newClipName;
}
};
};
mediaRecorder.ondataavailable = function (e) {
chunks.push(e.data);
};
};
let onError = function (err) {
console.log("The following error occured: " + err);
};
navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
} else {
console.log("getUserMedia not supported on your browser!");
}

Mixing two audio buffers, put one on background of another by using web Audio Api

I want to mix two audio sources by put one song as background of another into single source.
for example, i have input :
<input id="files" type="file" name="files[]" multiple onchange="handleFilesSelect(event)"/>
And script to decode this files:
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new window.AudioContext();
var sources = [];
var files = [];
var mixed = {};
function handleFilesSelect(event){
if(event.target.files.length <= 1)
return false;
files = event.target.files;
readFiles(mixAudioSources);
}
function readFiles(index, callback){
var freader = new FileReader();
var i = index ? index : 0;
freader.onload = function (e) {
context.decodeAudioData(e.target.result, function (buf) {
sources[i] = context.createBufferSource();
sources[i].connect(context.destination);
sources[i].buffer = buf;
if(files.length > i+1){
readFiles(i + 1, callback);
} else {
if(callback){
callback();
}
}
});
};
freader.readAsArrayBuffer(files[i]);
}
function mixAudioSources(){
//So on our scenario we have here two decoded audio sources in "sources" array.
//How we can mix that "sources" into "mixed" variable by putting "sources[0]" as background of "sources[1]"
}
So how i can mix this sources into one source? For example i have two files, how i can put one source as background of another and put this mix into single source?
Another scenario: if i read input stream from microphone for example and i want to put this input on background song (some kind of karaoke) it is possible to do this work on client with html5 support? What about performance? Maybe better way to mix this audio sources on server side?
If it possible, so what the possible implementation of mixAudioSources function?
Thanks.
Two approach originally posted at Is it possible to mix multiple audio files on top of each other preferably with javascript, adjusted to process File objects at change event of <input type="file"> element.
The first approach utilizes OfflineAudioContext(), AudioContext.createBufferSource(), AudioContext.createMediaStreamDestination(), Promise constructor, Promise.all(), MediaRecorder() to mix audio tracks, then offer mixed audio file for download.
var div = document.querySelector("div");
function handleFilesSelect(input) {
div.innerHTML = "loading audio tracks.. please wait";
var files = Array.from(input.files);
var duration = 60000;
var chunks = [];
var audio = new AudioContext();
var mixedAudio = audio.createMediaStreamDestination();
var player = new Audio();
var context;
var recorder;
var description = "";
player.controls = "controls";
function get(file) {
description += file.name.replace(/\..*|\s+/g, "");
return new Promise(function(resolve, reject) {
var reader = new FileReader;
reader.readAsArrayBuffer(file);
reader.onload = function() {
resolve(reader.result)
}
})
}
function stopMix(duration, ...media) {
setTimeout(function(media) {
media.forEach(function(node) {
node.stop()
})
}, duration, media)
}
Promise.all(files.map(get)).then(function(data) {
var len = Math.max.apply(Math, data.map(function(buffer) {
return buffer.byteLength
}));
context = new OfflineAudioContext(2, len, 44100);
return Promise.all(data.map(function(buffer) {
return audio.decodeAudioData(buffer)
.then(function(bufferSource) {
var source = context.createBufferSource();
source.buffer = bufferSource;
source.connect(context.destination);
return source.start()
})
}))
.then(function() {
return context.startRendering()
})
.then(function(renderedBuffer) {
return new Promise(function(resolve) {
var mix = audio.createBufferSource();
mix.buffer = renderedBuffer;
mix.connect(audio.destination);
mix.connect(mixedAudio);
recorder = new MediaRecorder(mixedAudio.stream);
recorder.start(0);
mix.start(0);
div.innerHTML = "playing and recording tracks..";
// stop playback and recorder in 60 seconds
stopMix(duration, mix, recorder)
recorder.ondataavailable = function(event) {
chunks.push(event.data);
};
recorder.onstop = function(event) {
var blob = new Blob(chunks, {
"type": "audio/ogg; codecs=opus"
});
console.log("recording complete");
resolve(blob)
};
})
})
.then(function(blob) {
console.log(blob);
div.innerHTML = "mixed audio tracks ready for download..";
var audioDownload = URL.createObjectURL(blob);
var a = document.createElement("a");
a.download = description + "." + blob.type.replace(/.+\/|;.+/g, "");
a.href = audioDownload;
a.innerHTML = a.download;
document.body.appendChild(a);
a.insertAdjacentHTML("afterend", "<br>");
player.src = audioDownload;
document.body.appendChild(player);
})
})
.catch(function(e) {
console.log(e)
});
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<input id="files"
type="file"
name="files[]"
accept="audio/*"
multiple
onchange="handleFilesSelect(this)" />
<div></div>
</body>
</html>
The second approach uses AudioContext.createChannelMerger(), AudioContext.createChannelSplitter()
var div = document.querySelector("div");
function handleFilesSelect(input) {
div.innerHTML = "loading audio tracks.. please wait";
var files = Array.from(input.files);
var chunks = [];
var channels = [
[0, 1],
[1, 0]
];
var audio = new AudioContext();
var player = new Audio();
var merger = audio.createChannelMerger(2);
var splitter = audio.createChannelSplitter(2);
var mixedAudio = audio.createMediaStreamDestination();
var duration = 60000;
var context;
var recorder;
var audioDownload;
var description = "";
player.controls = "controls";
function get(file) {
description += file.name.replace(/\..*|\s+/g, "");
console.log(description);
return new Promise(function(resolve, reject) {
var reader = new FileReader;
reader.readAsArrayBuffer(file);
reader.onload = function() {
resolve(reader.result)
}
})
}
function stopMix(duration, ...media) {
setTimeout(function(media) {
media.forEach(function(node) {
node.stop()
})
}, duration, media)
}
Promise.all(files.map(get)).then(function(data) {
return Promise.all(data.map(function(buffer, index) {
return audio.decodeAudioData(buffer)
.then(function(bufferSource) {
var channel = channels[index];
var source = audio.createBufferSource();
source.buffer = bufferSource;
source.connect(splitter);
splitter.connect(merger, channel[0], channel[1]);
return source
})
}))
.then(function(audionodes) {
merger.connect(mixedAudio);
merger.connect(audio.destination);
recorder = new MediaRecorder(mixedAudio.stream);
recorder.start(0);
audionodes.forEach(function(node, index) {
node.start(0)
});
div.innerHTML = "playing and recording tracks..";
stopMix(duration, ...audionodes, recorder);
recorder.ondataavailable = function(event) {
chunks.push(event.data);
};
recorder.onstop = function(event) {
var blob = new Blob(chunks, {
"type": "audio/ogg; codecs=opus"
});
audioDownload = URL.createObjectURL(blob);
var a = document.createElement("a");
a.download = description + "." + blob.type.replace(/.+\/|;.+/g, "");
a.href = audioDownload;
a.innerHTML = a.download;
player.src = audioDownload;
document.body.appendChild(a);
document.body.appendChild(player);
};
})
})
.catch(function(e) {
console.log(e)
});
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<input id="files"
type="file"
name="files[]"
accept="audio/*"
multiple onchange="handleFilesSelect(this)" />
<div></div>
</body>
</html>
I just want to complement the excellent answer of guest271314 and post here the solution based on answer of guest271314 for second scenario (the second source is microphone input). Actually is client karaoke. Script:
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new window.AudioContext();
var playbackTrack = null;
function handleFileSelect(event){
var file = event.files[0];
var freader = new FileReader();
freader.onload = function (e) {
context.decodeAudioData(e.target.result, function (buf) {
playbackTrack = context.createBufferSource();
playbackTrack.buffer = buf;
var karaokeButton = document.getElementById("karaoke_start");
karaokeButton.style.display = "inline-block";
karaokeButton.addEventListener("click", function(){
startKaraoke();
});
});
};
freader.readAsArrayBuffer(file);
}
function stopMix(duration, mediaRecorder) {
setTimeout(function(mediaRecorder) {
mediaRecorder.stop();
context.close();
}, duration, mediaRecorder)
}
function startKaraoke(){
navigator.mediaDevices.getUserMedia({audio: true,video: false})
.then(function(stream) {
var mixedAudio = context.createMediaStreamDestination();
var merger = context.createChannelMerger(2);
var splitter = context.createChannelSplitter(2);
var duration = 5000;
var chunks = [];
var channel1 = [0,1];
var channel2 = [1, 0];
var gainNode = context.createGain();
playbackTrack.connect(gainNode);
gainNode.connect(splitter);
gainNode.gain.value = 0.5; // From 0 to 1
splitter.connect(merger, channel1[0], channel1[1]);
var microphone = context.createMediaStreamSource(stream);
microphone.connect(splitter);
splitter.connect(merger, channel2[0], channel2[1]);
merger.connect(mixedAudio);
merger.connect(context.destination);
playbackTrack.start(0);
var mediaRecorder = new MediaRecorder(mixedAudio.stream);
mediaRecorder.start(1);
mediaRecorder.ondataavailable = function (event) {
chunks.push(event.data);
}
mediaRecorder.onstop = function(event) {
var player = new Audio();
player.controls = "controls";
var blob = new Blob(chunks, {
"type": "audio/mp3"
});
audioDownload = URL.createObjectURL(blob);
var a = document.createElement("a");
a.download = "karaokefile." + blob.type.replace(/.+\/|;.+/g, "");
a.href = audioDownload;
a.innerHTML = a.download;
player.src = audioDownload;
document.body.appendChild(a);
document.body.appendChild(player);
};
stopMix(duration, mediaRecorder);
})
.catch(function(error) {
console.log('error: ' + error);
});
}
And Html:
<input id="file"
type="file"
name="file"
accept="audio/*"
onchange="handleFileSelect(this)" />
<span id="karaoke_start" style="display:none;background-color:yellow;cursor:pointer;">start karaoke</span>
Here the working plnkr example: plnkr

could not append media source buffer after few buffers

I am developing a web application in which application downloads the encrypted chunks of data. And after then I have to decrypt and play the video. But I cannot let the user to wait for all decryption. Hence I am using Media Stream API. It is working. But I am getting this error after decryption of last chunk.
"Uncaught DOMException: Failed to execute 'addSourceBuffer' on 'MediaSource': This MediaSource has reached the limit of SourceBuffer objects it can handle. No additional SourceBuffer objects may be added.(…)"
<script type="text/javascript">
//////////
var no_of_files = 0;
var no_of_dlfiles = 0;
var FilesURL = [];
var files_str = 'video/vid_1.webm, video/vid_2.webm, video/vid_3.webm, video/vid_4.webm, video/vid_5.webm';
var file_counter = 0;
var mimeCodec = 'video/webm; codecs="vorbis,vp8"';
var passkey = "014bcbc0e15c4fc68b098f9b16f62bb7shahbaz.hansinfotech#gmail.com";
FilesURL = files_str.split(',');
no_of_files = FilesURL.length;
var player = document.getElementById('videoplayer');
if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
var mediaSource = new MediaSource;
//console.log(mediaSource.readyState); // closed
player.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
console.error('Unsupported MIME type or codec: ', mimeCodec);
}
//////////
function sourceOpen (_) {
console.log("start");
var mediaSource = this;
var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
sourceBuffer.mode = "sequence";
function WriteDatatoTemp()
{
//console.log(this.readyState); // open
if(file_counter<FilesURL.length)
{
console.log(file_counter);
no_of_dlfiles++;
$("#decryptionRatio").text(no_of_dlfiles+" of "+no_of_files);
$("#decryption_status").show();
getFileObject(FilesURL[file_counter], function (fileObject) {
//
var outputFile = fileObject;
var reader = new FileReader();
reader.onloadend = function(e){
var decrypted_data = JSON.parse(CryptoJS.AES.decrypt(e.target.result, passkey, {format: CryptoJSAesJson}).toString(CryptoJS.enc.Utf8));
var byteArray = Base64Binary.decodeArrayBuffer(decrypted_data);
sourceBuffer.addEventListener('updateend', function(){
file_counter++;
// console.log(file_counter);
if(player.paused)
{
player.play();
}
if(file_counter == FilesURL.length - 1)
{
mediaSource.endOfStream();
}
WriteDatatoTemp();
});
try
{
while(!sourceBuffer.updating)
{
sourceBuffer.appendBuffer(byteArray);
}
}
catch(e)
{
console.log(e);
}
};
reader.readAsText(outputFile);
//
});
}
}
WriteDatatoTemp();
}
///
var getFileBlob = function (url, cb) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.addEventListener('load', function() {
cb(xhr.response);
});
xhr.send();
};
var blobToFile = function (blob, name) {
blob.lastModifiedDate = new Date();
blob.name = name;
return blob;
};
var getFileObject = function(filePathOrUrl, cb) {
getFileBlob(filePathOrUrl, function (blob) {
cb(blobToFile(blob, 'vid.webm'));
});
};
</script>

GetElementsbyClassName requires more?

so im using one of the javascript bgm player solution that works perfectly in a small website that i created.
basically what i want is duplicating the music controller so that there are two music controls i can place on my website.
the script uses javascript's 'GetElementbyID' attribute in order to identify the div, but In order to get the result i want, i would have to make a slight change to 'GetElementsByClassName' so that there are multiple divs.
but for some reason, it didn't work out as i expected, so i did some stackoverflowing and found out the attribute returning some extra array or something like that,
again, im only a starter ar both javascript and jquery,
and i do not know what i need to add or modify in order to make it work.
can anyone help me out? or is there simpler way to make this work using jquery?
see the script below.
and the live site with the BGM player is here : http://xefrontier.com/
if(!window.AudioContext) {
if(!window.webkitAudioContext) {
alert('no audiocontext found');
}
window.AudioContext = window.webkitAudioContext;
}
var context;
var audioBuffer;
var sourceNode;
var analyser;
var javascriptNode;
var ctx = jQuery("#canvas").get()[0].getContext("2d");
var gradient = ctx.createLinearGradient(0, 0, 0, 400);
gradient.addColorStop(0, '#FFFFFF');
gradient.addColorStop(0.50, '#00D4CD');
gradient.addColorStop(0.65, '#FF00CC');
gradient.addColorStop(1, '#1A1A1A');
var currentlyPlaying = 0;
var playing = false;
var trackList = [{
title: "Back For You",
artist: "One Direction",
filename: "bg2.mp3"
}, {
title: "Does He Know",
artist: "One Direction",
filename: "bg3.mp3"
}];
var playbackButton = document.getElementBy("playback-button");
playbackButton.onclick = togglePlayState;
var playIconClass = "fontawesome-play";
var pauseIconClass = "fontawesome-pause";
var nextButton = document.getElementById("next-button");
nextButton.onclick = nextTrack;
var prevButton = document.getElementById("prev-button");
prevButton.onclick = prevTrack;
var playbackIcon = document.getElementById("playback-icon");
function togglePlayState() {
if(!playing) {
startPlayback(currentlyPlaying);
return;
}
switch(context.state) {
case 'running':
playbackIcon.className = playIconClass;
context.suspend();
break;
case 'suspended':
playbackIcon.className = pauseIconClass;
context.resume();
break;
}
}
function displayPlaying() {
var trackInfoElement = document.getElementById("trackInfo");
trackInfoElement.innerHTML = trackList[currentlyPlaying]['title'] + " - " + trackList[currentlyPlaying]['artist'];
}
function displayPlaylist() {
var playlist = document.getElementById('playlist');
playlist.innerHTML = "";
for(var i = currentlyPlaying; i < trackList.length; i++) {
var track = document.createElement('li');
if(i === currentlyPlaying) track.class = "playing";
track.innerHTML = trackList[i]['title'] + ' - ' + trackList[i]['artist'];
playlist.appendChild(track);
}
}
function startPlayback(trackID) {
displayPlaylist()
if(playing) {
stopPlayback();
}
context = new AudioContext()
setupAudioNodes();
loadSound("/xe/" + trackList[trackID]['filename']);
playing = true;
playbackIcon.className = pauseIconClass;
}
function stopPlayback() {
context.close();
playing = false;
playbackIcon.className = playIconClass;
}
function nextTrack() {
if(currentlyPlaying === trackList.length - 1) currentlyPlaying = 0;
else currentlyPlaying += 1;
startPlayback(currentlyPlaying);
}
function prevTrack() {
if(currentlyPlaying === 0) currentlyPlaying = trackList.length - 1;
else currentlyPlaying -= 1;
startPlayback(currentlyPlaying);
}
function setupAudioNodes() {
javascriptNode = context.createScriptProcessor(2048, 1, 1);
javascriptNode.connect(context.destination);
javascriptNode.onaudioprocess = function() {
var array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
ctx.clearRect(0, 0, 2000, 1000);
ctx.fillStyle = gradient;
drawSpectrum(array);
}
analyser = context.createAnalyser();
analyser.smoothingTimeConstant = 0.3;
analyser.fftSize = 512;
sourceNode = context.createBufferSource();
sourceNode.onended = function() {
nextTrack();
}
sourceNode.connect(analyser);
analyser.connect(javascriptNode);
sourceNode.connect(context.destination);
}
function loadSound(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
playSound(buffer);
}, onError);
}
request.send();
}
function playSound(buffer) {
sourceNode.buffer = buffer;
sourceNode.start(0);
}
function onError(e) {
console.log(e);
}
function drawSpectrum(array) {
for(var i = 0; i < (array.length); i++) {
var value = array[i];
ctx.fillRect(i * 8, 350 - value, 2, 50);
}
};

How to decode only part of the mp3 for use with WebAudio API?

In my web application, I have a requirement to play part of mp3 file. This is a local web app, so I don't care about downloads etc, everything is stored locally.
My use case is as follows:
determine file to play
determine start and stop of the sound
load the file [I use BufferLoader]
play
Quite simple.
Right now I just grab the mp3 file, decode it in memory for use with WebAudio API, and play it.
Unfortunately, because the mp3 files can get quite long [30minutes of audio for example] the decoded file in memory can take up to 900MB. That's a bit too much to handle.
Is there any option, where I could decode only part of the file? How could I detect where to start and how far to go?
I cannot anticipate the bitrate, it can be constant, but I would expect variable as well.
Here's an example of what I did:
http://tinyurl.com/z9vjy34
The code [I've tried to make it as compact as possible]:
var MediaPlayerAudioContext = window.AudioContext || window.webkitAudioContext;
var MediaPlayer = function () {
this.mediaPlayerAudioContext = new MediaPlayerAudioContext();
this.currentTextItem = 0;
this.playing = false;
this.active = false;
this.currentPage = null;
this.currentAudioTrack = 0;
};
MediaPlayer.prototype.setPageNumber = function (page_number) {
this.pageTotalNumber = page_number
};
MediaPlayer.prototype.generateAudioTracks = function () {
var audioTracks = [];
var currentBegin;
var currentEnd;
var currentPath;
audioTracks[0] = {
begin: 4.300,
end: 10.000,
path: "example.mp3"
};
this.currentPageAudioTracks = audioTracks;
};
MediaPlayer.prototype.show = function () {
this.mediaPlayerAudioContext = new MediaPlayerAudioContext();
};
MediaPlayer.prototype.hide = function () {
if (this.playing) {
this.stop();
}
this.mediaPlayerAudioContext = null;
this.active = false;
};
MediaPlayer.prototype.play = function () {
this.stopped = false;
console.trace();
this.playMediaPlayer();
};
MediaPlayer.prototype.playbackStarted = function() {
this.playing = true;
};
MediaPlayer.prototype.playMediaPlayer = function () {
var instance = this;
var audioTrack = this.currentPageAudioTracks[this.currentAudioTrack];
var newBufferPath = audioTrack.path;
if (this.mediaPlayerBufferPath && this.mediaPlayerBufferPath === newBufferPath) {
this.currentBufferSource = this.mediaPlayerAudioContext.createBufferSource();
this.currentBufferSource.buffer = this.mediaPlayerBuffer;
this.currentBufferSource.connect(this.mediaPlayerAudioContext.destination);
this.currentBufferSource.onended = function () {
instance.currentBufferSource.disconnect(0);
instance.audioTrackFinishedPlaying()
};
this.playing = true;
this.currentBufferSource.start(0, audioTrack.begin, audioTrack.end - audioTrack.begin);
this.currentAudioStartTimeInAudioContext = this.mediaPlayerAudioContext.currentTime;
this.currentAudioStartTimeOffset = audioTrack.begin;
this.currentTrackStartTime = this.mediaPlayerAudioContext.currentTime - (this.currentTrackResumeOffset || 0);
this.currentTrackResumeOffset = null;
}
else {
function finishedLoading(bufferList) {
instance.mediaPlayerBuffer = bufferList[0];
instance.playMediaPlayer();
}
if (this.currentBufferSource){
this.currentBufferSource.disconnect(0);
this.currentBufferSource.stop(0);
this.currentBufferSource = null;
}
this.mediaPlayerBuffer = null;
this.mediaPlayerBufferPath = newBufferPath;
this.bufferLoader = new BufferLoader(this.mediaPlayerAudioContext, [this.mediaPlayerBufferPath], finishedLoading);
this.bufferLoader.load();
}
};
MediaPlayer.prototype.stop = function () {
this.stopped = true;
if (this.currentBufferSource) {
this.currentBufferSource.onended = null;
this.currentBufferSource.disconnect(0);
this.currentBufferSource.stop(0);
this.currentBufferSource = null;
}
this.bufferLoader = null;
this.mediaPlayerBuffer = null;
this.mediaPlayerBufferPath = null;
this.currentTrackStartTime = null;
this.currentTrackResumeOffset = null;
this.currentAudioTrack = 0;
if (this.currentTextTimeout) {
clearTimeout(this.currentTextTimeout);
this.textHighlightFinished();
this.currentTextTimeout = null;
this.currentTextItem = null;
}
this.playing = false;
};
MediaPlayer.prototype.getNumberOfPages = function () {
return this.pageTotalNumber;
};
MediaPlayer.prototype.playbackFinished = function () {
this.currentAudioTrack = 0;
this.playing = false;
};
MediaPlayer.prototype.audioTrackFinishedPlaying = function () {
this.currentAudioTrack++;
if (this.currentAudioTrack >= this.currentPageAudioTracks.length) {
this.playbackFinished();
} else {
this.playMediaPlayer();
}
};
//
//
// Buffered Loader
//
// Class used to get the sound files
//
function BufferLoader(context, urlList, callback) {
this.context = context;
this.urlList = urlList;
this.onload = callback;
this.bufferList = [];
this.loadCount = 0;
}
// this allows us to handle media files with embedded artwork/id3 tags
function syncStream(node) { // should be done by api itself. and hopefully will.
var buf8 = new Uint8Array(node.buf);
buf8.indexOf = Array.prototype.indexOf;
var i = node.sync, b = buf8;
while (1) {
node.retry++;
i = b.indexOf(0xFF, i);
if (i == -1 || (b[i + 1] & 0xE0 == 0xE0 )) break;
i++;
}
if (i != -1) {
var tmp = node.buf.slice(i); //carefull there it returns copy
delete(node.buf);
node.buf = null;
node.buf = tmp;
node.sync = i;
return true;
}
return false;
}
BufferLoader.prototype.loadBuffer = function (url, index) {
// Load buffer asynchronously
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = "arraybuffer";
var loader = this;
function decode(sound) {
loader.context.decodeAudioData(
sound.buf,
function (buffer) {
if (!buffer) {
alert('error decoding file data');
return
}
loader.bufferList[index] = buffer;
if (++loader.loadCount == loader.urlList.length)
loader.onload(loader.bufferList);
},
function (error) {
if (syncStream(sound)) {
decode(sound);
} else {
console.error('decodeAudioData error', error);
}
}
);
}
request.onload = function () {
// Asynchronously decode the audio file data in request.response
var sound = {};
sound.buf = request.response;
sound.sync = 0;
sound.retry = 0;
decode(sound);
};
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);
};
There is no way of streaming with decodeAudioData(), you need to use MediaElement with createMediaStreamSource and run your stuff then. decodeAudioData() cannot stream on a part.#zre00ne And mp3 will be decoded big!!! Verybig!!!

Categories

Resources