Javascript Web音频api,优雅地停止声音

Javascript Web音频api,优雅地停止声音,javascript,audio,web-audio-api,Javascript,Audio,Web Audio Api,web audio api提供了停止声音的方法.stop()。 我希望我的声音在停止前降低音量。为此,我使用了一个增益节点。然而,我面临着一些奇怪的问题,有些声音无法播放,我不知道为什么 以下是我所做工作的简化版本: 如果使用setTimeout()删除该行,您将听到每个声音都会播放。设置超时时,并非所有声音都会播放。真正让我困惑的是,我使用push和shift来找到正确的音源,但似乎是另一种声音停止播放。我能看到这种情况的唯一方法是,如果AudioContext.decodeAudioDat

web audio api提供了停止声音的方法
.stop()
。 我希望我的声音在停止前降低音量。为此,我使用了一个增益节点。然而,我面临着一些奇怪的问题,有些声音无法播放,我不知道为什么

以下是我所做工作的简化版本:

如果使用
setTimeout()
删除该行,您将听到每个声音都会播放。设置超时时,并非所有声音都会播放。真正让我困惑的是,我使用
push
shift
来找到正确的音源,但似乎是另一种声音停止播放。我能看到这种情况的唯一方法是,如果
AudioContext.decodeAudioData
不同步。只需试着使用JSFIDLE就可以更好地理解并戴上耳机

以下是JSFIDLE的代码:

  let url = "https://raw.githubusercontent.com/gleitz/midi-js-soundfonts/gh-pages/MusyngKite/acoustic_guitar_steel-mp3/A4.mp3";
  let soundContainer = {};
  let notesMap = {"A4": [] };
  let _AudioContext_ = AudioContext || webkitAudioContext;
  let audioContext = new _AudioContext_();

  var oReq = new XMLHttpRequest();
  oReq.open("GET", url, true);
  oReq.responseType = "arraybuffer";
  oReq.onload = function (oEvent) {
    var arrayBuffer = oReq.response; 
    makeLoop(arrayBuffer);
  };
  oReq.send(null);

  function makeLoop(arrayBuffer){
     soundContainer["A4"] = arrayBuffer;
     let currentTime = audioContext.currentTime;
     for(let i = 0; i < 10; i++){
        //playing at same intervals
            play("A4", currentTime + i * 0.5);
        setTimeout( () => stop("A4"), 500 + i * 500); //remove this line you will hear all the sounds.
     }
  }

  function play(notePlayed, start) {    

      audioContext.decodeAudioData(soundContainer[notePlayed], (buffer) => {
      let source; 
      let gainNode; 
        source = audioContext.createBufferSource(); 
        gainNode = audioContext.createGain();
        // pushing notes in note map
        notesMap[notePlayed].push({ source, gainNode });
        source.buffer = buffer;                   
        source.connect(gainNode);
        gainNode.connect(audioContext.destination);
        gainNode.gain.value = 1;
        source.start(start);
       });
    }

      function stop(notePlayed){    
        let note = notesMap[notePlayed].shift();

        note.source.stop();
     }
和一个
play()
fct,在这里我播放声音后填充数组:

  play(sound) { 
    // sound is just { soundName, velocity, start}   
    let source; 
    let gainNode; 
    // sound container is just a map from soundname to the sound data.
    this.audioContext.decodeAudioData(this.soundContainer[sound.soundName], (buffer) => {
      source = this.audioContext.createBufferSource(); 
      gainNode = this.audioContext.createGain();
      gainNode.gain.value = sound.velocity;
      // pushing sound in sound map
      this.soundMap[sound.soundName].push({ source, gainNode });
      source.buffer = buffer;                   
      source.connect(gainNode);
      gainNode.connect(this.audioContext.destination);
      source.start(sound.start);
     });
  }
现在是停止声音的部分:

  stop(sound){   
    //remember above, soundMap is a map from "soundName" to {gain, source} 
    let dasound = this.soundMap[sound.soundName].shift();
    let gain = dasound.gainNode.gain.value - 0.1;

    // we lower the gain via incremental values to not have the sound stop abruptly
    let i = 0;
    for(; gain > 0; i++, gain -= 0.1){ // watchout funky syntax
      ((gain, i) => {
        setTimeout(() => dasound.gainNode.gain.value = gain, 50 * i );
      })(gain, i)
    }
    // we stop the source after the gain is set at 0. stop is in sec
    setTimeout(() => note.source.stop(), i * 50);
  }

