/// <reference path="../CommandGlobals.d.ts" />

// Hey, thanks for checking out WavTool! Let's make a delay!

// We'll start by setting up some controls.
// It's always best to do this at the very start, but it also gives us a good overview of the delay effect concept.

// Calls to wavtool.numberSetting create sliders over in the "Run Code" area to the right.
// When you hit run, the value of the delayTimeMS constant will be whatever you dial in on the slider!
const delayTimeMS = wavtool.numberSetting({
  name: 'Delay Time (MS)',
  default: -50,

  // Since WavTool commands run offline (that is, NOT in real time), we can do cool stuff like allowing negative delay times.
  min: -500,
  max: 500
});

// Mix, or Dry/Wet, controls how much of the Delay signal we hear, vs the original signal.
// 0 means only original signal. 1 means only delayed signal.
const mix = wavtool.numberSetting({
  name: 'Dry/Wet',
  default: 0.2,
  min: 0,
  max: 1
});

// Feedback controls how much the delay "hears" itself. With no feedback we'll just hear a single delayed copy.
// With full feedback, any delayed sound will repeat forever.
const feedback = wavtool.numberSetting({
  name: 'Feedback',
  default: 0.5,
  min: 0,
  max: 1
});

// In wavtool, time values usually need to be converted to samples.
// This math converts a number in milliseconds to a whole number of samples, for the current sample rate.
const delaySamples = Math.round((delayTimeMS / 1000) * project.sampleRate);


// Okay, time for the exciting bit! First a quick overview of WavTool projects. (They're simple, I promise.)

// Each "Track" in WavTool contains an AudioBuffer, which may have one or more channels.
/**
 * Project
 *   |- AudioBuffer
 *   |  |- Channel
 *   |  |- Channel
 *   |
 *   |- AudioBuffer
 *      |- Channel
 */

// project.mapSelectedChannels will loop through each channel that has some samples selected.
// Then, each selected channel's data will be replaced with the data we return for that channel!
project.mapSelectedChannels((inputData, selectionStart, selectionEnd) => {

  // In WavTool, audio channel data is represented with JavaScript's standard Float32Array.
  // These arrays have some restrictions, but often we can work with them just like normal JS arrays.
  // You can learn more about Float32Arrays here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array

  // To start, let's make a new Float32Array to hold our output data.
  // Constructing it with inputData will cause this array to start out with the same
  // contents as inputData.
  const outputData = new Float32Array(inputData);

  // Let's also make another Float32Array to contain the delayed signal.
  // By constructing it with inputData.length, this array will have the same length
  // as inputData, but be filled with zeroes.
  const delayedSignal = new Float32Array(inputData.length);

  // We're going to step through every sample in our data and apply the delay to it.
  // The code that actually applies the delay is going to be reused in a bit, so let's
  // put it into a function.
  const applyDelay = (i) => {

    // To apply a delay, we're going to step through each sample in our input data,
    // and then mix it with another, older sample.
    // (Or, if the delay time is negative, the other sample will be from the future.)
    // Let's get the index of that other sample.
    const delayedSampleIndex = i - delaySamples;

    // Let's make sure we're not trying to read a sample from outside of the buffer.
    // If we're not careful, failing to do this might put a NaN into our output data.
    if (delayedSampleIndex >= 0 && delayedSampleIndex < delayedSignal.length) {

      // Now we read the delayed sample from our input, and mix it with whatever sample is
      // at the same point in our existing delay data.

      // This is where feedback comes in! If feedback is zero, we'll just hear a delay from
      // the input signal. But if feedback is greater than zero, we'll hear some repeats of
      // earlier delayed signals too.
      delayedSignal[i] = inputData[delayedSampleIndex] + (feedback * delayedSignal[delayedSampleIndex]);

      // Finally, we mix the sample from delayedSignal with our inputData signal!
      outputData[i] = (mix * delayedSignal[i]) + ((1 - mix) * inputData[i]);
    }
  }

  // Here's where we step through each sample in the delay!
  // If the delay time happens to be negative, we step through in reverse order.
  // This allows the delay feedback to mix in delay taps from the future.
  if (delaySamples < 0) {

    // We're only going to apply the delay to samples in the user's selection.
    // It's up to you how you involve the selection in your WavTool code, but it's usually
    // nice to avoid changing samples outside of the user's selection where possible.
    for (let i = selectionEnd - 1; i >= selectionStart; i --) {
      applyDelay(i);
    }
  } else {
    for (let i = selectionStart; i < selectionEnd; i ++) {
      applyDelay(i);
    }
  }

  // And we're done!
  return outputData;
});
