All files / jianpu-renderer/models JianpuNote.ts

0% Statements 0/285
0% Branches 0/1
0% Functions 0/1
0% Lines 0/285

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
/**
 * 简谱音符数据模型
 * 
 * @description 定义简谱渲染所需的音符数据结构
 */

/** 升降号枚举 */
export enum Accidental {
  Sharp = 'sharp',
  Flat = 'flat',
  Natural = 'natural',
}

/**
 * 歌词信息接口
 */
export interface JianpuLyric {
  /** 歌词文本 */
  text: string;
  /** 歌词索引(0=第1遍,1=第2遍...) */
  index: number;
  /** 音节类型(single单音节, begin开始, middle中间, end结束) */
  syllabic?: 'single' | 'begin' | 'middle' | 'end';
}

/**
 * 演奏技法类型
 */
export type ArticulationType = 'staccato' | 'accent' | 'tenuto' | 'staccatissimo' | 'fermata';

/**
 * 装饰音记号类型
 */
export type OrnamentType = 'trill' | 'mordent' | 'inverted-mordent' | 'turn' | 'tremolo';

/**
 * 连音符信息
 */
export interface TupletInfo {
  /** 连音符类型(如3=三连音,5=五连音) */
  actualNotes: number;
  /** 正常音符数量 */
  normalNotes: number;
  /** 在连音符组中的位置(0=第一个,1=中间,2=最后一个) */
  position: 'start' | 'middle' | 'end';
  /** 是否显示数字 */
  showNumber: boolean;
  /** 是否显示括号 */
  showBracket: boolean;
}

/**
 * 延音线信息
 */
export interface TieInfo {
  /** 延音线类型 */
  type: 'start' | 'stop' | 'continue';
  /** 关联的音符ID */
  linkedNoteId?: string;
}

/**
 * 连线(圆滑线)信息
 */
export interface SlurInfo {
  /** 连线类型 */
  type: 'start' | 'stop' | 'continue';
  /** 连线编号(用于多条连线的匹配) */
  number: number;
}

/**
 * 装饰音组信息
 */
export interface GraceNoteGroupInfo {
  /** 装饰音列表(音高数组) */
  notes: Array<{
    pitch: number;
    octave: number;
    accidental?: 'sharp' | 'flat' | 'natural';
  }>;
  /** 是否有斜杠(短倚音) */
  slash: boolean;
}

/**
 * 修饰符集合接口
 */
export interface JianpuModifiers {
  // ===== 演奏技法 =====
  /** 演奏技法列表 */
  articulations: ArticulationType[];
  
  // ===== 装饰音记号 =====
  /** 装饰音记号列表 */
  ornaments: OrnamentType[];
  
  // ===== 连音符 =====
  /** 连音符信息 */
  tuplet?: TupletInfo;
  
  // ===== 延音线和连线 =====
  /** 延音线(同音高连接) */
  tie?: TieInfo;
  /** 连线/圆滑线(不同音高连接) */
  slur?: SlurInfo;
  
  // ===== 装饰音组 =====
  /** 前置装饰音组(倚音等) */
  graceNotesBefore?: GraceNoteGroupInfo;
  
  // ===== 力度记号 =====
  /** 力度记号(如p, f, mf等) */
  dynamic?: string;
  
  // ===== 其他 =====
  /** 延长记号(fermata)- 也包含在articulations中 */
  hasFermata: boolean;
  /** 琶音记号 */
  hasArpeggio: boolean;
}

export interface JianpuNote {
  // ===== 基础属性 =====
  /** 音符唯一ID(用于生成DOM id: vf-{id}) */
  id: string;
  
  /** 音高:1-7表示do re mi fa sol la si,0表示休止符 */
  pitch: number;
  
  /** 八度偏移:0=中音,1=高音,-1=低音,可累加(2=高高音) */
  octave: number;
  
