音调与频域

此章中如果对音乐部分不感兴趣,可忽略

代码部分也没有更多的新 api ,重要的还是相关的物理与声音的相关知识

到目前为止我们已经学过了声音的基础属性:定时与音量。为了能处理更复杂的的情况,例如声音的均衡(比如,增加低音和降低高音),我们需要更复杂的工具。此章节将介绍一些用于更有趣的转换工具,这些工具能模拟不同的声音环境并且可以直接使用 JavaScript 操作。

重要理论:音调基础

音乐是由多种声音同时播放所构成的。由乐器产生的声音可以非常的复杂,因为声音通过乐器的各个部分反射,并以独特的方式形成。然而这些音调都有共同的特征:从物理上讲,它们都是周期性的波形。这些周期波形在我们耳朵听来就是音调。衡量音的音调由频率决定,每秒重复波形的次数被叫作赫兹。频率是波峰之间的时间差(单位为秒)。如图 4-1 所示,如果我们时间维度减半,我们最终得到的是相应加倍的频率,同样的音色,高一个八度。与之相反,如果我们将频率延展两倍,这会使音色降低一个八度。所以,我们的耳朵感知到的音调(就像音量)是指数级地:每一个八度,频率翻一倍。

注:八度音,是指两个音之间的音高距离,其中一个音的频率是另一个音的两倍

注: 国际标准音高 A4 为 440 赫兹, 比 A4 高一个八度的 A5 即为 880 赫兹,比 A4 低一个八度的 A3 则为 220 赫兹

图 4-1

八度音分成12个半音。每个相邻的半音对具有相同的频率比(至少在同等程度上的音调是这样), 换句话说, A4 到 A#4 的比率 与 A#4 到 B 是相等的。

A#4 在音乐中表示一个音符, A# 指 A 音升高半音的表示方法,4 指的是音高的位置

图 4-1 展示了我们如何推导每个连续半音之间的比例。得到的结论:

  1. 为了把一个音符向上转一个八度,我们把这个音符的频率加倍
  2. 每个八度音分成12个半音,同等程度的音调拥有相等的频率比

让我们定义 f0 表示某个频率, f1 表示高八度的同音。我们知道它们之间的关系:

f1 = 2 * f0

接下来,设 k 作为 两个连续半音间的乘数。由于八度音阶有12个半音,可得:

f1 = f0 * k * k * k * ... * k(12x) = f0 * k^12

解上面的方程组,得:

2 * f0 = f0 * k^12

解得 k:

k = 2^(1/12) = 1.0595...

其实没那么麻烦,所有这些与半音相关的偏移实际上都不需要手动执行,因为很多音频环境(包括 Web Audio API)包含了失谐(detune)的概念,它使频域线性化。Detune 的单位是分,每个八度包含 1200 分, 每个半音包含了 100 分。通过指定1200的调谐,就提高了一个八度,指定 -1200 则降低一个八度。

音调(Pitch) 与 playbackRate

Web Audio API 在每个 AudioSourceNode 上提供了一个 playbackRate 参数。这个参数值可以影响任何 sound Buffer。注意,在这种情况下,音高和样本的时长都会受到影响。有一些复杂的方法试图只影响音高而不影响音频的时长,但是要以通用的方式做到这一点是非常困难的,否则就会在混合中引入奇奇怪怪的东西。

在“重要理论:音调基础”一节中讨论过, 我们简单的通过半音比率 2^(1/12) 乘以频率计算出连续的半音频率. 如果你正在开发一种乐器或在游戏设置中使用随机音调,这将非常有用。下面的代码演示了在半音中以给定的频率偏移音调:

function playNote(semitones) {
// 假定新的 source 从 buffer 中创建了.
var semitoneRatio = Math.pow(2, 1/12);
source.playbackRate.value = Math.pow(semitoneRatio, semitones);
source.start(0);
}

