node js await buffer - javascript

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

Related

not able to make multiple work group working and causing freeze

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");
});
}
}

How to repeat a function without effecting repeating the token

How do I modify the repeat loop function without it repeating the token variable in the function rowcount()?
function rowcount()
{
var token = getAccessToken();
var module = "sHistory";
var rows = 0;
var go = true;
var i = 1;
var data;
while (go) {
data = getRecordsByPage(i,200,token,module);
if (Number(data.info.count) < 200) {
go = false;
};
if ((i%10) == 0) {
go = false;
}
rows = Number(rows) + Number(data.info.count);
i++;
Logger.log(rows)
}
return rows
}
function repeatloop()
{
let counter = 0;
for(var i = 1; i <= 93; i++)
{
{
Utilities.sleep(10000);
Logger.log(i);
counter += rowcount();
Logger.log(counter);
}
}
return rowcount().rows;
}
What I am also trying to do is let the count continue because right now it goes in an increment of 2000, but I need it to continue like 200...400..600..800...1000...1200...1400...1600...1800...2000...2200. and it goes on until the loop stops.
You can make the token a global variable like this:
var token;
function rowcount()
{
var module = "sHistory";
var rows = 0;
var go = true;
var i = 1;
var data;
while (go) {
data = getRecordsByPage(i,200,token,module);
if (Number(data.info.count) < 200) {
go = false;
};
if ((i%10) == 0) {
go = false;
}
rows = Number(rows) + Number(data.info.count);
i++;
Logger.log(rows)
}
return rows
}
function repeatloop()
{
let counter = 0;
token = getAccessToken();
for(var i = 1; i <= 93; i++)
{
{
Utilities.sleep(10000);
Logger.log(i);
counter += rowcount();
Logger.log(counter);
}
}
return rowcount().rows;
}
Or did I understand you wrong?
I would pass the token as an optional parameter, insted to use GLOBAL variable:
function rowcount(_token = null)
{
let token;
if (_token) {token = _token;}
else {token = getAccessToken();}
var module = "sHistory";
var rows = 0;
var go = true;
var i = 1;
var data;
while (go) {
data = getRecordsByPage(i,200,token,module);
if (Number(data.info.count) < 200) {
go = false;
};
if ((i%10) == 0) {
go = false;
}
rows = Number(rows) + Number(data.info.count);
i++;
Logger.log(rows)
}
return rows
}
function repeatloop()
{
let token = getAccessToken();
let counter = 0;
for(var i = 1; i <= 93; i++)
{
{
Utilities.sleep(10000);
Logger.log(i);
counter += rowcount(token);
Logger.log(counter);
}
}
return rowcount(token).rows;
}

For loops breaks when using await

