WebAudio Deep Note,第2.1部分:靴子和猫

In the previous installment we came across the idea of creating noise via an oscillator and via a buffer filled with your own values (as opposed to values being read from a pre-recorded file). I thought a bit of elaboration is in order, even though we're not going to use these ideas for the Deep Note. So... a little diversion. But it's all in the name of WebAudio exploration!

在上一期文章中,我们遇到了通过振荡器和填充有您自己的值(而不是从预先记录的文件中读取的值)的缓冲区创建噪声的想法。 我认为需要进行一些详细说明,即使我们不会在Deep Note中使用这些想法。 所以...有点转移。 但这全都是以WebAudio探索的名义!

靴子和猫 (Boots and Cats)

You know these electronic, EDM-type stuff, 80's electronica, etc. When you have "four on the floor" (kick drum hit on every beat) and some sort of snare drum on every other beat. It sounds a bit as if you're saying Boots & Cats & Boots & Cats & Boots & Cats & so on.

您会知道这些电子的EDM型东西,80年代的电子乐器等等。当您“落在地板上四个”(每个节拍都击打鼓)并且每隔一个节拍有某种军鼓时,您就会知道。 听起来好像您是在说“靴子和猫”以及“靴子和猫”以及“靴子和猫”等等。

Let's see how we can generate similar sounds using a sine wave for the kick drum (Boots!) and a random buffer of white noise for the snare drum (Cats!).

让我们看看如何使用正弦波为踢鼓(Boots!)和随机的白噪声缓冲区(军鼓(Cats!))生成类似的声音。

Generating realistic-sounding instruments is a wide topic, here we're just exploring the WebAudio API so let's keep it intentionally simple. If you want to dig deeper though, here's a good start.

生成逼真的乐器是一个广泛的话题,这里我们只是在探索WebAudio API,因此让我们故意使其保持简单。 如果您想更深入地研究,这是一个很好的开始

Here's a demo of the end result.

这是最终结果的演示

用户界面 (UI)

Just two buttons:

只需两个按钮:

<button onclick="kick()">
  🥾🥾🥾<br>
  <abbr title="shortcut key: B">B</abbr>oots
</button>
<button onclick="snare()" id="cats">
  🐈🐈🐈<br>
  <abbr title="shortcut key: C, also X">C</abbr>ats
</button>
<p>Tip: Press B for Boots and C (or X) for Cats</p>

Boots and Cats UI

建立(Setup)

Setting up the audio context and the keydown hooks:

设置音频上下文和keydown挂钩:

if (!window.AudioContext && window.webkitAudioContext) {
  window.AudioContext = window.webkitAudioContext;
}
const audioContext = new AudioContext();

function kick() {
  // implement me!
}

function snare() {
  // me too!
}

onkeydown = (e) => {
  if (e.keyCode === 66) return kick();
  if (e.keyCode === 67 || e.keyCode === 88) return snare();
};

Now all we need is to implement the kick() and snare() functions.

现在,我们所需要做的就是实现kick()snare()函数。

(Cats)

The "Cats!" snare drum is white noise. White noise is random vibrations evenly distributed across all frequencies. (Compare this to e.g. pink noise which is also random but adjusted to human hearing - less low frequencies and more highs.)

猫!” 小军鼓是白噪声。 白噪声是在所有频率上均匀分布的随机振动。 (将其与例如粉红噪声进行比较,该噪声也是随机的,但会根据人的听力进行调整-低频次数较少,而高频次数较多。)

The snare() function can be really simple. Just like before, create a buffer source, give it an audio buffer (stuff to play), connect to the audio destination (speakers) and start playing.

snare()函数可以非常简单。 与之前一样,创建一个缓冲源,为其提供音频缓冲(可播放的东西),连接到音频目的地(扬声器)并开始播放。

function snare() {
  const source = audioContext.createBufferSource();
  source.buffer = buffer;
  source.connect(audioContext.destination);
  source.start();
}

