乐趣区

关于前端:Chrome插件云音乐听歌识曲

图片起源:Chrome 插件 - 云音乐听歌

本文作者:空吾

当你用网页在视频网站刷视频的时候,有没有碰到过一个 BGM 激发你心田的波澜,而你却不晓得它的名字。此时只能关上手机进行听歌识曲,而通过一个浏览器的插件却更容易解决这个问题。不须要繁琐的掏出手机,也不会因为须要外放而烦扰别人,更不会因为环境乐音而辨认艰难。

如果你恰好也有这个须要,无妨试一下云音乐出品的 Chrome 浏览器插件「云音乐听歌」,还能够间接进行红心珍藏哦。也能够到插件官网预览理论运行的成果。

背景

目前 Chrome 商店上存在的听歌识曲插件,大都是国外出品,国内产品寥寥,对于国内音乐反对较差。既然云音乐有这个能力,咱们心愿将这样的性能笼罩每一个角落,传递音乐美妙力量。与此同时市面上的插件大多还是基于 manifest v2 实现(绝对于 manifest v3,安全性、性能、隐衷性均较差),广泛的做法是将音频录制之后间接交给服务端,通过服务端进行指纹提取,徒增服务端计算压力,减少网络传输。
那么有没有方法既能应用 manifest v3 协定进行性能实现,同时将音频指纹提取这一计算放在前端呢?

Chrome 浏览器插件新协定

本文的重心不在如何实现一个浏览器插件自身,如果你不理解插件自身的开发,可查阅 Google 官网的开发文档。

特地阐明的是,manifest v2(MV2)行将被废除,在 2022 年逐渐不承受更新,2023 年将会逐渐不能运行,本文所有的内容都是基于更平安、性能更好、隐衷更强的 manifest v3(MV3)进行实现。

协定降级对性能的实现形式也会带来一些变动,因为 MV3 更平安的限度,一些基于 MV2 灵便的实现形式(例如:执行近程代码、能够应用 eval、new Function(…) 等不平安办法)将不能应用。而这会对听歌识曲插件带来一些实现上的难题。

MV3 协定对插件实现外围影响点:

  • 原有的 Background Page 应用 Service Worker 进行代替,这意味着在 Background Page 不再能进行 Web API 等操作。
  • 近程代码托管不再反对,无奈进行动静加载代码,意味着可执行的代码都须要间接打包到插件中。
  • 内容安全策略调整,不再反对不平安代码的间接执行。WASM 初始化相干函数无奈间接运行。

    听歌识曲的实现

    听歌识曲自身技术比拟成熟,整体的思路是通过 音频数字采样 ,进行音频 指纹的提取 ,最初将指纹在数据库进行 匹配,特征值最高的即是所认为辨认到的歌曲。

浏览器插件中的音频提取

利用插件进行网页内的音视频录制其实非常简单,只须要 chrome.tabCapture API 即可实现网页自身的音频录制,获取到的流数据咱们须要针对音频数据进行采样,保障计算 HASH 的规定和数据库数据保持一致。

针对获取的 stream 流能够进行音频的转录采样,个别有三种解决形式:

  • createScriptProcessor:此办法用于音频解决最为简略,然而此办法曾经在 W3C 规范里标记为废除。不倡议应用
  • MediaRecorder:借助媒体 API 也能够实现音频的转录,然而没有方法做到精密解决。
  • AudioWorkletNode:用于代替 createScriptProcessor 进行音频解决,能够解决同步线程解决导致导致的对主线程的压力,同时能够按 bit 进行音频信号处理,这里也抉择此种形式进行音频采样。

基于 AudioWorkletNode 实现音频的采样及采样时长管制办法:

  1. 模块注册,这里的模块加载是通过文件的加载形式,PitchProcessor.js 对应的是根目录下的文件:

    const audio_ctx = new window.AudioContext({sampleRate: 8000,});
    await audio_ctx.audioWorklet.addModule("PitchProcessor.js");
  2. 创立 AudioWorkletNode,次要用于接管通过 port.message 从 WebAudio 线程传递回来的数据信息,从而能够在主线程进行数据处理:

    class PitchNode extends AudioWorkletNode {
      // Handle an uncaught exception thrown in the PitchProcessor.
      onprocessorerror(err) {
     console.log(`An error from AudioWorkletProcessor.process() occurred: ${err}`
     );
      }
    
      init(callback) {
     this.callback = callback;
     this.port.onmessage = (event) => this.onmessage(event.data);
      }
    
      onmessage(event) {if (event.type === 'getData') {if (this.callback) {this.callback(event.result);
       }
     }
      }
    }
    
    const node = new PitchNode(audio_ctx, "PitchProcessor");
  3. 解决 AudioWorkletProcessor.process,也就是 PitchProcessor.js 文件内容:

    process(inputs, outputs) {const inputChannels = inputs[0];
      const inputSamples = inputChannels[0];
      if (this.samples.length < 48000) {this.samples = concatFloat32Array(this.samples, inputSamples);
      } else {this.port.postMessage({ type: 'getData', result: this.samples});
     this.samples = new Float32Array(0);
      }
      return true;
    }

    取第一个输出通道的第一个声道进行数字信号的收集,收集到合乎定义的长度(例如这里的 48000)之后告诉到主线程进行信号的辨认解决。