I have a problem when using await in a for loop. Every time it hits the await funtion it executes it fine, but it stops loping through the rest of the array that is looping through. I'm using nodejs with axios to send http request to a restAPI. The function that is in the api is big. So I thought maby that is the problem but it wasn't. Also the api uses its own await functions to, but that wasn't the problem either(As far as I know).
Here is my code for the request
var files = await globPromise("assets/series/**/*.json");
var json = null;
var file = null;
var series = [];
for (i = 0; i < files.length; i++) {
file = files[i];
var raw = fs.readFileSync(file);
json = JSON.parse(raw);
if (json.type === "series") {
try {
var userId = null;
if (req.user == null) {
userId = req.query.userid;
} else {
userId = req.user.id;
}
const response = await axios.get("http://" + host + "/series/info/" + json.id + "?userid=" + userId);
series.push(JSON.parse(response.data));
} catch (error) {
console.log(error);
series.push(json);
}
}
}
res.json(JSON.stringify(series));
});
And also the api side:
app.get('/series/info/:id', async(req, res) => {
var files = await globPromise("assets/series/**/*.json");
var json = null;
var file = null;
for (i = 0; i < files.length; i++) {
file = files[i];
var raw = fs.readFileSync(file);
json = JSON.parse(raw);
if (json.type === "series" && json.id === req.params.id) {
break;
}
json = null;
}
let path = pathFs.dirname(file) + "/";
try {
var seriesFiles = await fsPromise.readdir(path);
var latestWatchedVideo = null;
var latestWatchedTime = null;
var latestWatchTime = null;
var latestWatchDuration = null;
var seasonCount = 0;
var seasons = [];
for (i = 0; i < seriesFiles.length; i++) {
seriesFile = seriesFiles[i];
if (fs.lstatSync(path + "/" + seriesFile).isDirectory()) {
if (!seriesFile.startsWith("s")) {
continue;
}
seasonCount++;
try {
var videoFiles = await fsPromise.readdir(path + "/" + seriesFile + "/");
var videos = [];
for (let i = 0; i < videoFiles.length; i++) {
const video = videoFiles[i];
if (video.endsWith(".json")) {
var rawVideo = fs.readFileSync(path + "/" + seriesFile + "/" + video);
videoJson = JSON.parse(rawVideo);
const query = util.promisify(con.query).bind(con);
var userId = null;
if (req.user == null) {
userId = req.query.userid;
} else {
userId = req.user.id;
}
var results = await query(`SELECT * FROM watched WHERE video_id = "${videoJson.id}" AND user_id = "${userId}"`);
if (results.length > 0) {
var updated = JSON.parse(JSON.stringify(results[0].updated));
var duration = JSON.parse(JSON.stringify(results[0].duration));
var time = JSON.parse(JSON.stringify(results[0].time));
if (latestWatchedVideo == null) {
latestWatchedVideo = videoJson.id;
latestWatchedTime = updated;
latestWatchTime = time;
latestWatchDuration = duration;
} else {
if (latestWatchedTime < updated) {
latestWatchedVideo = videoJson.id;
latestWatchedTime = updated;
latestWatchTime = time;
latestWatchDuration = duration;
}
}
}
videos.push(videoJson);
}
}
function compare(a, b) {
if (a.episode < b.episode) {
return -1;
}
if (a.episode > b.episode) {
return 1;
}
return 0;
}
videos.sort(compare);
seasons.push({
season: seasonCount,
title: seriesFile.replace("s" + seasonCount, ""),
videos: videos
});
} catch (error) {
console.log(error);
}
}
}
json.seasonCount = seasonCount;
json.seasons = seasons;
json.latestWatchDuration = latestWatchDuration;
json.latestWatchTime = latestWatchTime;
json.latestWatchedVideo = latestWatchedVideo;
json.latestWatchedTime = latestWatchedTime;
res.json(JSON.stringify(json));
} catch (error) {
console.log(error);
}
});
Is there something (important) about await and async that I've missed?
Edit: my problem is that is loops fine through the first item and the await is working fine too, but it stops the loop and executes the next lines of code like there are no other items in my array.
Solved: I tried using the for/of and it works now. I don't know whats is so different between de default for and this one but it works!

JavaScript : updating DOM in loop with Promise

