Search code examples
javascriptweb-audio-apitone.js

What do the values from Tone.Waveform().getValues() represent?


I am trying to draw the waveform of my audio using tone.js

// setup
const wave = new Tone.Waveform()
Tone.Master.connect(wave)

// later
wave.getValue() // returns an array (length 1024) of numbers between -1 and 1

This works fine, except that I'm having trouble understanding what these number represent, excatly. I assume they are the amplitude of the wave over time, but if that's the case, how far into the past do they go?

Or maybe I'm misunderstanding something entirely, in either case, I would appreciate if you could shed some light on this!

The documentation is very short and can be found here, it reads:

Gets the waveform of the audio source. Returns the waveform data of length size as a Float32Array with values between -1 and 1.


Solution

  • This is really interesting! Since I am not too much familiar with audio APIs, I've followed the source code of Tone.js to reveal what does it mean.

    The Tone.Waveform property, which is new Tone().Waveform in the code does include the implementation of .getValue().

    // https://github.com/Tonejs/Tone.js/blob/053b5d4397b595ea804b5d6baf6108158c8e0696/Tone/component/analysis/Waveform.ts#L43-L45
    // Waveform.ts
    class Waveform extends MeterBase {
      // ...
      /**
       * Return the waveform for the current time as a Float32Array where each value in the array
       * represents a sample in the waveform.
       */
      getValue(): Float32Array {
        return this._analyser.getValue() as Float32Array;
      }
    }
    

    Waveform._analyser property is coming from MeterBase. It implements ._analyser property with Analyser

    // https://github.com/Tonejs/Tone.js/blob/053b5d4397b595ea804b5d6baf6108158c8e0696/Tone/component/analysis/MeterBase.ts
    // MeterBase.ts
    class MeterBase {
      // ...
      protected _analyser: Analyser;
    }
    

    looking at the Analyser.ts, it is revealed that getValue() is coming from analysers[x].getFloatTimeDomainData(). Now we have one lead, that getValue() seems like a time series data of some float signal.

    // https://github.com/Tonejs/Tone.js/blob/b1526c357854d8017765328bd1278a91d1e14e50/Tone/component/analysis/Analyser.ts#L17-L21
    // Analyser.ts
    /**
    * Wrapper around the native Web Audio's [AnalyserNode](http://webaudio.github.io/web-audio-api/#idl-def-AnalyserNode).
    * Extracts FFT or Waveform data from the incoming signal.
    * @category Component
    */
    class Analyser {
      // ...
      private _analysers: AnalyserNode[] = [];
    }
    

    analysers is a wrapper around array of AnalyserNode, as stated in the comment.

    The linked official W3C document of AnalyserNode reveals that .getFloatTimeDomainData() is a result of blackman window and fourier transform over time series data of frequency in decibel(dB). This document has better details. Please check it if it interests you.

    To simply put, this is a normalized data of how decibels(dB) change over time. I believe it uses normalized timeframe of 1024 and -1 ~ 1 for dB to save memory. In nature, waveforms have curve forms which can be gracefully compressed with mathematical expressions. So in theory it does not have clear limit since everything can be normalized but it may lose some quality by normalization rate(smoothness).

    I personally think the document author of Tone.js did a great job of simplifying the meaning of getValues(). It just explains what it is and what it can be in a short sentence, instead of giving unnecessary lectures. It may need more elaboration for some but I think anyone who fiddled with WebAudio probably understood what it is.

    Thanks to @Hoff for bringing in such awesome question.