WymanLyu / noteDigger

在线前端频率分析扒谱 front-end music transcription

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

logo
noteDigger
~前端辅助人工扒谱工具~

noteDigger!

“Note Digger”——音符挖掘者,即扒谱。模仿的是软件wavetone,但是是双击即用、现代UI的前端应用。
目标是全部自己造轮子!即:不使用框架、不使用外部库;目的是减小项目大小,并掌握各个环节。目前频谱分析的软件非常多,功能也超级强大,自知比不过……所以唯一能一战的就是项目体积了!作为一个纯前端项目,就要把易用的优点完全发扬!
在线使用
视频演示(视频发布与更细节奏对齐之前) 点击封面跳转视频

使用流程

  1. 在线or下载到本地,用主流现代浏览器打开(开发使用Chrome)。
  2. 导入音频——文件-上传,或直接将音频拖拽进去!
  3. 选择声道分析,或者导入之前分析的结果(只有选择音频之后才有导入之前结果的接口)
  4. 根据频谱分析,开始绘制midi音符!调整音量,反复比对。
  5. 导出为midi等,或者暂时导出项目(下次继续)

导入导出说明

  • 导出进度: 结果是.nd的二进制文件,保存分析结果(频谱图)和音符音轨。导入的时候并不会强制要求匹配原曲!(会根据文件名判断一下,但不强制)
  • 导出为midi: 只保证能听,节拍默认4/4,bpm默认60,midi类型默认1(同步多音轨)。第10轨不会分配为鼓点轨(本项目设计并不考虑扒鼓)。
  • 导入midi: 将midi音符导入,只保证音轨、音符、音色能对应,音量默认127。如果导入后没有超过总音轨数,会在后面增加;否则会覆盖后面几轨(有提示)。

常规操作

  • 空格: 播放
  • 双击时间轴: 从双击的位置开始播放
  • 在时间轴上拖拽: 设置重复区间
  • 在时间轴上拉动小节线: 设置小节bpm
  • 鼠标中键时间轴: 将时间设置到点击位置,播放状态保持上一刻
  • 鼠标右键时间轴(上半/下半): 具体设置重复时间/小节
  • 按住空白拖动: 在当前音轨绘制一个音符
  • 按住音符左半边拖动: 改变位置
  • 按住音符右半边拖动: 改变时长
  • Ctrl+点击音符: 多选音符
  • delete: 删除选中的音符
  • Ctrl+滚轮: 横向缩放
  • 按住中键拖拽、触摸板滑动: 移动视野
  • ←↑→↓: 视野移动一格

快捷键

只有在导入并分析音频之后才能使用这些快捷键

  • Ctrl+Z: 撤销(音轨状态的改变不会引发存档,且只记录16次历史)
  • Ctrl+Y: 重做
  • Ctrl+A: 全选当前音轨
  • Ctrl+Shift+A: 全选所有音轨
  • Ctrl+D: 取消选中
  • Ctrl+C: 复制选中的音符
  • Ctrl+X: 剪贴选中的音符
  • Ctrl+V: 粘贴到选中的音轨上(暂不实现跨页面粘贴)
  • Ctrl+B: 呼出/收回音轨面板
  • Shift+右键: 菜单,包含撤销/重做、复制/粘贴、反选当前轨、删除

小细节

  • 滑动条,如果旁边有数字,点击就可以恢复初始值。
  • 多次点击“笔”右侧的选择工具,可以切换选择模式。(注意,只能选中当前音轨的音符)
  • 点击某个音符可以选中该轨。

其他说明

分析-自动填充,原理是将大于阈值的标记出来,效果不堪入目……目前没有什么好的算法。如有想法欢迎call me

关于节奏对齐

