webaudio_WebAudio Deep Note,第4部分:多种声音

webaudio

webaudio

Previously in this series:

本系列之前的内容:

  1. intro

    介绍

  2. play a sound

    播放声音

    play a sound

    播放声音

  3. loop and change pitch

    循环并改变音高

We need to play 30 sounds at the time, that is 30 instances of the same cello sample, all pitched all over the place and over time. (If that sounds odd, please revisit the intro post.) Let's ignore the "over time" changing of pitches for now and focus on the final chord. It's a D major chord (meaning D, A and F#) notes, where each note is played in several octaves and each note in each octave is played by several voices. Meaning for example the same A3 note is played twice. The full list of notes to play is:

我们需要同时播放30种声音,即同一大提琴样本的30个实例,并且在整个位置和整个时间都变高。 (如果听起来很奇怪,请重新看一下介绍性的帖子。)现在让我们忽略音高的“随时间变化”,而将注意力集中在最后的和弦上。 这是D大和弦(表示D,A和F#)的音符,其中每个音符以几个八度音阶演奏,而每个八度音阶中的每个音符以几种音色演奏。 例如,相同的A3音符会演奏两次。 要播放的音符的完整列表为:

const notes = {
  D1: {rate: 1/4, voices: 4},
  D2: {rate: 1/2, voices: 4},
  A2: {rate: 3/4, voices: 2},
  D3: {rate: 1,   voices: 2},
  A3: {rate: 3/2, voices: 2},
  D4: {rate: 2,   voices: 2},
  A4: {rate: 3,   voices: 2},
  D5: {rate: 4,   voices: 2},
  A5: {rate: 6,   voices: 2},
  D6: {rate: 8,   voices: 2},
  Fs: {rate: 10,  voices: 6},
};

As you see each note has a number of voices. The rate is how we're going to pitch things (see the previous post re: pitching). Because we already know how to pitch D3 based on our C3 sample, we'll use this as a starting point and call it rate 1, meaning no slow downs or speed ups. All other notes in the final chord are multiples of this D3.

如您所见,每个音符都有许多voicesrate是我们如何推销事物的方式(请参阅上一篇文章“推销”)。 因为我们已经知道如何根据C3样本对D3进行音高调整,所以我们将以此为起点,并将其称为1速率,这意味着不会减慢或加快速度。 最后和弦中的所有其他音符均为该D3的倍数。

As discussed already, a note (say D4) that is an octave up from the same note (D3) has twice the frequency. This means we play it twice as fast to get the correct frequency. Hence D4 is a rate of 2 compared to D3 "base" rate of 1. D5 then is twice the D4 or the rate of 4. D6 is twice D5, or rate of 8. In the other direction D2 is half of D3's frequency. So rate of 1/2. D1 is half of D2 or a quarter of D3. So rate of 1/4. That takes case of all the Ds.

如前所述,从同一音符(D3)起八度音阶的音符(例如D4)的频率为两倍。 这意味着我们以两倍的速度播放它以获得正确的频率。 因此,D4的比率为2,而D3的“基本”比率为1。D5是D4的两倍或比率4。D6是D5的两倍,即比率8。在另一个方向上,D2是D3频率的一半。 所以比率为1/2。 D1是D2的一半或D3的四分之一。 因此比率为1/4。 这需要所有D的情况。

Then A3 has the "perfect" ratio of 3:2 to D3. (Recall that string length illustration). And so the rate is 3/2. (In music theory parlance A is the interval of the "perfect fifth" of D.) A4 is 2 * A3 or a simple 3. A5 is 3 * 2 or 6. On the other side A2 is half of A3, so (3/2)/2 or 3/4.

那么A3与D3的“完美”比率为3:2。 (回想一下该字符串长度的插图)。 因此比率为3/2。 (在音乐理论中,A是D的“完美的第五个”的间隔。)A4是2 * A3或简单的3。A5是3 * 2或6。另一方面,A2是A3的一半,所以(3 / 2)/ 2或3/4。

Finally the top note F# (music theory: the major third above D) has the ratio of 5:4 in our perfect just tuning. We only have one F# and that is F#6. So it's 5/4 of D6. 8 * 5/4 = 10.

最后,顶帖F#(音乐理论:上面d大三)具有5:4的比例在我们完美的只是调整。 我们只有一个F#,也就是F#6。 所以它是D6的5/4。 8 * 5/4 = 10。

(Why 5:4? What happened to 4:3? We have 2:1 (octave), 3:2 (perfect fifth) and 4:3 is called perfect fourth. These, and 1:1, which is the same note (unison), are all the "perfect" intervals. After that things are not so perfect. They didn't sound like they go together that well to the people who came up with these names. So there. 5:4 is a major third. 6:5 is a minor third. But we only worry about octaves and fifths and a single major third in our Deep Note case.)

(为什么5:4?4:3发生了什么?我们有2:1(八度音程),3:2(完美的五度音)和4:3称为完美的四度。这些和1:1,这是相同的注解(统一)都是“完美”的时间间隔。在那之后,事情还不是那么完美。听起来似乎对想出这些名字的人并没有那么好。5:4 6:5是次要的三分之一。但是我们在Deep Note情况下只担心八度和五分之一以及一个主要的三分之一。)

Alright let's see some code. First I've decided to finally divorce loading a sample from playing it. So here it comes now, the load() function:

好吧,让我们看一些代码。 首先,我决定最终决定从加载样本中离婚。 因此,现在有了load()函数:

function load(files) {
  return new Promise((resolve, reject) => {
    const buffers = new Map;
    files.forEach(f => {
      fetch(f)
        .then(response => response.arrayBuffer())
        .then(arrayBuffer => audioContext.decodeAudioData(arrayBuffer))
        .then(audioBuffer => {
          buffers.set(f, audioBuffer);
          if (buffers.size === files.length) {
            resolve(buffers);
          }
        })
        .catch(e => console.log('uff'));
    });    
  });
}

The function takes an array of samples to load. Convenient when you want to be done with all samples you need to load (or preload when the user hovers a button maybe). The result of the function is a map of buffers, each keyed with the file name.

该函数需要加载样本数组。 当您要处理需要加载的所有样本时很方便(或者当用户将按钮悬停时可以预加载)。 该函数的结果是一个缓冲区映射,每个缓冲区都用文件名键入。

Next, some constants:

接下来,一些常量:

const C3 = 130.81;
const c3d150 = 150 / C3; // 1.1467013225;

const SAMPLE = 'Roland-SC-88-Cello-C3-glued-01.wav';
const sources = [];

You know what the first three are about. The last one is where we'll keep an array of buffer sources, ready to play (or stop). We'll have 30 buffer sources, one for each voice.

您知道前三个是什么。 最后一个是我们将保留一系列缓冲源的位置,准备播放(或停止)。 我们将有30个缓冲源,每个声音一个。

So when you want to stop all these sounds you loop through all sources and stop them. You can also delete them, since they can not be reused. If we need to play the same thing again, the 30 buffer sources will have to be recreated.

因此,当您想要停止所有这些声音时,您会遍历所有来源并停止它们。 您也可以删除它们,因为它们无法重复使用。 如果我们需要再次玩同一件事,则必须重新创建30个缓冲源。

function stop() {
  for (let i = 0; i < sources.length; i++) {
    sources[i] && sources[i].stop();
    delete sources[i];
  }
}

Now, time to play:

现在,是时候玩了:

function play() {
  load([SAMPLE]).then(buffers => {
    for (let note in notes) {    
      for (let i = 0; i < notes[note].voices; i++) {
         // todo
      }
    };
  });
}

This function loads the samples and loops through all the notes we need to play (the notes object from the top of this post) and then loops again for each repeating voice that plays the same note.

此函数加载样本并循环播放我们需要播放的所有音符(此文章顶部的notes对象),然后针对播放相同音符的每个重复voice再次循环。

In the body of the loop you'll find the same thing you already know. The new bits are setting the rate (to control the pitch) and pushing to the array of sources.

在循环的主体中,您将找到已经知道的相同内容。 新的位将设置速率(以控制音高)并推送到源数组。

function play() {
  load([SAMPLE]).then(buffers => {
    for (let note in notes) {    
      for (let i = 0; i < notes[note].voices; i++) {
        const source = audioContext.createBufferSource();
        source.buffer = buffers.get(SAMPLE);
        source.loop = true;
        source.playbackRate.value = c3d150 * notes[note].rate;    
        source.connect(audioContext.destination);
        source.start();
        sources.push(source);
      }
    };
  });
}

And this is it - this is how we play multiple sounds. The demo is here.

就是这样-这就是我们播放多种声音的方式。 演示在这里

Just make sure your volume is way down when you hit play. Because it might get loud. In the next installment we'll learn how to manage the volume, a.k.a. gain.

只需确保在播放时音量降低即可。 因为它可能会很大声。 在下一部分中,我们将学习如何管理音量(又称增益)

Tell your friends about this post on Facebook and Twitter

FacebookTwitter上告诉您的朋友有关此帖子的信息

翻译自: https://www.phpied.com/webaudio-deep-note-part-4-multiple-sounds/

webaudio

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值