正如我们之前讨论过的那样,我们的耳朵是以指数级感知音调的。将音调当作指数量对待是不方便的,这样的话我们会经常和一些怪怪的值打交道比如 2 的 12 次方根。取而代之的是我们可以使用失谐参数(detune) 指定要偏移多少分。 所以你可以以更简单的方式重写上面的失谐函数了:

function playNote(semitones) {
source.detune.value = semitones * 100;
source.start(0);
}

如果你的音调转了太多的半音(比如,playNote(24)), 你会开始听到失真。正因如此,数字钢琴为每个乐器包括多个样本。好的数字钢琴完全避免音高弯曲,并包括为每个键专门录制的单独样本。

优秀的数字钢琴通常为每个键包含多个样本,这些样本会根据按键的速度回放

多个声音多种变化

在游戏中的一个重要特点就是会同时播放多个音效。想象一下在射击游戏内多角色用机枪扫射的效果。每把机枪每秒多次扫射,这会同时播放十多种音效。用相同的资源播放实现不同的效果这正是 Web Audio API 的亮点。

如果枪声效果都是都一种,那就显得单调了,就算声音根据目标距离的不同而变化(稍后会讨论到“Spatialized Sound” 空间音频),但这效果显示还不够。幸运的是 Web Audio API 提供了之前例子中提到过的可简单改变音效的至少两种方式:

  1. 子弹发射期间的轻微变化

  2. 通过改变音调模拟真实世界的随机

利用我们已掌握的时间与音调的知识,实现这两种效果就相当简单直接了:

function shootRound(numberOfRounds, timeBetweenRounds) {
var time = context.currentTime;
// 使用同一个 buffer 实现快速连续播放多种音效
for (var i = 0; i < numberOfRounds; i++) {
var source = this.makeSource(bulletBuffer);
source.playbackRate.value = 1 + Math.random() * RANDOM_PLAYBACK;
source.start(time + i * timeBetweenRounds + Math.random() * RANDOM_VOLUME);
}
}

以上代码实现 demo 可参考 https://github.com/willian12345/WebAudioAPI/tree/master/examples/ch04/index.html

Web Audio API 会在播放时自动一次性合并,本质上就是将多个音频波形叠加在一起。这会导致一些问题比如音频被裁剪,这我们之前已经讨论过了。此例中在加载的音频文件的 AudioBuffer 上添加了一些变化。在完全合成的音频情况下,这点小缺点是值得接受的。

重要理论:理解频域(Frequency Domain)

到目前为止,我们的理论讨论,我们只研究了声音随时间变化的压强函数。另一种观察声音的有用方法是绘制振幅,看看它是如何随频率变化的。

时域和频域图之间的关系图是基于傅立叶分解。正如之前所见,自然界中音波经常是周期循环。在数学上来讲,周期性的音波可视作一系列简单的不同幅度与频率正弦波的叠加。越多的正弦波叠加,越能更准确的模拟原函数。通过。傅立叶变换我们可以找到组成信号的各个正弦波,更多的细节已超出本书讨论的范围。存在很多算法也能达到相同的目的,比较知名的算法是 Fast Fourier Transform(FFT)。好消息是 Web Audio API 实现了这一算法。我们后面会讨论到。

一般来说,我们可以取一个声波,算出其组成的波,在新图上以点(频率,振幅)的形式绘制得到频域图。

如图 4-2 展示了存一个 440Hz 的纯音符(称为 A4)

图 4-2 一个完美的 1-KHz 正弦波在时间与频域上的表示

让我们关注在频域上,我们可以更好的感受到声音的质量,包括音调内容,噪声,等等。高级的音调检测算法就是基于频域。真实乐器产生的声音都包含泛音(overtones) , 所以 A4 被钢琴演奏出的频域图和小号演奏出的同一个声调看起来非常不一样。不管声音的复杂性如何,同样的傅里叶分解思想都适用。图 4-3 展示了一个更复杂的音频片断的频域。

图 4-3 一个更复杂的音频片断的时域和频域

