做一个在线调音器

本来想做一个网页版的调音器,在网上搜了一下,发现已经有人实现过了,但是自己对这个原理还是很感兴趣,所以参照人家的博客源码,用svelte重新实现了,并且放在了我的网站上。

一般用的调音器都是表盘式的,工作原理就是设备听到声音,分析声音的频率,判断出音高之后在屏幕上显示出来,在这个过程中我们可能需要解决这些问题:

  1. 获取设备麦克风权限
  2. 获取声音频率
  3. 将声音频率换算成音高
  4. 将当前声音最接近的音名显示到表盘中间,并用指针显示当前声音与该音名对应的声音之间的偏差。偏低则向左指,偏低则向右指。

看下这个过程的代码实现。

获取麦克风权限

获取麦克风权限使用 navigator.mediaDevices.getUserMedia() 这个api,
它接受一个MediaStreamConstraints (en-US) 对象,指定了请求的媒体类型和相对应的参数。返回对象是一个Promise.

这里只需要处理音频,所以参数传入{audio:true} 就可以

    navigator.mediaDevices
      .getUserMedia({
    audio: true })
      .then(handleStream)
      .catch(function (error) {
   
        alert(error.name + ': ' + error.message);
      });

获取到媒体流之后,要从中分析得到声音频率。

这里有一个坑,我把代码部署到服务器之后,发现无法获取到麦克风权限,控制台报错
在这里插入图片描述
查了一下,TypeError: Cannot read property ‘getUserMedia’ of undefined
在这里插入图片描述
发现必须使用HTTPS加密通信才能获取getUserMedia(),这个问题需要注意下。

获取声音数据

用来处理音频的API是AudioContext

AudioContext接口表示由链接在一起的音频模块构建的音频处理图,每个模块由一个AudioNode表示。音频上下文控制它包含的节点的创建和音频处理或解码的执行。在做任何其他操作之前,您需要创建一个AudioContext对象,因为所有事情都是在上下文中发生的。建议创建一个AudioContext对象并复用它,而不是每次初始化一个新的AudioContext对象,并且可以对多个不同的音频源和管道同时使用一个AudioContext对象。

分析音频流需要使用 AudioContext.createAnalyser()

AudioContext的createAnalyser()方法能创建一个AnalyserNode,可以用来获取音频时间和频率数据,以及实现数据可视化。

用js处理音频流需要用到
AudioContext.createScriptProcessor()

AudioContext 接口的createScriptProcessor() 方法创建一个ScriptProcessorNode 用于通过JavaScript直接处理音频.

scriptProcessor是一个ScriptProcessorNode

ScriptProcessorNode 接口允许使用JavaScript生成、处理、分析音频. 它是一个 AudioNode, 连接着两个缓冲区音频处理模块, 其中一个缓冲区包含输入音频数据,另外一个包含处理后的输出音频数据. 实现了 AudioProcessingEvent (en-US) 接口的一个事件,每当输入缓冲区有新的数据时,事件将被发送到该对象,并且事件将在数据填充到输出缓冲区后结束.

监听它的audioprocess事件就可以获得输入音频数据。

音频通路需要连接最终的目标节点
AudioContext.destination

AudioContext的destination属性返回一个AudioDestinationNode表示context中所有音频(节点)的最终目标节点,一般是音频渲染设备,比如扬声器。

将以上几个节点依次连接起来

 // 获取AudioContext实例
const audioContext = new window.AudioContext();  
// 创建 analyser用来获取音频频率数据
const analyser = audioContext.createAnalyser();
// 
const bufferSize = 4096;
const scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);

const handleStream = (stream) => {
   
   // 创建MediaStreamAudioSourceNode
   // 把AudioBufferSourceNode连接到analyser 
  audioContext.createMediaStreamSource(stream).connect(analyser);
  // 将analyser连接到 scriptProcessor,用js处理
  analyser.connect(scriptProcessor);
  //将scriptProcessor与destination连接,这样才能形成到达扬声器的通路
  scriptProcessor.connect(audioContext.destination);
    scriptProcessor.addEventListener('audioprocess', function (event) {
   
    const data = event.inputBuffer.getChannelData(0);  // 获取到音频数据
    })
};

计算声音频率

音高检测算法已经很成熟了,不乏论文和资料,诸如 java、c/c++ 都有现成的库可用,而 js 在这方面显然是有缺失的。因为我的目的是快速实现一个调音器,所以我是基于一个现有的 c 音频库 aubio 用 emscripten 编译成 js,以便在浏览器里运行。

这里将c语言的音频库转译成js涉及到

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值