  /** 时值(以四分音符为单位,如0.5=八分音符,1.0=四分音符,2.0=二分音符) */
  duration: number;
  
  // ===== 修饰符 =====
  /** 升降号 */
  accidental?: 'sharp' | 'flat' | 'natural';
  
  /** 附点数量(0, 1, 2) */
  dots: number;
  
  // ===== 时间信息 =====
  /** 在小节中的相对时间戳(以四分音符为单位,从0开始) */
  timestamp: number;
  
  /** 绝对开始时间(秒) */
  startTime: number;
  
  /** 绝对结束时间(秒) */
  endTime: number;
  
  // ===== 位置信息(由布局引擎计算) =====
  /** X坐标(像素) */
  x: number;
  
  /** Y坐标(像素) */
  y: number;
  
  /** 音符占据的宽度(像素) */
  width: number;
  
  /** 音符高度(像素) */
  height: number;
  
  // ===== 声部和小节信息 =====
  /** 所在声部索引(0开始) */
  voiceIndex: number;
  
  /** 所在小节索引(0开始) */
  measureIndex: number;
  
  // ===== 渲染标记 =====
  /** 是否是休止符 */
  isRest: boolean;
  
  /** 是否是装饰音 */
  isGraceNote: boolean;
  
  /** 是否是顿音(staccato) */
  isStaccato: boolean;
  
  /** 是否属于连音符组(tuplet) */
  isPartOfTuplet?: boolean;
  
  // ===== 修饰符详细信息 =====
  /** 修饰符集合 */
  modifiers: JianpuModifiers;
  
  // ===== 歌词信息 =====
  /** 歌词数组(支持多遍歌词,索引从0开始,对应第1遍、第2遍...) */
  lyrics: JianpuLyric[];
  
  // ===== OSMD兼容数据(用于业务功能) =====
  osmdCompatible: {
    /** 原始OSMD Note对象引用 */
    noteElement: any;
    
    /** SVG元素引用信息 */
    svgElement: {
      attrs: { id: string };
      modifiers?: any[];
    };
    
    /** 半音值(用于MIDI和评测) */
    halfTone: number;
    
    /** 音频频率(Hz,用于评测) */
    frequency: number;
    
    /** 实际音高(用于指法显示) */
    realKey?: number;
  };
}

/** 创建音符时的可选参数 */
export type CreateNoteOptions = Partial<Omit<JianpuNote, 'id' | 'osmdCompatible' | 'modifiers'>> & {
  modifiers?: Partial<JianpuModifiers>;
};

/** ID计数器 */
let noteIdCounter = 0;

/**
 * 创建默认修饰符对象
 */
export function createDefaultModifiers(): JianpuModifiers {
  return {
    articulations: [],
    ornaments: [],
    hasFermata: false,
    hasArpeggio: false,
  };
}

/**
 * 创建默认音符
 * @param options 可选的音符属性
 * @returns 新的音符对象
 */
export function createDefaultNote(options: CreateNoteOptions = {}): JianpuNote {
  const id = `note-${++noteIdCounter}`;
  const { modifiers: modifiersOptions, ...restOptions } = options;
  
  return {
    id,
    pitch: 1,
    octave: 0,
    duration: 1,
    accidental: undefined,
    dots: 0,
    timestamp: 0,
    startTime: 0,
    endTime: 0,
    x: 0,
    y: 0,
    width: 0,
    height: 0,
    voiceIndex: 0,
    measureIndex: 0,
    isRest: false,
    isGraceNote: false,
    isStaccato: false,
    modifiers: {
      ...createDefaultModifiers(),
      ...modifiersOptions,
    },
    lyrics: [],
    osmdCompatible: {
      noteElement: null,
      svgElement: { attrs: { id } },
      halfTone: 60,
      frequency: 261.63, // C4
    },
    ...restOptions,
  };
}

/**
 * 重置音符ID计数器(用于测试)
 */
export function resetNoteIdCounter(): void {
  noteIdCounter = 0;
}