Selecting Music Folder html - javascript

I would like to be able to select a music folder and shuffle all of the songs once on load and Have a skip button + return the song file name to the visualizer so that It can visualize each song. I'm still learning array's and for loops so I'm unsure of how to go about this. I also want to keep away from extra libraries for now because everything is already provided. Heres a code snippet of what I have so far
window.onload = function() {
var file = document.getElementById("file");
var audio = document.getElementById("audio");
file.onchange = function() {
var files = this.files;
audio.src = URL.createObjectURL(files[0]);
audio.load();
audio.play();
var context = new AudioContext();
var src = context.createMediaElementSource(audio);
var analyser = context.createAnalyser();
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
src.connect(analyser);
analyser.connect(context.destination);
analyser.fftSize = 256;
var bufferLength = analyser.frequencyBinCount;
console.log(bufferLength);
var dataArray = new Uint8Array(bufferLength);
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var barWidth = (WIDTH / bufferLength) * 1;
var barHeight;
var x = 0;
function renderFrame() {
requestAnimationFrame(renderFrame);
x = 0;
analyser.getByteFrequencyData(dataArray);
ctx.fillStyle = "#1b1b1b";
ctx.fillRect(0, 0, WIDTH, HEIGHT);
for (var i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
var r = 5;
var g = 195;
var b = 45;
ctx.fillStyle = "rgb(5,195,45)"
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 2;
}
}
audio.play();
renderFrame();
};
};
#file {
position: fixed;
top: 10px;
left: 10px;
z-index: 100;
}
#canvas {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
audio {
position: fixed;
left: 350px;
top: 10px;
width: calc(50% - 20px);
}
<div id="content">
<input type="file" id="file" accept="audio/*" />
<canvas id="canvas"></canvas>
<audio id="audio" controls></audio>
</div>

You can set webkitdirectory and allowdirs attributes at <input type="file"> element to enable directory upload.
Recursively, or using Promise repeatedly iterate directories, including directories within directories, push all files in directories to a globally defined array of File objects, see How to upload and list directories at firefox and chrome/chromium using change and drop events; where JavaScript at Answer is modified to remove drop event handler, which is specifically not a part of requirement.
Use Array.prototype.reduce() and Promise to call a function for each File object in sequence to play the media and return a fulfilled Promise at ended event of HTMLMediaElement; for example, the anonymous function at change handler at Question, modified where necessary to achieve expected requirement of a playlist created from upload of N directories or N nested directories. Note that when calling AudioContent.createMediaElementSource() more than once with the same <audio> element as parameter an exception will be thrown
Uncaught DOMException: Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode
see remove createMediaElementSource. You can define the variables globally and reference the variable using OR || to avoid the exception.
The created Blob URL is assigned to a variable and revoked at ended event of HTMLMediaElement and before being re-assigned when function is called again, if ended event is not reached.
Included <select> element for ability to select and play any one of the uploaded files.
var input = document.getElementById("file");
var audio = document.getElementById("audio");
var selectLabel = document.querySelector("label[for=select]");
var audioLabel = document.querySelector("label[for=audio]");
var select = document.querySelector("select");
var context = void 0,
src = void 0,
res = [],
url = "";
function processDirectoryUpload(event) {
var webkitResult = [];
var mozResult = [];
var files;
console.log(event);
select.innerHTML = "";
// do mozilla stuff
function mozReadDirectories(entries, path) {
console.log("dir", entries, path);
return [].reduce.call(entries, function(promise, entry) {
return promise.then(function() {
return Promise.resolve(entry.getFilesAndDirectories() || entry)
.then(function(dir) {
return dir
})
})
}, Promise.resolve())
.then(function(items) {
var dir = items.filter(function(folder) {
return folder instanceof Directory
});
var files = items.filter(function(file) {
return file instanceof File
});
if (files.length) {
// console.log("files:", files, path);
mozResult = mozResult.concat.apply(mozResult, files);
}
if (dir.length) {
// console.log(dir, dir[0] instanceof Directory);
return mozReadDirectories(dir, dir[0].path || path);
} else {
if (!dir.length) {
return Promise.resolve(mozResult).then(function(complete) {
return complete
})
}
}
})
};
function handleEntries(entry) {
let file = "webkitGetAsEntry" in entry ? entry.webkitGetAsEntry() : entry
return Promise.resolve(file);
}
function handleFile(entry) {
return new Promise(function(resolve) {
if (entry.isFile) {
entry.file(function(file) {
listFile(file, entry.fullPath).then(resolve)
})
} else if (entry.isDirectory) {
var reader = entry.createReader();
reader.readEntries(webkitReadDirectories.bind(null, entry, handleFile, resolve))
} else {
var entries = [entry];
return entries.reduce(function(promise, file) {
return promise.then(function() {
return listDirectory(file)
})
}, Promise.resolve())
.then(function() {
return Promise.all(entries.map(function(file) {
return listFile(file)
})).then(resolve)
})
}
})
function webkitReadDirectories(entry, callback, resolve, entries) {
console.log(entries);
return listDirectory(entry).then(function(currentDirectory) {
console.log(`iterating ${currentDirectory.name} directory`, entry);
return entries.reduce(function(promise, directory) {
return promise.then(function() {
return callback(directory)
});
}, Promise.resolve())
}).then(resolve);
}
}
function listDirectory(entry) {
console.log(entry);
return Promise.resolve(entry);
}
function listFile(file, path) {
path = path || file.webkitRelativePath || "/" + file.name;
console.log(`reading ${file.name}, size: ${file.size}, path:${path}`);
webkitResult.push(file);
return Promise.resolve(webkitResult)
};
function processFiles(files) {
Promise.all([].map.call(files, function(file, index) {
return handleEntries(file, index).then(handleFile)
}))
.then(function() {
console.log("complete", webkitResult);
res = webkitResult;
res.reduce(function(promise, track) {
return promise.then(function() {
return playMusic(track)
})
}, displayFiles(res))
})
.catch(function(err) {
alert(err.message);
})
}
if ("getFilesAndDirectories" in event.target) {
return (event.type === "drop" ? event.dataTransfer : event.target).getFilesAndDirectories()
.then(function(dir) {
if (dir[0] instanceof Directory) {
console.log(dir)
return mozReadDirectories(dir, dir[0].path || path)
.then(function(complete) {
console.log("complete:", webkitResult);
event.target.value = null;
});
} else {
if (dir[0] instanceof File && dir[0].size > 0) {
return Promise.resolve(dir)
.then(function() {
console.log("complete:", mozResult);
res = mozResult;
res.reduce(function(promise, track) {
return promise.then(function() {
return playMusic(track)
})
}, displayFiles(res))
})
} else {
if (dir[0].size == 0) {
throw new Error("could not process '" + dir[0].name + "' directory" + " at drop event at firefox, upload folders at 'Choose folder...' input");
}
}
}
}).catch(function(err) {
alert(err)
})
}
files = event.target.files;
if (files) {
processFiles(files)
}
}
function displayFiles(files) {
select.innerHTML = "";
return Promise.all(files.map(function(file, index) {
return new Promise(function(resolve) {
var option = new Option(file.name, index);
select.appendChild(option);
resolve()
})
}))
}
function handleSelectedSong(event) {
if (res.length) {
var index = select.value;
var track = res[index];
playMusic(track)
.then(function(filename) {
console.log(filename + " playback completed")
})
} else {
console.log("No songs to play")
}
}
function playMusic(file) {
return new Promise(function(resolve) {
audio.pause();
audio.onended = function() {
audio.onended = null;
if (url) URL.revokeObjectURL(url);
resolve(file.name);
}
if (url) URL.revokeObjectURL(url);
url = URL.createObjectURL(file);
audio.load();
audio.src = url;
audio.play();
audioLabel.textContent = file.name;
context = context || new AudioContext();
src = src || context.createMediaElementSource(audio);
src.disconnect(context);
var analyser = context.createAnalyser();
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
src.connect(analyser);
analyser.connect(context.destination);
analyser.fftSize = 256;
var bufferLength = analyser.frequencyBinCount;
console.log(bufferLength);
var dataArray = new Uint8Array(bufferLength);
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var barWidth = (WIDTH / bufferLength) * 1;
var barHeight;
var x = 0;
function renderFrame() {
requestAnimationFrame(renderFrame);
x = 0;
analyser.getByteFrequencyData(dataArray);
ctx.fillStyle = "#1b1b1b";
ctx.fillRect(0, 0, WIDTH, HEIGHT);
for (var i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
var r = 5;
var g = 195;
var b = 45;
ctx.fillStyle = "rgb(5,195,45)"
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 2;
}
}
renderFrame();
})
}
input.addEventListener("change", processDirectoryUpload);
select.addEventListener("change", handleSelectedSong);
<div id="content">
Upload directory: <input id="file" type="file" accept="audio/*" directory allowdirs webkitdirectory/><br>
<br>Now playing: <label for="audio"></label><br>
<br><label for="select">Select a song to play:</label><br>
<select id="select">
</select>
<canvas id="canvas"></canvas>
<audio id="audio" controls></audio>
</div>