啊,是的,是的,是的!我终于找到了很多东西,终于费心阅读了文档中的“一切”(对角线)。让我告诉你这个api是一个未经加工的钻石。不管怎么说,他们确实有我想要的:

AudioParam接口表示与音频相关的参数,通常是AudioNode的参数(如GainNode.gain)。一 AudioParam可以设置为特定值或值的变化,以及 可以安排在特定时间和特定时间之后发生 模式

它有一个功能

他们甚至有一个我问的例子

// create audio context
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();

// set basic variables for example
var myAudio = document.querySelector('audio');
var pre = document.querySelector('pre');
var myScript = document.querySelector('script');

pre.innerHTML = myScript.innerHTML;

var linearRampPlus = document.querySelector('.linear-ramp-plus');
var linearRampMinus = document.querySelector('.linear-ramp-minus');

// Create a MediaElementAudioSourceNode
// Feed the HTMLMediaElement into it
var source = audioCtx.createMediaElementSource(myAudio);

// Create a gain node and set it's gain value to 0.5
var gainNode = audioCtx.createGain();

// connect the AudioBufferSourceNode to the gainNode
// and the gainNode to the destination
gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
source.connect(gainNode);
gainNode.connect(audioCtx.destination);

// set buttons to do something onclick
linearRampPlus.onclick = function() {
  gainNode.gain.linearRampToValueAtTime(1.0, audioCtx.currentTime + 2);
}

linearRampMinus.onclick = function() {
  gainNode.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 2);
}


它们也有不同类型的计时,比如指数而不是线性斜坡,我想这更适合这种情况。

“我能看到这种情况发生的唯一方法是AudioContext.decodeAudioData不同步。”你是对的,
。decodeAudioData
不同步。@guest271314好吧,该死,我必须重新处理一切“网络音频api文档中没有这些内容“您正在引用哪些文档?看@guest271314当然,当我把一切都安排好的时候,我还没有开始,但我已经准备好了to@guest271314完成了,很容易。我删除了整个声音容器,因为它有点不相关。JSFIDLE在我的答案的底部,更改顶部的var以更改增益递减的进程很好的示例,但是您想做什么呢?>“我希望我的声音在停止之前降低音量”。在音量降低到0左右后,您是否找到了停止声音(source.stop())的方法?我正在尝试检测Linearamp函数的结尾。但不幸的是,文件中没有这方面的内容。我可以设置一个间隔并检查增益值X倍,然后一旦该值接近0,我就可以停止源。但我更愿意在linearRamp过程的“真正末端”停止源代码。有什么想法吗?@Faks如果持续时间是0.3秒,你可以在0.3秒后停止。我不确定它是否完美,但如果我没记错的话,我就是这么做的。如果你放大一点,声音应该是0,所以不会有任何问题,不是吗?比如0.7秒后停止。谢谢;)这是我已经做过的,但这并不完美,如果函数本身能够在最后进行回调会更好。另外:这只是为使用Cordova的Web音频API的开发人员提供的信息。在iOS上,linearranpovalueattime()函数可以工作,但如果同时播放多个声音,则会在扬声器上随机发出吱吱声/嘎吱声/咔哒声/脏音。如果你的应用同时针对Android和iOS,你应该使用两个不同的函数,分别是适用于Android的linearRampToValueAtTime()和iOS上的SetInterval上的gainNode.gain.value(在Chrome 64上不推荐)。
// create audio context
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();

// set basic variables for example
var myAudio = document.querySelector('audio');
var pre = document.querySelector('pre');
var myScript = document.querySelector('script');

pre.innerHTML = myScript.innerHTML;

var linearRampPlus = document.querySelector('.linear-ramp-plus');
var linearRampMinus = document.querySelector('.linear-ramp-minus');

// Create a MediaElementAudioSourceNode
// Feed the HTMLMediaElement into it
var source = audioCtx.createMediaElementSource(myAudio);

// Create a gain node and set it's gain value to 0.5
var gainNode = audioCtx.createGain();

// connect the AudioBufferSourceNode to the gainNode
// and the gainNode to the destination
gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
source.connect(gainNode);
gainNode.connect(audioCtx.destination);

// set buttons to do something onclick
linearRampPlus.onclick = function() {
  gainNode.gain.linearRampToValueAtTime(1.0, audioCtx.currentTime + 2);
}

linearRampMinus.onclick = function() {
  gainNode.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 2);
}