Skip to content

badnotes/piano-score

Repository files navigation

PianoScore

基于 Rust 的钢琴乐谱渲染引擎,将自定义 DSL 文本转换为 SVG 格式的专业钢琴谱。支持标准五线谱、指法标注、歌词展示、简谱等多种显示模式,可通过 WASM 在 Web 环境中使用。

特性

  • V2 行式 DSL:VexTab 风格行式语法,简洁高效(V1 块式语法已废弃
  • 多种显示模式:标准五线谱、五线谱+指法、仅指法、简谱+指法、测验模式、自定义模式
  • 图层控制:音符、符杆、符尾、连梁、指法、歌词、简谱等图层可独立显隐
  • 专业符号:Bravura (SMuFL) 字形已提取为 SVG path,零字体依赖,任何查看器都不出现方块
  • 演奏法标记:staccato / accent / tenuto / marcato / fermata 等渲染
  • 指法标注:支持左右手 1-5 指法标记,位置和样式可定制
  • 歌词显示:支持多音节词、连字符、多段歌词
  • 简谱模式:自动生成简谱数字和八度标记
  • 精确时值运算:有理数(整数比例)计算时值,三连音等 tuplet 无浮点累积误差
  • SVG 输出:自包含矢量输出(~19KB),无需外部字体
  • WASM 绑定:编译为 WebAssembly,可在浏览器中实时渲染
  • 配置驱动:通过 JSON 配置或预设模板控制所有渲染参数

项目结构

piano-score/
├── crates/
│   ├── pscore-core/       # 领域模型(Score, Staff, Measure, Note, Pitch 等)
│   ├── pscore-parser/     # DSL 解析器(Lexer → AST → 语义分析 → Score)
│   ├── pscore-layout/     # 排版引擎(计算各元素的坐标和尺寸)
│   ├── pscore-render/     # SVG 渲染器(将布局结果转为 SVG 图形)
│   ├── pscore-config/     # 配置系统(显示模式、图层、布局、样式)
│   └── pscore-wasm/       # WASM 绑定(Web 环境集成接口)
├── examples/
│   └── demo/              # 命令行示例程序
└── assets/                # Bravura 字体文件

快速开始

安装依赖

# 需要 Rust 工具链
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

运行示例

cargo run -p demo

将在项目根目录生成以下 SVG 文件:

  • output_standard.svg — 标准五线谱
  • output_fingering.svg — 五线谱 + 指法
  • output_finger_only.svg — 仅指法
  • output_lyrics.svg — 五线谱 + 歌词
  • output_numbered.svg — 简谱 + 指法

代码中使用

use pscore_config::{ScoreConfig, DisplayMode};

// 1. 解析 V2 DSL 文本(推荐)
let score = pscore_parser::parse_v2(dsl).expect("解析失败");

// 2. 选择显示模式并计算布局
let config = ScoreConfig::from_preset(DisplayMode::Standard);
let layout = pscore_layout::layout(&score, &config).expect("布局失败");

// 3. 渲染为 SVG
let svg = pscore_render::render_svg(&layout, &config).expect("渲染失败");

// 4. 保存或输出
std::fs::write("output.svg", svg).expect("写入失败");

DSL 语法参考(V2 行式语法)

基本结构

V2 采用 VexTab 风格的行式语法,每行一个声明或一组音符:

title "小星星"
tempo 120
key C
time 4/4
stave treble key=C time=4/4
notes C4:q D4:q E4:q F4:q |
notes G4:q G4:q A4:h |
stave bass
notes C3:q E3:q G3:q C4:q |
  • title "..." / composer "..." / tempo N / key C / time 4/4 — 元数据行(全局)
  • stave <clef> [key=...] [time=...] — 声明谱表(treble/bass/alto/tenor)
  • notes <音符>... | — 音符行,| 分隔小节,支持 || |] |: :| 等小节线
  • // — 行注释

音符表示

格式:音名八度:时值[附点][合并属性]

