web-audio Alterazione in tempo reale di due sorgenti audio


Esempio

Questo esempio mostra come utilizzare due sorgenti audio e modificarne una basata sull'altra. In questo caso creiamo un Ducker audio, che abbassa il volume della traccia principale se la traccia secondaria produce suono.

ScriptProcessorNode invierà eventi regolari al relativo gestore di audioprocess. In questo gestore, che è collegato alla sorgente audio secondaria, calcoliamo il "volume" dell'audio e lo usiamo per alterare un compressore dinamico sulla sorgente audio primaria. Entrambi vengono poi inviati agli altoparlanti / cuffie dell'utente. Il risultato è cambiamenti di volume molto bruschi nella traccia audio primaria quando viene rilevato il suono nella traccia audio secondaria. Potremmo rendere più fluida l'uso di una media e utilizzare una linea di ritardo per modificare il volume prima che venga rilevato l'audio secondario, ma in questo esempio il processo dovrebbe essere chiaro.

//The DuckingNode will heavily compress the primary source when the secondary source produces sound
class DuckingNode {
  constructor(context) {
    let blocksize = 2048;
    let normalThreshold = -50;
    let duckThreshold = 0.15;
  
    //Creating nodes
    this.compressor = context.createDynamicsCompressor();
    this.processor = context.createScriptProcessor(blocksize, 2, 2);
    
    //Configuring nodes
    this.compressor.threshold.value = normalThreshold;
    this.compressor.knee.value = 0;
    this.compressor.ratio.value = 12;
    this.compressor.reduction.value = -20;
    this.compressor.attack.value = 0;
    this.compressor.release.value = 1.5;
    
    let self = this;
    this.processor.onaudioprocess = function(audioProcessingEvent) {
      let inputBuffer = audioProcessingEvent.inputBuffer;
      let outputBuffer = audioProcessingEvent.outputBuffer;
      let rms;
      let total = 0.0;
      let len = blocksize * outputBuffer.numberOfChannels;

      for (let channel = 0; channel < outputBuffer.numberOfChannels; channel++) {
        let inputData = inputBuffer.getChannelData(channel);
        let outputData = outputBuffer.getChannelData(channel);

        for (let sample = 0; sample < inputBuffer.length; sample++) {
          // make output equal to the same as the input
          outputData[sample] = inputData[sample];
          total += Math.abs(inputData[sample]);
        }
      }
      
      //Root mean square
      rms = Math.sqrt( total / len );

      //We set the threshold to at least 'normalThreshold'
      self.compressor.threshold.value = normalThreshold + Math.min(rms - duckThreshold, 0) * 5 * normalThreshold;
    }
  }
  
  get primaryInput () {
    return this.compressor;
  }
  
  get secondaryInput () {
    return this.processor;
  }
  
  connectPrimary(node) {
    this.compressor.connect(node);
  }
  
  connectSecondary(node) {
    this.processor.connect(node);
  }
};
let audioContext = new (window.AudioContext || window.webkitAudioContext)();

//Select two <audio> elements on the page. Ideally they have the autoplay attribute
let musicElement = document.getElementById("music");
let voiceoverElement = document.getElementById("voiceover");

//We create source nodes from them
let musicSourceNode = audioContext.createMediaElementSource(musicElement);
let voiceoverSourceNode = audioContext.createMediaElementSource(voiceoverElement);

let duckingNode = new DuckingNode(audioContext);

//Connect everything up
musicSourceNode.connect(duckingNode.primaryInput);
voiceoverSourceNode.connect(duckingNode.secondaryInput);
duckingNode.connectPrimary(audioContext.destination);
duckingNode.connectSecondary(audioContext.destination);

Parte di questo esempio viene da questa risposta di Kevin Ennis e dal codice su mdn .