gpt4 book ai didi

javascript - Web音频api,优雅地停止声音

转载 作者:太空宇宙 更新时间:2023-11-04 16:10:53 32 4
gpt4 key购买 nike

网络音频 API 提供方法 .stop() 来停止声音。我希望在停止之前降低声音的音量。为此,我使用了增益节点。然而,我遇到了奇怪的问题,有些声音无法播放,而且我不明白为什么。

这是我所做的一个简化版本:

https://jsfiddle.net/01p1t09n/1/

如果您删除带有 setTimeout() 的行,您会听到每个声音都会播放。当 setTimeout 存在时,并非所有声音都会播放。真正让我困惑的是,我相应地使用 pushshift 来找到正确的声音来源,但似乎是另一个停止播放的声音。我能看到这种情况发生的唯一方法是 AudioContext.decodeAudioData 不同步。只需尝试 jsfiddle 即可更好地理解,并显然戴上耳机。

这是jsfiddle的代码:

  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();
}
<小时/><小时/>

这只是为了解释为什么我这样做,你可以跳过它,这只是为了解释为什么我不使用 stop()

我这样做的原因是因为我想优雅地停止声音,所以如果有可能在不使用 setTimeout 的情况下做到这一点,我很乐意接受。

基本上,我的顶部有一张 map ,其中包含我的声音(注释如 A1、A#1、B1...)。

soundMap = {"A": [], "lot": [], "of": [], "sounds": []};

和一个 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);
}

最佳答案

啊啊,是啊,是啊,是啊!通过最终费心阅读文档中的“所有内容”(对 Angular 线),我终于发现了很多东西。让我告诉你这个 API 是一颗未经雕琢的钻石。不管怎样,他们确实有我想要的 Audio param :

The AudioParam interface represents an audio-related parameter, usually a parameter of an AudioNode (such as GainNode.gain). An AudioParam can be set to a specific value or a change in value, and can be scheduled to happen at a specific time and following a specific pattern.

它有一个功能linearRampToValueAtTime()

他们甚至还提供了我所要求的示例!

// 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);
}

Working example here

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

关于javascript - Web音频api,优雅地停止声音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41511541/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com