随着时间的推移,这些图上表现非常不同。如果你非常慢速地回放图 4-3 中的声音,并观察它沿着每个图象移动,您会注意到时域图是(在左边)从左向右分析记录移动。频域图(右侧)是波形在某一时刻的频率分析,它可能变化的更快更不可预测。

重要地是,当检测的声音不被认为具有特定的音调时,频域分析研究声音时还是时分有用的。风声,敲击声,枪声,在频域上有着明显的区别表达。举个例子,白噪声拥有的是比较平坦的频域频谱范围,这是因为每个频率都是相等的。

基于振荡器(oscillator)的直接声音合成

正如本书早前讨论过的,声音信号在 Web Audio API 中被表示成 AudioBuffer 浮点数组形式。大多情况下, buffer 缓冲是从加载的一个声音文件或者从声音流创建的。在一些项目中,我们可能希望合进自己的声音。我们可以通过JavaScript 来进它进行编程创建 audio buffer,简单的数学函数周期计算得到值并把值赋值给数组。通过这种方法,我们可以手动更改正弦波的幅度与频率,甚至联合多个正弦波创建任意的声音 [ 回想一下之前提到的傅里叶变换 ]。

想象中用 JavaScript 来做这些工作可能低效且复杂。但是 Web Audio API 提供了原函数让你使用振荡器来实现:振荡器节点(OscillatorNode). 这些节点拥有可配置的频率和失谐。它们也有着自己这一类的波形。内建有 正弦,三角,锯齿,方波,如下图示:

图 4-4,振荡器(oscillator)能计算得到的几种声波图

利用 AudioBufferSourceNodes 振荡器(oscillator)可以很方便的在音频图中使用。以下是一个例子:

function play(semitone) {
// Create some sweet sweet nodes.
var oscillator = context.createOscillator();
oscillator.connect(context.destination);
// Play a sine type curve at A4 frequency (440hz).
oscillator.frequency.value = 440;
oscillator.detune.value = semitone * 100;
// Note: this constant will be replaced with "sine".
oscillator.type = oscillator.SINE;
oscillator.start(0);
}

除了这些基本的波类型,你还可以使用谐波表为你的振荡器创建一个自定义的波表, 这样你就可以更高效的创建比上面这个例子中更复杂的波形。这在音乐合成应用中时分重要,但这超出了本书讨论的范围。


注:转载请注明出处博客园:王二狗Sheldon池中物 (willian12345@126.com)

Web Audio API 第4章 音调与频域的更多相关文章

  1. 【HTML5】Web Audio API打造超炫的音乐可视化效果

    HTML5真是太多炫酷的东西了,其中Web Audio API算一个,琢磨着弄了个音乐可视化的demo,先上效果图: 项目演示:别说话,点我!  源码已经挂到github上了,有兴趣的同学也可以去st ...

  2. 关于HTML5音频——audio标签和Web Audio API各平台浏览器的支持情况

    对比audio标签 和 Web Audio API 各平台浏览器的支持情况:   audio element Web Audio API desktop browsers Chrome 14 Yes  ...

  3. [Javascript] Intro to the Web Audio API

    An introduction to the Web Audio API. In this lesson, we cover creating an audio context and an osci ...

  4. 关于Web Audio API的入门

    Web Audio API提供了一个简单强大的机制来实现控制web应用程序的音频内容.它允许你开发复杂的混音,音效,平移以及更多. 可以先看一下MDN的这篇文章<Web Audio API的运用 ...

  5. 使用Web Audio API绘制音波图

    摘要:Web Audio API是对<audio> 标签功能上的补充,我们可以用它完成混音.音效.平移等各种复杂的音频处理,本文简单的使用其完成音波图的绘制. PS:本例子使用ES6编程, ...

  6. H5的Web Audio Api

    概述 研究Web Audio Api的主要原因是:工作中需要在ios中实现声音的淡出效果,主要是通过setInterval来改audio标签的volume属性实现的,但是ios上面volume属性是只 ...

  7. Web Audio API之手把手教你用web api处理声音信号:可视化音乐demo

    1.Web Audio API 介绍 Web Audio API 提供了在Web上控制音频的一个非常有效通用的系统 ,这些通用系统通俗的讲就是我们可以利用Web Audio API提供的各种方法操作各 ...

  8. HTML5 ——web audio API 音乐可视化(二)

    上一篇 web audio API 音乐可视化(一)介绍了一些基本的API,以及如何简单的播放一个音频,本篇介绍一下怎么对获取到的音频进行分析,并将分析后的数据绘制成图像. 最终效果请戳这里; 完整版 ...

  9. HTML5 ——web audio API 音乐可视化(一)

    使用Web Audio API可以对音频进行分析和操作,最终实现一个音频可视化程序. 最终效果请戳这里; 完整版代码请戳这里,如果还看得过眼,请给一个start⭐ 一.API AudioContext ...

  10. 【Web Audio API】 — 那些年的 web audio

    转 TAT.Jdo:[Web Audio API] - 那些年的 web audio 这主题主要是早期对 web audio api的一些尝试,这里整理一下以便以后翻阅,如有错误,诚请指正. 在这之前 ...

