Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to calculate maximum possible value of amplitude in amplitudeSpectrum data? #891

Open
tyohan opened this issue Jun 23, 2021 · 3 comments

Comments

@tyohan
Copy link

tyohan commented Jun 23, 2021

@hughrawlinson i was thinking to post it here before I asked you on the Twitter because there is no issue template for a question.

Basically I'm building a visualization for audio and use Meyda to extract the frequency domain data with amplitudeSpectrum feature. With NodeJS I can get the maximum amplitude value by scanning the whole values on the arrays, but with live visualization with Web Audio API, I only have access to current amplitude array.

Is there any way to calculate possible maximum amplitude value? Because I need the value to scale the amplitude with my canvas height. So for example if I know the max value will be 255 like in AnalyserNode.getByteFrequencyData() from Web Audio API, I can scale the height of the value like

const barHeight = amplitude/maxAmplitude * canvasHeight;

Thanks!

@tyohan tyohan added the bug label Jun 23, 2021
@hughrawlinson
Copy link
Member

there is no issue template for a question.

Thanks for pointing that out! I've gone ahead and created one now.

Is there any way to calculate possible maximum amplitude value?

This is a really good question. I thought I knew the answer when you messaged on Twitter and I now realize that I don't. Our documentation specifies that each bin in the amplitude spectrum can go up to half of the sample rate. I went digging in our demo code to find a scaling factor, but I've forgotten how that works, and can't figure out what that is.

I know that the max value for a bin in the amplitude spectrum is equal to the hypotenuse of the triangle formed by the real and imaginary components of the complex spectrum. But unfortunately, the fft code for the complex spectrum is slightly beyond me, so I'm not sure what the maximum values are for the complex spectrum.

I'm pretty sure the answer is closely related to the bounds of the amplitude of the input audio - the audio is represented as Float32s internally, and in Web Audio, audio goes between -1 and 1 - but other audio encodings might represent audio as UInt16 (or even UInt32), and I'm not sure we'd scale it down to the -1 to 1 range.

So the remaining question that should unlock all this is "for signals in the amplitude range of -1 to 1, what are the bounds of the bins in the complex spectrum - and that's a question I'm fairly sure @nevosegal will know the answer to off the top of his head. Hopefully he'll chime in to help!

@hughrawlinson
Copy link
Member

if the complex spectrum is correctly documented as having a maximum value of sampleRate/2 for each real and imaginary component, then the maximum value in the amp spectrum is sqrt(2*sampleRate^2) - I think. Would need @nevosegal (or anyone else who knows) to confirm. This is precluding any relationship between the real/imag components of the complex spectrum that for example prevents the sum of the two components being above some magnitude.

Sorry the answer isn't clearer yet!

@tyohan
Copy link
Author

tyohan commented Jun 24, 2021

Thanks for the answer Hugh, even it does not answer the question directly but at least it gives me more information about the possibilities to find the maximum value.

I found an issue about the amplitude spectrum and how is it different with getByteFrequencyData() from Web Audio API. So decided to transform the value to decibels 0-255 like that API does.

For you who looking a way to do it, below is the sample code to have the same output with getByteFrequencyData()

// this is the default value from Web Audio API
// see https://webaudio.github.io/web-audio-api/#AnalyserNode-attributes
const minDecibels = -100;
const maxDecibels = -30;
const smoothingTimeConstant = 0.8;

const getSmoothingDataInByte = (features) => {
    const smoothingArrayInByte = [];
    const previousSmooth = new Float32Array(features.length);
    for (let i = 0; i < features.length; i++) {
      const v0 = features[i];

      // smooth over data
      // https://webaudio.github.io/web-audio-api/#fft-windowing-and-smoothing-over-time
      previousSmooth[i] = smoothingTimeConstant * previousSmooth[i] + (1 - smoothingTimeConstant) * v0;
     
      // convert to dB
      // https://webaudio.github.io/web-audio-api/#conversion-to-db
      const v1 = 20 * Math.log10(previousSmooth[i]);

      // convert to byte
      // https://webaudio.github.io/web-audio-api/#AnalyserNode-methods
      const byte = (255/( maxDecibels - minDecibels))*((Number.isFinite(v1) ? v1 : 0) - minDecibels);

      smoothingArrayInByte[i] = byte;
    }
    return smoothingArrayInByte;
  }
const meyda = Meyda.createMeydaAnalyzer({
      audioContext: audioContext,
      source: audioSource,
      bufferSize: 256,
      windowingFunction: 'blackman',
    });

//call it in in requestAnimationFrame to get the data before render it to canvas.
const features = meyda.get('amplitudeSpectrum');
const smoothingDataInByte = getSmoothingDataInByte(features);

//now you can use the data for visualization with 0-255 range value for each frequency bin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants