I am working on JS pitch shifter for uploaded files.
As a part of my code I used this source:
https://github.com/urtzurd/html-audio
Now I'm stuck on export. So I can upload locally stored file, then pitch-shift it, but then I want to export it, but can not found a way to do this.
I tried recorder.js from here:
https://github.com/mattdiamond/Recorderjs
but it seems to work only for live recorded files and I want to specifically export uploaded file.
Here is my code.
var pitchShifter = (function() {
var audioContext,
audioSources = [],
pitchShifterProcessor,
spectrumAudioAnalyser,
sonogramAudioAnalyser;
var validPitchRatio = [0.5, 1.0, 1.5, 2.0],
pitchRatio = validPitchRatio[1],
grainSize = 512,
overlapRatio = 0.50,
spectrumFFTSize = 128,
spectrumSmoothing = 0.8,
sonogramFFTSize = 2048,
sonogramSmoothing = 0;
function BufferLoader(context, urlList, callback) {
this.context = context;
this.urlList = urlList;
this.onload = callback;
this.bufferList = new Array();
this.loadCount = 0;
}
BufferLoader.prototype.loadBuffer = function(url, index) {
// Load buffer asynchronously
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = "arraybuffer";
var loader = this;
request.onload = function() {
// Asynchronously decode the audio file data in request.response
loader.context.decodeAudioData(request.response, function(buffer) {
if(!buffer) {
alert('error decoding file data: ' + url);
return;
}
loader.bufferList[index] = buffer;
if(++loader.loadCount == loader.urlList.length) loader.onload(loader.bufferList);
}, function(error) {
console.error('decodeAudioData error', error);
});
}
request.onerror = function() {
alert('BufferLoader: XHR error');
}
request.send();
}
BufferLoader.prototype.load = function() {
for(var i = 0; i < this.urlList.length; ++i) this.loadBuffer(this.urlList[i], i);
}
hannWindow = function(length) {
var window = new Float32Array(length);
for(var i = 0; i < length; i++) {
window[i] = 0.5 * (1 - Math.cos(2 * Math.PI * i / (length - 1)));
}
return window;
};
linearInterpolation = function(a, b, t) {
return a + (b - a) * t;
};
initAudio = function() {
if(!navigator.webkitGetUserMedia) {
alert('Your browser does not support the Media Stream API');
} else {
navigator.webkitGetUserMedia({
audio: true,
video: false
}, function(stream) {
audioSources[1] = audioContext.createMediaStreamSource(stream);
}, function(error) {
alert('Unable to get the user media');
});
}
spectrumAudioAnalyser = audioContext.createAnalyser();
spectrumAudioAnalyser.fftSize = spectrumFFTSize;
spectrumAudioAnalyser.smoothingTimeConstant = spectrumSmoothing;
sonogramAudioAnalyser = audioContext.createAnalyser();
sonogramAudioAnalyser.fftSize = sonogramFFTSize;
sonogramAudioAnalyser.smoothingTimeConstant = sonogramSmoothing;
var bufferLoader = new BufferLoader(audioContext, ['audio/voice.mp3'], function(bufferList) {
audioSources[0] = audioContext.createBufferSource();
audioSources[0].buffer = bufferList[0];
audioSources[0].loop = false;
audioSources[0].connect(pitchShifterProcessor);
audioSources[0].start(0);
});
bufferLoader.load();
};
initProcessor = function() {
if(pitchShifterProcessor) {
pitchShifterProcessor.disconnect();
}
if(audioContext.createScriptProcessor) {
pitchShifterProcessor = audioContext.createScriptProcessor(grainSize, 1, 1);
} else if(audioContext.createJavaScriptNode) {
pitchShifterProcessor = audioContext.createJavaScriptNode(grainSize, 1, 1);
}
pitchShifterProcessor.buffer = new Float32Array(grainSize * 2);
pitchShifterProcessor.grainWindow = hannWindow(grainSize);
pitchShifterProcessor.onaudioprocess = function(event) {
var inputData = event.inputBuffer.getChannelData(0);
var outputData = event.outputBuffer.getChannelData(0);
for(i = 0; i < inputData.length; i++) {
// Apply the window to the input buffer
inputData[i] *= this.grainWindow[i];
// Shift half of the buffer
this.buffer[i] = this.buffer[i + grainSize];
// Empty the buffer tail
this.buffer[i + grainSize] = 0.0;
}
// Calculate the pitch shifted grain re-sampling and looping the input
var grainData = new Float32Array(grainSize * 2);
for(var i = 0, j = 0.0; i < grainSize; i++, j += pitchRatio) {
var index = Math.floor(j) % grainSize;
var a = inputData[index];
var b = inputData[(index + 1) % grainSize];
grainData[i] += linearInterpolation(a, b, j % 1.0) * this.grainWindow[i];
}
// Copy the grain multiple times overlapping it
for(i = 0; i < grainSize; i += Math.round(grainSize * (1 - overlapRatio))) {
for(j = 0; j <= grainSize; j++) {
this.buffer[i + j] += grainData[j];
}
}
// Output the first half of the buffer
for(i = 0; i < grainSize; i++) {
outputData[i] = this.buffer[i];
}
};
pitchShifterProcessor.connect(spectrumAudioAnalyser);
pitchShifterProcessor.connect(sonogramAudioAnalyser);
pitchShifterProcessor.connect(audioContext.destination);
};
return {
init: function() {
if('AudioContext' in window) {
audioContext = new AudioContext();
} else {
alert('Your browser does not support the Web Audio API');
return;
}
initAudio();
initProcessor();
}
}
}());
window.addEventListener("DOMContentLoaded", pitchShifter.init, true);
Would like to hear any tips. Thank you!
Related
I am doing create multiple worker threads, in my case, i am trying to create 2:
This is my code to create work thread
function createWorker(data1, data2) {
return new Promise((resolve) => {
let worker = new Worker();
worker.postMessage(data1, data2);
worker.onmessage = (event) => {
postMessageRes = event.data;
if (postMessageRes == 200) {
// loadCOPC();
} else {
workerCount += 1;
let position = postMessageRes[0];
let color = postMessageRes[1];
for (let i = 0; i < position.length; i++) {
positions.push(position[i]);
colors.push(colors[i]);
}
resolve(true);
}
};
});
}
and using it in my loop
for (let m = 0; m < keyCountMap.length; ) {
let remaining = totalNodes - doneCount;
let numbWorker = Math.min(chunk, remaining);
for (let i = 0; i < numbWorker; i++) {
promises.push(createWorker([keyCountMap[m], keyCountMap[m + 1]]));
m += 2;
}
Promise.all(promises).then((response) => {
console.log("one chunk finishes");
});
}
The code works fine if i instead of all these use one static work thread and call postMessage in the loop for only one but not while i am trying to make chunk like here in the code.
When i run the code, my browser freeze
This is my worker file:
import { Copc, Key } from "copc";
import * as THREE from "three";
const color = new THREE.Color();
const colors = [];
let firstTime = true;
var nodePages, pages, receivedData, copc;
let x_min, y_min, z_min, x_max, y_max, z_max, width;
let positions = [];
let filename = "https://s3.amazonaws.com/data.entwine.io/millsite.copc.laz";
const readPoints = (id, getters) => {
let returnPoint = getXyzi(id, getters);
positions.push(
returnPoint[0] - x_min - 0.5 * width,
returnPoint[1] - y_min - 0.5 * width,
returnPoint[2] - z_min - 0.5 * width
);
const vx = (returnPoint[3] / 65535) * 255;
color.setRGB(vx, vx, vx);
colors.push(color.r, color.g, color.b);
firstTime = false;
};
function getXyzi(index, getters) {
return getters.map((get) => get(index));
}
async function load() {
copc = await Copc.create(filename);
let scale = copc.header.scale[0];
[x_min, y_min, z_min, x_max, y_max, z_max] = copc.info.cube;
width = Math.abs(x_max - x_min);
// let center_x = (x_min + x_max) / 2;
// let center_y = (y_min + y_max) / 2;
// let center_z = (z_min + z_max) / 2;
receivedData = await Copc.loadHierarchyPage(
filename,
copc.info.rootHierarchyPage
);
nodePages = receivedData.nodes;
pages = receivedData.pages;
postMessage(200);
}
async function loadData(myRoot, pointCount) {
const view = await Copc.loadPointDataView(filename, copc, myRoot);
let getters = ["X", "Y", "Z", "Intensity"].map(view.getter);
for (let j = 0; j < pointCount; j += 1) {
readPoints(j, getters);
}
postMessage([positions, colors]);
}
load();
onmessage = function (message) {
let mapIndex = message.data[0];
let pointCount = message.data[1];
console.log(mapIndex);
let myRoot = nodePages[mapIndex];
loadData(myRoot, pointCount);
};
The issue might be due to creating multiple worker threads in a loop, which could lead to a large number of worker threads and result in high memory usage and ultimately lead to freezing of the browser.
One approach to resolve this issue is to limit the number of worker threads created at a time by setting a maximum number of workers, and creating new workers only when some workers have completed their tasks.
Here's an example of the code that implements this approach:
const MAX_WORKERS = 4;
let promises = [];
let workerCount = 0;
function createWorker(data1, data2) {
return new Promise((resolve) => {
let worker = new Worker();
worker.postMessage(data1, data2);
worker.onmessage = (event) => {
postMessageRes = event.data;
if (postMessageRes == 200) {
// loadCOPC();
} else {
workerCount += 1;
let position = postMessageRes[0];
let color = postMessageRes[1];
for (let i = 0; i < position.length; i++) {
positions.push(position[i]);
colors.push(colors[i]);
}
if (workerCount === MAX_WORKERS) {
workerCount = 0;
promises = [];
}
resolve(true);
}
};
});
}
for (let m = 0; m < keyCountMap.length; ) {
let remaining = totalNodes - doneCount;
let numbWorker = Math.min(chunk, remaining);
for (let i = 0; i < numbWorker; i++) {
promises.push(createWorker([keyCountMap[m], keyCountMap[m + 1]]));
m += 2;
}
if (workerCount === MAX_WORKERS) {
Promise.all(promises).then((response) => {
console.log("one chunk finishes");
});
}
}
I have a problem in sending file through webscoket in browser with js to c# server.
When I want to save the buffer, if it is a txt file, there is no problem
But if it is a photo, for example, the photo will not be opened after saving
Where am I going wrong?
private void BeginReceive()
{
try
{
Buffer = new byte[BufferSize];
Socket.BeginReceive(Buffer, 0, BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), this);
}
catch (SocketException ex)
{
Console.WriteLine("{0}: {1}", "BeginReceive", ex.Message);
Close();
}
catch (Exception ex)
{
Console.WriteLine("{0}: {1}", "BeginReceive", ex.Message);
}
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
if (CheckState(ar))
{
int bytesRead = Socket.EndReceive(ar);
Console.WriteLine("{0}: {1}", "bytesRead: ", bytesRead);
File.WriteAllText(#"c:\01.jpg", Helper.DecodedBytes(Buffer, bytesRead));
}
}
catch (SocketException)
{
Close();
}
catch (Exception ex)
{
Console.WriteLine("{0}: {1}", "ReceiveCallback", ex.Message);
}
finally
{
if(!Helper.SocketIsDisposed(Socket)) BeginReceive();
}
}
I think the problem is with the decode function
public static string DecodedBytes(byte[] buffer, int length)
{
if(buffer[0] == 136 && (buffer[1] == 130 || buffer[1] == 128)) throw new System.Net.Sockets.SocketException(10054); //[10054]: Connection reset by peer
byte b = buffer[1];
int dataLength = 0;
int totalLength = 0;
int keyIndex = 0;
if (b - 128 <= 125)
{
dataLength = b - 128;
keyIndex = 2;
totalLength = dataLength + 6;
}
if (b - 128 == 126)
{
dataLength = BitConverter.ToInt16(new byte[] { buffer[3], buffer[2] }, 0);
keyIndex = 4;
totalLength = dataLength + 8;
}
if (b - 128 == 127)
{
dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0);
keyIndex = 10;
totalLength = dataLength + 14;
}
if (totalLength > length)
throw new Exception("The buffer length is small than the data length");
byte[] key = new byte[] { buffer[keyIndex], buffer[keyIndex + 1], buffer[keyIndex + 2], buffer[keyIndex + 3] };
int dataIndex = keyIndex + 4;
int count = 0;
for (int i = dataIndex; i < totalLength; i++)
{
buffer[i] = (byte)(buffer[i] ^ key[count % 4]);
count++;
}
return Encoding.UTF8.GetString(buffer, dataIndex, dataLength);
}
and js code:
connect:function(){
var root = this;
root.websocket = new WebSocket(root.url);
root.websocket.binaryType = "arraybuffer";
root.websocket.onopen = () => root.fireEvent('connect');
root.websocket.onmessage = (e) => {
root.fireEvent('receive',JSON.parse(e.data));
}
window.addEventListener("beforeunload", function() {
root.websocket.onclose = function () {};
root.websocket.close();
});
},
sendFile:function(){
var root = this;
var file = document.getElementById('filename').files[0];
var loader = new FileReader();
loader.onload = (e) => {
var a = e.target.result;
root.controller.websocket.send(e.target.result);
}
loader.readAsText(file)
},
Problem solved
JS code changed to:
sendFile:function(){
var root = this;
var file = document.getElementById('filename').files[0];
var loader = new FileReader();
loader.onload = (e) => {
var byteArray = new Uint8Array(e.target.result);
root.controller.websocket.send(byteArray.buffer);
}
loader.readAsArrayBuffer(file);
}
and C# code changed to:
WriteAllText to WriteAllBytes
File.WriteAllBytes(#"c:\01.jpg", Helper.DecodedBytes(Buffer, bytesRead));
and Encoding.Default.GetString(buffer, dataIndex, dataLength) to buffer.Skip(dataIndex).ToArray()
return buffer.Skip(dataIndex).ToArray();
I am adding data to buffer with getOdds
getOdds = async(data) => {
send({"Command":"GetMatchMarkets","Params":data});
var message = JSON.stringify({"Command":"GetMatchMarkets","Params":data});
var length = Buffer.byteLength(message),
buffer = new Buffer(4 + Buffer.byteLength(message));
buffer.writeUInt32LE(length, 0);
buffer.write(message, 4);
client.write(buffer);
I WANT TO GET DATA FROM receiveCB getOdds
}
Read data with receiveBuff.
after.
I'm sending to receiveCB
function receiveBuff (buf) {
var offset = 0;
if (bytesToReceive === 0) {
if(buf.length < 4){ return; }
bytesToReceive = buf.readUInt32LE(0);
offset = 4;
}
var currentCommandBytes = Math.min(bytesToReceive, buf.length - offset);
receivedData += buf.slice(offset, offset + currentCommandBytes);
bytesToReceive -= currentCommandBytes;
if (bytesToReceive === 0) {
bytesToReceive = 0;
receiveCB(JSON.parse(receivedData));
receivedData = "";
}
if (currentCommandBytes < buf.length - offset) {
receiveBuff(buf.slice(currentCommandBytes+offset))
}
}
receiveCB = async(response) => {
return response;
}
I want to get data from receiveCB getOdds
Please Click image
I have noticed that when using google.maps location you can track the location of your car or walking very closely. Yet when I try this of code in javascript:
navigator.geolocation.getCurrentPosition(
success,
error, {
maximumAge: 600000,
timeout: 10000,
enableHighAccuracy: true
});
It returns success with an accuracy of 800m --> 1200m (meters) I must be totally wrong on how to accurately track location in javascript. I will be using window.setInterval( to update the position continuously! Thanks everyone.
Use watchPosition() instead of repeatedly calling getCurrentPosition().
You can also use position.coords.accuracy to decide if the reading is accurate enough for you to use. The value is in meters and can be thought of as a radius of a circle and you're in there somewhere.
If you want to spend the time/effort you can also provide a sophisticated filter and sanity checker such as: -
'use strict';
/* identification division.
* program-id. TravelManagerPolyfill.
* author. Richard Maher.
* version. 1.0
*/
// Simple polyfill paper-tiger
ServiceWorkerRegistration.prototype.travelManager
= new BackgroundGeolocation();
/*
* This module simulates the work that needs to be done by the UA or
* SW daemon in order to facilitate background geolocation on the web.
*
* Each client even in the same scope will have their own TravelManager
* object/registration. With individual "options" configuration.
*
* TODO: Ask someone why registrations just can't be done in Ultimate
* Web App manifests?
*
* NB: Please treat this as a black-box and concentrate on what it does
* and not how it does it. Most of the magic will be in the UA. Some of
* the proposed throttle functionality may be duplicating existing UA
* functionality and therefore be redundant.
*/
function BackgroundGeolocation()
{
const EARTH_RADIUS = 6371000;
const SHUSH = 60000;
const NUMBER = "number";
const STRING = "string";
const BOOLEAN = "boolean";
const DATE = "date";
const MIN_DATE = -864*Math.pow(10,13);
const MAX_DATE = 864*Math.pow(10,13);
const MSECS = 1000;
const TIMEOUT_IS_USELESS
= Number.POSITIVE_INFINITY;
const toRad = function(num){return num*Math.PI/180};
const DEFAULT_OPTIONS =
{
"maxSilence" : 900000,
"minSilence" : 4000,
"maxSnail" : 15000,
"minProgress": 15,
"maxAge" : 0,
"accurate" : true,
"dropDodgy" : false
}
var options = DEFAULT_OPTIONS;
var lastOptions = options;
var seqNum = 0;
var lastPos, trackerId, loiterTimer, deltaMetres, lastDrop,
replayTimer, timeDetent, spaceDetent, loiterDetent,
maxLocAge, accurate, maxSilence, watchCnt, acceptCnt,
lastUpdate, kept, broken, currActive, regId, subscription,
dropDodgy, mostConfident, leastConfident, recalibrateTimer
;
var OptionElem = function(name,valType,minValue,maxValue,shift)
{
this.name = name;
this.type = valType;
this.minValue = minValue;
this.maxValue = maxValue;
this.shift = shift;
return this;
}
var optionRules =
[
new OptionElem("maxSilence", NUMBER, 0,Number.POSITIVE_INFINITY,MSECS),
new OptionElem("minSilence", NUMBER, 0,SHUSH,MSECS),
new OptionElem("maxSnail", NUMBER, 0,Number.POSITIVE_INFINITY,MSECS),
new OptionElem("maxAge", NUMBER, 0,Number.POSITIVE_INFINITY,MSECS),
new OptionElem("minProgress",NUMBER, 0,Number.POSITIVE_INFINITY,1),
new OptionElem("accurate", BOOLEAN,0,1,0),
new OptionElem("dropDodgy", BOOLEAN,0,1,0),
];
var subscribe =
function(userOptions)
{
if(!navigator.geolocation)
return Promise.reject(new Error("Unsupported browser - No Geolocation support"));
if (regId) return Promise.resolve(subscription);
if (userOptions != undefined) {
parseOptions(userOptions);
lastOptions = options;
}
regId = ++seqNum;
subscription =
{
getId: function(){return regId},
setOptions: setOptions,
unsubscribe: unsubscribe
}
return new Promise((resolve, reject) =>
{
kept = resolve;
broken = reject;
getCurrent(startPosition, startError);
});
}
var getCurrent =
function(currentSuccess, currentFailure)
{
navigator.geolocation.getCurrentPosition(currentSuccess, currentFailure ,{
maximumAge: Number.POSITIVE_INFINITY,
timeout: TIMEOUT_IS_USELESS
});
}
var startPosition =
function(position)
{
kept(subscription);
kept = null;
broken = null;
fireTravelEvent({
"cmd":"start",
"position": pos4Net(position)
},function(){
lastPos = position;
watchCnt = 1;
acceptCnt = 0;
mostConfident = position.coords.accuracy.toFixed();
leastConfident = mostConfident;
startWatch();
loiterTimer = setTimeout(loiterLimit, loiterDetent);
})
}
var startError =
function(positionError){
regId = null;
broken(positionError);
broken = null;
kept = null;
}
var startWatch =
function()
{
trackerId = navigator.geolocation.watchPosition(filterLocation, locError, {
enableHighAccuracy: accurate,
maximumAge: maxLocAge,
timeout: TIMEOUT_IS_USELESS
});
recalibrateTimer = setTimeout(recalibrate, maxSilence);
}
var stopWatch =
function()
{
navigator.geolocation.clearWatch(trackerId);
clearTimeout(recalibrateTimer);
trackerId = null;
recalibrateTimer = null;
}
var fireTravelEvent =
function(msg,callback)
{
try {
currActive.postMessage(msg);
console.log("Msg Sent to SW");
if (callback) callback();
} catch (e) {
if (e.name == "InvalidStateError" || e.name == "TypeError") {
navigator.serviceWorker.ready
.then(reg => {
currActive = reg.active;
fireTravelEvent(msg, callback)})
} else {
throw e;
}
}
}
var vetOption = function(userOption,rule)
{
var result;
switch(rule.type){
case NUMBER:
case DATE:
result = Number(userOption*rule.shift);
if (Number.isNaN(result) || result < rule.minValue || result > rule.maxValue) {
result = null;
}
break;
case STRING:
result = String(userOption);
if (typeof result != STRING){
result = null;
}
break;
case BOOLEAN:
result = Boolean(userOption);
if (typeof result != BOOLEAN){
result = null;
}
break;
default:
console.log("Invalid data type '"+rule.type+"'")
}
if (result == null) {
console.log("Invalid parameter '"+rule.name+"'")
}
return result;
}
var setOptions =
function(userOptions)
{
parseOptions(userOptions);
for (var x in options) {
if (options[x] != lastOptions[x]){
stopWatch();
startWatch();
break;
}
}
lastOptions = options;
}
var parseOptions =
function(userOptions)
{
var rawOptions = userOptions || {};
for (var i=0; i<optionRules.length; i++){
var currOption = optionRules[i].name;
if ((currOption in rawOptions)){
var currentTarget = vetOption(rawOptions[currOption], optionRules[i]);
if (currentTarget){
options[currOption] = currentTarget;
} else {
console.log("Invalid option "+optionRules[i].name+" value = " + rawOptions[currOption])
}
}
}
for (var opt in rawOptions){
if (!optionRules.some(function(rule){return rule.name==this},opt)){
console.log("Unknown option '"+opt+"'")
}
}
timeDetent = options.minSilence;
maxSilence = options.maxSilence;
spaceDetent = options.minProgress;
loiterDetent = options.maxSnail;
maxLocAge = options.maxAge;
accurate = options.accurate;
dropDodgy = options.dropDodgy;
if (timeDetent > maxSilence){
timeDetent = maxSilence;
console.log("Minimum Silence overridden by Maximum Silence");
}
if (loiterDetent > maxSilence){
loiterDetent = maxSilence;
console.log("Maximum Snail overridden by Maximum Silence");
}
if (loiterDetent < timeDetent) {
loiterDetent = timeDetent;
console.log("Maximum Snail overridden by Minimum Silence");
}
return;
}
var locError =
function(error)
{
fireTravelEvent({"cmd":"error","error": {
"code": error.code,
"message": error.message
}});
}
var recalibrate =
function()
{
console.log("recalibrating");
stopWatch();
startWatch();
mostConfident = leastConfident;
}
var filterLocation =
function(position)
{
watchCnt++;
if (position.timestamp <= lastPos.timestamp) return;
var currTime = Date.now();
var updateDelta = currTime - lastUpdate;
var dropping = (updateDelta < timeDetent);
deltaMetres = calculateDistance(
position.coords.latitude,
position.coords.longitude,
lastPos.coords.latitude,
lastPos.coords.longitude)
if (deltaMetres.toFixed() < spaceDetent) return;
if (dropping) {
lastDrop = position;
if (!replayTimer)
replayTimer = setTimeout(moveReplay, (timeDetent - updateDelta));
return;
}
var giveOrTake = position.coords.accuracy.toFixed();
if (giveOrTake > leastConfident) leastConfident = giveOrTake;
if (giveOrTake < mostConfident ) mostConfident = giveOrTake;
if (dropDodgy &&
giveOrTake > spaceDetent &&
giveOrTake > deltaMetres &&
giveOrTake > (2*mostConfident)) {
return; // Not legit. Dicky phone tower or access point?
}
acceptCnt++;
clearTimeout(recalibrateTimer);
recalibrateTimer = setTimeout(recalibrate, maxSilence);
clearTimeout(loiterTimer);
loiterTimer = setTimeout(loiterLimit, loiterDetent);
fireTravelEvent({
"cmd":"travel",
"position":pos4Net(position)
})
lastPos = position;
lastUpdate = currTime;
}
var loiterLimit =
function()
{
loiterTimer = null;
fireTravelEvent({"cmd":"loiter","position":pos4Net(lastPos)})
}
var moveReplay =
function()
{
replayTimer = null;
if ((lastDrop.timestamp > lastPos.timestamp)) {
filterLocation(lastDrop);
}
}
var calculateDistance =
function(lat1, lon1, lat2, lon2){
var dLat = toRad(lat2 - lat1);
var dLon = toRad(lon2 - lon1);
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRad(lat1)) *
Math.cos(toRad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
var distance = EARTH_RADIUS * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return distance;
}
var unsubscribe =
function(cancel)
{
if (!regId) return Promise.resolve(false);
stopWatch();
if (loiterTimer) {
clearTimeout(loiterTimer);
loiterTimer = null;
}
if (replayTimer) {
clearTimeout(replayTimer);
replayTimer = null;
}
regId = null;
lastUpdate = 0;
if (cancel) {
return Promise.resolve(true);
} else {
return new Promise((resolve, reject) =>
{
kept = resolve;
broken = reject;
getCurrent(endPosition, endError);
});
}
}
var endPosition =
function(position)
{
fireTravelEvent({
"cmd":"end",
"position":pos4Net(position)
})
kept(true);
kept = null;
broken = null;
}
var endError =
function(error)
{
fireTravelEvent({"cmd":"error","error": {
"code": error.code,
"message": error.message
}});
broken(false);
broken = null;
kept = null;
}
var pos4Net =
function(pos)
{
return {
coords: {
latitude: pos.coords.latitude,
longitude: pos.coords.longitude,
accuracy: pos.coords.accuracy.toFixed()
},
timestamp: pos.timestamp
}
}
return {subscribe: subscribe};
}
This function is make "cards" array in target object, and I added some codes to draw each element in "cards" array(see the mark : this part), but it doesn't work. How can i do?
var player = {
cards = [];
};
function giveNCards(cardsArr, target, n) {
target.cards = [];
for (var i = 0; i < n; i++) {
target.cards.push(cardsArr.pop());
}
/////////// this part //////////
for (var i = 0; i < target.cards.length; i++) {
var cardImage = new Image();
cardImage.onload = (function(value) {
return function() {
ctx.drawImage(this, i * 100, 0);
}
})(i);
cardImage.src = "./images/" + target.cards[i] + ".png"
}
//////////////////////////////////////////
console.log(target.cards);
return target.cards;
}
cardImage is getting overwritten on each loop.
Variables (when defined with the 'var' keyword) are local to the function (function scope) and get 'hoisted' (moved to the top). What does that mean? Basically, your code will run like this:
function giveNCards(cardsArr, target, n) {
var i, cardImage // <-- cardImage defined first here (hoisted)
target.cards = [];
for (i = 0; i < n; i++) {
target.cards.push(cardsArr.pop());
}
for (i = 0; i < target.cards.length; i++ ){
cardImage = new Image();
cardImage.onload = (function(value){
return function(){
ctx.drawImage(this, i * 100, 0);
}
})(i);
cardImage.src = "./images/" + target.cards[i] + ".png"
}
console.log(target.cards);
return target.cards;
}
The simplest fix for this (though there are others) is to move the contents of your second for loop to another function which will give it it's own scope.
function giveNCards(cardsArr, target, n) {
var i
target.cards = [];
for (i = 0; i < n; i++) {
target.cards.push(cardsArr.pop());
}
for (i = 0; i < target.cards.length; i++ ){
renderCard(target, i)
}
console.log(target.cards);
return target.cards;
}
function renderCard(target, i) {
var cardImage = new Image();
cardImage.onload = (function(value){
return function(){
ctx.drawImage(this, i * 100, 0);
}
})(i);
cardImage.src = "./images/" + target.cards[i] + ".png"
}
Pay attention, the i value that you used inside the IFFE is from the global scope, therefore you always modifies the same value
var player = {
cards = [];
};
function giveNCards(cardsArr, target, n) {
target.cards = [];
for (var i = 0; i < n; i++) {
target.cards.push(cardsArr.pop());
}
/////////// this part //////////
for ( var i = 0; i < target.cards.length; i++ ){
var cardImage = new Image();
cardImage.onload = (function(value){
return function(){
ctx.drawImage(this, value * 100, 0);
// -------------------^
}
})(i);
cardImage.src = "./images/" + target.cards[i] + ".png"
}
//////////////////////////////////////////
console.log(target.cards);
return target.cards;
}