网络音频 API 互相播放音符

Web Audio API Play Notes after each other

我对 Javascript 比较陌生,我尝试使用网络音频 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) 指定实际结束时间,而不是那个丑陋的 hack :) 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();