This snare function will play the exact same buffer every time, so we only need to generate the buffer once and then replay it. If you think this is boring... well the buffer is random so no one will ever know that the buffer is the same every time. But you can always generate a new buffer every time (expensive maybe) or create a longer buffer than you need and play different sections of it.

这个小军曲功能每次都会播放完全相同的缓冲区,因此我们只需要生成一次缓冲区,然后重播它即可。 如果您认为这很无聊,那么缓冲区是随机的,因此没人会每次都知道缓冲区是相同的。 但是您总是可以每次都生成一个新的缓冲区(可能很昂贵),或者创建比您需要的缓冲区更长的缓冲区,并播放其中的不同部分。

And what's in that buffer? As you saw in the previous post it's an array with a (lot of!) values between -1 and 1, describing samples of a wave of some sort. When these values are random, the wave is not that pretty. And the result we, humans, perceive as noise. But turns out that, strangely enough, short bursts of random noise sound like a snare drum of some sort.

那缓冲区里有什么? 如您在上一篇文章中所看到的,这是一个数组,其值(很多!)在-1和1之间,描述了某种波形的样本。 当这些值是随机的时,波动就不会那么漂亮。 结果,我们人类认为是噪音。 但是事实证明,奇怪的是,短时的随机噪音听起来像某种军鼓。

OK, enough talking, let's generate the bugger, I mean the buffer.

OK,足够多的讨论,让我们生成Bugger,我的意思是缓冲区。

const buffer = audioContext.createBuffer(1, length, audioContext.sampleRate);

What length? As you know, if the length is the same as the sample rate you get 1 second of sound. That's a looong snare hit. Experiment a bit and you'll see you need a lot less:

什么长度如您所知,如果长度与采样率相同,您将获得1秒的声音。 这简直是​​个小军鼓。 进行一些实验,您会发现需要的东西更少了:

const length = 0.05 * audioContext.sampleRate;

Now you have an empty buffer, 0.05 seconds long. You can access its content with:

现在您有了一个空缓冲区,长度为0.05秒。 您可以通过以下方式访问其内容:

let data = buffer.getChannelData(0);

0 gives you access to the first channel. Since we created a mono buffer, it only has that one channel. If you create a stereo buffer, you can populate the two channels with different random samples, if you feel so inclined.

0允许您访问第一个频道。 由于我们创建了单声道缓冲区,因此只有一个通道。 如果创建立体声缓冲器,则可以在两个通道中填充不同的随机样本(如果您觉得这样的话)。

Finally, the randomness to fill the channel data:

最后,填充通道数据的随机性:

for (let i = 0; i < length; i++) {
  data[i] = Math.random() * 2 - 1;
}

The whole * 2 - 1 is because Math.random() generates numbers from 0 to 1 and we need -1 to 1. So if the random number is 0 it becomes 0 * 2 - 1 = -1. And then if the random number is 1, it becomes 1 * 2 - 1 = 1. Cool.

整个* 2 - 1 2-1是因为Math.random()生成从0到1的数字,而我们需要从-1到1。因此,如果随机数为0,则它​​变为0 * 2 - 1 = -1 。 然后,如果随机数为1,则它变为1 * 2 - 1 = 1 。 凉。

In this case the white noise is louder than the kick sine wave, so making the amplitude of the noise between -0.5 and +0.5 gives us a better balance. So data[i] = Math.random() - 1; it is.

在这种情况下,白噪声要比反正弦波响亮,因此将噪声的幅度设置在-0.5至+0.5之间可以使我们达到更好的平衡。 因此data[i] = Math.random() - 1; 它是。

All together:

全部一起:

const length = 0.05 * audioContext.sampleRate;
const buffer = audioContext.createBuffer(1, length, audioContext.sampleRate);
let data = buffer.getChannelData(0);
for (let i = 0; i < length; i++) {
  data[i] = Math.random() - 1;
}

function snare() {
  const source = audioContext.createBufferSource();
  source.buffer = buffer;
  source.connect(audioContext.destination);
  source.start();
}

The buffer is created once and reused for every new buffer source. The buffer sources needs to be created for every hit though.

