ARCHITECTURE_DESIGN.md 31 KB

乐谱渲染引擎架构设计文档

版本:1.2
创建日期:2026-01-30
最后更新:2026-01-30
状态:设计阶段


目录

  1. 项目愿景
  2. 整体架构
  3. 核心数据模型设计
  4. 首调与固定调设计
  5. MusicXML元素覆盖清单
  6. 重构路线图
  7. 目录结构规划
  8. 开发原则

1. 项目愿景

1.1 目标

构建一个通用的乐谱渲染引擎,具备以下能力:

  • 输入:MusicXML(标准乐谱交换格式)
  • 输出
    • 简谱(数字谱)
    • 首调模式(Movable-Do):主音为 do(1)
    • 固定调模式(Fixed-Do):C 永远为 do(1)
    • 五线谱(Staff Notation)
    • 未来可扩展其他格式
  • 运行时切换:支持在五线谱、首调简谱、固定调简谱之间动态切换

1.2 设计原则

  1. 规范先行:开发前必须完整学习 MusicXML 4.0 规范,理解所有标签的含义和业务影响
  2. 完整性:100%覆盖MusicXML 4.0规范中的所有元素
  3. 通用性:数据模型与渲染方式解耦,支持多种输出格式
  4. 可扩展性:易于添加新的渲染器或输入格式
  5. 高性能:针对大型乐谱优化渲染性能
  6. 独立性:不依赖第三方乐谱库(如OSMD),完全自主可控
  7. 模式可切换:首调/固定调模式可运行时切换,无需重新解析

⚠️ 重要提醒:在开始任何编码工作之前,必须先完成 MusicXML 规范的完整学习。 详见 工作计划 - 阶段零

1.3 与现有方案的对比

特性 OSMD VexFlow 我们的引擎
简谱支持
五线谱支持 ✅(规划中)
首调/固定调
MusicXML解析
自主可控 ❌(依赖) ❌(依赖)
定制性

2. 整体架构

2.1 三层架构

┌─────────────────────────────────────────────────────────────┐
│                      输入层 (Parser Layer)                   │
│  ┌─────────────────┐  ┌─────────────────┐  ┌──────────────┐ │
│  │ MusicXML Parser │  │ MIDI Parser    │  │ Other Parser │ │
│  │ (核心,优先实现) │  │ (未来扩展)      │  │ (未来扩展)   │ │
│  └────────┬────────┘  └────────┬────────┘  └──────┬───────┘ │
└───────────┼─────────────────────┼──────────────────┼─────────┘
            │                     │                  │
            ▼                     ▼                  ▼
┌─────────────────────────────────────────────────────────────┐
│                   核心数据模型层 (Model Layer)                │
│                                                              │
│   Score ─┬─ Part ─┬─ Measure ─┬─ Note                       │
│          │        │           ├─ Forward/Backup              │
│          │        │           ├─ Direction                   │
│          │        │           ├─ Attributes                  │
│          │        │           ├─ Barline                     │
│          │        │           └─ Harmony                     │
│          │        └─ Attributes (key, time, clef, etc.)      │
│          └─ PartInfo (name, instrument, etc.)                │
│                                                              │
│   【核心原则】:完整保留MusicXML的所有信息,不做任何简化        │
└─────────────────────────────────────────────────────────────┘
            │                     │                  │
            ▼                     ▼                  ▼
┌─────────────────────────────────────────────────────────────┐
│                    渲染层 (Renderer Layer)                   │
│  ┌─────────────────┐  ┌─────────────────┐  ┌──────────────┐ │
│  │ Jianpu Renderer │  │ Staff Renderer  │  │ Other        │ │
│  │ (简谱渲染器)     │  │ (五线谱渲染器)   │  │ (未来扩展)   │ │
│  │ 【已有,需重构】 │  │ 【待实现】       │  │              │ │
│  └─────────────────┘  └─────────────────┘  └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
            │                     │                  │
            ▼                     ▼                  ▼
┌─────────────────────────────────────────────────────────────┐
│                      输出层 (Output Layer)                   │
│            SVG / Canvas / PDF / 其他格式                     │
└─────────────────────────────────────────────────────────────┘

2.2 数据流