组成 说明 示例
音名 C D E F G A B(大写) C4, G4, A4
八度 数字(4 = 中央C所在八度) C4, C5, C3
时值 字母:w/h/q/e/s/t 或 数字:1/2/4/8/16/32 C4:qC4:4
附点 时值后加 ... D4:h. (附点二分), C4:q.. (复附点)
休止符 r:时值 r:q, r:e
和弦 <C4 E4 G4>:时值 <C4 E4 G4>:q
连音 ^3^^5,4^(套用后续 N 个事件) ^3^ C4:e D4:e E4:e

合并属性块

[...] 将指法、歌词、演奏法合并到一块,| 分隔:

语法 说明 示例
R1R5 右手 1-5 指 [R1]
L1L5 左手 1-5 指 [L2]
文字 歌词 [一闪]
staccato / accent / tenuto 演奏法 [staccato]

组合示例:C4:e [R1|一闪|staccato] — 右手1指 + 歌词"一闪" + 断音

完整示例:小星星(V2)

title "小星星"
tempo 120
key C
time 4/4

stave treble
notes C4:e[R1|一] C4:e[R1|闪] G4:e[R3|一] G4:e[R3|闪] A4:q[R4|亮] G4:q[R3|晶] |
notes F4:e[R2|满] F4:e[R2|天] E4:e[R1|都] E4:e[R1|是] D4:h.[R1|星] |

stave bass
notes C3:q E3:q G3:q C4:q |
notes F3:q A3:q C4:q F4:q |

V1 DSL(已废弃)

V1 块式语法(score "..." { staff ... { measure { ... } } })已标记 #[deprecated], 仍可通过 pscore_parser::parse() 使用,但推荐迁移到 V2 pscore_parser::parse_v2()

显示模式

模式 说明 预设名称
标准五线谱 仅显示五线谱音符 Standard
五线谱+指法 五线谱 + 指法标注 WithFingering
五线谱+音名 五线谱 + 指法 + 音名 WithNoteNames
仅指法 隐藏五线谱,只显示指法 FingeringOnly
简谱+指法 简谱数字 + 指法标注 NumberedWithFingering
测验模式 部分隐藏,用于练习 QuizMode
自定义 完全自定义图层组合 Custom

代码中使用预设

// 使用预设
let config = ScoreConfig::from_preset(DisplayMode::WithFingering);

// 自定义:在预设基础上微调
let mut config = ScoreConfig::from_preset(DisplayMode::Standard);
config.layers.lyrics = true;        // 显示歌词
config.layers.fingering = true;     // 显示指法
config.layers.note_names = true;    // 显示音名
config.staff.line_style = LineStyle::Dashed;  // 虚线谱表

配置系统

ScoreConfig 由四个子配置组成:

LayoutConfig — 布局参数

参数 默认值 说明
page_width 800.0 页面宽度 (px)
page_height 300.0 页面高度 (px)
margin_top 20.0 上边距
margin_bottom 20.0 下边距
margin_left 20.0 左边距
margin_right 20.0 右边距
staff_spacing 100.0 谱表间距(高低音谱表之间)
line_spacing 10.0 五线谱线间距
measure_padding 5.0 小节内边距

LayerConfig — 图层显隐

参数 默认值 说明
notes true 音符头
stems true 符杆
flags true 符尾
beams true 连梁
fingering false 指法标注
lyrics false 歌词
note_names false 音名
numbered_notes false 简谱数字
ledger_lines true 加线
ties true 连音线
slurs true 圆滑线
articulations true 演奏法标记

StyleConfig — 外观样式

参数 默认值 说明
scale 1.0 整体缩放
note_color #000000 音符颜色
staff_line_color #000000 谱表线颜色
fingering_color #cc0000 指法颜色(红色)
lyric_color #000000 歌词颜色
note_name_color #0066cc 音名颜色(蓝色)
music_font Bravura 音乐字体
lyric_font_size 12.0 歌词字号
fingering_font_size 10.0 指法字号
note_name_font_size 9.0 音名字号