Related

Visualization of audio(webaudio) not working in chrome

I want to visualize audio that is coming in from webrtc(JSSIP). I used WebAudio, connected analyzer node and it seems to be working fine with Firefox.
In chrome browser, thing seems to be not working. I can listen to the audio in Chrome but am not able to visualize it, it doesn't throw any error. I checked the array, it shows -Infinity.
What could be the reason for it? How can I visualize in chrome?
console.log(dataArray);
>> Float32Array(512) [-Infinity, -Infinity, -Infinity, -Infinity, -Infinity, -Infinity, -Infinity, …]
Code for the same
const AudioContext = window.AudioContext || window.webkitAudioContext;
var audioElement;
var audioCtx;
var analyser;
const canvas = document.getElementById("visualizer");
const canvasCtx = canvas.getContext("2d");
canvas.width = window.innerWidth - 50;
canvas.height = canvas.width / 2;
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var drawVisual;
const socketUrl = "wss://website.com:8089/ws";
var ua;
var configuration = {
sockets: [],
'uri': "",
'password': "",
'username': "",
'register': true
};
var room = "";
var session;
var callOptions = {
'mediaConstraints': { 'audio': true, 'video': false },
'pcConfig': {
'iceServers': [
{ 'urls': ['stun:stun.l.google.com:19302'] },
]
}
};
const callBtn = document.getElementById('call-btn');
const hangupBtn = document.getElementById('hangup-btn');
hangupBtn.setAttribute('disabled','disabled');
var initAudioElement = function () {
audioElement = new window.Audio();
audioElement.autoplay = true;
audioElement.crossOrigin = "anonymous";
document.body.appendChild(audioElement);
}
var audioCtxInit = function () {
try {
audioCtx = new AudioContext();
analyser = audioCtx.createAnalyser();
} catch (e) {
alert('Web audio not supported.');
}
var audioTrack = audioCtx.createMediaElementSource(audioElement);
audioTrack.connect(analyser);
analyser.connect(audioCtx.destination);
}
var initializeJsSIP = function () {
if (ua) {
ua.stop();
ua = null;
}
try {
const socket = new JsSIP.WebSocketInterface(socketUrl);
JsSIP.debug.disable('JsSIP:*'); // Debugger
configuration.sockets = [socket];
configuration.uri = "";
ua = new JsSIP.UA(configuration);
} catch (error) {
console.log(error);
}
ua.on("connecting", () => {
console.log('UA "connecting" event');
});
ua.on("connected", () => {
console.log('UA "connected" event');
});
ua.on("disconnected", () => {
console.log('UA "disconnected" event');
});
ua.on("registered", (data) => {
console.log('UA "registered" event', data);
});
ua.on("unregistered", () => {
console.log('UA "unregistered" event');
});
ua.on("registrationFailed", (data) => {
console.log('UA "registrationFailed" event', data);
});
ua.on("newRTCSession", ({ originator, session: rtcSession, request: rtcRequest }) => {
console.log('UA "newRTCSession" event');
// identify call direction
if (originator === "local") {
const foundUri = rtcRequest.to.toString();
const delimiterPosition = foundUri.indexOf(";") || null;
console.log(`foundUri: ${foundUri}`);
} else if (originator === "remote") {
const foundUri = rtcRequest.from.toString();
const delimiterPosition = foundUri.indexOf(";") || null;
console.log(`foundUri: ${foundUri}`);
}
if (session) { // hangup any existing call
session.terminate();
}
session = rtcSession;
session.on("failed", (data) => {
console.log('rtcSession "failed" event', data);
session = null;
});
session.on("ended", () => {
console.log('rtcSession "ended" event');
session = null;
});
session.on("accepted", () => {
console.log('rtcSession "accepted" event');
[
audioElement.srcObject,
] = session.connection.getRemoteStreams();
var playPromise = audioElement.play();
if (typeof playPromise !== "undefined") {
playPromise
.then(() => {
console.log("playing");
visualize();
})
.catch((error) => {
console.log(error);
});
}
});
});
ua.start();
}
function visualize() {
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
analyser.fftSize = 2 ** 10;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Float32Array(bufferLength);
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
function draw() {
drawVisual = requestAnimationFrame(draw);
analyser.getFloatFrequencyData(dataArray);
canvasCtx.fillStyle = 'rgb(0, 0, 0)';
canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
var barWidth = (WIDTH / bufferLength) * 2.5;
var barHeight;
var x = 0;
for (var i = 0; i < bufferLength; i++) {
barHeight = (dataArray[i] + 140) * 2;
canvasCtx.fillStyle = 'rgb(' + Math.floor(barHeight + 100) + ',50,50)';
canvasCtx.fillRect(x, HEIGHT - barHeight / 2, barWidth, barHeight / 2);
x += barWidth + 1;
}
}
draw();
}
callBtn.addEventListener('click', function () {
callBtn.setAttribute('disabled','disabled');
hangupBtn.removeAttribute('disabled');
if (!audioCtx) {
audioCtxInit();
}
// check if context is in suspended state (autoplay policy)
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
ua.call(room, callOptions);
});
hangupBtn.addEventListener('click', function () {
callBtn.removeAttribute('disabled');
hangupBtn.setAttribute('disabled', 'disabled');
if (drawVisual) {
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
canvasCtx.fillStyle = 'rgb(0, 0, 0)';
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
window.cancelAnimationFrame(drawVisual);
drawVisual = null;
}
ua.terminateSessions();
});
window.onload = function () {
readyStateTimer = setInterval(function () {
if (document.readyState === "complete") {
clearInterval(readyStateTimer);
initAudioElement();
initializeJsSIP();
}
}, 500);
}
window.onbeforeunload = function () {
audioElement.parentNode.removeChild(this.audioElement);
delete audioElement;
if (ua) {
ua.stop();
ua = null;
}
};