我一直以来都是扒数字谱的,所以没关注过节奏。但是只能用于数字谱这个应用也太弱了。所以加入了小节对齐功能。“丑话说在前面”,绘制音符大概是不可能对齐小节线了(但是导出midi的时候会对齐),需要强迫症忍受一下
乐谱的单位是"x分音符",而音乐的单位是"秒"。如果要实现"小节对齐",单位要换成"x分音符"。整个程序时间轴一定要按照"秒"为单位,这是由频谱分析决定的;如果要实现制谱软件一样的对齐,那么音符绘制需要换成"x分音符"的对齐方式。这意味着在120bpm的小节下的音符,拉到60bpm的小节下,在以秒为尺度的时间轴下,音符会变长。wavetone就是这样处理的。
但是对着原曲扒谱,最好还是根据"秒"来绘制音符。用wavetone扒谱的体验中,我最讨厌的就是被"x分音符"限制。用秒可以保证和原曲完全贴合,使用很灵活。但是这样导出的midi就不能直接制谱。按照"x分音符"来绘制音符还会导致程序很难写。开发者和使用者都不快乐。
扒谱用秒为单位合适,而制谱用x分音符合适。为了跨越这个鸿沟,我决定这样设计程序:使用midi文件作为对外的桥梁,在我的程序内用秒为单位扒谱,导出为midi的时候根据小节进行四舍五入的量化,形成规整的midi用于制谱。具体实现是:在秒轴上加入小节轴,用户可以拖动小节轴的某个小节调节后面紧跟的bpm相同的小节。小节轴只提供视觉上的辅助,对于画音符没一点限制。
对齐算法有一定的限制,比如四分音符按照八分音符的划分对齐、八分音符按十六分音符的划分对齐……比如四分音符不可能在第三个16分音符开始,只可能在整数倍个8分音符的时长处开始。所以,绘制音符的时候到底可以偏差小节线多远心里有数了吧?

文件结构

│  app.js: 最重要的文件,主程序
|  beatBar.js: 节奏信息的稀疏数组存储
│  channelDiv.js: 多音轨的UI界面类, 可拖拽列表
│  contextMenu.js: 右键菜单类
│  favicon.ico: 小图标
│  index.html: 程序入口, 其js主要是按钮的onclick
│  LICENSE
│  midi.js: midi创建、解析类
│  myRange.js: 横向滑动条的封装类
│  README.md
│  saver.js: 二进制保存相关
│  siderMenu.js: 侧边栏菜单类
│  snapshot.js: 快照类, 实现撤销和重做
│  tinySynth.js: 合成器类, 负责播放音频
│  todo.md: 一些设计思路和权衡
│
├─dataProcess
│      analyser.js: 频域数据分析与简化
│      fft_real.js: 执行实数FFT获取频域数据
│      midiExport.js: 对绘制的音符进行近似以导出为足以制谱的midi
│
├─img
│      github-mark-white.png
│      logo-small.png
│      logo.png
│      logo_text.png
│
└─style
    │  askUI.css: 达到类似<dialog>效果
    │  channelDiv.css: 多音轨UI样式
    │  contextMenu.css: 右键菜单样式
    │  myRange.css: 包装滑动条
    │  siderMenu.css: 侧边菜单样式
    │  style.css: index中独立元素的样式
    │
    └─icon: 从阿里图标库得到的icon
            iconfont.css
            iconfont.ttf
            iconfont.woff
            iconfont.woff2

重要更新记录

2024 2 22

加入了节拍对齐功能,使用逻辑是:扒谱界面提供视觉辅助,导出midi会自动对齐,以实现制谱友好。详细对齐的原理请参看“关于节奏对齐”板块和midiExport.js文件。
有一些细节:

  1. 如果每个小节bpm都不一样(原曲的速度不稳,有波动),那导出midi前的对齐操作会以上一小节bpm为基准进行动态适应:先根据本小节的bpm量化音符为"x分音符",如果本小节bpm和上一小节的bpm差别在一定范围内,则再将"x分音符"的bpm设置全局量BPM;否则将全局BPM设置为当前小节的bpm。这个算法的要求是:的确要变速的前后bpm差异应该较大。

  2. 在一个小节内,音符的近似方法:

    1. 记一个四分音符的格数为aqt(因为音符的实际使用单位是格。这里隐含了一个时间到格数的变换),某时刻t对应音符长度为ntlen,小节开始时刻记为mt。首先获取音符时刻相对小节开头的位置nt=t-mt。(音符时刻:将一个音符拆分为开始时刻和结束时刻。一个音符可能跨好几个小节,因此这样处理最为合适)
    2. 假设前提:时长长的音符的起点和终点的精度也低(精度这里指最小单位时长,低精度指单位时长对应的实际时间长)。因此近似精度accu采用自适应的方式:该音符可以用(ntlen/aqt)个四份音符表示,设其可以用一个(4*2^n)分音符近似,其中n满足:(1/2)^n<=ntlen/aqt<(1/2)^(n-1),则该音符的时长为aqt/(2^n),则精度设置为这个近似音符的一半:accu = aqt/(2^(n+1))。比如四份音符的精度是一个八分音符的时长。
    3. 近似后的时刻为:round(nt/accu)*accu。同时设置一个最低精度:八分音符。因此accu=min(aqt/2, aqt/(2^(n+1))),其中(1/2)^n<=ntlen/aqt<(1/2)^(n-1)。
  3. 小节信息如何存储、数据结构如何设计需要好好想想。大部分情况下(在原音频节奏稳定的情况下)只会变速几次,此时存变动时刻的bpm值就足矣。极端情况下每个小节都单独设置了bpm。如何设计数据结构能在两种情况下都取得较好的性能?使用稀疏数组。

