Need to upsample audio from 8k to 16k - javascript

I was using below code for downsampling my audio while recording
let inputSampleRate; let inputBuffer = [];
function init(x) {
inputSampleRate = x;
}
function process(inputFrame) {
for (let i = 0; i < inputFrame.length; i++) {
inputBuffer.push((inputFrame[i]) * 32767);
}
const PV_SAMPLE_RATE = 16000;
const PV_FRAME_LENGTH = 512;
while ((inputBuffer.length * PV_SAMPLE_RATE / inputSampleRate) > PV_FRAME_LENGTH) {
let outputFrame = new Int16Array(PV_FRAME_LENGTH);
let sum = 0;
let num = 0;
let outputIndex = 0;
let inputIndex = 0;
while (outputIndex < PV_FRAME_LENGTH) {
sum = 0;
num = 0;
while (inputIndex < Math.min(inputBuffer.length, (outputIndex + 1) * inputSampleRate / PV_SAMPLE_RATE)) {
sum += inputBuffer[inputIndex];
num++;
inputIndex++;
}
outputFrame[outputIndex] = sum / num;
outputIndex++;
}
postMessage(outputFrame);
inputBuffer = inputBuffer.slice(inputIndex);
}
}
Can anyone please suggest how can I edit this one, so that it can be used to upsample my audio from 8k to 16k?

The traditional way to do upsampling (and downsampling) can be found at Wikipedia article about upsampling.
If you want something really cheap and dirty, just linearly interpolate between samples. So if you have samples x0 and x1, the upsampled values are y0=x0, y2=x1, and the new sample y1=(x0+x1)/2. This isn't great and you might hear artifacts.
Edit:
In your code, you can try something like this:
s0 = inputBuffer[inputIndex];
s1 = inputBuffer[inputIndex + 1];
outputFrame[outputIndex] = s0;
outputFrame[outputIndex + 1] = (s0 + s1)/2;
outputFrame[outputIndex + 2] = s1
You'll have to keep track of the indices so you don't try to access beyond the length of the arrays. This is totally untested.

Related

Issues with understanding Javascript yarn pattern program

I came across the Javascript YarnPattern program from Stanford's CS106AX. However, as I was going through the code, I have difficulty understanding some lines.
In the main function YarnPattern(), I don't quite understand the bold chunk written between the stars " ** ". I'm not quite sure what it means when the variables are initialised with thisPeg = 0 and nextPeg = -1.
Also, I'm not quite sure about the line "nextPeg = (thisPeg + DELTA) % pegs.length". I was wondering how this line of code can help reaching a suitable peg's index position.
It would be great if someone can provide a clear explanation with regards to these lines of code. Your help is much appreciated.
// Constants
const GWINDOW_WIDTH = 1000;
const GWINDOW_HEIGHT = 625;
const N_ACROSS = 80;
const N_DOWN = 50;
const DELTA = 113;
// main function
function YarnPattern() {
let gw = GWindow(GWINDOW_WIDTH, GWINDOW_HEIGHT);
let pegs = createPegArray(GWINDOW_WIDTH, GWINDOW_HEIGHT, N_ACROSS, N_DOWN);
**let thisPeg = 0;
let nextPeg = -1;
while (thisPeg !== 0 || nextPeg === -1) {
nextPeg = (thisPeg + DELTA) % pegs.length;
let p0 = pegs[thisPeg];
let p1 = pegs[nextPeg];
let line = GLine(p0.x, p0.y, p1.x, p1.y);**
line.setColor("Magenta");
gw.add(line);
thisPeg = nextPeg;
}
}
function createPegArray(width, height, nAcross, nDown) {
let dx = width / nAcross;
let dy = height / nDown;
let pegs = [ ];
for (let i = 0; i < nAcross; i++) {
pegs.push(Point(i * dx, 0));
}
for (let i = 0; i < nDown; i++) {
pegs.push(Point(nAcross * dx, i * dy));
}
for (let i = nAcross; i > 0; i--) {
pegs.push(Point(i * dx, nDown * dy));
}
for (let i = nDown; i > 0; i--) {
pegs.push(Point(0, i * dy));
}
return pegs;
}
function Point(x, y) {
if (x === undefined) {
x = 0;
y = 0;
}
return { x: x, y: y };
}

Counting duplicate random numbers from a for loop