How to cache or pre-render canvas animation

I am in the process of doing some slight modification (color keying) to an inline html5 video using canvas. Now, this video is a loop and I'm thinking there are more optimal ways then having a looping video being evaluated and repainted every single time. What optimizations if any can I do to either a) pre-render the entire animation or b) cache the original loop so that no more processing/evaluation needs to occur. I find that I'm being totally inefficient in CPU / memory usage by constantly letting it run.
I am currently using the following code (note, im using Vue.js, so assume that all the current function and variable assignments work correctly already) :
loadVideo() {
this.video = document.getElementById('globe');
this.c1 = document.getElementById("c1");
this.ctx1 = this.c1.getContext("2d");
let that = this;
this.video.addEventListener("play", function() {
that.vWidth = that.video.videoWidth / 2;
that.vHeight = that.video.videoHeight / 2;
that.fpsInterval = 1000 / 120;
that.then = Date.now();
that.startTime = that.then;
that.computeFrame();
}, false);
}
computeFrame() {
requestAnimationFrame(this.computeFrame);
this.now = Date.now();
this.elapsed = this.now - this.then;
if (this.elapsed > this.fpsInterval) {
this.ctx1.canvas.width = this.video.offsetWidth;
this.ctx1.canvas.height = this.video.offsetHeight;
if (this.video.offsetWidth > 0 && this.video.offsetHeight > 0) {
this.then = this.now - (this.elapsed % this.fpsInterval);
this.ctx1.drawImage(this.video, 0, 0, this.ctx1.canvas.width, this.ctx1.canvas.height);
let frame = this.ctx1.getImageData(0, 0, this.ctx1.canvas.width, this.ctx1.canvas.height);
let l = frame.data.length / 4;
let primaryColor = this.ctx1.getImageData(0, 0, 8, 8).data;
let primaryR = primaryColor[60];
let primaryG = primaryColor[61];
let primaryB = primaryColor[62];
for (let i = 0; i < l; i++) {
let r = frame.data[i * 4 + 0];
let g = frame.data[i * 4 + 1];
let b = frame.data[i * 4 + 2];
if (r == primaryR && g == primaryG && b == primaryB) {
frame.data[i * 4 + 1] = 255;
frame.data[i * 4 + 2] = 0;
frame.data[i * 4 + 3] = 0;
}
}
this.ctx1.putImageData(frame, 0, 0);
}
}
}
loadVideo();
You can use a MediaRecorder to record the video stream returned by your cavnas captureStream() method to record the first pass, and then read the resulting recorded video directly in a looping <video>:
btn.onclick = e => {
// initialise the <video>
const vid = document.createElement('video');
vid.muted = true;
vid.crossOrigin = true;
vid.src = "https://upload.wikimedia.org/wikipedia/commons/transcoded/a/a4/BBH_gravitational_lensing_of_gw150914.webm/BBH_gravitational_lensing_of_gw150914.webm.480p.webm";
vid.playbackRate = 2;
vid.onplaying = startProcessing;
vid.play();
btn.remove();
log.textContent = 'fetching';
};
function startProcessing(evt) {
// when video is playing
const vid = evt.target;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = vid.videoWidth;
canvas.height = vid.videoHeight;
// show the canvas for first round
document.body.appendChild(canvas);
// force first frame
anim();
const chunks = []; // we'll store our recorder's data here
const canvasStream = canvas.captureStream();
const vp = 'video/webm; codecs="vp',
vp8 = vp + '8"',
vp9 = vp + '9"';
const recorder = new MediaRecorder(canvasStream, {
mimeType: MediaRecorder.isTypeSupported(vp9) ? vp9:vp8,
videoBitsPerSeconds: 5000000
});
// every time new data is available
recorder.ondataavailable = evt => chunks.push(evt.data);
// record until the video ends
vid.onended = evt => {
recorder.stop();
};
recorder.onstop = exportVid;
recorder.start();
log.textContent = "recording";
function anim() {
if(vid.paused) {
console.log('stop drawing');
return;
}
ctx.drawImage(vid, 0, 0);
applyFilter(ctx);
requestAnimationFrame(anim);
}
function exportVid() {
// concatenate all our chunks in a single Blob
const blob = new Blob(chunks);
const url = URL.createObjectURL(blob);
// we reuse the same <video> (for Safari autoplay)
vid.onplaying = vid.onended = null;
vid.src = url;
vid.loop = true;
vid.playbackRate = 1;
log.textContent = "playing";
vid.play().then(()=> canvas.replaceWith(vid));
}
}
function applyFilter(ctx) {
const img = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const data = new Uint32Array(img.data.buffer);
for(let i=0; i<data.length; i++) {
if(data[i] < 0xFF111111) data[i] = ((Math.random()*0xFFFFFF) + 0xFF000000 | 0);
}
ctx.putImageData(img, 0, 0);
}
canvas,video{
max-height: 100vh;
max-width: 100vw;
}
<button id="btn">start</button><pre id="log"></pre>

