音调与频域

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

代码部分也没有更多的新 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. 求求你别再用OkHttp调用API接口了,快来试试这款HTTP客户端库吧

    引言 在日常业务开发中,我们时常需要使用一些其他公司的服务,调用第三方系统的接口,这时就会涉及到网络请求,通常我们可以使用HttpClient,OkHttp等框架去完成网络请求.随着RESTful A ...

  2. 【Azure 应用服务】在Azure App Service for Windows 中部署Java/NodeJS/Python项目时,web.config的配置模板内容

    问题描述 在Azure App Service for Windows 中部署web项目时候,需要在wwwroot下设置web.config,对于不同语言的项目,web.config文件中的httpP ...

  3. 【Azure Fabric Service】怎样关闭 Azure Service Fabric?

    问题描述 怎样关闭Azure Service Fabric服务呢?在Azure门户上没有找到 Stop 按钮. 问题回答 Azure Service Fabric 默认是无法停止的,可以删除. 虽然可 ...

  4. [linux 爬坑] 几个linux发行版尝试和令人崩溃的ssr安装体验

    最近电脑上的manjaro好像出了问题,长时间不用就会死机.也懒得追究原因了,正好决定尝试几个发行版.首先尝试安装银河麒麟    这个发行版实际上就是ubuntu,甚至源什么的都是ubuntu的,也不 ...

  5. 【规范】Apifox就应该这么玩

    前言 缘由 好的工具就要配好的玩法 起因是最近在回顾项目时,看到了年事已高并且长时间不用的Postman,发现之前自己整理的接口文档十分混乱且没有规律.遂打开现在使用的Apifox,将本狗目前项目中使 ...

  6. Java 设计模式----单例模式--懒汉式

    1 package com.bytezreo.singleton; 2 3 /** 4 * 5 * @Description 单例模式 ---懒汉式 6 * @author Bytezero·zhen ...

  7. Java 封装+构造器+this 小测试

    1 package com.bytezero.account; 2 3 4 public class Account 5 { 6 private int id; //账号 7 private doub ...

  8. 你想要一个简单的 MQ 吗?(最简单的那种)

    FolkMQ 一个简单的消息中间件(全球最简单的那种,要比谁都简单!).追世间简单为何物,可叫我生死相许! 面向简单编程 1) 启动服务 docker run -p 18602:18602 -p 86 ...

  9. 搞清楚Promise.all的异常处理

    参考资料: https://www.jianshu.com/p/356f10ee476d https://blog.csdn.net/aaqingying/article/details/122966 ...

  10. java服务OOM和CPU飙升排查

    一.JVM参数 -D 可以是系统默认有的参数,也可以是自己定义的参数 -Dfile.encoding=UTF-8 -Dmaven.test.skip=true -Dspring.profiles.ac ...