MusicXML文件
    │
    ▼
┌──────────────┐
│ MusicXML     │  解析XML,生成通用数据模型
│ Parser       │  
└──────┬───────┘
       │
       ▼
┌──────────────┐
│ Score        │  通用音乐数据模型
│ (核心模型)    │  完整保留所有MusicXML信息
└──────┬───────┘
       │
       ├─────────────────┬─────────────────┐
       ▼                 ▼                 ▼
┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│ Jianpu       │  │ Staff        │  │ MIDI         │
│ Renderer     │  │ Renderer     │  │ Generator    │
└──────┬───────┘  └──────┬───────┘  └──────┬───────┘
       │                 │                 │
       ▼                 ▼                 ▼
   简谱 SVG          五线谱 SVG         MIDI文件

3. 核心数据模型设计

3.1 模型概览

数据模型1:1对应MusicXML结构,确保不丢失任何信息。

// 顶层结构
Score
├── identification      // 作品信息(标题、作者等)
├── defaults            // 默认设置(页面、字体等)
├── credits[]           // 版权信息
├── partList[]          // 声部列表
└── parts[]             // 声部内容
    └── Part
        └── measures[]
            └── Measure
                ├── attributes    // 拍号、调号、谱号等
                ├── notes[]       // 音符和休止符
                ├── forwards[]    // 时间前进
                ├── backups[]     // 时间后退
                ├── directions[]  // 方向标记
                ├── harmonies[]   // 和弦符号
                └── barlines[]    // 小节线

3.2 核心类定义

Score(乐谱)

interface Score {
  // 作品标识
  workTitle?: string;
  workNumber?: string;
  movementTitle?: string;
  movementNumber?: string;
  
  // 创作者信息
  identification?: Identification;
  
  // 默认设置
  defaults?: Defaults;
  
  // 声部
  partList: PartInfo[];
  parts: Part[];
}

Part(声部)

interface Part {
  id: string;
  measures: Measure[];
}

interface PartInfo {
  id: string;
  name?: string;
  abbreviation?: string;
  instrument?: Instrument;
  midiInstrument?: MidiInstrument;
}

Measure(小节)

interface Measure {
  number: number;
  width?: number;
  implicit?: boolean;
  
  // 小节内容(按时间顺序)
  elements: MeasureElement[];
  
  // 便捷访问
  attributes?: Attributes;
  notes: Note[];
  directions: Direction[];
  barlines: Barline[];
}

type MeasureElement = 
  | Note 
  | Forward 
  | Backup 
  | Direction 
  | Attributes 
  | Harmony 
  | Barline;

Note(音符)

interface Note {
  // 基本信息
  pitch?: Pitch;          // 音高(休止符为空)
  rest?: Rest;            // 休止符信息
  unpitched?: Unpitched;  // 无音高(打击乐)
  
  // 时值
  duration: number;       // 实际时值(divisions单位)
  type?: NoteType;        // 音符类型(whole, half, quarter等)
  dots: number;           // 附点数量
  
  // 连接
  tie?: Tie[];
  chord?: boolean;        // 是否是和弦的一部分
  
  // 声部和谱表
  voice: number;
  staff?: number;
  
  // 符干和符杠
  stem?: Stem;
  beam?: Beam[];
  
  // 记号
  notations?: Notations;
  
  // 歌词
  lyrics?: Lyric[];
  
  // 装饰音
  grace?: Grace;
  cue?: boolean;
}

Pitch(音高)

interface Pitch {
  step: 'C' | 'D' | 'E' | 'F' | 'G' | 'A' | 'B';
  octave: number;         // 八度(4 = 中央C所在八度)
  alter?: number;         // 变音(-1=降, 0=还原, 1=升, 0.5=微分音)
}

Notations(记号)

interface Notations {
  // 连线
  tied?: Tied[];
  slur?: Slur[];
  
  // 连音
  tuplet?: Tuplet[];
  
  // 滑音
  glissando?: Glissando[];
  slide?: Slide[];
  
  // 演奏技法
  articulations?: Articulations;
  ornaments?: Ornaments;
  technical?: Technical;
  dynamics?: Dynamics;
  