I am trying to create a score predictor based on a teams goal difference (football). I am new to JavaScript, and I have managed to get this far.
I want it to be like spinning a ten-sided dice 20 times + the team's goal difference. I have got this bit sorted I think. With my code now I have a list of random numbers logged in the console which is what I wanted. Now I would like to choose a number (e.g., 2) and see how many times this occurs in the list. I'd like to save this in a new variable called homeFinalScore (So if '2' occurs three times in the list of random numbers, the homeFinalScore variable should be 3). I've tried several things but have been unable to sort it yet!
Any help would be extremely helpful. Thank you in advance!
var homeFinalScore = 0;
function calculateScore(){
var homeTeam = document.getElementById("HomeTeam").value;
var awayTeam = document.getElementById("AwayTeam").value;
var homeGd = parseInt(document.getElementById("HomeGD").value);
var awayGd = parseInt(document.getElementById("AwayGD").value);
var homeGd = 20 + homeGd;
var awayGd = 15 + awayGd;
for (i = 0; i < homeGd; i++) {
var randNum = Math.floor(Math.random() * 11);
console.log(randNum);
}
}
You can create an array, use Array.prototype.push() to push randNum to the array, then use Array.prototype.filter(), .length to determine how many occurrences of a value are present within array.
var homeGd = 20 + 2;
var awayGd = 15 + 2;
var arr = [];
function countOccurrences(n, arr) {
return arr.filter(function(value) {
return value === n
}).length;
}
for (i = 0; i < homeGd; i++) {
var randNum = Math.floor(Math.random() * 11);
arr.push(randNum);
}
console.log(arr);
console.log(countOccurrences(2, arr));
Alternatively, you can increment a variable when randNum is equal to a value.
var homeGd = 20 + 2;
var awayGd = 15 + 2;
var n = 0;
var num = 2;
for (i = 0; i < homeGd; i++) {
var randNum = Math.floor(Math.random() * 11);
console.log(randNum);
if (randNum === num) {
++n
}
}
console.log("occurrences of 2:", n);
const homeGd = 10;
const randomNumbers = []; // array of random numbers
for (i = 0; i < homeGd; i++) {
randomNumbers.push(Math.floor(Math.random() * 11));
}
const countBy = randomNumbers.reduce((acc, current) => {
acc[current] = (acc[current] || 0) + 1;
return acc;
}, {});
console.log(countBy);

JS: get x amount of random numbers inside interval with while loop

I'm working on a card game with six available suits, but there can be 2-6 players and I only want the number of suits to match the amount of players.
let players = 4
const suits = [1, 2, 3, 4, 5, 6]
Of course, I want them to come out randomly.
I have come up with the following solution:
export function getRandomSuits(nrOfPlayers) {
const rangeMin = 1;
const rangeMax = 6;
const amount = nrOfPlayers;
let nums = [];
let getRandomNumber = function() {
return Math.floor((Math.random() * rangeMax) + rangeMin);
}
while (nums.length < amount) {
let nr = getRandomNumber();
var numberExists = _.indexOf(nums, nr);
console.log(numberExists);
if(numberExists < 0) {
nums.push(nr);
}
}
return nums;
}
It's been a while since I used the "while loop" thingy, so I'm not feeling very comfortable with it.
My questions are:
Is this good practice or is there a better solution?
Are there any performance or other practical reasons why this solution is bad?
Am I overthinking it?
In my opinion, I see no need of the function getRandomNumber(). Nonetheless, this is up to preference.
I would go with:
export function getRandomSuits(nrOfPlayers) {
const rangeMin = 1;
const rangeMax = 6;
let nums = [];
while (nums.length < nrOfPlayers) {
let nr = Math.floor((Math.random() * rangeMax) + rangeMin);
var numberExists = _.indexOf(nums, nr);
if(numberExists < 0) {
nums.push(nr);
}
}
return nums;
}
Not necessarily. Merely a matter of cleanliness and preference.
Maybe? :-)
Second method (Slightly better) with a temporary array:
export function getRandomSuits(nrOfPlayers) {
const rangeMin = 1;
const rangeMax = 6;
var tempNum = [];
for(i = 0; i <= rangeMax - rangeMin; i++){
tempNum[i] = rangeMin + i;
}
let nums = [];
while (nums.length < nrOfPlayers) {
let index = Math.floor(Math.random() * tempNum.length);
var numberExists = _.indexOf(nums, tempNum[index]);
if(numberExists < 0) {
nums.push(tempNum[index]);
tempNum.splice(tempNum, index));
}
}
return nums;
}

Downsampling a PCM audio buffer in javascript

