首页 > 解决方案 > 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);
            }
        }
    );

标签: javascriptweb-audio-api

解决方案


有很多方法可以做到这一点。例如,您可以为每个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 功能:Promiseswith 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();


推荐阅读