免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

LilyPond 输出简谱全攻略:四路线对比与实操技巧

LilyPond 输出简谱全攻略:四路线对比与实操技巧 玩 LilyPond 的人十个里有八个会问一句能不能出简谱也是LilyPond 这个排版引擎确实强五线谱、吉他谱、古谱都能折腾唯独简谱不在官方支持列表里。但需求就摆在那——中文歌曲、民乐、戏曲、中小学音乐教材几乎全是简谱的天下。这篇文章我就把自己折腾的几条路线整理出来从手工打数字到半自动脚本再到社区成熟的 jianpu.ly 方案到底该怎么选、怎么配、怎么避坑一篇给你讲明白。想用 LilyPond 直接出简谱的照着抄就行。1. 先搞清楚简谱和五线谱到底差在哪要解决“LilyPond 怎么输出简谱”首先得明白简谱不是五线谱的简单替换。它俩表达音乐的方式完全不同如果你直接把五线谱渲染逻辑套到简谱上十个有九个要翻车。1.1 简谱的基本构成元素简谱的核心是“数字记谱法”用 1 到 7 表示 do、re、mi、fa、sol、la、si用 0 表示休止符。很多人以为简谱就是七个数字真做起来才发现简谱的时值表达比五线谱更“符号化”增时线数字右边画一条短横线表示这个音延长一个单位拍。一个四分音符写成1 -全音符写成1 - - -。减时线数字下方画一条短横线表示时值减半。八分音符写成下划线1带一条线十六分音符带两条线。附点数字右边或增时线后加一个小圆点时值增加一半。高低音点数字上方或下方加圆点表示高八度或低八度。变音记号数字左边加 # 或 b表示升高或降低半音。调号标记如“1C”表示这首曲子以 C 为主音也就是简谱里的 1 对应 C。五线谱里音高看“位置”时值看“符头符杆符尾/连音线”简谱里音高看“数字点”时值看“横线下划线”。所以要让 LilyPond 输出简谱本质上是把五线谱引擎里的“音符图形”整个替换成另一套图形系统而不是简单改个字体。1.2 为什么不能直接用五线谱改改我自己最早试过把 NoteHead 改成数字结果只出来一排孤零零的数字时值没了增时线没有下划线没有高低音点也没有。勉强能看出旋律轮廓但根本没法读更别说出版。后来才明白LilyPond 渲染一个音符时涉及的不只是符头还有 Stem符杆、Flag符尾、Beam连音线、Dot附点等多个 grob图形对象。要做出真正的简谱这些对象都得重新设计。这也是为什么社区方案普遍要写一坨 Scheme 代码的原因——不是人家故弄玄虚是确实需要动到排版引擎的底层机制。2. LilyPond 到底有没有可能输出简谱直接回答官方 LilyPond原生不支持简谱。但在 LilyPond 的世界里“原生不支持”不代表“没办法”。它的排版引擎底层是一套 Scheme 解释器所有音符、小节线、谱号都是可编程的对象。你可以改字体、改形状、改位置甚至可以自定义 engraver负责把音乐事件变成图形对象的“雕刻师”。简谱方案就是利用这套机制做出来的。2.1 LilyPond 的“可编程性”是突破口LilyPond 有一个核心概念叫grobgraphical object也就是最终出现在 PDF 里的每个图形元素NoteHead、Stem、Slur、Clef 等等。每个 grob 都有 stencil模板属性stencil 决定了这个图形长什么样。默认情况下NoteHead 的 stencil 是一个椭圆但你可以把它替换成任意函数比如让它输出一个“数字文本”。这就铺平了简谱的路只要能把数字正常打印出来剩下就是怎么处理时值线、增时线、高低音点这些细节。2.2 简谱方案要解决的最小问题集社区里所有 LilyPond 简谱实现本质上都在解决下面这些问题音高转数字把 c、d、fis 这些音高转换成 1-7 的数字而且要处理升号、降号、八度。调性判断简谱是首调记谱。1C 时C 是 11G 时G 是 1C 就变成了 4。所以音高转数字前得先知道调号。时值转符号四分音符后面加增时线八分音符下方加减时线十六分音符加两条。高低音点超过一个八度数字上方或下方加圆点。休止符用 0 表示。和弦/多声部简谱里纵向叠置的数字要处理好上下关系。看似不多但要每一项都做得专业工作量并不小。所以我的建议是如果你只是想偶乐出个简谱别从零写先用成熟方案理解原理后再说。3. 四条路线选哪个对比后你就懂了针对“LilyPond 输出简谱”目前我能跑通的大致有四条路线。每条路线的自动化程度、维护成本和最终效果都不一样我直接做成表格方便你选。方案自动化程度时值表现难易程度适合场景手工 markup 标注零自动手写控制最灵活低只有一两行旋律、不追求通用NoteNames 改音名半自动缺增时线/减时线要手补低快速预览旋律走向NoteHead stencil 替换自动出数字缺时值线要额外开发中学习 LilyPond 内部机制的练手社区 jianpu.ly 完整方案全自动完整支持增时线/减时线/附点中高正式排版、出版级输出3.1 最推荐社区 jianpu.ly我排查过 GitHub 和 LilyPond 官方 Snippet 库发现社区有几个简谱实现文件名常见的是jianpu.ly或jianpu-ly。这类方案通常定义了一个JianpuStaff或JianpuMusic上下文你只要把普通音符写进去它会自动完成数字转换、时值线绘制、高低音点添加。虽然作者不同细节有差异但整体使用思路一致。后面第四章我会给出一个能跑的简化替代让你先理解原理第五章再讲怎么把它升级成完整可用的状态。3.2 最快速NoteNames 半自动方案LilyPond 自带一个NoteNames上下文本来是用来给音符标音名的C、D、E 那种字母。我们可以通过 Scheme 钩子把字母改成数字。好处是代码量很小不到二十行就能看到数字旋律坏处是时值线完全没有只能当你想要快速核对音符对不对时用。我自己做复杂谱子时偶尔会用它当“临时调试器”比耳朵听 MIDI 还直观。3.3 最练手自己写 NoteHead stencil如果你想深入理解 LilyPond 的排版模型强烈建议手动写一次 NoteHead 替换。你会亲手碰到 stencil、event-cause、text-interface 这些概念搞懂后再看别人的简谱方案基本一眼就能明白原理。下面第四章会详细给出这段代码和逐行解释。4. 最小可用的简化实现从 NoteHead 替换开始这一章先给一个不依赖任何外部文件的简化方案。它最大的价值在于让你用最短的代码看到“数字从音符里蹦出来”的过程。我会逐行解释方便你根据版本微调。4.1 核心代码把音符头换成数字新建一个.ly文件输入以下内容我用的是 LilyPond 2.22 语法\version 2.22.2 % 将 pitch 转换成 1-7 的数字 #(define (pitch-jianpu pitch) (let* ((note-name (ly:pitch-notename pitch)) (degree (modulo note-name 7))) ( degree 1))) jianpu { % 让 NoteHead 按文本方式打印 \override NoteHead.stencil #ly:text-interface::print % 文本内容由 pitch 决定 \override NoteHead.text #(lambda (grob) (let* ((cause (ly:event-cause grob)) (pitch (ly:event-property cause pitch))) (make-simple-markup (number-string (pitch-jianpu pitch))))) % 去掉五线谱线、谱号、拍号 \override StaffSymbol.stencil ##f \override Clef.stencil ##f \override TimeSignature.stencil ##f } \score { \new Staff { \jianpu c4 d e f g a b c4 } }如果你编译成功会看到 PDF 里只有一行数字1 2 3 4 5 6 7 1。虽然还没有增时线和减时线但“自动从音符生成简谱数字”这一步已经跑通了。4.2 逐行拆解这坨 Scheme 到底在干什么很多新手看到 Scheme 就头大其实这段只是几个关键点ly:pitch-notename返回音名的索引。C0、D1、E2……B6升降号暂时忽略。modulo note-name 7不管跨几个八度都映射回 0-6。c 和 c 的 note-name 不同但取模后都是 0所以都转成 1。ly:event-causeNoteHead 这个 grob 是由哪个音乐事件生成的通过它能拿到音符的 pitch 信息。ly:text-interface::printLilyPond 里专门负责打印文本的 stencil 函数。把 NoteHead 的 stencil 换成它音符头就不再是椭圆而是 text 属性里写的那个字符串。make-simple-markup把 Scheme 字符串包装成 LilyPond 的 markup 对象否则 text 属性没法显示。这里有个容易踩的坑如果你把\override NoteHead.text #(lambda ...)写成了\override NoteHead.text 1那就所有音符都变 1因为 text 属性被固定成常量字符串。要让每个音符不同必须用函数动态生成。4.3 这个简化方案能做什么、不能做什么能做的自动把单声部旋律变成一串数字方便快速读谱。在终端里配合-dno-point-and-click编译用来当“音高直译器”很顺手。作为理解 LilyPond Scheme 机制的入门案例。不能做的没有时值线四分音符的增时线、八分音符的减时线全部缺失。没有高低音点c 和 c 都显示成 1八度信息丢了。没有处理调号1G 时G 应该当 1但这段代码还是把 C 当 1。没有休止符休止符在 event-cause 里取不到 pitchly:event-property cause pitch会返回空值可能直接报错或空白。所以我一直强调这段代码是“坦克底盘”不是“成品车”。真正要出出版级简谱还得上完整方案。5. 进阶实操使用社区 jianpu.ly 方案出完整简谱当我需要把简谱真正用到印刷、教学、排练时不太会在生产环境用上面那段半成品。这两年我用得最顺的是社区流传的jianpu.ly方案。它把上一章的思路封装成了一个上下文能自动处理时值线、高低音点、休止符连附点都给你画好。5.1 获取与安装 jianpu.lyjianpu.ly这个文件在 GitHub 上可以搜到作者和版本各有不同但使用接口大体一致。下载后放到 LilyPond 的 include 路径下Windows 一般是C:\Users\你的用户名\AppData\Roaming\LilyPond\2.22.2\lymacOS/Linux 可以把文件放在当前项目目录直接\include jianpu.ly就行。提示不同版本的 jianpu.ly 可能定义了不同的上下文名常见的有JianpuStaff、JianpuMusic、JianpuVoice建议先看文件头部的注释说明或者直接找带示例的仓库。我用的版本核心用法如下\version 2.22.2 \include jianpu.ly \score { \new JianpuStaff { \time 4/4 \key c \major c4 d e f | g2 a4 b | c1 } }编译后你会得到一份真正的简谱数字下面有下划线表示八分音符数字右边有横线表示延长拍还有小节线、调号标记“1C”自动出现。到这里脚本终于从“玩具”变成了“工具”。5.2 里面到底做了什么核心机制拆解我扒过几个版本的 jianpu.ly 源码虽然具体写法差异不小但核心思路惊人一致自定义了一个JianpuStaffcontext继承自 Staff然后把默认的Staff_symbol_engraver绘制五线谱线的雕刻器移除这样 PDF 里就没有五条线了。单独写了一个 engraver负责监听note-head-interface事件把音符的 pitch 拿过来算成数字再给 NoteHead 注入一个 markup 文本。时值线、高低音点不是靠 NoteHead而是通过自定义Stem和Flag的 stencil 画出来的。有的方案用\markup \vcenter叠符号有的直接画短线。调号\key c \major会被捕获换算成首调的音级偏移量。所以\key g \major时g就自动变成 1。看到这里你应该明白上一章的手写方案其实和 jianpu.ly 是“同源”的只是 jianpu.ly 把每个环节都做完了。这也解释了为什么我建议你先把第四章跑一遍——因为只有亲手改过 NoteHead你才能真正看懂 jianpu.ly 的每一行配置。5.3 调整字体和大小别让你的数字看起来像残废简谱数字默认用的还是 LilyPond 的五线谱字体偏小或偏细打印出来不够醒目。我一般会在 score 前加一段全局设置#(set-global-staff-size 20) \layout { \context { \JianpuStaff \override JianpuNumber.font-name Noto Sans CJK SC \override JianpuNumber.font-size 2 } }这里假设 jianpu.ly 的数字对象叫JianpuNumber实际以你下载的文件为准。字体调到 Noto Sans CJK SC 后中文环境也不容易缺字特别是歌名、歌词里面出现汉字时稳得多。常见坑如果你直接\override NoteHead.font-size可能不生效因为 jianpu.ly 已经换掉了 stencil它实际用的是另一个 grob 名。要看源码里定义的数字对象是什么别凭猜。5.4 处理多声部和和弦简谱里多声部的情况比五线谱少见但一旦遇到比五线谱繁琐得多。我常用的办法是给不同声部分别建 JianpuVoice然后用\voiceOne和\voiceTwo区分\new JianpuStaff \new JianpuVoice upper { \voiceOne c4 c c c } \new JianpuVoice lower { \voiceTwo a4 a a a } 如果两个声部节奏一致数字会上下对齐阅读体验很好。如果节奏不一致LilyPond 会自动把数字错开但有时会挤在一起这时候我会手动把低音声部整体\shiftOn或调整\override JianpuNumber.padding。和弦的话简谱里通常是把音级数字纵向叠在一起。有的 jianpu.ly 版本能通过c e g这种写法自动把和声音符叠成上下数字不能的话我一般用\chordmode生成音符再把每个音手动转成数字文本工作量不小但效果可控。6. 把简谱做成出版物排版细节和避坑指南自动化能帮你省 80% 的功夫剩下 20% 是排版细节。以下是这段时间我反复折腾总结出的一线经验。6.1 增时线和连音线容易被系统误判在五线谱里一个全音符就是空心符头在简谱里全音符既要有数字还要在右边画三条增时线。有的 jianpu.ly 实现是用 markup 模拟增时线当音有多拍时效果还行但遇到延音连线tie时代码经常分不清“延长时值”和“两个音连成一个音”结果画出多余符号或者漏画。我自己的处理习惯能不用 tie 就不用改用增时线组合表示长音如果是跨小节的长音再用\tie并手动检查渲染效果。说实话这个领域还没有一劳永逸的方案出谱前一定要肉眼扫一遍。6.2 调号与首调转换的“方言”问题简谱的记谱法是首调1C 就代表 C 音是主音但具体在电脑实现时有的 jianpu.ly 用\key指令直接换算有的要用\transpose来变调。你下载的方案到底支持哪种最好在 README 或示例里看清楚。举个例子1G 的《小星星》第一句正确数字是5 5 6 6 5 4 3 2 1。但如果你在 LilyPond 里写\key g \major g4 g a a g f e d c有的 jianpu.ly 会输出1 1 2 2 1 7 6 5 4首调正确有的则直接按固定调输出5 5 6 6 5 4 3 2 1反而歪打正着。所以一定要先跑一个短句测试确认它用的是哪套逻辑。6.3 数字和文字标注意外重叠简谱加减时线时数字下方空间很紧张。如果稍微复杂点再来个歌词对齐就容易出现数字压字、下划线和歌词黏连。我一般会用\override LyricText.extra-offset #(0 . 0.5)把歌词整体下移一点或者给 JianpuNumber 加 padding\override JianpuNumber.padding 1.2方向不是死的要根据实际渲染效果调。经验法则是数字和歌词之间至少留出 1 个像素级别的空档太挤的谱子没人愿意看。7. 简谱和五线谱对照排版教学场景的杀手锏很多时候你不需要纯简谱而是“上面五线谱、下面简谱”的对照谱。音乐课堂、合唱排练、流行歌曲伴奏谱这种对照格式最受欢迎。LilyPond 做这个其实是强项甚至比纯简谱还简单因为简谱部分直接复用同一个音符序列。7.1 两手抓的排版示例\version 2.22.2 \include jianpu.ly \score { \new StaffGroup \new Staff { \key c \major \time 4/4 \relative c { c d e f | g a b c | } } \new JianpuStaff { \key c \major \time 4/4 \relative c { c d e f | g a b c | } } \layout { } }这样一份 PDF上面是标准五线谱下面是完整简谱所有音符一一对应。学生看惯了简谱可以对照着认五线谱乐队排练也方便不同阅读习惯的乐手直接看自己熟悉的那一行。7.2 对齐问题两行谱节奏保持一致才好看对照谱最忌讳上下节奏对不上。如果五线谱和简谱用了完全相同的音符序列LilyPond 通常能自动对齐。但如果简谱部分省略了某些装饰音或者用了不同时值的写法两行谱就会错位。我一般会强制开启属性\new StaffGroup \new Staff { … } \new JianpuStaff \with { \override TimeSignature.stencil ##f } { … } 然后对照谱检查每个小节的开始位置是否在同一纵坐标。错位时优先查是不是某一行的\time或\key不一致。小技巧在五线谱那行加入\hide StaffSymbol可以隐藏五线谱线得到“无谱线数字谱”效果反过来如果只需要对照就保留五线谱线视觉上更专业。8. 常见问题速查我踩过的八个坎最后整理一份排查表都是真实编译中遇到的高频问题每条都附带解决思路。现象可能原因解决方案音符变成了字母而不是数字NoteNames 方案没改 text 属性用\override NoteName.text #(lambda …)覆盖默认字母数字出来了但没有增时线时值线没被绘制确认 jianpu.ly 版本支持时值线或手动加 markup八分音符没有下划线Stem/Flag 的 stencil 被#f 掉了不要屏蔽 AStem.stencil应该让简谱包处理升号 # 变成空或错误pitch-jianpu忽略了升降号在函数里检查ly:pitch-alteration1G 调出来不对方案用固定调而非首调换用支持首调的 jianpu.ly或手动 transpose多声部数字重叠缺乏 shift 设置给低声部加\shiftOn或\override JianpuNumber.padding打开 PDF 中文歌名乱码字体不支持中文设置全局字体为 Noto Sans CJK SC 等中文字体休止符直接报错老版本不支持 0 事件升级 jianpu.ly或手动把 Rest 转成 markup 0这里面第八条最坑。我最早用的一个老版本简谱包一遇到 r4 就崩溃排查了很久才意识到是它没有处理 Rest 事件后来换了个新版本才解决。所以如果你下载的简谱包年代久远先写一句r1试编译能过再写完整谱子。另一个容易被忽略的坑是“数字跟歌词对不齐”。简谱里数字下方经常要加减时线减时线又挤占歌词空间稍微一多就糊成一团。我现在的做法是凡是歌词密集的谱子给 LyricText 设置\override LyricText.extra-offset #(0 . 0.8)让歌词整体下沉半毫米视觉上立刻清爽。最后说点实在的折腾 LilyPond 输出简谱从原理上讲就是“让制谱引擎用另一套符号说话”。官方不支持不代表做不出来只要理解 stencil、grob、Scheme 这三个关键词你的自由度会远超想象。我在实际使用中最大的感受是别追求一条路走到黑。短旋律用 NoteNames 快速预览正式排版上 jianpu.ly教学对照就做成上下两行谱不同需求挑不同工具比硬让一个方案通吃所有场景要省心得多。如果你手里的 LilyPond 版本比较新做之前先跑一个最小例子验证简谱包兼容性能少走一大半弯路。
返回列表