  // 其他
  fermata?: Fermata[];
  arpeggiate?: Arpeggiate;
  nonArpeggiate?: NonArpeggiate;
  accidentalMark?: AccidentalMark[];
}

Direction(方向标记)

interface Direction {
  placement?: 'above' | 'below';
  directive?: boolean;
  
  directionTypes: DirectionType[];
  offset?: number;
  sound?: Sound;
  staff?: number;
  voice?: number;
}

type DirectionType =
  | { type: 'words'; value: Words }
  | { type: 'metronome'; value: Metronome }
  | { type: 'dynamics'; value: Dynamics }
  | { type: 'wedge'; value: Wedge }
  | { type: 'pedal'; value: Pedal }
  | { type: 'octaveShift'; value: OctaveShift }
  | { type: 'segno'; value: Segno }
  | { type: 'coda'; value: Coda }
  | { type: 'rehearsal'; value: Rehearsal }
  | { type: 'dashes'; value: Dashes }
  | { type: 'bracket'; value: Bracket }
  | /* ... 更多类型 */;

Barline(小节线)

interface Barline {
  location: 'left' | 'middle' | 'right';
  barStyle?: BarStyle;
  repeat?: Repeat;
  ending?: Ending;
  fermata?: Fermata[];
  segno?: Segno;
  coda?: Coda;
}

type BarStyle = 
  | 'regular' | 'dotted' | 'dashed' | 'heavy'
  | 'light-light' | 'light-heavy' | 'heavy-light' | 'heavy-heavy'
  | 'tick' | 'short' | 'none';

3.3 模型设计原则

  1. 完整性:每个MusicXML元素都有对应的TypeScript类型
  2. 可选性:大部分属性是可选的(符合XML的灵活性)
  3. 类型安全:使用TypeScript严格类型
  4. 可序列化:模型可以轻松序列化为JSON

4. 首调与固定调设计

4.1 概念说明

简谱渲染需要支持两种唱名模式,业务层通过切换开关选择:

模式 英文名 核心规则 适用场景
首调 Movable-Do 调式主音 = do(1) 声乐、民乐、音乐教育
固定调 Fixed-Do C = do(1),永不改变 器乐、绝对音高训练

4.2 渲染差异对比

G大调 的旋律为例(实际音高:G A B C D E F#):

实际音高 固定调渲染 首调渲染
G4 5 1
A4 6 2
B4 7 3
C5 (高音1) 4
D5 5
E5 6
F#5 #4̇ 7

关键差异

  1. 数字不同:同一个音高在两种模式下显示的数字完全不同
  2. 升降号处理
    • 固定调:调内音需要标注升降号(如 G大调的 F# 显示为 #4
    • 首调:调内音不需要升降号(F# 显示为 7),只有临时变化音需要标注
  3. 八度点位置:由于音级重新分配,高低音点的位置也会变化
  4. 调号显示
    • 固定调:显示 1=C(始终以C为基准)
    • 首调:显示 1=G(显示当前调的主音)

4.3 数据模型设计

4.3.1 核心原则:绝对音高 + 渲染时转换

设计思路:核心数据模型始终保存绝对音高(与MusicXML一致),渲染时根据模式动态转换。

// 核心模型:始终保存绝对音高
interface Note {
  pitch: {
    step: 'C' | 'D' | 'E' | 'F' | 'G' | 'A' | 'B';  // 绝对音名
    octave: number;                                   // 绝对八度
    alter?: number;                                   // 变音记号
  };
  // ... 其他属性
}

// 简谱渲染时的转换结果
interface JianpuPitch {
  number: number;        // 简谱数字 1-7,0=休止符
  octave: number;        // 八度偏移(相对于中音区)
  accidental?: 'sharp' | 'flat' | 'natural';  // 升降号
}

4.3.2 渲染配置

/** 唱名模式 */
type SolfegeMode = 'movable' | 'fixed';

/** 谱面类型 */
type NotationType = 'staff' | 'jianpu-movable' | 'jianpu-fixed';

/** 渲染配置 */
interface RenderConfig {
  // ... 其他配置
  
  /** 
   * 唱名模式
   * - 'movable': 首调(主音为do)
   * - 'fixed': 固定调(C永远为do)
   */
  solfegeMode: SolfegeMode;
}

4.4 音高转换算法

4.4.1 固定调转换

固定调转换简单直接:音名直接映射到数字。

const STEP_TO_JIANPU: Record<string, number> = {
  'C': 1, 'D': 2, 'E': 3, 'F': 4, 'G': 5, 'A': 6, 'B': 7
};

function toFixedDo(pitch: Pitch): JianpuPitch {
  return {
    number: STEP_TO_JIANPU[pitch.step],
    octave: pitch.octave - 4,  // 4为中央C的八度
    accidental: alterToAccidental(pitch.alter),
  };
}

4.4.2 首调转换

首调转换需要考虑调号,计算音符相对于主音的音级。

const STEP_ORDER = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];

/**
 * 将绝对音高转换为首调简谱
 * @param pitch 绝对音高
 * @param keySignature 调号信息
 */
function toMovableDo(pitch: Pitch, keySignature: KeySignature): JianpuPitch {
  const keyRoot = keySignature.key;  // 如 'G', 'D', 'F' 等
  
  const stepIndex = STEP_ORDER.indexOf(pitch.step);
  const keyIndex = STEP_ORDER.indexOf(keyRoot[0]);  // 取第一个字符,处理 'Bb' 等情况
  
  // 计算相对音级(0-6)
  const relativeDegree = (stepIndex - keyIndex + 7) % 7;
  const number = relativeDegree + 1;  // 转为1-7
  
  // 计算八度偏移
  const octaveOffset = calculateOctaveOffset(pitch, keySignature);
  
  // 计算升降号(只有非调内音需要显示)
  const accidental = calculateAccidentalForMovable(pitch, keySignature);
  
  return { number, octave: octaveOffset, accidental };
}

/**
 * 计算首调模式下的升降号
 * 规则:调内音不显示升降号,临时变化音需要显示
 */
function calculateAccidentalForMovable(
  pitch: Pitch, 
  keySignature: KeySignature
): 'sharp' | 'flat' | 'natural' | undefined {
  const expectedAlter = getExpectedAlterInKey(pitch.step, keySignature);
  const actualAlter = pitch.alter ?? 0;
  
  if (actualAlter === expectedAlter) {
    return undefined;  // 调内音,不显示
  }
  
  // 临时变化音
  if (actualAlter > expectedAlter) return 'sharp';
  if (actualAlter < expectedAlter) return 'flat';
  if (actualAlter === 0 && expectedAlter !== 0) return 'natural';
  
  return undefined;
}

4.5 调号与升降号映射

4.5.1 五度圈调号表

/** 调号信息(fifths = 升号数,负数为降号数) */
const KEY_SIGNATURE_MAP: Record<number, { 
  key: string; 
  alterations: Map<string, number>;  // 调内音的升降
}> = {
  // 升号调
  0:  { key: 'C',  alterations: new Map() },
  1:  { key: 'G',  alterations: new Map([['F', 1]]) },
  2:  { key: 'D',  alterations: new Map([['F', 1], ['C', 1]]) },
  3:  { key: 'A',  alterations: new Map([['F', 1], ['C', 1], ['G', 1]]) },
  4:  { key: 'E',  alterations: new Map([['F', 1], ['C', 1], ['G', 1], ['D', 1]]) },
  5:  { key: 'B',  alterations: new Map([['F', 1], ['C', 1], ['G', 1], ['D', 1], ['A', 1]]) },
  6:  { key: 'F#', alterations: new Map([['F', 1], ['C', 1], ['G', 1], ['D', 1], ['A', 1], ['E', 1]]) },
  7:  { key: 'C#', alterations: new Map([['F', 1], ['C', 1], ['G', 1], ['D', 1], ['A', 1], ['E', 1], ['B', 1]]) },
  
  // 降号调
  -1: { key: 'F',  alterations: new Map([['B', -1]]) },
  -2: { key: 'Bb', alterations: new Map([['B', -1], ['E', -1]]) },
  -3: { key: 'Eb', alterations: new Map([['B', -1], ['E', -1], ['A', -1]]) },
  -4: { key: 'Ab', alterations: new Map([['B', -1], ['E', -1], ['A', -1], ['D', -1]]) },
  -5: { key: 'Db', alterations: new Map([['B', -1], ['E', -1], ['A', -1], ['D', -1], ['G', -1]]) },
  -6: { key: 'Gb', alterations: new Map([['B', -1], ['E', -1], ['A', -1], ['D', -1], ['G', -1], ['C', -1]]) },
  -7: { key: 'Cb', alterations: new Map([['B', -1], ['E', -1], ['A', -1], ['D', -1], ['G', -1], ['C', -1], ['F', -1]]) },
};

4.6 业务层集成

4.6.1 切换接口

class JianpuRenderer {
  private config: RenderConfig;
  
  /**
   * 设置唱名模式
   * @param mode 'movable' | 'fixed'
   */
  setSolfegeMode(mode: SolfegeMode): void {
    this.config.solfegeMode = mode;
    this.rerender();  // 触发重新渲染
  }
  
  /**
   * 获取当前唱名模式
   */
  getSolfegeMode(): SolfegeMode {
    return this.config.solfegeMode;
  }
}

4.6.2 业务层使用示例

// 业务层切换开关
function onNotationModeChange(mode: 'staff' | 'jianpu-movable' | 'jianpu-fixed') {
  switch (mode) {
    case 'staff':
      // 切换到五线谱渲染器
      staffRenderer.render(score);
      break;
    case 'jianpu-movable':
      // 简谱 + 首调
      jianpuRenderer.setSolfegeMode('movable');
      jianpuRenderer.render(score);
      break;
    case 'jianpu-fixed':
      // 简谱 + 固定调
      jianpuRenderer.setSolfegeMode('fixed');
      jianpuRenderer.render(score);
      break;
  }
}

4.7 调号显示规则

模式 调号显示格式 示例(G大调)
首调 1={主音} 1=G
固定调 1=C(可选显示原调) 1=C1=C (G大调)

4.8 实现优先级

优先级 功能 说明
P0 固定调渲染 当前已实现,保持不变
P0 首调渲染 核心功能,需要实现
P1 运行时模式切换 业务层切换不重新解析
P2 调号变化处理 乐曲中间转调时的处理
P2 小调支持 自然小调、和声小调等

5. MusicXML元素覆盖清单

5.1 元素分类与优先级

优先级 分类 包含元素 状态
P0 结构 score-partwise, part, measure 🟡 部分
P0 音符 note, pitch, rest, duration, type 🟡 部分
P0 属性 attributes, divisions, key, time, clef 🟡 部分
P1 小节线 barline, bar-style, repeat, ending 🟢 已实现
P1 连接 tie, slur, beam 🟡 部分
P1 歌词 lyric, syllabic, text 🟡 部分
P2 方向 direction, words, dynamics, wedge, metronome 🔴 待实现
P2 记号 articulations, ornaments, fermata 🔴 待实现
P2 装饰音 grace, cue 🟡 部分
P3 和弦 harmony, root, kind, bass 🔴 待实现
P3 高级 figured-bass, print, sound 🔴 待实现

5.2 详细元素清单

结构元素

元素 描述 状态
<score-partwise> 按声部组织的乐谱 🟢
<score-timewise> 按时间组织的乐谱 🔴
<work> 作品信息 🟡
<identification> 标识信息 🟡
<defaults> 默认设置 🔴
<credit> 版权信息 🔴
<part-list> 声部列表 🟡
<part> 声部 🟢
<measure> 小节 🟢

音符元素

元素 描述 状态
<note> 音符 🟢
<pitch> 音高 🟢
<rest> 休止符 🟢
<unpitched> 无音高 🔴
<duration> 时值 🟢
<type> 音符类型 🟢
<dot> 附点 🟢
<accidental> 临时升降号 🟡
<time-modification> 连音时值修改 🔴
<stem> 符干 🔴
<notehead> 符头 🔴
<beam> 符杠 🔴
<notations> 记号容器 🟡
<lyric> 歌词 🟢
<chord> 和弦标记 🟡
<grace> 装饰音 🟡
<cue> 提示音符 🔴

Notations子元素

元素 描述 状态
<tied> 连音线 🟡
<slur> 圆滑线 🟡
<tuplet> 连音符 🟡
<glissando> 滑音 🔴
<slide> 滑奏 🔴
<ornaments> 装饰音记号 🔴
<articulations> 演奏技法 🟡
<dynamics> 力度 🟡
<fermata> 延音记号 🟡
<arpeggiate> 琶音 🔴
<technical> 技术指示 🔴

Articulations子元素

元素 描述 状态
<accent> 重音 🔴
<strong-accent> 强重音 🔴
<staccato> 断奏 🟡
<tenuto> 保持音 🔴
<detached-legato> 断连 🔴
<staccatissimo> 顿音 🔴
<spiccato> 跳弓 🔴
<breath-mark> 呼吸记号 🔴
<caesura> 停顿 🔴

Ornaments子元素

元素 描述 状态
<trill-mark> 颤音 🔴
<turn> 回音 🔴
<delayed-turn> 延迟回音 🔴
<inverted-turn> 逆回音 🔴
<mordent> 波音 🔴
<inverted-mordent> 逆波音 🔴
<schleifer> 滑音 🔴
<tremolo> 震音 🔴

Direction元素

元素 描述 状态
<direction> 方向标记容器 🔴
<direction-type> 方向类型 🔴
<words> 文字 🔴
<dynamics> 力度 🔴
<wedge> 渐强渐弱 🔴
<metronome> 速度标记 🟡
<octave-shift> 八度移位 🔴
<pedal> 踏板 🔴
<segno> 反复记号 🔴
<coda> 尾声记号 🔴
<dashes> 虚线 🔴
<bracket> 括号 🔴
<rehearsal> 排练号 🔴

Barline元素

元素 描述 状态
<barline> 小节线 🟢
<bar-style> 线型 🟢
<repeat> 反复 🟢
<ending> 跳房子 🟢
<fermata> 延音记号 🟡

Attributes元素

元素 描述 状态
<divisions> 时值单位 🟢
<key> 调号 🟢
<time> 拍号 🟢
<clef> 谱号 🔴
<staves> 谱表数 🔴
<transpose> 移调 🔴

Harmony元素

元素 描述 状态
<harmony> 和弦符号 🔴
<root> 根音 🔴
<kind> 和弦类型 🔴
<bass> 低音 🔴
<degree> 和弦变化 🔴

图例

  • 🟢 已实现
  • 🟡 部分实现
  • 🔴 待实现

6. 重构路线图

阶段一:核心数据模型与解析器(基础)

目标:建立完整的通用数据模型和MusicXML解析器

任务清单

  1. 创建 src/music-core/ 目录结构
  2. 实现所有核心数据模型(Score, Part, Measure, Note等)
  3. 实现MusicXML解析器,完整解析所有P0/P1元素
  4. 单元测试覆盖

产出

  • 能够解析任何MusicXML文件
  • 生成完整的通用数据模型
  • 不丢失任何信息

验收标准

  • 所有P0元素完整解析
  • 所有P1元素完整解析
  • 测试覆盖率 > 80%
  • 能解析项目中所有测试XML文件

阶段二:简谱渲染器重构

目标:将现有简谱渲染器迁移到新架构

任务清单

  1. 创建 src/renderers/jianpu/ 目录
  2. 实现通用模型到简谱模型的转换器
  3. 迁移现有绘制逻辑
  4. 保持现有API兼容

产出

  • 简谱渲染器基于通用数据模型工作
  • 保持现有功能不变
  • API向后兼容

验收标准

  • 现有所有功能正常
  • visual-test.html测试通过
  • 业务层代码无需修改

阶段三:五线谱渲染器(并行开发)

目标:实现五线谱渲染器

任务清单

  1. 创建 src/renderers/staff/ 目录
  2. 实现五线谱布局引擎
  3. 实现各类绘制器(谱表、符头、符干、符杠等)
  4. 实现记号和方向标记渲染

产出

  • 完整的五线谱渲染能力
  • 与简谱共享数据源

阶段四:完善与优化

目标:覆盖所有MusicXML元素,优化性能

任务清单

  1. 实现所有P2/P3元素
  2. 性能优化
  3. 完善文档
  4. 边缘情况处理

7. 目录结构规划

最终目录结构

src/
├── music-core/                 # 【新增】通用核心层
│   ├── models/                 # 数据模型
│   │   ├── Score.ts
│   │   ├── Part.ts
│   │   ├── Measure.ts
│   │   ├── Note.ts
│   │   ├── Pitch.ts
│   │   ├── Duration.ts
│   │   ├── Attributes.ts
│   │   ├── Direction.ts
│   │   ├── Notations.ts
│   │   ├── Barline.ts
│   │   ├── Harmony.ts
│   │   └── index.ts
│   ├── parser/                 # 解析器
│   │   ├── MusicXMLParser.ts   # 主解析器
│   │   ├── NoteParser.ts       # 音符解析
│   │   ├── DirectionParser.ts  # 方向解析
│   │   ├── AttributesParser.ts # 属性解析
│   │   └── index.ts
│   ├── utils/                  # 工具函数
│   │   ├── PitchUtils.ts
│   │   ├── DurationUtils.ts
│   │   └── index.ts
│   └── index.ts
│
├── renderers/                  # 【重组】渲染层
│   ├── common/                 # 共享渲染工具
│   │   ├── SVGRenderer.ts
│   │   ├── LayoutEngine.ts
│   │   └── index.ts
│   │
│   ├── jianpu/                 # 简谱渲染器
│   │   ├── JianpuRenderer.ts
│   │   ├── converter/          # 模型转换
│   │   │   └── JianpuConverter.ts
│   │   ├── drawer/             # 绘制器
│   │   │   ├── NoteDrawer.ts
│   │   │   ├── LineDrawer.ts
│   │   │   ├── LyricDrawer.ts
│   │   │   └── ...
│   │   ├── layout/             # 布局
│   │   │   └── JianpuLayoutEngine.ts
│   │   └── index.ts
│   │
│   └── staff/                  # 【新增】五线谱渲染器
│       ├── StaffRenderer.ts
│       ├── drawer/
│       │   ├── StaveDrawer.ts
│       │   ├── NoteHeadDrawer.ts
│       │   ├── StemDrawer.ts
│       │   ├── BeamDrawer.ts
│       │   ├── ClefDrawer.ts
│       │   └── ...
│       ├── layout/
│       │   └── StaffLayoutEngine.ts
│       └── index.ts
│
├── adapters/                   # 适配器(兼容层)
│   └── OSMDCompatibilityAdapter.ts
│
└── index.ts                    # 统一导出

命名规范

  • 通用核心层:使用音乐术语,如 Note, Pitch, Measure
  • 简谱渲染器:可保留 Jianpu 前缀,如 JianpuRenderer
  • 五线谱渲染器:使用 Staff 前缀,如 StaffRenderer

8. 开发原则

8.1 代码原则

  1. 数据完整性:解析时不丢失任何MusicXML信息
  2. 类型安全:使用TypeScript严格类型检查
  3. 单一职责:每个类/函数只做一件事
  4. DRY原则:避免重复代码,提取公共逻辑
  5. 良好注释:关键逻辑必须有注释

8.2 测试原则

  1. 单元测试:每个解析器和绘制器都要有测试
  2. 可视化测试:保留visual-test.html用于人工验证
  3. 覆盖率目标:核心模块 > 80%

8.3 文档原则

  1. 及时更新:代码变更同步更新文档
  2. 清晰示例:每个API都要有使用示例
  3. 变更日志:记录重要变更

附录

A. 参考资料

B. 术语表

术语 英文 说明
简谱 Jianpu / Numbered Musical Notation 用数字1-7表示音高的记谱法
五线谱 Staff Notation 用五条线表示音高的记谱法
首调 Movable-Do / Movable Solfège 调式主音永远唱 do(1),音级相对于主音计算
固定调 Fixed-Do / Fixed Solfège C 永远唱 do(1),音名与唱名固定对应
小节 Measure / Bar 乐谱的基本时间单位
声部 Part / Voice 乐谱中的独立旋律线
时值 Duration 音符的持续时间
音高 Pitch 音符的高低
八度 Octave 音高的循环单位
调号 Key Signature 指定调性的升降号
拍号 Time Signature 指定节拍的记号
谱号 Clef 指定音高位置的记号
主音 Tonic 调式的第一音,决定调性
音级 Scale Degree 音符在调式中的相对位置(1-7)

文档结束