基于 process 办法能够做很多有意思的尝试,比方最根底的白乐音生成等。

音频指纹提取

提取到音频信号之后,下一步要做的就是对信号数据进行指纹提取,咱们提取到的其实就是一段二进制数据,须要对数据进行傅里叶变换,转换为频域信息进行特色示意。具体指纹的提取的逻辑是有一套规整的简单算法,惯例的指纹提取办法:1) 基于频带能量的音频指纹;2)基于 landmark 的音频指纹;3)基于神经网络的音频指纹,对算法感兴趣的能够浏览相干论文,例如:A Highly Robust Audio Fingerprinting System
。整个运算有肯定的性能要求,基于 WebAssembly 进行运算,能够取得更好的 CPU 性能。现如今,C++/C/Rust 都有比拟便捷的形式编译成 WebAssembly 字节码,这里不再开展。

接下来,当你尝试通过在插件场景中运行 WASM 模块初始化的时候,你大概率会遇到如下异样:

Refused to compile or instantiate WebAssembly module because 'wasm-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src'self''unsafe-inline' 'unsafe-eval' ...

这是因为在应用 WebAssembly 的时候须要遵循严格的 CSP 定义,对于 Chrome MV2 能够通过追加 "content_security_policy":"script-src'self''unsafe-eval';" 进行申明解决。而在 MV3 中,因为更加严格的隐衷及平安限度,曾经不容许这种简略粗犷的执行形式了。
MV3 中,对于插件页面 CSP 定义中的script-src object-src worker-src 只容许取值为:

  • self
  • none
  • localhost
    也就是没有方法定义 unsafe-eval 等属性,所以想单纯在插件页面里间接运行 wasm 曾经不可行了。
    到这仿佛曾经到了死路?办法总比问题多,细品文档,发现文档有这样一句形容:

    CSP modifications for sandbox have no such new restrictions. —— Chrome 插件开发文档

也就是说这种平安限度在沙盒模式下是没有的。插件自身能够定义 sandbox 页面,这种页面尽管无法访问 web/chrome API,然而它能够运行一些所谓“不平安”的办法,例如 eval、new Function、WebAssembly.instantiate 等。
所以能够借助沙盒页面进行 WASM 模块的加载及运行,将计算的后果返回给主页面,整体的指纹采集的流程就变成,如下图:

对于主页面和沙盒页面如何进行数据通信,能够通过在主页面里边加载 iFrame 的形式,借助 iFrame 的 contentWindow 和主 window 进行数据联通,数据流程如下图:

到这里实现了根本的音频的提取及指纹提取的过程,剩下的局部就是通过指纹在数据库进行特色匹配。

特色匹配

提取到的音频指纹后,接下来就是到指纹库里进行音频检索。指纹库能够用散列表实现,每个表项示意雷同指纹对应的音乐 ID 和音乐呈现的工夫,构建出指纹数据库。从数据库中拜访提取的指纹即可获取匹配的歌曲。当然这只是一个根本流程,具体的算法优化形式各家还是有很大的差别,除了版权起因,算法间接导致了各家匹配的效率和正确率。而插件这里的实现还是以效率优先的形式。

写在最初

以上大抵形容了基于 WebAssembly 与 MV3 实现听歌识曲插件的大抵流程。插件尽管灵便易用,然而 Google 也意识到了插件带来的一些平安、隐衷等问题,从而进行了一次大规模的迁徙。MV3 协定更加具备隐衷和安全性,但也限度了不少性能的实现,在 2023 年之后会有大批量的插件无奈持续应用。

对于听歌识曲插件目前已实现的性能包含音频辨认、红心歌单珍藏等,后续还将持续性能拓展,心愿这个小性能能够帮忙到你。

参考资料

  • https://developer.mozilla.org/en-US/
  • https://developer.chrome.com/docs/apps/
  • https://www.w3.org/TR/webaudio/#widl-AudioContext-createScriptProcessor-ScriptProcessorNode-unsigned-long-bufferSize-unsigned-long-numberOfInputChannels-unsigned-long-numberOfOutputChannels
  • https://developer.mozilla.org/zh-CN/docs/WebAssembly/C_to_wasm
  • http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=152C085A95A4B5EF1E83E9EECC283931?doi=10.1.1.103.2175&rep=rep1&type=pdf

本文公布自网易云音乐技术团队,文章未经受权禁止任何模式的转载。咱们长年招收各类技术岗位,如果你筹备换工作,又恰好喜爱云音乐,那就退出咱们 grp.music-fe(at)corp.netease.com!

退出移动版