D28 - 走!去浏览器玩转黑胶唱片 Web Audio API

前言

今天玩转黑胶唱片,Audio Web API 接起来~~

Audio Web API

Audio Web API 可於网页上操作并播放音讯档案

根据 MDN 定义:

Web Audio API 是根据模组化路由 (Modular routing) 的概念所设计。所谓的模组化路由,即是以「音讯节点 (Audio nodes)」执行基本的音讯作业,节点又互相连接而构成「音讯路由图 (Audio routing graphs)」。在同一环境 (Audio context) 内,又可支援数个音源与多样的声道配置。此模组化设计可提供更高的灵活度,并能建立复杂的音讯函式与动态效果。

实作黑胶盘播放器

设计想法:当点击时黑胶盘开始转动播音乐,再点击一下暂停,另外有音量条调整音量大小。

基本设定

<!-- html  -->
<body>
    <audio id="music" src="音源.mp3"></audio>
    <h1>DROP THE MUSIC</h1>
    <section>
        <div id="disk" data-playing="false"></div>
        <input id="volume" type="range" min="0" max="4" step="0.1" value="2" />
        <label for="volume">volume</label>
    </section>
</body>

CSS 的黑胶动画效果设定

/* css  */
#disk {
      width: 500px;
      height: 500px;
      background-image: url('黑胶图片');
      background-repeat: no-repeat;
      background-size: contain;
      background-position: center;
      margin-bottom: 50px;
}
    
/*  将黑胶转动 css 另外写才方便 JS 套用     */
.rotating {
      animation: rotate 2s linear infinite;
}

/* 动画为黑胶从 0deg 转动到 360deg 模拟转动画面 */
@keyframes rotate {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}

基本设定完成,正式进入 JavaScript

1. 第一步:建立 AudioContext

要使用 Web API 首先要创建 audioContext,可以把它想像成一个容器用来装声音,在里面可以对声音进行各种处理。


图片来源MDN

使用建构子 AudioContext 创建

   let audioCtx = new AudioContext();

2. 第二步:建立音源节点

audioContext 内需要有音源的输入,可以是根据 url 载入的 audioNode 或者是靠 振荡器 Oscillators 产生的音源,而今天要用 HTML 标签的<audio> 来载入音源。

使用 audioContext.createMediaElementSource(音源节点)

// 先取得 audio tag 的节点
let music = document.querySelector('#music');

// 透过刚刚创建了 audioContext 创建音源节点
let source = audioCtx.createMediaElementSource(music)

3. 第三步: 建立 gainNode 音量节点

gainNode 节点是用来控制音量的大小,透过 gainNode.gain.value 可以知道目前音乐的音量,进而改变数值。

let gainNode = audioCtx.createGain();

4. 第四步:建立连结 connect

透过 connect 将音源的输入和输出串连,这样 Web API 的音源绑定就好罗~

source.connect(gainNode).connect(audioCtx.destination);

5. 第五步:设定黑胶点击事件

决定播放还是暂停可以设定一个属性值 playing 做状态转换,当 playing = 'false' 时点击播放,playing='true' 点击暂停,要撷取这个属性值当前的值使用 dataset.属性名称

如:

// html 设定 <div data-playing='false'></div>

document.querySelector('div').dataset.playing // false

点击时透过 classList.toggle 加上 rotating class 启动黑胶转动

disk.addEventListener('click', function (event) {
this.classList.toggle('rotating');
if (audioCtx.state === 'suspended') {
    audioCtx.resume();
}
if (this.dataset.playing === 'false') {
    music.play();
    this.dataset.playing = 'true';
} else if (this.dataset.playing === 'true') {
    music.pause();
    this.dataset.playing = 'false';
    }
});

6. 第六步:设定音量条

音量条的 HTML 使用 input 标签的 type='range'
加上其他属性 最小值min、最大值max、每次移动大小step、预设显示位置value,就可以取得拉动时的 value 资讯。

监听事件绑定 input (拉动音量条时), gainNode 节点的 value 值等同於 input 标签上拉动的数值。

let volume = get('#volume');
volume.addEventListener('input',  function (event) {
gainNode.gain.value = this.value});

完成

Reference

MDN - AudioContext
MDN - gainNode
MDN 实作
初探 Web Audio API
Chapter1-DJ最爱的音频动感图像(III)妈妈叫你不要玩音乐,现在知道当DJ很难了吧
音源来源

结语

Audio Web API 实在有太~多东西可以学了,还不知道如何取网路的 url,只能先引入本地端的 mp3 档案,今天试试水温!之後继续挑战
/images/emoticon/emoticon08.gif


<<:  (特别篇)Documents-Delivered-Data,Data-DrivenDocuments—爬虫D3做成D3(上)

>>:  每日挑战,从Javascript面试题目了解一些你可能忽略的概念 - Day28

关於 物件(Object)与类别(class)

正在复习C#~(书 和影片 文章 看到头晕) 发现有些观念真的简单又不简单 一定要用自己的方式搞懂~...

[Day 12] Reactive Programming - Reactor(publishOn/subscribeOn)

前言 在上一篇介绍了Reactor提供Scheduler来帮助开发者,这篇就是来说明具体是如何使用。...

[Day16] Andoroid - Kotlin笔记:null type & none-null type

这边先预祝大家中秋节快乐 连假比较忙的关系今天就挑个简单的主题来写 kotlin对於null的处理相...

【Day1】如何调整WIN10上应用程序的拨放音量?

在疫情期间相信大家都有在线上上课或会议的经验, 有时候我们会觉得老师上课的声音(或会议应用程序拨放的...

资料的含义 | ML#Day9

实务上,我们可能并没有自己想的那麽了解系统的真实面,这也造就一些起步上的困难,反思一下,这也关於问题...