How to calculate DB SPL given DB FS - javascript

I'm working on creating a volume fader using web audio api that allows for a gain of +12db. So far I've calculated the dbFS value for the peak meter.
How do I derive a dbSPL value given the dbFS? From what I understand, volume faders in daw use dbSPL while peak meters use dbFS. Somehow a conversion must be taking place. How do I implement the conversion ?
what I have so far:
//create random amplitude values ranging 0 to 1
let amplitudes = Array.from({ length: 16 }, (_,i) => Math.random() * 1);
//calculate average amplitude
let average = amplitudes.reduce((a, b) => a + b) / 16;
// derive dbFS of given average amplitude value
let dbFS = 20 * Math.log10(average)
//update input element value
peakMeter.value = dbFS;
Now I would like to do the same for volume fader
let dbSPL = //?????
fader.value = spl
//Translate dbSPL back to dbFS then update gain value
audioNode.gain.value = // ???
Any feedback would be helpful.

Related

Given min, max, mean and sample size, return a normal distribution as an array using javascript

For example, given:
min = 1
max = 5
Ave = 3
size = 5
ie. at this stage, we have [1,?,?,?,5]
How can I calculate the missing numbers? (This one is easy -> [1,2,3,4,5] - but how do I write a JS function to return this array)
or, given:
min = 23
max = 2500
Ave = 1007
size = 800
ie. at this stage, we have [23,?..,2500]
How can I calculate the missing numbers? (ie. return a completed array (I think this is called uniform distribution...))
Some info about the returned Array
The numbers in the returned array must be rounded to 2 decimal places.
Each number in the array is >= the preceding number.
I expect the distribution if plotted to return a shallow S shaped curve.
This is what I have so far:
const min = 1; //lowest value
const max = 5; //highest value
const ave = 4; //not currently used
const supply = 3; //supply (- 2 because min and max are appended)
const data = Array.from({length: supply}, (_, i) => i + 1);
const distArray = [];
data.map(n => {
distArray.push(Math.round((min+n*(max-min) / (supply+1)) * 1e2 ) / 1e2);
});
distArray.unshift(min);//append min value
distArray.push(max);//append max value
console.log(distArray); // returns [1,2,3,4,5]
The above function returns an array of length supply+2 between the min and max values.
The next step is to include the ave variable. What if you were now told that the average of the 5 numbers was 2.5, not 3 (as it is in the normal distribution from min to max). This would return a different array based on the average value... This is my question - how do I do that?
This is what I have tried, but it's wrong... It kinda works for small arrays, but for larger arrays, the distribution is all concentrated around the middle - I need a better formula to calculate the distribution...
const min = 5; //lowest value
const max = 12; //highest value
const supply = 5; //supply (- 2 because min and max are appended)
const ave = 8; //Average value
const data = Array.from({length: supply}, (_, i) => i + 1);
const distArray = [];
const ksum = (supply + 2) * ave; //known sum
const usum = ksum - (min + max); // unknown sum
const parts = data.reduce((a, n) => a + n, 0);//Num of remainder to share
const b = Math.floor(usum / supply); //base value to add to each
const r = usum % supply; //remainder to share in parts
data.map(n => {
distArray.push(Math.round((b + (n/parts * r)) * 1e2 ) / 1e2);
});
distArray.unshift(min);//append min value
distArray.push(max);//append max value
console.log(distArray); // returns [5, 7.27, 7.53, 7.8, 8.07, 8.33, 12]
Consider the following "math"
avg = sum(all) / size
sum(all) = sum(known) + sum(unknown)
sum(unknown) = avg x size - sum(known)
unknown = sum(unknown) / (size - size(known))
Would that work for you?

Is it possible to specify a range for the volume from AudioWorkletProcessor?

