Audioworklet loop

164 Views Asked by At

I'm trying to create an audioworklet that loops a single instance of a sound to create a sort of sustain effect. No matter how many sections of the input I loop, I keep hearing a blip type skipping sound. How many calls to the processor is one instance?

To give you an idea, this is what I have so far:

        constructor() {
            super();
            this.sound = [];
            this.count = 20;
            this.step = [0, 0];
        }
        process(inputs, outputs, parameters) {
            if (inputs && inputs.length) {
                for (var i = 0; i < inputs[0].length; i++) {
                    var input = inputs[0][i];
                    var output = outputs[0][i];
                    if (!this.sound[i]) {
                        this.sound[i] = [];
                    }
                    if (this.sound[i].length < this.count) {
                        this.sound[i].push([]);
                        for (var j = 0; j < input.length; j++) {
                            this.sound[i][this.sound[i].length - 1][j] = input[j];
                        }
                    } else if (this.sound[i]) {
                        var s = this.sound[i][this.step[i] % this.sound[i].length];

                        for (var j = 0; j < s.length; j++) {
                            output[j] = s[j];
                        }

                        this.step[i]++;
                    }
                }
            }
            return true;
        }

So the idea is that I capture the incoming input in an array of N length for each channel(in my case there are 2 channels). Then once that array is full, I cycle through that array to fill the output until the node is disabled.

1

There are 1 best solutions below

0
On

Thanks for the fiddle. Very helpful.

I didn't look to see what was causing the clicks, but I took your example and modified it very slightly like so:

// @channels 2
// @duration 1.0
// @sampleRate 44100

var bufferSize = 4096;
let ctx = context;
let osc = ctx.createOscillator();
osc.start();

let myPCMProcessingNode = ctx.createScriptProcessor(bufferSize, 2, 2);

let _count = 1;
var sound = [];
var count = _count++;
console.log(count)
var hesitate = 0;
var step = [0, 0];

//Logic to pay attention to
myPCMProcessingNode.onaudioprocess = function(e) {
    for(var i = 0; i<2; i++){
    var input = e.inputBuffer.getChannelData(i);
    var output = e.outputBuffer.getChannelData(i);
    if (!sound[i]) {
      sound[i] = [];
    }
    if (sound[i].length < count) {
      sound[i].push([]);
      for (var j = 0; j < input.length; j++) {
        sound[i][sound[i].length - 1][j] = input[j];
      }
    } else if (sound[i]) {
      var s = sound[i][step[i] % sound[i].length];
      for (var j = 0; j < s.length; j++) {
        output[j] = s[j];
      }
      step[i]++;
    }
  }
}
        //To hear

osc.connect(myPCMProcessingNode).connect(ctx.destination);

I pasted this in the code window at https://hoch.github.io/canopy. Press the top left button (arrow) to the left of the code window, and you can see the rendered audio. You can see that the output (frequency is 10 instead of 440 to make it easier to see) is discontinuous. This causes the clicks you hear. You can also change the frequency to 100 and find discontinuities in the output.

I hope this is enough to help you figure out what's wrong with your buffering.

An alternative is to create an AudioBufferSourceNode with an AudioBuffer of the basic sample. You can set the AudioBufferSourceNode to loop the whole buffer. This doesn't solve the clicking problem, but it is somewhat simpler.

But this is a general problem of looping any buffer. Unless you arrange the last sample to be close to the first sample, you will hear clicks when you wrap around. You either need to grab chunks where the first and last samples are nearly the same, or modified the chunks so they ramp up at the beginning in some way and ramp down at the end in some way.