StaffDisplayConfig — 谱表显示

参数 默认值 说明
visible true 谱表是否可见
line_count 5 谱表线数
line_style Solid 线样式(Solid/Dashed/Dotted/Hidden)
show_clef true 显示谱号
show_key_signature true 显示调号
show_time_signature true 显示拍号

领域模型

核心数据结构层次:

Score
 ├── metadata (标题、作曲、BPM 等)
 └── staves: Vec<Staff>
      ├── clef (谱号)
      ├── key_signature (调号)
      ├── time_signature (拍号)
      └── measures: Vec<Measure>
           └── voices: Vec<Voice>
                └── events: Vec<NoteEvent>
                     ├── Note(NoteData)
                     │    ├── pitch: Pitch (音高,MIDI 编号)
                     │    ├── duration: Duration (时值 + 附点 + 连音)
                     │    ├── lyric: Option<Lyric>
                     │    ├── fingering: Option<Fingering>
                     │    ├── articulations: Vec<Articulation>
                     │    └── tie: TieType
                     ├── Chord(Vec<NoteData>)
                     └── Rest(Duration)

渲染管线

DSL 文本
  │
  ▼  Lexer(词法分析)
Tokens
  │
  ▼  Parser(语法分析)
AST
  │
  ▼  Semantic Analysis(语义分析)
Score(领域模型)
  │
  ▼  Layout Engine(排版引擎)
LayoutResult(坐标 + 尺寸)
  │
  ▼  SVG Renderer(渲染引擎)
SVG 字符串

排版引擎

两阶段排版:

  1. 度量计算:计算每个元素的宽度和高度
  2. 页面布局:确定每个元素的绝对坐标(处理换行、谱表间距等)

SVG 渲染

分层渲染,从底到顶:

  1. 背景层
  2. 谱表线层
  3. 符号层(音符头、谱号、拍号等)
  4. 标记层(指法、歌词、音名等)

音乐符号使用 Bravura 字体(SMuFL 标准)通过 Unicode 代码点渲染:

符号 SMuFL 代码点 说明
黑色音符头 U+E0A4 四分及更短时值
空心音符头 U+E0A3 二分音符
全音符头 U+E0A2 全音符
高音谱号 U+E050 G 谱号
低音谱号 U+E062 F 谱号
八分符尾 U+E240 单符尾向上
休止符 U+E4E5 四分休止符

WASM 集成

构建

wasm-pack build crates/pscore-wasm --target web

在 JavaScript 中使用

import init, { ScoreRenderer } from './pkg/pscore_wasm.js';

await init();

const renderer = new ScoreRenderer();

// 加载 V2 DSL 文本
renderer.load(`
  title "测试"
  stave treble
  notes C4:q D4:q E4:q F4:q |
`);

// 设置显示模式
renderer.set_display_mode("with_fingering");

// 切换图层
renderer.set_layer_visible("lyrics", true);
renderer.set_layer_visible("fingering", true);

// 渲染为 SVG
const svgString = renderer.render_svg();

// 插入页面
document.getElementById('score-container').innerHTML = svgString;

// 自定义配置(JSON)
renderer.set_config(JSON.stringify({
  mode: "Standard",
  layers: { notes: true, fingering: true, lyrics: true },
  style: { note_color: "#333333", scale: 1.2 }
}));

WASM API

方法 说明
new() 创建渲染器实例
load(dsl) 加载 V2 DSL 文本(推荐)
load_v1(dsl) 加载 V1 DSL 文本(已废弃)
set_display_mode(mode) 设置显示模式
set_layer_visible(layer, visible) 切换图层显隐
set_config(json) 设置完整配置(JSON)
get_config() 获取当前配置(JSON)
render_svg() 渲染为 SVG 字符串

显示模式值:standard | with_fingering | with_note_names | fingering_only | numbered_with_fingering | quiz | custom

图层值:fingering | lyrics | note_names | numbered_notes | stems | flags | staff_lines

License

MIT OR Apache-2.0

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages