0

I'm working on an audio editor based on web, in Javascript. It's not a live audio player (like a virtual dj console) where filters and effects are applied in real time. It's a web app that allows user to cut, copy, paste track segments. The track is loaded from a file. I'm using Web Audio Api, React with Wavesurfer.js library (link) and its plugins. Thank's by Regions plugin (link) and some guides (link) I can allow user to cut, copy, paste and change volume track segments.

Until now I manipulated the array buffer (Float32Array Object) and the AudioBuffer object of the track.

For example it's easy to adjust volume to a track segment/region:

function adjustGainToRegion(region, gain) {
  const start = region.start;
  const end = region.end;

  const originalAudioBuffer = waveSurfer.getDecodedData();

  var newAudioBuffer = audioContext.createBuffer(
    originalAudioBuffer.numberOfChannels,
    originalAudioBuffer.length,
    originalAudioBuffer.sampleRate
  );

  for (var channel = 0; channel < originalAudioBuffer.numberOfChannels; channel++) {
    var originalChanData = originalAudioBuffer.getChannelData(channel);
    var newChanData = newAudioBuffer.getChannelData(channel);

    var midData = originalChanData.subarray(start * originalAudioBuffer.sampleRate, end * originalAudioBuffer.sampleRate);

    newChanData.set(originalChanData);

    for(let i=0;i<midData.length;i++){
      newChanData[i] = newChanData[i] * gain;
    }
  }

  return newAudioBuffer;
}

But, how to manipulate the AudioBuffer object or array buffer for apply filters (highpass, lowpass, bandpass, notch) and effects (reverb, delay, echo, ect...)? Is it possible? Or is a workaround needed?

EDIT

The above function isn't correct, because it always updates the newAudioBuffer from track start (0s) and not from region start.

function adjustGainToRegion(region, gain) {
  const start = region.start;
  const end = region.end;
  const regionDuration = Math.round(end - start);

  const originalAudioBuffer = waveSurfer.getDecodedData();

  var newAudioBuffer = audioContext.createBuffer(
      originalAudioBuffer.numberOfChannels,
      originalAudioBuffer.length,
      originalAudioBuffer.sampleRate
    );

  for (var channel = 0; channel < originalAudioBuffer.numberOfChannels; channel++) {
    var originalChanData = originalAudioBuffer.getChannelData(channel);
    var newChanData = newAudioBuffer.getChannelData(channel);

    var beforeData = originalChanData.subarray(0, start * originalAudioBuffer.sampleRate);
    var midData = originalChanData.subarray(start * originalAudioBuffer.sampleRate, end * originalAudioBuffer.sampleRate);
    var afterData = originalChanData.subarray(Math.floor(end * originalAudioBuffer.sampleRate), (originalAudioBuffer.length * originalAudioBuffer.sampleRate));

    for(let i=0;i<midData.length;i++){
      midData[i] = midData[i] * gain;
    }

    newChanData.set(beforeData);
    newChanData.set(midData, (start * newAudioBuffer.sampleRate));
    newChanData.set(afterData, (start + regionDuration) * newAudioBuffer.sampleRate);
  }

  return newAudioBuffer;
}

1 Answer 1

1

This can be done with an OfflineAudioContext. You could for example rewrite your code to use an AudioBufferSourceNode to slice the original audio data and a GainNode to manipulate the gain.

function adjustGainToRegion(region, gain) {
    const originalAudioBuffer = waveSurfer.getDecodedData();
    const offlineAudioContext = new OfflineAudioContext({
        length: Math.round((region.end - region.start) * originalAudioBuffer.sampleRate),
        numberOfChannels: originalAudioBuffer.numberOfChannels,
        sampleRate: originalAudioBuffer.sampleRate
    });
    const audioBufferSourceNode = new AudioBufferSourceNode(
        offlineAudioContext,
        { buffer: originalAudioBuffer }
    );
    const gainNode = new GainNode(offlineAudioContext, { gain });

    audioBufferSourceNode
        .connect(gainNode)
        .connect(offlineAudioContext.destination);
    audioBufferSourceNode.start(0, region.start);

    return offlineAudioContext.startRendering();
}

You can for example replace or combine the GainNode with a BiquadFilterNode or any other node provided by the Web Audio API.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank's a lot!! I solved!! But my 'adjustGainToRegion' function isn't correct. I will edit my post. I have a question, after offlineAudioContext is used will the memory be cleaned? For example if I use the function many times, the offlineAudioContext is cleaned every times and then recreated.
Yes, the memory should get garbage-collected once there is no reference to an OfflineAudioContext anymore. But there have been some problems with that in the past. github.com/Tonejs/Tone.js/issues/1128

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.