随机推荐

  1. 【Azure Cloud Service】Cloud Service(Classic) 迁移失败,找不到解决方案怎么办?

    问题描述 很老很老的云服务,在迁移到 Cloud Service(Extended Support)[云服务外延支持] 时,迁移的验证步骤不通过,因为资源中没有包含虚拟网络(Virtual Netwo ...

  2. 【Azure 应用服务】Azure Function 部署槽交换时,一不小心把预生产槽上的配置参数交换到生产槽上,引发生产错误

    问题描述 部署Function代码先到预生产槽中,进行测试后通过交换方式,把预生产槽中的代码交换到生产槽上,因为在预生产槽中的设置参数值与生产槽有不同,但是在交换的时候,没有仔细检查.导致在交换的时候 ...

  3. 论文《Attention is all you need》阅读笔记

    Attention is all you need Transformer模型 Model Architecture Transformer结构上和传统的翻译模型相同,拥有encoder-decode ...

  4. spark-sql 与hive 常用函数

    窗口函数与分析函数应用场景:(1)用于分区排序(2)动态Group By(3)Top N(4)累计计算(5)层次查询 窗口函数FIRST_VALUE:取分组内排序后,截止到当前行,第一个值LAST_V ...

  5. C++ //list容器 构造函数 //list赋值和交换 //list容器大小操作 //list插入和删除,移除 //清空 //list数据存取back(); front() //list 反转和排序

    1 //list容器 构造函数 //list赋值和交换 //list容器大小操作 2 //list插入和删除,移除 //清空 //list数据存取back(); front() 3 //list 反转 ...

  6. 深入解析ASP.NET Core MVC的模块化设计[下篇]

    ASP.NET Core MVC的"模块化"设计使我们可以构成应用的基本单元Controller定义在任意的模块(程序集)中,并在运行时动态加载和卸载.<设计篇>介绍了 ...

  7. 使用python连接hive数仓

    1 版本参数 查看hadoop和hive的版本号 ls -l /opt # 总用量 3 # drwxr-xr-x 11 root root 227 1月 26 19:23 hadoop-3.3.6 # ...

  8. guava和redis 都是缓存用的,不过redis要起服务,guava不用起服务

    guava和redis 都是缓存用的,不过redis要起服务,guava不用起服务

  9. ubuntu环境下python下使用OpenCV库读取USB摄像头的画面

    一 概念 OpenCV是一个开源的计算机视觉和机器学习软件库.它可以使用pip命令行中的以下命令安装:"pip install opencv-python" 这个做视觉处理,非常的 ...

  10. 已安装docker-compose,安装harbor时还是提示docker-compose未安装或者Permission denied的解决方案

    安装Harbor时,下载安装了docker-compose并赋予权限 sudo curl -L "https://github.com/docker/compose/releases/dow ...