缓冲区仅创建一次,然后可用于每个新的缓冲区源。 但是,需要为每个匹配创建缓冲源。

Moving on, the boots!

继续前进,靴子!

靴子 (Boots)

The kick (Boots!) is a low-frequency sine wave. We create the wave using createOscillator():

踢(Boots!)是低频正弦波。 我们使用createOscillator()创建wave:

const oscillator = audioContext.createOscillator();

There are a few types of oscillators. Sine is one of them:

有几种类型的振荡器。 正弦波就是其中之一:

oscillator.type = 'sine';

60Hz is fairly low, yet still fairly audible, frequency:

60Hz的频率相当低,但仍然可以听到:

oscillator.frequency.value = 60;

Finally, same old, same old - connect and play:

最后,同样古老,同样古老-连接并播放:

oscillator.connect(audioContext.destination);
oscillator.start();

This creates a low-frequency wave and plays it indefinitely. To stop it we call stop() and schedule it 0.1 seconds later.

这样会产生低频波并无限期播放。 要停止它,我们调用stop()并将其安排在0.1秒后。

oscillator.stop(audioContext.currentTime + 0.1);

Here currentTime is the audio context's internal timer: the number of seconds since the context was created.

这里的currentTime是音频上下文的内部计时器:自创建上下文以来的秒数。

This is cool and all, but we can do a little better without adding too much complexity. Most instruments sound different when the sound is initiated (attack!) vs later on (sustain). So the sine wave can be our sustain and another, shorter and triangle wave can be the attack.

这很酷,但是我们可以做得更好一点,而不会增加太多复杂性。 发出声音(攻击!)后再发出声音(延音),大多数乐器的声音有所不同。 因此,正弦波可以作为我们的持续力,另一个较短的三角波可以作为攻击力

(BTW, the types of oscillators are sine, triangle, square and sawtooth. Play with them all!)

(顺便说一句,振荡器的类型为sinetrianglesquaresawtooth 。全部使用!)

Here's what I settled on for the triangle wave:

这是我为三角波确定的条件:

const oscillator2 = audioContext.createOscillator();
oscillator2.type = 'triangle';
oscillator2.frequency.value = 10;
oscillator2.connect(audioContext.destination);
oscillator2.start();
oscillator2.stop(audioContext.currentTime + 0.05);

10Hz is way too low for the human hearing, but the triangle wave has overtones at higher frequencies, and these are audible.

10Hz对于人类的听觉来说太低了,但是三角波在较高的频率下具有泛音,并且可以听到。

So the final kick is:

因此,最后一跳是:

function kick() {
  const oscillator = audioContext.createOscillator();
  oscillator.type = 'sine';
  oscillator.frequency.value = 60;
  oscillator.connect(audioContext.destination);
  oscillator.start();
  oscillator.stop(audioContext.currentTime + 0.1);

  const oscillator2 = audioContext.createOscillator();
  oscillator2.type = 'triangle';
  oscillator2.frequency.value = 10;
  oscillator2.connect(audioContext.destination);
  oscillator2.start();
  oscillator2.stop(audioContext.currentTime + 0.05);
}

下一个...(Next...)

Alrighty, diversion over, next time we pick up with Deep Note. Meanwhile you can go play Boots & Cats.

好吧,转移注意力,下一次我们与Deep Note接洽。 同时,您可以玩Boots&Cats

Oh, you may hear clicks in Firefox and Safari (Chrome is ok) when the sine wave stops. That's annoying but you'll see later on how to deal with it. Spoiler: turn down the volume and then stop. But in order to turn down the volume you need a volume knob (a gain node) and you'll see these in action soon enough.

哦,当正弦波停止时,您可能会听到Firefox和Safari中的点击声(Chrome可以)。 这很烦人,但是稍后您将看到如何处理它。 扰流板:调低音量然后停止。 但是,要降低音量,您需要一个音量旋钮(一个增益节点),您会很快看到它们的作用。

Tell your friends about this post on Facebook and Twitter

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

翻译自: https://www.phpied.com/webaudio-deep-note-part-2-1-boots-and-cats/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值