JQuery and XHR Request asyncronious issues

I'm trying to manage to apply an generated image of an audio graph for every div that a music url is found with a Google Chrome Extension.
However, the process of downloading the music from the url and processing the image, takes enough time that all of the images keep applying to the last div.
I'm trying to apply the images to each div as throughout the JQuery's each request. All the div's have the /renderload.gif gif playing, but only the last div flashes as the images finished processing one by one.
Example being that the src is being set to /renderload.gif for all 1,2,3,4,5
but once the sound blob was downloaded and image was generated, only 4-5 gets the images and it continues on loading the queue, repeating the issue.
Here's an example of what I'm trying to deal with.
Here's my latest attempts to add queueing to avoid lag by loading all the audios at once, but it seems the issue still persists.
// context.js
function Queue(){
var queue = [];
var offset = 0;
this.getLength = function(){
return (queue.length - offset);
}
this.isEmpty = function(){
return (queue.length == 0);
}
this.setEmpty = function(){
queue = [];
return true;
}
this.enqueue = function(item){
queue.push(item);
}
this.dequeue = function(){
if (queue.length == 0) return undefined;
var item = queue[offset];
if (++ offset * 2 >= queue.length){
queue = queue.slice(offset);
offset = 0;
}
return item;
}
this.peek = function(){
return (queue.length > 0 ? queue[offset] : undefined);
}
}
var audioqueue=new Queue();
var init=0;
var current=0;
var finished=0;
function RunGraphs(x) {
if (x==init) {
if (audioqueue.isEmpty()==false) {
current++;
var das=audioqueue.dequeue();
var divparent=das.find(".original-image");
var songurl=das.find(".Mpcs").find('span').attr("data-url");
console.log("is song url "+songurl);
console.log("is data here "+divparent.attr("title"));
divparent.css('width','110px');
divparent.attr('src','https://i.pinimg.com/originals/a4/f2/cb/a4f2cb80ff2ae2772e80bf30e9d78d4c.gif');
var blob = null;
var xhr = new XMLHttpRequest();
xhr.open("GET",songurl,true);
xhr.responseType = "blob";//force the HTTP response, response-type header to be blob
xhr.onload = function() {
blob = xhr.response;//xhr.response is now a blob object
console.log(blob);
SCWFRobloxAudioTool.generate(blob, {
canvas_width: 110,
canvas_height: 110,
bar_width: 1,
bar_gap : .2,
wave_color: "#ecb440",
download: false,
onComplete: function(png, pixels) {
if (init == x) {
divparent.attr('src',png);
finished++;
}
}
});
}
xhr.send();
OnHold(x);
}
}
}
function OnHold(x) {
if (x==init) {
if (current > finished+7) {
setTimeout(function(){
OnHold(x)
},150)
} else {
RunGraphs(x)
}
}
}
if (window.location.href.includes("/lib?Ct=DevOnly")){
functionlist=[];
current=0;
finished=0;
init++;
audioqueue.setEmpty();
$(".CATinner").each(function(index) {
(function(x){
audioqueue.enqueue(x);
}($(this)));
});
RunGraphs(init);
};
The SCWFAudioTool is from this github repository.
Soundcloud Waveform Generator
The Queue.js from a search request, slightly modified to have setEmpty support.Queue.js
Please read the edit part of the post
I mad a usable minimal example of your code in order to check your Queue and defer method. there seems to be no error that i can find (i don't have the html file and cant check for missing files. Please do that yourself by adding the if (this.status >= 200 && this.status < 400) check to the onload callback):
// context.js
function Queue(){
var queue = [];
var offset = 0;
this.getLength = function(){
return (queue.length - offset);
}
this.isEmpty = function(){
return (queue.length == 0);
}
this.setEmpty = function(){
queue = [];
return true;
}
this.enqueue = function(item){
queue.push(item);
}
this.dequeue = function(){
if (queue.length == 0) return undefined;
var item = queue[offset];
if (++ offset * 2 >= queue.length){
queue = queue.slice(offset);
offset = 0;
}
return item;
}
this.peek = function(){
return (queue.length > 0 ? queue[offset] : undefined);
}
}
var audioqueue=new Queue();
var init=0;
var current=0;
var finished=0;
function RunGraphs(x) {
if (x==init) {
if (audioqueue.isEmpty()==false) {
current++;
var songurl = audioqueue.dequeue();
console.log("is song url "+songurl);
var blob = null;
var xhr = new XMLHttpRequest();
xhr.open("GET",songurl,true);
xhr.responseType = "blob";//force the HTTP response, response-type header to be blob
xhr.onload = function() {
if (this.status >= 200 && this.status < 400) {
blob = xhr.response;//xhr.response is now a blob object
console.log('OK');
finished++;
} else {
console.log('FAIL');
}
}
xhr.send();
OnHold(x);
}
}
}
function OnHold(x) {
if (x==init) {
if (current > finished+7) {
setTimeout(function(){
OnHold(x)
},150)
} else {
RunGraphs(x)
}
}
}
var demoObject = new Blob(["0".repeat(1024*1024*2)]); // 2MB Blob
var demoObjectURL = URL.createObjectURL(demoObject);
if (true){
functionlist=[];
current=0;
finished=0;
init++;
audioqueue.setEmpty();
for(var i = 0; i < 20; i++)
audioqueue.enqueue(demoObjectURL);
RunGraphs(init);
};
Therefore if there are no errors left concerning missing files the only errors i can think off is related to the SCWFRobloxAudioTool.generate method.
Please check if the callback gets triggered correctly and that no errors accrue during conversion.
If you provide additional additional info, data or code i can look into this problem.
EDIT:
I looked into the 'SoundCloudWaveform' program and i think i see the problem:
The module is not made to handle multiple queries at once (there is only one global setting object. So every attempt to add another query to the api will override the callback of the previous one, and since the fileReader is a async call only the latest added callback will be executed.)
Please consider using an oop attempt of this api:
window.AudioContext = window.AudioContext || window.webkitAudioContext;
Array.prototype.max = function() {
return Math.max.apply(null, this);
};
function SoundCloudWaveform (){
this.settings = {
canvas_width: 453,
canvas_height: 66,
bar_width: 3,
bar_gap : 0.2,
wave_color: "#666",
download: false,
onComplete: function(png, pixels) {}
}
this.generate = function(file, options) {
// preparing canvas
this.settings.canvas = document.createElement('canvas');
this.settings.context = this.settings.canvas.getContext('2d');
this.settings.canvas.width = (options.canvas_width !== undefined) ? parseInt(options.canvas_width) : this.settings.canvas_width;
this.settings.canvas.height = (options.canvas_height !== undefined) ? parseInt(options.canvas_height) : this.settings.canvas_height;
// setting fill color
this.settings.wave_color = (options.wave_color !== undefined) ? options.wave_color : this.settings.wave_color;
// setting bars width and gap
this.settings.bar_width = (options.bar_width !== undefined) ? parseInt(options.bar_width) : this.settings.bar_width;
this.settings.bar_gap = (options.bar_gap !== undefined) ? parseFloat(options.bar_gap) : this.settings.bar_gap;
this.settings.download = (options.download !== undefined) ? options.download : this.settings.download;
this.settings.onComplete = (options.onComplete !== undefined) ? options.onComplete : this.settings.onComplete;
// read file buffer
var reader = new FileReader();
var _this = this;
reader.onload = function(event) {
var audioContext = new AudioContext()
audioContext.decodeAudioData(event.target.result, function(buffer) {
audioContext.close();
_this.extractBuffer(buffer);
});
};
reader.readAsArrayBuffer(file);
}
this.extractBuffer = function(buffer) {
buffer = buffer.getChannelData(0);
var sections = this.settings.canvas.width;
var len = Math.floor(buffer.length / sections);
var maxHeight = this.settings.canvas.height;
var vals = [];
for (var i = 0; i < sections; i += this.settings.bar_width) {
vals.push(this.bufferMeasure(i * len, len, buffer) * 10000);
}
for (var j = 0; j < sections; j += this.settings.bar_width) {
var scale = maxHeight / vals.max();
var val = this.bufferMeasure(j * len, len, buffer) * 10000;
val *= scale;
val += 1;
this.drawBar(j, val);
}
if (this.settings.download) {
this.generateImage();
}
this.settings.onComplete(this.settings.canvas.toDataURL('image/png'), this.settings.context.getImageData(0, 0, this.settings.canvas.width, this.settings.canvas.height));
// clear canvas for redrawing
this.settings.context.clearRect(0, 0, this.settings.canvas.width, this.settings.canvas.height);
},
this.bufferMeasure = function(position, length, data) {
var sum = 0.0;
for (var i = position; i <= (position + length) - 1; i++) {
sum += Math.pow(data[i], 2);
}
return Math.sqrt(sum / data.length);
},
this.drawBar = function(i, h) {
this.settings.context.fillStyle = this.settings.wave_color;
var w = this.settings.bar_width;
if (this.settings.bar_gap !== 0) {
w *= Math.abs(1 - this.settings.bar_gap);
}
var x = i + (w / 2),
y = this.settings.canvas.height - h;
this.settings.context.fillRect(x, y, w, h);
},
this.generateImage = function() {
var image = this.settings.canvas.toDataURL('image/png');
var link = document.createElement('a');
link.href = image;
link.setAttribute('download', '');
link.click();
}
}
console.log(new SoundCloudWaveform());
Also consider simply using an array for the queue:
function Queue(){
var queue = [];
var offset = 0;
this.getLength = function(){
return (queue.length - offset);
}
this.isEmpty = function(){
return (queue.length == 0);
}
this.setEmpty = function(){
queue = [];
return true;
}
this.enqueue = function(item){
queue.push(item);
}
this.dequeue = function(){
if (queue.length == 0) return undefined;
var item = queue[offset];
if (++ offset * 2 >= queue.length){
queue = queue.slice(offset);
offset = 0;
}
return item;
}
this.peek = function(){
return (queue.length > 0 ? queue[offset] : undefined);
}
}
var q = new Queue();
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
console.log(q.dequeue());
console.log(q.dequeue());
console.log(q.dequeue());
console.log(q.dequeue());
var q2 = [];
q2.push(1)
q2.push(2)
q2.push(3)
console.log(q2.shift());
console.log(q2.shift());
console.log(q2.shift());
console.log(q2.shift());
It prevents confusion ant the speedup of it is minimal in your application.
On your open method on the xhr object, set the parameter to true, also... try using the onload() instead of onloadend(). Good Luck!
var xmlhttp = new XMLHttpRequest(),
method = 'GET',
url = 'https://developer.mozilla.org/';
xmlhttp.open(method, url, true);
xmlhttp.onload = function () {
// Do something with the retrieved data
};
xmlhttp.send();

canvas is distorting itself and moving (fabric.js)

I just need an advice on where to look at or where to start as right now I am confused. Not sure if this is CSS issue, javascript, or fabric.js
Problem:
- I am adding a background image to canvas and sometimes it behaves really strange. And canvas is distorting itself sometimes on click, sometimes on resize, sometimes just without doing nothing. But this does not happen always.
These are two videos with the issue:
https://jumpshare.com/v/4FU3xWjqk4h6j0OwqaFs (this is just without
doing nothing).
https://jumpshare.com/v/tBS7NJC5VgccWnIssETw (this is me clicking on canvas)
This is how I add an image to canvas (fetching url via API):
$scope.openBlob = function($event, imageURL) {
$rootScope.isLoading();
$rootScope.centerSpinner();
if ($rootScope.currentTab === 1) {
var upload_thumb = angular.element($event.target.parentElement);
var circular = angular.element('<md-progress-circular md-mode="indeterminate" id="circular-images" md-diameter="30" ng-show="isAlsoLoading"></md-progress-circular>');
$compile(circular)($scope);
upload_thumb.prepend(circular);
} else {
var upload_thumb = angular.element($event.target.parentElement);
var circular = angular.element('<md-progress-circular md-mode="indeterminate" id="circular-new-middle" md-diameter="30" ng-show="isAlsoLoading"></md-progress-circular>');
$compile(circular)($scope);
upload_thumb.prepend(circular);
}
$rootScope.isAlsoLoading = true;
$http({
method: "GET",
url: imageURL.full,
responseType: "blob"
}).then(
function(res) {
var reader = new FileReader();
reader.readAsDataURL(res.data);
reader.onload = function(e) {
canvas.loadMainImage(e.target.result);
$rootScope.started = true;
$rootScope.uploaded = true;
$rootScope.changeStyling();
};
},
function(res) {
console.log("For some reasons it failed to open", res);
});
if ($rootScope.currentTab === 1) {
$rootScope.$on('editor.mainImage.loaded', function() {
$rootScope.isAlsoLoading = false;
upload_thumb.find("#circular-images").remove();
$rootScope.resetFilters();
$rootScope.isNotLoading();
});
} else {
$rootScope.$on('editor.mainImage.loaded', function() {
$rootScope.isAlsoLoading = false;
upload_thumb.find("#circular-new-middle").remove();
$rootScope.resetFilters();
$rootScope.isNotLoading();
});
}
}
LoadMainImage function content:
loadMainImage: function(url, height, width, dontFit, callback) {
var object;
fabric.util.loadImage(url, function(img) {
//img.crossOrigin = 'anonymous';
object = new fabric.Image(img, canvas.imageStatic);
object.name = 'mainImage';
if (width && height) {
object.width = width;
object.height = height;
}
canvas.mainImage = object;
canvas.fabric.forEachObject(function(obj) {
console.log('image object', obj);
if (obj && obj.name == 'mainImage') {
canvas.fabric.remove(obj);
}
});
canvas.fabric.add(object);
object.top = -0.5;
object.left = -0.5;
object.moveTo(0);
canvas.fabric.setHeight(object.height);
canvas.fabric.setWidth(object.width);
canvas.original.height = object.height;
canvas.original.width = object.width;
$rootScope.canvaswidth = canvas.original.width;
$rootScope.canvasheight = canvas.original.height;
$rootScope.calculateImageType(canvas.original.width, canvas.original.height);
if ($rootScope.gridadded) {
$rootScope.removeGrid();
$rootScope.addGrid();
}
if (!dontFit) {
canvas.fitToScreen();
}
$rootScope.$apply(function() {
$rootScope.$emit('editor.mainImage.loaded');
});
if (callback) {
callback();
}
});
},
Fit to screen function:
$scope.fitToScreen = function () {
$scope.zoom = canvas.currentZoom * 100;
if (canvas.mainImage) {
var oWidth = canvas.mainImage.width,
oHeight = canvas.mainImage.height;
} else {
var oWidth = canvas.original.width,
oHeight = canvas.original.height;
}
var maxScale = Math.min(3582 / oHeight, 5731 / oWidth) * 100,
minScale = Math.min(141 / oHeight, 211 / oWidth) * 100;
$scope.maxScale = Math.ceil(maxScale);
$scope.minScale = Math.ceil(minScale);
canvas.zoom(canvas.currentZoom);
};
UPDATE: I am assuming that it might be scaling (zooming in and zooming out issue)