I am attempting to downsample the sample rate i am getting from audioContext. I believe it is coming in at 44100, and i want it to be 11025. I thought i could just average every 3 samples and it plays back at the correct rate, but the pitch of is too high, as if we were all on helium.
What is the correct way to downsample a float32Array from 44100 to a int16Array at 11025 samples.
var context = new Flash.audioContext();
var audioInput = context.createMediaStreamSource(stream);
var recorder = context.createScriptProcessor(null, 1, 1);
recorder.onaudioprocess = onAudio;
audioInput.connect(recorder);
recorder.connect(context.destination);
var onAudio = function (e) {
var left = e.inputBuffer.getChannelData(0);
bStream.write(Flash.convertFloat32ToInt16(left));
}
var convertFloat32ToInt16 = function(buffer) {
var l = buffer.length;
var point = Math.floor(l/3);
var buf = new Int16Array(point);
for (var x = l; x > 0;) {
var average = (buffer[x] + buffer[x-1] + buffer[x-2]) / 3;
buf[point] = average*0x7FFF;
point -= 1;
x -= 3;
}
return buf.buffer;
}
For anyone else who needs the answer.
var downsampleBuffer = function (buffer, sampleRate, outSampleRate) {
if (outSampleRate == sampleRate) {
return buffer;
}
if (outSampleRate > sampleRate) {
throw "downsampling rate show be smaller than original sample rate";
}
var sampleRateRatio = sampleRate / outSampleRate;
var newLength = Math.round(buffer.length / sampleRateRatio);
var result = new Int16Array(newLength);
var offsetResult = 0;
var offsetBuffer = 0;
while (offsetResult < result.length) {
var nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
var accum = 0, count = 0;
for (var i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {
accum += buffer[i];
count++;
}
result[offsetResult] = Math.min(1, accum / count)*0x7FFF;
offsetResult++;
offsetBuffer = nextOffsetBuffer;
}
return result.buffer;
}
this will return a sample rate smaller than the one passed to it, while also converting it to a int16Array instead of a float32.

Variable scope within methods

I was teaching myself how to make a binary genetic algorithm the other day. The goal was to make it so that it would match a randomly generated 35 length binary string. I ran into a problem where methods were editing variables that I did not think were in its scope. This caused my solution to slowly degrade in fitness instead of increase! After I found out where this was happening I fixed it by newP[0].join('').split('') so that newP[0] itself would not be edited. For convenience I've marked where the problem was happening below in comments.
While I have fixed this problem I'd like to hopefully get an understanding as to why this happens and also prevent without doing the join/split silliness.
Here is the code:
var GeneticAlgorithm = function () {};
GeneticAlgorithm.prototype.mutate = function(chromosome, p) {
for(var i = 0; i < chromosome.length; i++) {
if(Math.random() < p) {
chromosome[i] = (chromosome[i] == 0) ? 1 : 0;
}
}
return chromosome;
};
GeneticAlgorithm.prototype.crossover = function(chromosome1, chromosome2) {
var split = Math.round(Math.random() * chromosome1.length);
var c1 = chromosome1;
var c2 = chromosome2;
for(var i = 0; i < split; i++) {
c1[i] = chromosome2[i];
c2[i] = chromosome1[i];
}
return [c1, c2];
};
// fitness = function for finding fitness score of an individual/chromosome (0-1)
// length = length of string (35)
// p_c = percentage chance of crossover (0.6)
// p_m = percentage change of mutation (0.002)
GeneticAlgorithm.prototype.run = function(fitness, length, p_c, p_m, iterations) {
var iterations = 100;
var size = 100;
// p = population, f = fitnesses
var p = [];
var f = [];
for(var i = 0; i < size; i++) {
p.push(this.generate(length));
f.push(fitness(p[i].join('')));
}
while( iterations > 0 && Math.max.apply(Math, f) < 0.999 ) {
var mates = [];
var newP = [];
var newF = [];
mates = this.select(p, f);
newP.push(mates[0], mates[1]);
while(newP.length < size) {
/*-------------------> Problem! <-------------------*/
mates = [newP[0].join('').split(''), newP[1].join('').split('')];
/*
* If I passed newP[0] when mates[0] changed newP[0] would also change
*/
if(Math.random() < p_c) {
mates = this.crossover(mates[0], mates[1]);
}
mates[0] = this.mutate(mates[0], p_m);
mates[1] = this.mutate(mates[1], p_m);
newP.push(mates[0], mates[1]);
}
for(var i = 0; i < size; i++) {
newF.push(fitness(newP[i].join('')));
}
p = newP;
f = newF;
iterations--;
}
return p[f.indexOf(Math.max.apply(Math, f))].join('');
};

Categories