This is bothering my mind for a few weeks now. I have a working example of some Javascript that updates the DOM within a loop.
But I can't get this "trick" to work for my real code.
This is the link to my Plunk: https://plnkr.co/edit/oRf6ES74TJatPRetZEec?p=preview
The HTML:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css">
<link rel="stylesheet" href="style.css">
<script src="http://code.jquery.com/jquery-1.12.4.js"></script>
<script src="http://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="js/script.js"></script>
<script src="js/script2.js"></script>
<script src="js/json-util.js"></script>
<script src="js/scorito-dal.js"></script>
<script src="js/matching.js"></script>
</head>
<body>
<div id="log">..log here..</div>
<button onclick="doHeavyJob();">do heavy job</button>
<button onclick="doHeavyJobJquery();">do heavy job with jQuery</button>
<button onclick="match();">match</button>
</body>
</html>
The script in js/script.js, which is called from the button "do heavy job" works:
async function doHeavyJob() {
$('#log').html('');
for (var j=0; j<10; j++) {
await sleep(1000)
console.log('Iteration: ' + j);
(function (j) {
setTimeout(function() { // pause the loop, and update the DOM
var logPanel = document.getElementById('log');
var txt = 'DOM update' + j;
logPanel.innerHTML += txt + ', ';
}, 0);
})(j);
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
The exact same code does not work within a jQuery each(), this is appearantly due to jQuery, and easy to work around by using the for-loop. If you're interested, check script2.js in my plunk.
My real script in js/matching, which is called from the button "match" does NOT work.
var _keeperCombinations = [];
var _defenderCombinations = [];
var _midfielderCombinations = [];
var _attackerCombinations = [];
var _mostPoints = 0;
var _bestCombination = [];
function updateStatus(keeperCount, ixKeeper, msg) {
msg = '%gereed: ' + Math.round(ixKeeper / keeperCount * 100, 0);
console.log(msg);
$('#log').html(msg);
}
function match() {
$('#log').html('');
updateStatus(1, 1, 'Match started');
var playersData = scoritoDal.getPlayersData();
this.determineKeepers(playersData);
this.determineDefenders(playersData);
this.determineMidfielders(playersData);
this.determineAttackers(playersData);
var keeperCount = _keeperCombinations.length
for (var ixKeeper=0; ixKeeper<keeperCount; ixKeeper++) {
var keepers = _keeperCombinations[ixKeeper];
doMatching(keepers, keeperCount, ixKeeper);
}
if (_bestCombination.length === 0) {
alert('Er kon geen beste combinatie worden bepaald');
}
else {
alert('Ready: ' + _bestCombination.toString());
$(_bestCombination).each(function(ix, playerId) {
});
}
}
/*
* Match 2 keepers, 5 defenders, 6 midfielders and 5 attackers
* First pick the 5 best keepers, 10 best defenders, 12 best midfielders and 10 best attackers.
* 3 / 2 (k), 7 / 3 (d & a) and 8 / 4 (m) >> most points / most points per prices
*/
var _confirmed = false;
function doMatching(keepers, keeperCount, ixKeeper) {
// Make a new promise
let p = new Promise(
// The executor function is called with the ability to resolve or reject the promise
(resolve, reject) => {
for (var ixDefenders=0; ixDefenders<_defenderCombinations.length; ixDefenders++) {
var defenders = _defenderCombinations[ixDefenders];
for (var ixMidfielders=0; ixMidfielders<_midfielderCombinations.length; ixMidfielders++) {
var midfielders = _midfielderCombinations[ixMidfielders];
for (var ixAttackers=0; ixAttackers<_attackerCombinations.length; ixAttackers++) {
var attackers = _attackerCombinations[ixAttackers];
procesPlayers(keepers, defenders, midfielders, attackers);
}
}
resolve(ixDefenders);
}
});
p.then(
function(ixDefenders){
console.log('LOG: ' + keeperCount + " - " + ixKeeper);
updateStatus(keeperCount, ixKeeper);
}
);
}
async function procesPlayers(keepers, defenders, midfielders, attackers) {
var totals = calculateTotals(keepers, defenders, midfielders, attackers);
// check if total price is within budget
if (totals.TotalPrice <= 56500000) {
if (totals.TotalPoints > _mostPoints) {
_mostPoints = totals.TotalPoints;
setBestCombination(keepers, defenders, midfielders, attackers);
}
}
}
function calculateTotals(keepers, defenders, midfielders, attackers) {
var playerIds = [];
var totalPoints = 0;
var totalPrice = 0;
var allPlayers = keepers.concat(defenders, midfielders, attackers);
var checkTeams = [];
$(allPlayers).each(function(ix, player){
var club = checkTeams.find(t => t.Name === player.Club);
if (!club) {
club = {"Name":player.Club, "Count":1};
checkTeams.push(club);
}
else club.Count++;
if (club.Count > 4) {
totalPoints = 0;
return false;
}
playerIds.push(player.ID);
var factor = 1;
if (player.Position === 'Keeper' && ix > 0) factor = 0.5; // 2nd keeper gets less points
if (player.Position === 'Defender' && ix > 2) factor = 0.5; // 4th defender gets less points
if (player.Position === 'Midfielder' && ix > 3) factor = 0.5; // 5th midfielder gets less points
if (player.Position === 'Attacker' && ix > 2) factor = 0.5; // 4th attacker gets less points
var playerPoints = player.Points * factor;
totalPoints += playerPoints;
totalPrice += player.Price;
});
return {"TotalPoints":totalPoints,"TotalPrice":totalPrice};
}
function determineKeepers(playersData) {
console.log('Determining keepers');
$('#progres').text('Determine 5 best keepers');
var bestKeepers = this.determineBestPlayers(playersData, 'Keeper', 3, 2); // 3, 2
if (bestKeepers && $(bestKeepers).length > 0) {
// now determine all combinations
this.determineCombinations(bestKeepers, 2);
_keeperCombinations = this._combinations;
}
}
function determineDefenders(playersData) {
console.log('Determining defenders');
$('#progres').text('Determining 10 best defenders');
var bestDefenders = this.determineBestPlayers(playersData, 'Defender', 5, 3); // 6, 3
if (bestDefenders && $(bestDefenders).length > 0) {
// now determine all combinations
this.determineCombinations(bestDefenders, 5);
_defenderCombinations = this._combinations;
}
}
function determineMidfielders(playersData) {
console.log('Determining midfielders');
$('#progres').text('Determine 12 best midfielders');
var bestMidfielders = this.determineBestPlayers(playersData, 'Midfielder', 5, 3); // 7, 3
if (bestMidfielders && $(bestMidfielders).length > 0) {
// now determine all combinations
console.log('*** Determining all midfielder combinations ***');
this.determineCombinations(bestMidfielders, 6);
_midfielderCombinations = this._combinations;
}
}
function determineAttackers(playersData) {
console.log('Determining attackers');
$('#progres').text('Determine 10 best attackers');
var bestAttackers = this.determineBestPlayers(playersData, 'Attacker', 5, 3); // 6, 3
if (bestAttackers && $(bestAttackers).length > 0) {
// now determine all combinations
this.determineCombinations(bestAttackers, 5);
_attackerCombinations = this._combinations;
}
}
/*
* Determine the best players for a position
* - position: Keeper|Defender|Midfielder|Attacker
* - byPoints: the nr of best players by points
* - byFactor: the nr of best players by factor
*/
function determineBestPlayers(playersData, position, byPoints, byFactor) {
var players = $.grep(playersData, function(p) {return p.Position === position});
var playersByPoints = players.sort(jsonUtil.sortByProperty('Points', true));
var bestPlayersByPoints = playersByPoints.slice(0, byPoints);
var playersByFactor = players.sort(jsonUtil.sortByProperty('Factor', true));
var bestPlayersByFactor = playersByFactor.slice(0, byFactor);
// players could be in both lists, make it unique
var bestPlayers = jsonUtil.joinArrays(bestPlayersByPoints, bestPlayersByFactor, true, 'ID');
// if not the nr wanted, add extra players
// take theze by turn on points / on factor
var cnt = $(bestPlayers).length;
var nextByPoints = true;
var cntExtra = 0;
while (cnt < byPoints + byFactor) {
cntExtra++;
var isOK = false;
while (!isOK) {
var ix=0; // if the next player is already chosen, move to the next
var extraPlayer = {};
if (nextByPoints) {
extraPlayer = playersByPoints[byPoints+cntExtra+ix-1];
}
else {
extraPlayer = playersByFactor[byFactor+cntExtra+ix-1];
}
if (!bestPlayers.find(x => x.ID === extraPlayer.ID)) {
bestPlayers.push(extraPlayer);
isOK = true;
}
else {
ix++;
}
}
nextByPoints = !nextByPoints;
cnt++;
}
bestPlayers = bestPlayers.sort(jsonUtil.sortByProperty('Points', true)); // add the end we want the players sorted by total points
console.log('Best player for position ' + position);
console.log(bestPlayersToString(bestPlayers));
return bestPlayers;
}
function bestPlayersToString(bestPlayers) {
var result = '';
var sep = '';
$(bestPlayers).each(function(ix, player) {
result += sep;
result += JSON.stringify(player);
if (sep === '') sep = ',';
});
return result;
}
function setBestCombination(keepers, defenders, midfielders, attackers) {
_bestCombination = [];
$(keepers).each(function(ix, keeper){
_bestCombination.push(keeper.ID);
});
$(defenders).each(function(ix, defender){
_bestCombination.push(defender.ID);
});
$(midfielders).each(function(ix, midfielder){
_bestCombination.push(midfielder.ID);
});
$(attackers).each(function(ix, attacker){
_bestCombination.push(attacker.ID);
});
}
/* Combinations */
var _combinations = [];
var _curCombination = [];
function determineCombinations(players, cnt) {
_combinations = [];
_curCombination = [];
this.addCombinations(players, cnt);
}
/*
* Take 2 from 5 (keepers), 5 from 10 (defenders, attackera) or 6 from 12 (midfielders)
* It is a rather complex recursive method with nested iterations over the
* (remaining players).
*/
var _curLevel = 1;
function addCombinations(players, cnt) {
for (var i=0; i<players.length; i++) {
var player = players[i];
_curCombination.push(player);
if (_curCombination.length == cnt) {
_combinations.push(_curCombination.slice(0)); // slicing creates a clone
//console.log(curCombinationToString());
_curCombination.pop();
continue;
}
var curPlayers = players.slice(i+1);
_curLevel++;
addCombinations(curPlayers, cnt);
};
_curCombination.pop();
_curLevel--;
}
function curCombinationToString() {
var result = '';
var sep = '';
$(_curCombination).each(function(ix, player) {
result += sep;
result += JSON.stringify(player);
if (sep === '') sep = ',';
});
return result;
}
Any ideas will be greatly appreciated!!!!
finally got this to work, after numerous, NUMEROUS attempts.
And off course I'd like to share this with you.
Here's the essence to getting this to work:
the loop must be inside an async function
the code with the logic for 1 loop must be inside a Promise
the code also needs to be within setTimeout
the call for this logic must be 'annotated' with await
So this works:
'use strict';
async function testPromise() {
var msg;
for (var ix=1; ix<10; ix++) {
msg = 'Before loop #'+ix
$('#log').html(msg);
await doActualWork();
msg = 'After loop #'+ix
$('#log').html(msg);
}
}
/*
* Perform logic for one loop
*/
function doActualWork() {
return new Promise(
function (resolve, reject) {
window.setTimeout(
function () {
// do the actual work like calling an API
resolve();
}, 100);
}
);
}

Export buffer after pitch-shifting uploaded audio in JavaScripting

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!

Categories