2024 2 9

在今年完成了所有基本功能!本次更新了设置相关,简单地设计了调性分析的算法,已经完全可以用了!【随后在bilibil投稿了视频】

2024 2 8

文件系统已经完善!已经可以随心所欲导入导出保存啦!同时修复了一些小bug、完善了一些api。
界面上,本打算将文件相关选项放到logo上,但是侧边菜单似乎有些空了,于是就加入到侧边栏,而logo设置为刷新或开新界面(考察了其他网站的logo的用途)。同时给侧边菜单加入了“设置”和“分析”,但本次更新没做。
midi相关操作来自我的另一个项目的midi类。将用midi转的wav导入分析,再导入原midi,两者同步播放的感觉真好!

2024 2 5

已经能用于扒谱了!完成了midi和原曲的播放与同步,填补了扒谱过程最重要的一环。
UI基本完成!将侧边栏、滑动条封装成了js类。在此基础上,设计了类似VScode的菜单,用于存放不常用的功能和界面;而顶部窄窄一条用于放置常用功能。
此外,完成了logo的设计。在2月4日的commit记录中(因为现在已经删除)可以看到设计的多种logo,最终选定了“在勺子里的音符”,这是一个被勺子dig出来的音符。其他思路可以概括为:“音符和铲子的组合”(logo2)、“埋在地里的音符”(logo5 logo6)、“像植物一样生长的八分音符”(logo8 logo10)、“音符和铲子结合”(logo12)。

2024 2 1

完成了多音轨、合成器和主线的整合,象征着midi系统的完成!
统一了UI风格;完善了快捷键功能;新增框选功能;修复了大部分bug。

2024 1 30

完成了midi合成器tinySynth.js,实现了128种音色的播放。只有演奏音符的作用,控制器一点没做。
原理是多个基础波形合成一个音色。波形参数来自 https://github.com/g200kg/webaudio-tinysynth ,因此程序设计也参考了它的设计。修改记录在todo.md中
对于reference的解析(作者注释一点没写,变量命名极为简单,因此主要是变量解释)存放于“./tone/解析.md”(文件夹已被删除,请去历史提交查看)。文件夹中还有tinySynth的测试页面。在下一次push时将删除tone文件夹。
这段时间内还完成了以下内容(全部记录在commit history的comments内):

  • 基本程序界面(三个画布:键盘、时频图、时间轴;UI界面:右键菜单、多音轨、滑动条)
  • 基本逻辑功能:音符交互绘制、快捷键以及模块的关联协同

2023 12 13

从11月14日开始造js版fft轮子起,时隔一个月第一次提交项目,因为项目逻辑日渐复杂,需要能及时回退。主要完成了频谱绘制、钢琴键盘绘制、数据处理三部分,并初步确定了程序的结构框架。
数据处理核心:实数FFT,编写于我《数字信号处理》刚刚学完FFT算法之时,针对本项目的应用场景做了专门的设计,即针对音频STFT做了适配,具体表现为:实数加速、数据预计算、空间预分配、共用数组。
由于整个项目还没搭建起来,因此不能测试NoteAnalyser类的数据处理效果。此类用于将频域数据进一步离散为音符强度数据。
关于程序结构有一版废案,在文件夹"deprecated"中,设计思路是解耦、插件化,废弃理由是根本解耦不了。因此现在的代码耦合成一坨了。这个文件夹将在下一次push时被删除,存活于历史提交之中。
tone文件夹将存放我的合成器轮子,audioplaytest是我音频播放的实验文件夹,todo.md是部分设计思路。
2024/4/8补记:时频分析方法是STFT,但是面临时间和频率分辨率矛盾的问题,现在的分析精度只能到F#2。解决办法是用小波变换,或者更本质一点:用84个滤波器提取84个基准音以及其周围的频率的能量。这样能达到更高的频率分辨率和时间分辨率。但是现在的STFT用起来效果还可以,就不换了哈。

About

在线前端频率分析扒谱 front-end music transcription

License:GNU General Public License v3.0


Languages

Language:JavaScript 84.3%Language:HTML 9.4%Language:CSS 6.3%