YAOTU INSIGHTS

计算器弹歌:按键频率映射与简谱自动化生成

计算器弹歌:按键频率映射与简谱自动化生成
1. 计算器弹歌不是玄学先把发声单元摸清楚第一次见到有人拿一台办公计算器弹出一整段旋律多数人的反应是这机器里是不是装了扬声器。真拆开看过就知道里面根本没有扬声器只有一片指甲盖大小的压电陶瓷片焊在主板上靠引脚电压变化带动陶瓷片形变发出那种又干又薄的嘀声。这种声音在正常使用场景里是噪音可一旦每个按键对应的频率被固件写成了不同音高一台几十块钱的计算器就变成了一件音域有限、但绝对能弹出旋律的乐器。计算器弹歌曲谱子这件事的核心从来不是怎么弹而是怎么把一段连续的旋律压缩成这台机器有限的十几个按键。这条压缩链路上有三个环节按键频率的实测、简谱到音高的换算、音高到按键的最近邻映射。三个环节任何一个出错弹出来的东西就会听起来像但又不对这也是很多人照着网上抄来的谱子弹半天最后放弃的原因——谱子本身是给别人那台计算器写的按键频率根本没对齐。这篇内容写给三类人看。第一类是纯粹想玩、想拿计算器在聚会上露一手的后面有完整的实测方法和排版好的谱子格式照着做就能出效果。第二类是想搞清楚背后原理的会讲到十二平均律怎么落到按键上、音分偏差怎么算、为什么有的曲子天生就弹不了。第三类是想偷懒的我会给一套用计算机批量生成按键谱的做法把扒谱、换算、排版全自动化以后想弹什么歌改一下输入文本就行。三类人看同一篇切入点不同但底层是同一套东西。1.1 按键发声的物理原因决定了音域上限计算器的键盘是一张矩阵电路行线和列线交叉每个交叉点下面是一个薄膜开关。主控芯片在不停地做扫描把某一行拉低读所有列的电平就能知道这一行哪一列被按下。识别到按键之后芯片要做两件事一是把数字送进显示缓冲区二是往蜂鸣片的驱动脚输出一段方波。问题就出在这个方波上。方波的频率由芯片内部的分频系数决定而分频系数是固件里写死的常量表。也就是说一台计算器能发出几个音、每个音多高出厂那天就定死了用户层面完全改不了。我拆过几台不同定位的机器能做到每个按键音高都不同的通常是主打功能键多的科学计算器或者带语音的会计计算器而那种所有按键都是同一声嘀的低端机无论你怎么按出来的都是同一个频率这种机器拿来弹歌是没戏的别浪费时间。还有一个容易被忽略的点发声的触发时机。有些机型的蜂鸣是按下即响、松开即停这种可以做出类似断奏的效果有些是按下响固定时长、松手也继续响完这种弹快节奏曲子会糊成一片。前者更适合弹歌后者只能弹慢板。判断方法很简单按住一个键不放声音一直持续就是前者响一下就停就是后者。1.2 为什么同一份谱子换台机器就全乱了网上流传的计算器谱子基本都是按键序列比如999 888 777这种作者默认读者用的是一模一样的机型。可现实是同一个品牌的不同系列、甚至同一系列的不同批次按键频率表都可能不一样。我手上两台外观几乎一致的机器AC 键一个在 194Hz 左右另一个在 210Hz 上下差了将近一个全音。照着前者的谱子在后者的机器上弹整首曲子会听起来跑调跑到另一个调式去了。这就是为什么我一直强调别抄谱子抄方法。靠谱的流程是拿自己的机器测一遍频率建立一张私有频率表然后把简谱换算成音高再对着自己的表做映射。这套流程跑一遍大概二十分钟之后所有曲子都能复用。抄来的按键序列只是在别人的机器这个特定前提下成立的产物换个环境立刻失效这是最典型的知其然不知其所以然。频率偏差的来源还有一层压电片的谐振频率会随温度、装配应力、供电电压轻微漂移。夏天和冬天测同一台机器某些键能差十几音分。这个量级对弹歌影响不大但如果你想把谱子分享给别人最好在室温下测、并且注明偏差在 ±30 音分内可接受。2. 从简谱到按键把连续音高塞进离散格子简谱里的音高是连续的理论上一个八度内可以有无穷多个音计算器的按键是离散的只有十几个格子。把连续的东西塞进离散格子必然要做取舍取舍的规则决定了最后听起来准不准。这一节的思路就是把整个取舍过程数学化让每一步都有据可依而不是靠耳朵瞎试。2.1 十二平均律与音分给偏差一把尺子先说音高的度量。国际标准里 A4 定为 440Hz往上一个八度翻倍往下减半一个八度等分成十二份每份就是一个半音。任意两个频率之间的比值取以 2 为底的对数再乘 1200得到的就是音分cent一个半音正好 100 音分。这个单位的好处是把乘性关系变成加性关系人耳对音高的感知也接近对数所以判断准不准用音分最直观偏差 10 音分以内基本听不出来20 到 30 音分开始有点毛,超过 50 音分就是明显的走音了。把 MIDI 音符号转频率的公式是f 440 × 2^((n-69)/12)反过来频率转音分距离就是1200 × log2(f1/f2)。这两个公式是整个映射过程的地基后面所有计算都从这里出发。很多教程只告诉你按这个键就是 do但不告诉你那个键实际偏了多少结果弹到高音区偏差累积听起来越来越别扭。有了音分这把尺子你就能量化地知道这个键离目标音差了 27 音分勉强能用还是差了 80 音分得换个键或者改谱。十二平均律本身也不是唯一选择但计算器的频率表是厂家按某个固定律制定的我们只能接受。好在这套体系的容错空间比想象中大——旋律的辨识度主要靠音程关系和节奏绝对音高偏一点普通听众基本察觉不到。2.2 键位实测用手机就能测出每个键的频率测频率这件事不需要专业设备。准备一台计算器、一部装了频谱分析或调音器功能的手机、一间安静的房间就能开工。具体操作是把手机放在离计算器十厘米左右的位置逐个按键每个键按住约一秒观察频谱峰值读数。读数稳定后记下来连续测三轮取平均能有效排除偶然误差。方法听着简单坑不少。第一环境噪声会干扰低频读数空调、风扇、电脑风扇的低频嗡嗡声很容易被误判成按键的基频一定要关。第二压电片的声音高频成分很重基频反而不明显有些分析软件会自动抓最高峰抓到的可能是二次谐波也就是实际频率的两倍。判断办法是如果测出来的值集中在 400 到 800Hz而直觉上这台机器的声音没那么尖就把读数除以二验证一下看看是不是更符合音阶分布。第三按键要按实薄膜开关接触不良会产生抖动读出来的频谱会有一片噪声。实测完之后你会得到一张表长这样以下是一台虚构型号的计算器实测记录不同机器数值必然不同仅作格式参考按键实测频率 (Hz)最接近音名偏差 (音分)AC193.8G3-197218.6A3-128245.1B3-99263.4C411÷291.7D4-154331.2E485351.0F496390.5G4-7×442.3A491496.7B4102520.9C5-93589.4D56−656.8E5-60700.2F54.782.3G5-4883.1A56985.0B5-6这张表信息量很大。首先看音域从 G3 到 B5横跨两个多八度理论上能弹绝大多数流行歌的主旋律。其次看调式从 G3 开始往上排正好是 G 大调的自然音阶没有黑键。这意味着这台机器天生适合 G 大调弹 C 大调的曲子要么整体移调要么接受某些音要用偏差更大的键替代。第三看全半音关系3 到 4、7 到 1 之间的半音在这台机器上没有对应按键所以凡是需要半音升号、降号的曲子就得靠就近借用或者改谱跳过。提示测频这一步千万别省。省下来的二十分钟会在后面调谱时十倍地还回去。2.3 调式对齐先找基准键再谈整首曲子拿到频率表之后第二件事是确定基准。旋律是相对的Do 定在哪里整首歌就整体平移。如果硬要把 C 大调的曲子塞进一台 G 大调的机器效果就是每个音都要往上借三度偏差会不断累积。正确做法是找出这台机器的自然音阶起点把那台机器的 do 当成曲子的 do整首曲子整体移调。具体操作是把频率表按从低到高排序然后在相邻频率之间找大约两个半音和大约一个半音的间隔能拼出一条完整的大调音阶全全半全全全半那条音阶的第一个音就是这个机器的自然 do。上面那张表里G3 到 A3 是全音A3 到 B3 是全音B3 到 C4 是半音C4 到 D4 全音D4 到 E4 全音E4 到 F4 半音F4 到 G4 全音——标准 G 大调。所以这台机器的 do 就是 G3 那个键AC。找到 do 之后简谱上的 1 就对应 AC2 对应 73 对应 84 对应 9以此类推。整个过程就变成了纯查表。曲子里如果出现了 4fa以上的高音看一下表里有没有对应的更高八度键没有就用低八度的同名音替代或者直接改旋律——民歌、儿歌里这种改法通常听不出来。3. 用计算机批量生成谱子把重复劳动交给脚本手算一遍两遍还行曲目一多就受不了。把上面那套映射逻辑写进脚本输入一段简谱文本输出一张对着就能弹的按键表再顺手用计算机自己把旋律吹出来做校验这是效率最高的做法。这套流程我用了很久从扒谱到出成品谱子一首三分钟的曲子二十分钟内能搞定。3.1 输入格式设计怎么用纯文本写一首歌记谱格式越简单越好不要搞复杂的编辑器。我用的格式是这样的音符之间用空格分隔1到7是音级后面加表示高八度、加,表示低八度0表示休止-表示延长一拍|纯粹是视觉分隔符、解析时忽略。举个例子1 1 5 5 | 6 6 5 - | 4 4 3 3 | 2 2 1 - |这段就是一段经典的三段式儿歌旋律音全在 do 到 la 之间最适合拿来验证映射流程通不通。为什么选这种格式因为它跟简谱的书写习惯一致扒谱的时候手不用离开数字键区看一眼谱子就能敲进来同时它又是纯 ASCII脚本用split()一行就能切分不需要引入任何解析库。时值这块我故意做得很简陋只支持一拍和延长一拍。原因是实际弹奏的时候节奏本来就不可能精确人手按按键的抖动比时值误差大得多。与其在格式上追求精确不如让脚本输出的谱子在视觉上对齐靠人眼数拍子。这个取舍看着粗糙实际用下来反而效率最高。3.2 核心脚本解析、换算、映射三步走整段逻辑不复杂分三个函数。第一个负责把文本解析成[MIDI 音号, 时值]的列表import math BASE {1: 0, 2: 2, 3: 4, 4: 5, 5: 7, 6: 9, 7: 11} def parse_score(text): notes [] for tok in text.split(): if tok in (|, ): continue if tok -: if notes: notes[-1][1] 1 continue if tok[0] 0: notes.append([None, 1]) continue midi 60 BASE[tok[0]] for ch in tok[1:]: if ch : midi 12 elif ch ,: midi - 12 notes.append([midi, 1]) return notes这里60是 C4 的 MIDI 号把它当基准1 就是 C4。这个基准可以随便改改成 55 就是 G3 打头对应前面那台机器的自然调式。改基准等于整体移调一个数字的事。第二个函数把 MIDI 号转成频率第三个函数拿频率去表里找最近的键def midi_to_freq(m): return 440.0 * 2 ** ((m - 69) / 12) def cents(a, b): return 1200 * math.log2(a / b) def nearest_key(target, table): best min(table.items(), keylambda kv: abs(cents(target, kv[1]))) return best[0], abs(cents(target, best[1]))注意nearest_key返回两个值键名和偏差音分。偏差这个返回值很关键它让脚本能自动报警——偏差超过 50 音分的音说明这台机器根本没有合适的键得人工介入要么改谱要么接受走音。这一步把凭感觉判断变成了按阈值判断。3.3 让计算机先吹一遍用耳朵做最终校验映射完了别急着上手弹先让计算机把旋律放一遍。这一步的价值在于排除谱子本身写错了这种低级错误——耳朵对旋律的记忆比对按键的记忆可靠得多放一遍立刻能听出哪个音不对。Python 在 Windows 下用winsound就够了出来的音色是方波跟计算器的压电片声几乎一个味道代入感很强import time, winsound def beep_play(notes, bpm90, gate0.9): beat 60.0 / bpm for midi, dur in notes: ms int(dur * beat * 1000) if midi is None: time.sleep(ms / 1000) continue f int(midi_to_freq(midi)) winsound.Beep(f, int(ms * gate)) time.sleep(ms * (1 - gate) / 1000)gate这个参数控制音符的占空比设成 0.9 意味着每个音只响时值的 90%剩下 10% 留白。别小看这点留白它决定了旋律听起来是一个个音还是糊成一条线。弹计算器的时候同理两个音之间手指要抬起来不能粘着。跨平台的话浏览器里用 Web Audio API 更方便加个包络还能避免爆音const ctx new AudioContext(); function playNote(freq, dur) { const osc ctx.createOscillator(); const gain ctx.createGain(); osc.type square; osc.frequency.value freq; gain.gain.setValueAtTime(0, ctx.currentTime); gain.gain.linearRampToValueAtTime(0.25, ctx.currentTime 0.004); gain.gain.setValueAtTime(0.25, ctx.currentTime dur - 0.02); gain.gain.linearRampToValueAtTime(0, ctx.currentTime dur); osc.connect(gain).connect(ctx.destination); osc.start(); osc.stop(ctx.currentTime dur); }用square波形而不是sine是因为方波的谐波丰富听起来更电子、更像计算器正弦波太干净反而不像。3.4 输出排版谱子要能对着弹脚本最后一步是把结果打成一张能看的表。我习惯按小节分块每块一行每行下面标小节号音高和按键上下对齐简谱: 1 1 5 5 | 6 6 5 - 按键: AC 1 6 6 | × × 6 - 偏差: 0 -5 8 8 | -7 -7 8 -偏差列一定要留。弹的时候扫一眼就知道哪个音心虚需要按重一点或者稍微提前人耳对音准的宽容是有方向性的偏低比偏高更容易被察觉所以偏低的音可以适当缩短时值减少暴露时间。这些微调经验脚本算不出来只能靠人。4. 一次完整的实操记录从扒谱到弹顺理论讲完走一遍完整流程。挑一段结构简单的三段式旋律从原始旋律到能在计算器上流畅弹出来中间每一步都记下来。4.1 扒谱先把旋律落成数字扒谱这件事用不着绝对音感。找一段熟悉的录音反复听先把第一个音哼出来在计算器上试按找到一个听起来像起点的键这就定下了基准。之后每听一个音就在基准键附近试因为它大概率的走向是级进或者三度跳进试两三次就能定。整个过程像玩猜数字游戏比想象中快。把试出来的键名反查回简谱写进文本文件。这一步我建议手写不要用自动扒谱软件——自动扒谱对单声部旋律的准确率还行但会生成一堆装饰音和滑音对计算器这种只能弹离散音高的乐器来说是噪音。人的耳朵会自动过滤掉这些机器不会。扒完的谱子先肉眼过一遍检查两件事一是音域有没有超出机器的按键范围超了就在脚本里整体移调二是节奏有没有连续的十六分音符有的话考虑简化成八分计算器的按键响应跟不上那么快。4.2 映射与试弹第一次上手的正确姿势脚本跑完拿到按键表第一次试弹别贪快。我一般是先按一遍不求节奏只求把每个键都按对形成肌肉记忆。这一步很多人跳过直接跟原曲速度弹结果手忙脚乱按错一个音后面全乱。慢速按三遍手指会自己记住键位之间的相对位置这比脑子记谱子快得多。慢速过了之后开始加节拍。用手机开个节拍器先设 60BPM一拍按一个音。稳了之后每轮加 10BPM加到原曲速度为止。这个过程中会发现某些音按不过来通常是跨度过大的跳进比如从最左边一路跑到最右边。这种地方要么改谱要么接受它慢一点别硬练硬练出来的效果是手臂僵硬反而更慢。注意计算器的按键行程比电子琴短得多但触发力度并不小。连续弹五分钟后手指会酸尤其是拇指按 AC 键的位置。如果打算长时间练把机器平放在桌面上用食指按比拿在手里用拇指按省力得多。4.3 节奏不对怎么办用留白救场计算器最大的短板是节奏。按键的按下和抬起都是离散动作想做渐强渐弱、想做出连音效果基本没戏。但节奏上的问题可以用一个笨办法解决在容易糊的地方主动留白。两个音挨得太近会连成一片那就把前一个音弹短一点人为制造一个间隙耳朵会自动把间隙理解为音的边界。具体操作上遇到连续的同音反复比如5 5 5 5不要每个音都按得一样长改成长短短长或者短短长短短这种不均匀的时值听起来反而更有律动感。遇到快速的级进音阶直接把中间几个音省掉只保留首尾和转折点旋律的辨识度几乎不受影响——这是所有单音乐器演奏者都在用的减法计算器尤其需要。再一个技巧是利用休止。曲子里的休止符在按键表上是一个空这个空不要浪费把它当成换气口手指在这段时间从当前位置移动到下一个音的位置比边弹边找键从容得多。扒谱的时候如果发现某个跳进特别赶可以人为在它前面插一个极短的休止代价是节奏微微松一下收益是弹得稳。4.4 稳定性连弹十分钟不出错靠什么演出场景和练习场景是两回事。练习时允许重来演出时一次过。想做到一次过靠的是降低单次操作的信息量——把曲子在脑子里切成一节一节每节的第一音作为锚点弹到锚点就重新对齐一次中间弹错了不要回头救直接往下一个锚点走。这个策略能把一次错误的影响控制在四拍以内而不是毁掉整首。另外练的时候要故意在不舒适的状态下练。坐着练熟了站起来弹一遍安静环境练熟了开着电视弹一遍。计算器的声音穿透力不强环境一吵自己就听不清了听不清就慌慌了就快快了就乱。提前适应过这种状态现场就不会崩。5. 常见问题排查速查表踩过的坑整理成表遇到问题先查一遍八成能直接定位。5.1 音高类问题现象大概率原因处理办法整体偏高或偏低半个音基准键选错调式没对齐整体移调重新选 do 的位置某一个音特别刺耳该键频率与目标音偏差超过 50 音分改谱跳过或用低八度同名音替代低音区声音很闷、听不清压电片在低频段谐振不足减少低音区的使用把旋律上移一个八度换台机器整首跑调两台机器频率表不同重新测频重新跑映射脚本弹到高音区声音突然变小驱动电压不足高频衰减快换新电池或避开最高的三四个键5.2 节奏与响应类问题现象大概率原因处理办法快速连打时丢音按键扫描周期跟不上或薄膜开关回弹慢把连续十六分音符简化成八分或增加间隙声音拖尾音符黏在一起该机型是按下响固定时长型换机型或只弹慢板曲目弹到一半突然没声供电不足或按键进入省电静默换电池弹奏前先随便按几下唤醒同一个键手感时轻时重薄膜开关老化接触电阻不稳换键位避开或整机更换节奏整体越弹越快紧张导致无意识加速跟着节拍器练每小节强制对齐一次5.3 几个只有亲自上手才会知道的坑第一个坑不要在看谱子的时候低头找键。计算器的键位排布是标准的三列网格手指放上去之后靠触感就能定位眼睛一直盯着谱子手感会自然形成。一旦养成低头找键的习惯弹奏速度会被卡死在很低的水平因为每次低头抬头就损失半拍。第二个坑测频的时候别只测一遍。我第一次测的时候图快每个键按一下记一个数结果测出来的表内部矛盾——相邻两个键算出来是全音实际听感却是半音。后来重测三轮取中位数表才自洽。原因是个别按键在按下瞬间频率还不稳定要等几十毫秒才落到标称值读数取早了就偏。第三个坑分享谱子必须附带频率表。只发按键序列的谱子等于没发别人拿到手大概率弹不出效果。我现在的做法是把频率表、基准键、映射偏差一起写在谱子开头收到的人先比对一下自己的机器对不上就重跑一遍脚本十分钟的事。第四个坑别迷信万能谱。网上那种号称任何计算器都能弹的谱子本质是把每个音都映射到偏差最大的那个键上代价是整首曲子听起来五音不全。宁可换一首适合这台机器的曲子也别硬塞。第五个坑调式选对了一半的困难自动消失。我一开始不信这个非要把一首 C 大调的曲子塞进 G 大调的机器改谱改到崩溃。后来整体移调到 G所有音都落在按键上一次过。选曲的时候先看机器是什么调再决定弹什么这个顺序不能反。6. 把这套思路搬到别的地方去这套连续量映射到离散格子的思路其实不止用于计算器。手机上的按键音、老式电子表的蜂鸣、甚至某些玩具上的音阶按钮本质都是同一回事——固件里有一张频率表用户能做的只有选最接近的那个。掌握了一次测频、一次映射、一次校验的完整流程换任何设备都是重跑一遍脚本的事。还有个延伸方向是把谱子做成可听的。给一段简谱程序直接合成播放这对不方便看谱的人是实打实的帮助反过来给一段录音程序自动扒出简谱再合成中间那层文字谱就成了人和机器之间的通用语言。我试过用这套东西把一段旋律在计算器上翻译了一遍再翻译回来中间损失的细节刚好就是计算器这件乐器的性格——那些丢掉的滑音、忘掉的强弱反而让旋律变得更硬、更直听起来像另一种东西。这可能就是为什么这么多年过去还是有人乐此不疲地拿一台计算器弹歌。我在实际折腾这些东西的过程中最大的体会是别把它当成还原原曲把它当成用有限的十几个音重新写一遍。一旦心态从复制变成改编那些原本让人抓狂的限制——音域窄、没有半音、节奏做不出来——反而变成了风格本身。你按下的每一下都是在跟一台几十块钱的机器做交易它给你音高你给它节奏成交的产物就是你手里那段独一份的旋律。