0

I am trying to create a function that measures the average of a microphone input level over five minutes, then stores the sum of calculations in a variable.

At the moment, an interval is set to run the function every five minutes but only calculates the average of the single last input value, instead of the values over time.

function measureLevel() {

average = 0;
for (counter = 0; counter < 75000; counter++) {
    average += absoluteLevel / 75000;
}

averageAbsoluteLevel = Math.abs(average);
averageDbLevel = Tone.gainToDb(averageAbsoluteLevel) * scale + offset;

console.log('Counter reached. Average level is: ' + averageDbLevel);

}
window.setInterval(measureLevel, 300000);

Thanks in advance.

Alfie
  • 23
  • 6
  • 1
    From where is the value `absoluteLevel` coming from? – gurvinder372 Apr 09 '18 at 09:34
  • @gurvinder372 apologies for not including, absoluteLevel is coming from a function which gets the mic signal, it is the absolute value of Tone.Meter.getValue(), a function which gets a signal value between -1 and +1 in the Tone framework. – Alfie Apr 09 '18 at 09:35
  • absoluteLevel is not changed in for loop so you are using same value all the time – NoOorZ24 Apr 09 '18 at 09:37
  • ^^ That. `average` will equal `absoluteLevel` by the end of the loop. You're basically dividing it by 75000 and then adding it 75000 times. `(x / 75000) * 75000 == x` – Reinstate Monica Cellio Apr 09 '18 at 09:38
  • @NoOorZ24 absoluteLevel is constantly changing in a function that gets the microphone input signal as a linear value, that function has a setInterval of 0 ms – Alfie Apr 09 '18 at 09:39
  • 1
    absoluteLevel shoukld be an array and you should read it like: absoluteLevel[counter] or something like that depending on how your script works – NoOorZ24 Apr 09 '18 at 09:40

2 Answers2

1

but only calculates the average of the single last input value, instead of the values over time.

You are setting the average to 0 every time, you need to remember two things

  • Total sum of mic value
  • Number of times this timer has already run

Finally divide the total mic sum value by number of times this timer has run. Try

(function(){ //create a IIFE so that micSum and timerCounter is localized and don't pollute global namespace
   var micSum = 0;
   var timerCounter = 0;
   window.setInterval(function(){
       var absoluteLevel = Math.abs( Tone.Meter.getValue() );
       micSum  += Tone.gainToDb(absoluteLevel ) * scale + offset; //keep adding to the sum
       timerCounter++;
       console.log('Counter reached. Average level is: ' + (micSum/timerCounter) ); //take the average by dividing micSum with counter.
   }, 300000);
})(); //set the timer
gurvinder372
  • 66,980
  • 10
  • 72
  • 94
  • Thanks for the help, but this doesn't work unfortunately. I may have to try and store the absoluteLevel variables in an array and then use the array in a for loop. – Alfie Apr 09 '18 at 09:59
  • I've changed some bits around and it's closer to being there but not quite exact yet (when compared to average in Excel). `(function() { var micSum = 0; var timerCounter = 0; window.setInterval(function() { micSum += absoluteLevel / timerCounter; timerCounter++; averageAbsoluteLevel = Math.abs(absoluteLevel); averageDbLevel = Tone.gainToDb(averageAbsoluteLevel) * scale + offset; console.log('Counter reached. Average level is: ' + averageDbLevel); }, 300000); })();` – Alfie Apr 09 '18 at 10:17
  • @alfie did you tried changing the interval value to much lesser, say 100? – gurvinder372 Apr 09 '18 at 10:22
  • Yeah I did, to enable me to quickly test the values – Alfie Apr 09 '18 at 10:24
  • In that case you need to debug the value that is getting generated from Tone.gainToDb function. Is it already giving some kind of average? – gurvinder372 Apr 09 '18 at 10:26
  • Yeah it is getting the RMS of the input signal with some averaging applied, so I'm going to try and turn the averaging off, then retry. Thanks for your help this far – Alfie Apr 09 '18 at 10:33
0

The approach I think of would be to gather measurements, until 5 minutes are passed. You could use two timers for this, one to collect the measurements (interval, repeating), and one timeout when 5 minutes have passed to collect the average (and stop the interval).

So something like this should get you started:

var measurements = [] 

function collectMeasurement() { 
  var absoluteLevel = Math.abs( Tone.Meter.getValue() );
  measurements.push(absoluteLevel);
}

collectInterval = window.setInterval(collectMeasurement, 100); 

function calculateAverage() { 
  window.clearInterval(collectInterval); 
  // .. calculate average of all measurements here ..
  console.log('Measure for 5 minutes: average level is: ' + calculatedAverage);
} 

window.setTimeout(calculateAverage, 300000); 

So things to play around with:

  • vary the timeout between measurements? (how soon can you get a reliable measurements, how many measurements are wanted, ...)
  • instead of using a set time to measure, you could also use a set amount of measurements, which would make it easier, but probably less optimal?
nathanvda
  • 49,707
  • 13
  • 117
  • 139
  • This pretty much works great, thanks! Only one problem, the array needs to be cleared after every time the average is calculated. I've tried doing `measurements = [];` at the end of the `calculateAverage` function but unfortunately this doesn't do the job. Would you have any tips on this? – Alfie Apr 09 '18 at 17:49
  • Apparently the easiest way is to do something like `measurements.length = 0`. For more options + explanation see https://stackoverflow.com/questions/1232040/how-do-i-empty-an-array-in-javascript – nathanvda Apr 09 '18 at 19:28