javascript - Web Audio API 依次播放笔记
问题描述
我对 Javascript 比较陌生,我正在尝试使用 Web Audio API 播放音符。音符确实可以播放,但不是我想要的那样。我试图让音符一个接一个地演奏。我的代码中的每个节点都有一个频率数组。然后我使用 for 循环遍历这个数组并使用函数播放频率playNote
。我尝试使用 timeOut 在 for 循环中添加 playNote 调用的延迟,但这似乎不起作用。
class Node {
constructor(x, y, radius, color, frequency){
this.x = x
this.y = y
this.radius = radius
this.color = color
this.frequency = frequency
}
}
const player1 = new Node(100, 100, 30, 'blue',[1047])
const player2 = new Node(200, 100, 30, 'red',[1047, 1047])
var listOfNodes = []
listOfNodes.push(player1)
listOfNodes.push(player2)
function playNote(freq) {
oscillator = audioCtx.createOscillator()
gain = audioCtx.createGain()
oscillator.type = 'sine'
oscillator.connect(gain)
oscillator.frequency.value = freq
gain.connect(audioCtx.destination)
oscillator.start(0)
gain.gain.exponentialRampToValueAtTime(0.00001, audioCtx.currentTime + 1)
}
listOfNodes.forEach(node => {
var i;
for (i = 0; i < node.frequency.length; i++) {
setTimeout(function() {
playNote(node.frequency[i])
}, 2000 * i);
}
}
);
解决方案
有很多方法可以做到这一点。例如,您可以为每个playNote
. 像这样:
function playNote(freq, time) {
oscillator = audioCtx.createOscillator()
gain = audioCtx.createGain()
oscillator.type = 'sine'
oscillator.connect(gain)
oscillator.frequency.value = freq
gain.connect(audioCtx.destination)
oscillator.start(audioCtx.currentTime + time)
gain.gain.exponentialRampToValueAtTime(0.00001, audioCtx.currentTime + 1 + time)
}
for (i = 0; i < node.frequency.length; i++) {
playNote(node.frequency[i], i)
}
或者使用一个音符结束时执行的递归函数。但是为此,您需要指定实际结束时间oscillator.stop(audioCtx.currentTime + sometime)
,而不是那个丑陋的黑客:)gain.gain.exponentialRampToValueAtTime(0.00001, audioCtx.currentTime + 1 + time)
function playNote(freq) {
oscillator = audioCtx.createOscillator()
gain = audioCtx.createGain()
oscillator.type = 'sine'
oscillator.connect(gain)
oscillator.frequency.value = freq
gain.connect(audioCtx.destination)
oscillator.start(audioCtx.currentTime)
oscillator.stop(audioCtx.currentTime + 1)
oscillator.addEventListener('ended', () => {playNote(nextFrequency)}, { once: true });
}
但我认为最好的方法是使用现代 javascript 功能:Promises
with async await
const audioCtx = new AudioContext();
class Node {
constructor(x, y, radius, color, frequency){
this.x = x
this.y = y
this.radius = radius
this.color = color
this.frequency = frequency
}
}
const player1 = new Node(100, 100, 30, 'blue',[1047])
const player2 = new Node(200, 100, 30, 'red',[1047, 1047])
var listOfNodes = []
listOfNodes.push(player1)
listOfNodes.push(player2)
function playNote(freq) {
return new Promise((resolve) => {
oscillator = audioCtx.createOscillator()
gain = audioCtx.createGain()
oscillator.type = 'sine'
oscillator.connect(gain)
oscillator.frequency.value = freq
gain.connect(audioCtx.destination)
oscillator.start(audioCtx.currentTime)
oscillator.stop(audioCtx.currentTime + 1)
oscillator.addEventListener('ended', resolve, { once: true });
})
}
async function play() {
for (const node of listOfNodes) {
for (const frequency of node.frequency) {
console.log(`note with frequenty ${frequency} started`)
await playNote(frequency);
console.log(`note with frequency ${frequency} ended`)
}
}
}
play();
推荐阅读
- sql - 将一列变成多列
- algorithm - 如何在具有最大gcd的数组中找到对
- php - Flex-Flow Column Wrap - 带有标题的图标
- javascript - 反应角色 (JS)
- angular - 如何在 PrimeNg 的多选组件中添加额外的标签?
- openlayers - 如何从 OpenLayers 6 中的草图层获取草图坐标
- c++ - 我可以直接调用 std::terminate 吗?
- anylogic - 如何在池中配置资源以处理多个代理
- python - 使用 click.prompt 提示可选的 IntRange
- css - SASS - 由连接字符串和变量形成的新映射键