资讯动态

用 p5.js + ml5.js + CREPE 搭建尤克里里调音器:基于预训练音高检测模型的浏览器实时调音实践

发布时间:2026/10/13 16:33:29 来源:尧图企业网站定制
教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载本文以 Coding Train 的 Coding Challenge #151 为核心讲解如何在浏览器中仅用 JavaScript 实现一个实时尤克里里调音器通过 p5.js 采集麦克风音频、ml5.js 加载预训练的 CREPE 音高检测模型识别当前频率再与尤克里里标准调音G-C-E-A比对并可视化调音偏差。读完本文你将掌握 ml5.jspitchDetection()的完整调用链路、p5.sound 麦克风输入的标准流程以及如何用最少的代码把机器学习音高估计落地为一个可交互的调音工具。一、挑战背景用预训练模型替代传统调音算法在 Coding Challenge #151 中作者 Daniel Shiffman 提出的目标是仅用浏览器技术创建一个尤克里里调音器。传统调音器通常依赖自相关等信号处理算法估算基频而这个挑战选择了另一条路径——直接使用**预训练的机器学习模型 CREPEA Convolutional REpresentation for Pitch Estimation**来估计音高。CREPE 是一种基于卷积神经网络的音高估计模型它把音频波形映射为音高概率分布从而输出连续频率估计该模型的作者是 Jesse Engel 等人Hannah Davis 是向 Coding Train 推荐该模型的关键人物以上信息均可在本仓库的挑战文档元数据中确认。整个项目只有三个文件全部位于本仓库的 CC_151_Ukulele_Tuner/P5 目录 下文件职责index.html引入 p5.js、p5.sound、ml5.js 与样式表sketch.js麦克风采集、模型加载、音高识别与可视化全部逻辑style.css基础页面样式清空 margin、画布块级显示实现路径非常简洁麦克风 → CREPE 模型 → 频率 → 最近音符 → 偏差可视化。这也正是本挑战文档所描述的核心流程用 JavaScript 结合 p5.js、ml5.js 和预训练模型 CREPE 打造一个尤克里里调音器。二、依赖引入三个 CDN 一行搞定index.html 只做了三件事引入 p5.js 主库、引入 p5.sound 音频插件、引入 ml5.jsscript srchttps://cdn.jsdelivr.net/npm/p51.4.1/lib/p5.min.js/script script srchttps://cdn.jsdelivr.net/npm/p51.4.1/lib/addons/p5.sound.min.js/script script srchttps://unpkg.com/ml50.3.1/dist/ml5.min.js/script其中p5.js负责画布创建createCanvas(400, 400)与全部可视化绘制p5.sound提供p5.AudioIn麦克风输入与getAudioContext()音频上下文访问是获取实时音频流的入口ml5.js提供ml5.pitchDetection()高层 API封装了 CREPE 模型的加载与推理。页面主体只加载一个脚本 sketch.js所有逻辑都在其中。需要留意的是这里使用的 ml5 是 0.3.x 版本其pitchDetectionAPI 的调用签名与新版如 ml5 1.x 的ml5.pitchDetection(model, audioContext, stream, callback)有所不同本文以下内容均以仓库内实际代码为准。三、模型地址与尤克里里音符表sketch.js 的开头定义了两个关键常量模型 URL 与音符频率表。const model_url https://cdn.jsdelivr.net/gh/ml5js/ml5-data-and-models/models/pitch-detection/crepe/; let pitch; let mic; let freq 0; let threshold 1; let notes [ { note: A, freq: 440 }, { note: E, freq: 329.6276 }, { note: C, freq: 261.6256 }, { note: G, freq: 391.9954 } ];这里有两个值得注意的细节模型托管方式CREPE 模型被托管在 ml5 官方数据仓库中并通过 jsDelivr 的 GitHub repo 转 CDN 方式直接引用。这正是挑战文档中如何把任意 GitHub 仓库变成 CDN这条参考链接的用武之地——无需自行搭建模型托管服务器静态权重文件即可通过 CDN 加速加载。音符表对应尤克里里标准调音从下往上数第 1 弦到第 4 弦A4440 Hz、E4329.6276 Hz、C4261.6256 Hz、G4391.9954 Hz。注意弦序在表中是倒序的A 弦是离调音者最近的 1 弦而 G 弦是最远的 4 弦。这组频率是十二平均律下的标准值C4 为 261.6256 Hz中央 CE4 为 329.6276 HzG4 为 391.9954 HzA4 为 440 Hz。threshold 1是调音是否已准的判定阈值单位为 Hz当检测频率与目标音符频率的绝对偏差小于 1 Hz 时界面判定为已调准。四、麦克风接入与模型初始化流程setup() 完成了三件事创建 400×400 画布、获取音频上下文、启动麦克风function setup() { createCanvas(400, 400); audioContext getAudioContext(); mic new p5.AudioIn(); mic.start(listening); }getAudioContext()是 p5.sound 暴露的全局方法用于获取或创建Web Audio API 的AudioContext实例后续喂给 ml5 的pitchDetection使用new p5.AudioIn()创建麦克风输入对象mic.start(listening)在获得用户授权后调用回调listening。由于浏览器自动播放策略这个授权 → 回调的异步模式是 Web Audio 麦克风采集的标准写法。listening() 是模型初始化的真正入口function listening() { console.log(listening); pitch ml5.pitchDetection(model_url, audioContext, mic.stream, modelLoaded); }这里展示了 ml5 0.3.x 中pitchDetection的完整签名模型 URL、音频上下文、麦克风流mic.stream、模型加载完成回调。mic.stream是p5.AudioIn暴露的底层MediaStream对象ml5 会把它作为 CREPE 模型的实时输入。模型加载完成后回调modelLoaded()被触发。五、递归式音高获取循环modelLoaded() 与 gotPitch() 构成一个持续采样循环function modelLoaded() { console.log(model loaded); pitch.getPitch(gotPitch); } function gotPitch(error, frequency) { if (error) { console.error(error); } else { if (frequency) { freq frequency; } pitch.getPitch(gotPitch); } }要点解析pitch.getPitch(callback)是异步的CREPE 模型在后台对当前音频帧执行推理完成后通过回调返回结果回调签名为(error, frequency)frequency是模型估计出的基频单位 Hz拿到频率后立即再次调用pitch.getPitch(gotPitch)形成递归采样链保证每一帧都持续更新全局变量freq供draw()读取。这种回调里再发起下一次请求的模式是 ml5 音高检测 API 的典型用法避免了传统setInterval轮询的不确定性。freq 0的初始值会在模型尚未输出结果时保持界面会显示 0.00 Hz。六、实时可视化频率显示、最近音符与调音指示器draw() 承担全部界面绘制分为三块1. 当前频率文本text(freq.toFixed(2), width / 2, height - 150);在画布下方居中显示检测到的频率保留两位小数。toFixed(2)让数值稳定在两位精度方便调音时读数。2. 最近音符匹配let closestNote -1; let recordDiff Infinity; for (let i 0; i notes.length; i) { let diff freq - notes[i].freq; if (abs(diff) abs(recordDiff)) { closestNote notes[i]; recordDiff diff; } } text(closestNote.note, width / 2, height - 50);遍历四根弦的目标频率用线性搜索找出绝对偏差最小的音符并记录带符号偏差diff freq - target。diff的符号很有用正值表示当前音偏高负值表示偏低。这也是后续可视化指针位置的计算基础。3. 偏差指示器这是整个界面的核心交互元素由三部分组成sketch.jslet alpha map(abs(diff), 0, 100, 255, 0); rectMode(CENTER); fill(255, alpha); stroke(255); strokeWeight(1); if (abs(diff) threshold) { fill(0, 255, 0); } rect(200, 100, 200, 50); // 中央刻度条 stroke(255); strokeWeight(4); line(200, 0, 200, 200); // 中间基准线 noStroke(); fill(255, 0, 0); if (abs(diff) threshold) { fill(0, 255, 0); } rect(200 diff / 2, 100, 10, 75); // 游标指针设计意图非常清晰中央横向刻度条宽度 200 像素透明度alpha由abs(diff)经map(0→100, 255→0)映射——偏差越大越亮偏差越小越透明给用户一个接近目标的视觉反馈中间基准线代表目标频率位置即偏差为 0 处红色游标指针横向位置由200 diff / 2决定即每 1 Hz 偏差移动 0.5 像素偏差 100 Hz 时移到最右侧、-100 Hz 时移到最左侧直观显示偏高/偏低调准判定当abs(diff) threshold即偏差小于 1 Hz时游标与刻度条从红/白变为绿色表示该弦已调准。代码中被注释掉的lerpColor/color(255,0,0)/color(0,255,0)部分是作者最初设想的绿→红颜色渐变方案最终被简化为透明度映射 绿色命中态读者可以作为扩展灵感保留。七、整体运行流程与验证方式完整的调用链可以概括为setup() 创建画布 启动麦克风 → listening() 用 mic.stream 初始化 ml5.pitchDetection → modelLoaded() 首次发起 getPitch → gotPitch() 更新 freq 并递归发起下一次 getPitch持续循环 → draw() 每帧读取 freq匹配最近音符并绘制调音指示器运行与验证方式直接在浏览器打开 index.html或部署到任意静态服务器 / p5.js Web Editor首次访问时浏览器会请求麦克风权限授权后即可对着麦克风弹奏尤克里里。没有乐器时可以用挑战文档中提到的在线音调发生器Online Tone Generator播放对应频率的单音来验证播放 440 Hz 时界面应显示 A 且游标停在正中变绿播放 445 Hz 时游标右移且显示 A偏高 5 Hz播放 330 Hz 附近则切换到 E 弦。观察 sketch.js 中freq的变化即可确认模型推理链路工作正常。需要说明的限制CREPE 模型对乐器泛音丰富的音色鲁棒性较好但对环境噪声较敏感threshold 11 Hz的判定精度已高于人耳分辨能力实际调音时通常已足够。八、社区贡献同一模型的多种创意实现挑战文档在contributions一节收录了大量社区二次开发这些作品共享p5.js ml5.js CREPE的技术内核但在交互与界面设计上各有侧重可作为继续探索的参考Luciano Tonet的 Afinador把调音器扩展为吉他/贝斯/任意乐器的通用调音 Web 应用MineBill的 Ukulele/Guitar Tuner覆盖尤克里里与吉他的双乐器调音originalang的 Ukulele Tuner with Pitch Trail在调音基础上增加了音高轨迹可视化David Snyder的同模型新设计版本换皮不换芯演示了同一模型在界面层可以做多大变化Simon Tiger Houben的一系列变体包括同时调所有弦、点击切换下一根弦、甚至恶搞的随机数发生器版本展示了把同一核心逻辑重构为不同交互形态的思路Lokesh Kumar的版本改进了 UI 并改变了从模型获取频率的方式Joseph Azar的 A pitch detection Game把音高检测做成了录下旋律、用钢琴回放的音乐游戏把调音器技术延伸到作曲与游戏领域。这些贡献印证了挑战文档末尾的提问——你能做出一个界面更优雅、更具创意的版本吗——也说明了pitchDetectionAPI 的可复用性一旦拿到稳定的实时频率流上层交互就拥有无限的创作空间。九、相关参考与延伸学习ml5.js挑战使用的机器学习友好库本文所用的pitchDetection即其高层 APICREPE 模型挑战所依赖的卷积音高估计模型其名字 Convolutional REpresentation for Pitch Estimation 与本仓库中 model_url 指向的托管路径一一对应Hannah Davis向 Coding Train 推荐 CREPE 的艺术家/研究者其跨学科创作是本文思路的重要来源jsDelivr GitHub 转 CDN 方案本挑战用该方式直接托管模型权重避免自建模型服务器在线音调发生器Online Tone Generator文档建议的验证工具可生成指定频率单音来测试调音器的识别准确性。如需更系统地理解本项目所依赖的机器学习工具链可以在仓库的 _learning/ml5 系列文档中继续学习 ml5.js 的图像分类、特征提取、模型训练等知识这些内容与本挑战共享同一套预训练模型 浏览器推理的架构思路。赞分享教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载相关推荐ml5.js 音高检测PitchDetection指南基于 CREPE 模型实时估计音频基频ml5.js 音高检测PitchDetection指南基于 CREPE 模型实时估计音频基频 本文围绕 ml5.js 的 ml5.pitchDetecti人工智能机器学习深度学习计算机视觉NLPAll-in-RAG扩展实战如何用Jina v5-omni多模态Omni Embedding让文本、图像、音频、视频统一向量完整指南All in RAG扩展实战如何用Jina v5 omni多模态Omni Embedding让文本、图像、音频、视频统一向量完整指南 Datawhale教程文档示例工程ml5.js 图像分类实战用 MobileNet 预训练模型在 p5.js 中识别图片内容ml5.js 图像分类实战用 MobileNet 预训练模型在 p5.js 中识别图片内容 本文围绕 Coding Train 的 ml5.js 初学者指南系教程文档示例工程上一篇PeerTube 持续集成CI体系全解析基于 GitHub Actions 的自动化构建、测试与发布流水线下一篇Readest 中 Virtuoso 与 OverlayScrollbars 的集成实践在 Android/iOS WebView 中恢复可感知的滚动条创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