Multiplayer coins

Having a slight issue getting some coins to generate multiplayer using eureca.io websockets. I've got the players working multiplayer and from remote connections but I can't seem to get the coins to generate across the connection so they appear in the same place on all the players connections. I'm using phaser to generate the game and eureca and engine for my web connection. I've got the coins to spawn on the page with no problems, but whenever a new player joins, the coin always displays in a different place, I wondering how I can make them generate across the connection. My game code is below:
Game Code
var myId = 0;
var background;
var blueSquare;
var player;
var coins;
var goldCoin;
var squareList;
var coinList;
var cursors;
var score;
var highScore;
var ready = false;
var eurecaServer;
var eurecaClientSetup = function () {
var eurecaClient = new Eureca.Client();
eurecaClient.ready(function (proxy) {
eurecaServer = proxy;
});
eurecaClient.exports.setId = function (id)
{
myId = id;
create();
eurecaServer.handshake();
ready = true;
}
eurecaClient.exports.kill = function (id)
{
if (squareList[id]) {
squareList[id].kill();
console.log('Player has left the game ', id, squareList[id]);
}
}
eurecaClient.exports.spawnBlueSquare = function (i, x, y)
{
if (i == myId) return;
console.log('A new player has joined the game');
var blsq = new BlueSquare(i, game, blueSquare);
squareList[i] = blsq;
}
eurecaClient.exports.spawnCoins = function (c, x, y)
{
console.log('A coin has been generated');
var cn = new GenerateCoin(c, game, coin);
coinList[c] = cn;
}
eurecaClient.exports.updateState = function (id, state)
{
if (squareList[id]) {
squareList[id].cursor = state;
squareList[id].blueSquare.x = state.x;
squareList[id].blueSquare.y = state.y;
squareList[id].blueSquare.angle = state.angle;
squareList[id].update();
coinList.cursor = state;
coinList.blueSquare.x = state.x;
coinList.blueSquare.y = state.y;
coinList.update();
}
}
}
BlueSquare = function (index, game, player) {
this.cursor = {
left:false,
right:false,
up:false,
down:false,
}
this.input = {
left:false,
right:false,
up:false,
down:false,
}
var x = 0;
var y = 0;
this.game = game;
this.player = player;
this.currentSpeed = 0;
this.isBlue = true;
this.blueSquare = game.add.sprite(x, y, 'blueSquare');
this.blueSquare.anchor.set(0.5);
this.blueSquare.id = index;
game.physics.enable(this.blueSquare, Phaser.Physics.ARCADE);
this.blueSquare.body.immovable = false;
this.blueSquare.body.collideWorldBounds = true;
this.blueSquare.body.setSize(34, 34, 0, 0);
this.blueSquare.angle = 0;
game.physics.arcade.velocityFromRotation(this.blueSquare.rotation, 0, this.blueSquare.body.velocity);
}
BlueSquare.prototype.update = function () {
var inputChanged = (
this.cursor.left != this.input.left ||
this.cursor.right != this.input.right ||
this.cursor.up != this.input.up ||
this.cursor.down != this.input.down
);
if (inputChanged)
{
if (this.blueSquare.id == myId)
{
this.input.x = this.blueSquare.x;
this.input.y = this.blueSquare.y;
this.input.angle = this.blueSquare.angle;
eurecaServer.handleKeys(this.input);
}
}
if (this.cursor.left)
{
this.blueSquare.body.velocity.x = -250;
this.blueSquare.body.velocity.y = 0;
}
else if (this.cursor.right)
{
this.blueSquare.body.velocity.x = 250;
this.blueSquare.body.velocity.y = 0;
}
else if (this.cursor.down)
{
this.blueSquare.body.velocity.x = 0;
this.blueSquare.body.velocity.y = 250;
}
else if (this.cursor.up)
{
this.blueSquare.body.velocity.x = 0;
this.blueSquare.body.velocity.y = -250;
}
else
{
this.blueSquare.body.velocity.x = 0;
this.blueSquare.body.velocity.y = 0;
}
}
BlueSquare.prototype.kill = function () {
this.alive = false;
this.blueSquare.kill();
}
GenerateCoin = function (game, goldCoin) {
this.game = game;
this.goldCoin = goldCoin;
x = 0;
y = 0;
this.coins = game.add.sprite(x, y, 'coin');
game.physics.enable(this.coins, Phaser.Physics.ARCADE);
this.coins.body.immovable = true;
this.coins.body.collideWorldBounds = true;
this.coins.body.setSize(16, 16, 0, 0);
}
window.onload = function() {
function countdown( elementName, minutes, seconds )
{
var element, endTime, hours, mins, msLeft, time;
function twoDigits( n )
{
return (n <= 9 ? "0" + n : n);
}
function updateTimer()
{
msLeft = endTime - (+new Date);
if ( msLeft < 1000 ) {
element.innerHTML = "Game Over!";
} else {
time = new Date( msLeft );
hours = time.getUTCHours();
mins = time.getUTCMinutes();
element.innerHTML = (hours ? hours + ':' + twoDigits( mins ) : mins) + ':' + twoDigits( time.getUTCSeconds() );
setTimeout( updateTimer, time.getUTCMilliseconds() + 500 );
}
}
element = document.getElementById( elementName );
endTime = (+new Date) + 1000 * (60*minutes + seconds) + 500;
updateTimer();
}countdown( "countdown", 5, 0 );
}
var game = new Phaser.Game(700, 600, Phaser.AUTO, 'Square Hunt', { preload: preload, create: eurecaClientSetup, update: update, render: render });
function preload () {
game.load.spritesheet('coin', 'assets/coin.png', 18, 18);
game.load.image('blueSquare', 'assets/blue-square.png');
game.load.image('redSquare', 'assets/red-square.png');
game.load.image('earth', 'assets/sand.png');
}
function create () {
game.world.setBounds(0, 0, 1500, 1500);
game.stage.disableVisibilityChange = true;
background = game.add.tileSprite(0, 0, 800, 600, 'earth');
background.fixedToCamera = true;
squareList = {};
player = new BlueSquare(myId, game, blueSquare);
squareList[myId] = player;
blueSquare = player.blueSquare;
blueSquare.x = Math.floor(Math.random() * 650);
blueSquare.y = Math.floor(Math.random() * 650);
blueSquare.bringToTop();
game.camera.follow(blueSquare);
game.camera.deadzone = new Phaser.Rectangle(150, 150, 500, 300);
game.camera.focusOnXY(0, 0);
cursors = game.input.keyboard.createCursorKeys();
coinList = {};
goldCoin = new GenerateCoin(game, coins);
coinList = goldCoin;
coins = goldCoin.coins;
coins.x = Math.floor(Math.random() * 650);
coins.y = Math.floor(Math.random() * 650);
coins.bringToTop();
}
function update () {
if (!ready) return;
game.physics.arcade.collide(blueSquare, coins);
player.input.left = cursors.left.isDown;
player.input.right = cursors.right.isDown;
player.input.up = cursors.up.isDown;
player.input.down = cursors.down.isDown;
player.input.fire = game.input.activePointer.isDown;
player.input.tx = game.input.x+ game.camera.x;
player.input.ty = game.input.y+ game.camera.y;
background.tilePosition.x = -game.camera.x;
background.tilePosition.y = -game.camera.y;
for (var i in squareList)
{
if (!squareList[i]) continue;
var curBlue = squareList[i].blueSquare;
for (var j in squareList)
{
if (!squareList[j]) continue;
if (j!=i)
{
var targetBlue = squareList[j].blueSquare;
}
if (squareList[j].isBlue)
{
squareList[j].update();
}
}
}
}
function test(){
console.log('collsion');
}
function render () {}
Server.js Files
var express = require('express')
, app = express(app)
, server = require('http').createServer(app);
app.use(express.static(__dirname));
var clients = {};
var EurecaServer = require('eureca.io').EurecaServer;
var eurecaServer = new EurecaServer({allow:['setId', 'spawnBlueSquare', 'spawnCoins', 'kill', 'updateState']});
eurecaServer.attach(server);
eurecaServer.onConnect(function (conn) {
console.log('New Client id=%s ', conn.id, conn.remoteAddress);
var remote = eurecaServer.getClient(conn.id);
clients[conn.id] = {id:conn.id, remote:remote}
remote.setId(conn.id);
});
eurecaServer.onConnectionLost(function (conn) {
console.log('connection lost ... will try to reconnect');
});
eurecaServer.onDisconnect(function (conn) {
console.log('Client disconnected ', conn.id);
var removeId = clients[conn.id].id;
delete clients[conn.id];
for (var c in clients)
{
var remote = clients[c].remote;
remote.kill(conn.id);
}
});
eurecaServer.exports.handshake = function()
{
for (var c in clients)
{
var remote = clients[c].remote;
for (var cc in clients)
{
var x = clients[cc].laststate ? clients[cc].laststate.x: 0;
var y = clients[cc].laststate ? clients[cc].laststate.y: 0;
remote.spawnBlueSquare(clients[cc].id, x, y);
}
}
}
eurecaServer.exports.handleKeys = function (keys) {
var conn = this.connection;
var updatedClient = clients[conn.id];
for (var c in clients)
{
var remote = clients[c].remote;
remote.updateState(updatedClient.id, keys);
clients[c].laststate = keys;
}
}
server.listen(8000);
Instead of generating coins randomly positioned on each client, you could generate this random position(x and y) in server side(on conexion event) and then send this position to all clients so they can generate the coin in the same position.

Categories

Resources