In the Web API, whether it is canvas getImageData or audio getChannelData, as long as “get.. Data” apis are powerful apis that allow you to get relatively raw and complete Data, the former getting rGBA for every pixel, the latter getting PCM Data for every channel.

White noise

GetChannelData is for AudioBuffer. Let’s take a look at the white noise generation code written in ES5 on MDN. It will generate a 2s white noise and play it.

// Two channels, i.e. stereo
var channels = 2;

// 2s in total, multiplied by the sampleRate sampling rate is the total PCM data length
var frameCount = audioCtx.sampleRate * 2.0;
// Create a buffer with the number of channels, the length of PCM data stored in the buffer, and the sampling rate
var myArrayBuffer = audioCtx.createBuffer(channels, frameCount, audioCtx.sampleRate);

button.onclick = function() {
  // Fill each channel with white noise
  for (var channel = 0; channel < channels; channel++) {
    // Fill the current channel with two channels
    var nowBuffering = myArrayBuffer.getChannelData(channel);
    for (var i = 0; i < frameCount; i++) {
      // Fill the current channel with a -1 to 1 random number
      nowBuffering[i] = Math.random() * 2 - 1; }}// Create a container for AudioBuffer, that is AudioBufferSourceNode
  var source = audioCtx.createBufferSource();
  source.buffer = myArrayBuffer;

  // Link to the total output
  source.connect(audioCtx.destination);

  / / play
  source.start();
}
Copy the code

* Curious about filling Perlin Noise, not tried yet.

Manual rising tone

For AudioBufferSourceNode, there are built-in detune and playbackRate for lifting and lowering tones, but the two methods are not fundamentally different. In fact, they both use compression or stretching to change the tone. The former is “cents”, a very small unit of music. 1,200 cents is equal to an octave.

For example, if we have a sine wave, we raise it an octave and draw a simple diagram:

// Buffer indicates before the change and buffer_pitch indicates after the change
  const buffer_pitch = ctx.createBuffer(2, buffer0.length * 1/2, ctx.sampleRate);
  node.buffer = buffer_pitch;

  for (let i = 0; i < buffer_pitch.length; i += 1) {
    buffer_pitch.getChannelData(0)[i] = buffer.getChannelData(0)[i * 2];
    buffer_pitch.getChannelData(1)[i] = buffer.getChannelData(1)[i * 2];
  }
Copy the code

If you want modulated invariance, for this simple periodic waveform you just need to fill the missing time with the same modulated wave.

Channel to replace

If there are two versions of a song, you can double the joy by replacing one channel from one version with the other, one version on each side.

startBtn.addEventListener("click", e => {
  const ctx = new AudioContext();
  const node = ctx.createBufferSource();

  node.connect(ctx.destination);
  
  Promise.all([get("./0.mp3"), get("./1.mp3")]).then(res= > {
    Promise.all([ctx.decodeAudioData(res[0]), ctx.decodeAudioData(res[1])]).then(buffers= > {
      const buffer0 = buffers[0];
      const buffer1 = buffers[1];
      
      node.buffer = buffer0;
      
      for (let i = 0; i < buffer0.length; i += 1) {
        buffer0.getChannelData(0)[i] = buffer1.getChannelData(0)[i];
      }

      node.start();
    });
  });
});
Copy the code

However, traversing through the entire channelData is a time-consuming operation, so be sure to put it into the Web Worker.

The last

This is just the tip of the iceberg in the usage of getChannelData. Professional audio workers can do a lot of things with PCM data. If you are interested, go and learn more about it.