I followed the W3C's Documentation to implement a "vumeter" (code) using JavaScript's Web Audio API specifically the AudioWorkletProcessor and AudioWorkletNode interfaces. I would like to know why is the RMS level is compared with the previous volume multiplied by a "smoothing factor" (sorry I just started learning this topic so it's all new for me):
// Calculate the RMS level and update the volume.
rms = Math.sqrt(sum / samples.length);
this._volume = Math.max(rms, this._volume * SMOOTHING_FACTOR);
Also what is the range that the volume's variable can take? Is it possible to know so that we can assume that the volume will be a value in a 0...100 range?
Process method code:
process (inputs, outputs, parameters) {
const input = inputs[0];
// Note that the input will be down-mixed to mono; however, if no inputs are
// connected then zero channels will be passed in.
if (input.length > 0) {
const samples = input[0];
let sum = 0;
let rms = 0;
// Calculated the squared-sum.
for (let i = 0; i < samples.length; ++i)
sum += samples[i] * samples[i];
// Calculate the RMS level and update the volume.
rms = Math.sqrt(sum / samples.length);
this._volume = Math.max(rms, this._volume * SMOOTHING_FACTOR);
// Update and sync the volume property with the main thread.
this._nextUpdateFrame -= samples.length;
if (this._nextUpdateFrame < 0) {
this._nextUpdateFrame += this.intervalInFrames;
this.port.postMessage({volume: this._volume});
}
}
// Keep on processing if the volume is above a threshold, so that
// disconnecting inputs does not immediately cause the meter to stop
// computing its smoothed value.
return this._volume >= MINIMUM_VALUE;
}

How implement logic for increment by 100 on random bids in JS

I am testing Max Bid functionality via Cypress but I need to randomly bid the amount in a way that it will be incremented by 100.
var minval = 100; var maxval = 1000000; var bidAmount = Math.floor(Math.random() * maxval) + minval
The logic is given above is for any random amount.
When I implement this logic I get this error: "Bids should be incremented by 100, input was 589"
Please share your ideas. Thank you!
Several errors to fix
The most important is that you have done nothing to ensure the bid amounts are multiples of 100. A simple way to do this is to divide by 100, round to the nearest integer, and then multiply by 100.
Second, it would be good to set minval to a real minimum acceptable value, namely 100.
Third, if you really want the values to go between minval and maxval, you should multiply the Math.random() by (maxval-minval) so that when you add minval, it will be in the range you want.
const minval = 100;
const maxval = 1000000;
const rawBidAmount = Math.floor(Math.random() * (maxval-minval)) + minval
const roundedBidAmount = 100*Math.round(rawBidAmount/100)
console.log(roundedBidAmount)

A DFT analysis in a low speed data sampling

I have some sample data of vibrations analysis from sensors installed on electrical motors. The sampling is made once or, at most, 3 times per day. The values can be expressed in g, gE or mm/s.
I’m developing a personal algorithm in JavaScript to process some samples and perform a DFT. It’s a simple code that uses brute force to process my results. I compared the results (real and imaginary parts) from JavaScript and from MATLAB results and they matched perfectly.
However, my sampling rate is very slow. Because of this, I have a lot of questions which I couldn’t find the answers on my searches:
Is it possible to apply a DFT analysis on a slow sampling data as this?
How can I determine the correct frequency scale for the X axis? It’s complicated for me because I don’t have an explicit Fs (sampling rate) value.
In my case, would it be interesting to apply some window function like Hanning Window (suitable for vibrations analyses)?
JavaScriptCode:
//Signal is a pure one-dimensional of real data (vibration values)
const fft = (signal) => {
const pi2 = 6.2832 //pi const
let inputLength = signal.length;
let Xre = new Array(inputLength); //DFT real part
let Xim = new Array(inputLength); //DFT imaginary part
let P = new Array(inputLength); //Power of spectrum
let M = new Array(inputLength); //Magnitude of spectrum
let angle = 2 * Math.PI / inputLength;
//Hann Window
signal = signal.map((x, index) => {
return x * 0.5 * (1 - Math.cos((2 * Math.PI * index) / (inputLength - 1)));
});
for (let k = 0; k < inputLength; ++k) { // For each output element
Xre[k] = 0; Xim[k] = 0;
for (let n = 0; n < inputLength; ++n) { // For each input element
Xre[k] += signal[n] * Math.cos(angle * k * n);
Xim[k] -= signal[n] * Math.sin(angle * k * n);
}
P[k] = Math.pow(Xre[k], 2) + Math.pow(Xim[k], 2);
M[k] = Math.sqrt(Math.pow(Xre[k], 2) + Math.pow(Xim[k], 2));
}
return { Xre: Xre, Xim: Xim, P: P, M: M.slice(0, Math.round((inputLength / 2) + 1)) };
}
The first figure shows the charts results (time domain on the left side and frequency domain on the right side).
The second figure shows a little bit of my data samples:
Obs.: I'm sorry for the writing. I'm still a beginner English student.
The frequency doesn't matter. A frequency as low as 1/day is just as fine as any other frequency. But consider the Nyquist-Shannon theorem.
This is problematic. You need a fix sampling frequency for a DFT. You could do interpolation as preprocessing. But better would be to do the sampling at fix times.

How to generate skewed random numbers in Javascript?

Using Javascript, how can I generate random numbers that are skewed towards one end or the other of the distribution? Or ideally an point within the range?
For context: I'm creating a UI that has uses a grid of random grey squares. I'm generating the grey's RGB values using Math.random() but would like to be able to skew the greys to be on average darker or lighter while still having the full range from black to white represented.
(I think this is a similar question to Skewing java random number generation toward a certain number but I'm working with Javascript...)
Any help greatly appreciated.
Raise Math.random() to a power to get a gamma curve - this changes the distribution between 0 and 1, but 0 and 1 stay constant endpoints.
var r= Math.pow(Math.random(), 2);
var colour= 'rgb('+r*255+', '+r*255+', '+r*255+')';
For gamma>1, you will get darker output; for 0<gamma<1 you get lighter. (Here, '2' gives you the x-squared curve; the equidistant lightness would be '0.5' for the square-root curve.)
This seems a little crude and less graceful than #bobince's answer, but what the hell.
//setup
var colours = [], num_colours = 10, skew_to = 255, skew_chance = 20;
//get as many RGB vals as required
for (var i=0; i<num_colours; i++) {
//generate random grey
var this_grey = Math.floor(Math.random() * 256);
//skew it towards the #skew_to endpoint, or leave as-is?
if (Math.floor(Math.random() * 100) >= skew_chance && this_grey != skew_to) {
//skew by random amount (0 - difference between curr val and endpoint)
var skew_amount = Math.floor(Math.random() * Math.abs(this_grey - skew_to));
this_grey += ' (skewed to '+(skew_to < this_grey ? this_grey - skew_amount : this_grey + skew_amount)+')';
}
colours.push(this_grey);
}
console.log(colours);
Essentially it generates random greys then decides, based on probably specified (as a percentage) in skew_chance, whether to skew it or not. (In case you wanted to make this occasional, not constant). If it decides to skew, a random number is then added or subtracted from the grey value (depending on whether the skew endpoint is under or above the current value).
This random number is a number between 0 and the absolute difference between the current value and the endpoint, e.g. if current value is 40, and the endpoint is 100, the number added would be between 0 and 60.
Like I say, #bobince's answer is somewhat, er, more graceful!
[This might be a little different approach.]
This approach deals with getting the number in the following fashion:
random = numberToSkewTo + random(-1,1)*stdDeviation
Where:
numberToSkewTo is the number you want to skew towards.
stdDeviation is the deviation from numberToSkewTo
numberToSkewTo + abs(stdDeviation) <= MAX_NUMBER and
numberToSkewTo - abs(stdDeviation) >= MIN_NUMBER
What the following code does is, it pick a random number around the given number with constantly increasing standard deviations. It returns the average of results.
function skew(skewTo,stdDev){
var rand = (Math.random()*2 - 1) + (Math.random()*2 - 1) + (Math.random()*2 - 1);
return skewTo + rand*stdDev;
}
function getRandom(skewTo){
var difference = Math.min(skewTo-MIN_NUMBER, MAX_NUMBER-skewTo);
var steps = 5;
var total = 0.0;
for(var i=1; i<=steps; i++)
total += skew(skewTo, 1.0*i*difference/steps);
return total/steps
}

Categories

Resources