File tree Expand file tree Collapse file tree 4 files changed +51
-3
lines changed
packages/simple-notation/src/data Expand file tree Collapse file tree 4 files changed +51
-3
lines changed Original file line number Diff line number Diff line change @@ -73,11 +73,21 @@ export interface SNVoiceMeta {
7373
7474export type SNVoiceMetaClef = 'treble' | 'bass' | 'alto' | 'tenor' ;
7575
76+ /** 小节级别的歌词信息 */
77+ export interface SNMeasureLyric {
78+ /** 歌词段号(0 = w:, 1+ = W:) */
79+ verse : number ;
80+ /** 该小节的所有歌词音节 */
81+ syllables : string [ ] ;
82+ }
83+
7684export interface SNMeasureMeta {
7785 annotations ?: SNAnnotation [ ] ;
7886 chords ?: SNParserChord [ ] ;
7987 barline ?: SNBarline [ ] ;
8088 /** 行内声部标记 [V:数字],表示从该小节开始属于指定声部 */
8189 voiceId ?: string ;
90+ /** 小节级别的歌词(支持多段歌词) */
91+ lyrics ?: SNMeasureLyric [ ] ;
8292 [ key : string ] : unknown ;
8393}
Original file line number Diff line number Diff line change @@ -294,10 +294,11 @@ export class AbcParser extends BaseParser<SNAbcInput> {
294294 // 提取歌词
295295 const lyricLines = this . lyricParser . extractLyricLines ( measuresContent ) ;
296296
297- // 移除声部标记和歌词行
297+ // 移除声部标记、歌词行和注释行
298298 const musicContent = measuresContent
299299 . replace ( / \[ \s * V : \s * \d + \s * \] / g, '' )
300300 . replace ( / ^ \s * [ w W ] : \s * .* $ / gim, '' )
301+ . replace ( / ^ \s * % .* $ / gim, '' ) // 移除注释行
301302 . trim ( ) ;
302303
303304 // 分割小节
Original file line number Diff line number Diff line change @@ -231,10 +231,11 @@ export class AbcLyricParser {
231231 let measureCountBefore = 0 ;
232232 let musicLineIndex = lineIndex - 1 ;
233233
234- // 向前查找最近的音乐行
234+ // 向前查找最近的音乐行(跳过歌词行、注释行和空行)
235235 while (
236236 musicLineIndex >= 0 &&
237237 ( / ^ \s * [ w W ] : / i. test ( lines [ musicLineIndex ] ) ||
238+ / ^ \s * % / . test ( lines [ musicLineIndex ] ) ||
238239 ! lines [ musicLineIndex ] . trim ( ) )
239240 ) {
240241 musicLineIndex -- ;
@@ -243,7 +244,12 @@ export class AbcLyricParser {
243244 // 统计之前所有音乐行的小节数
244245 for ( let i = 0 ; i < musicLineIndex ; i ++ ) {
245246 const prevLine = lines [ i ] ;
246- if ( ! / ^ \s * [ w W ] : / i. test ( prevLine ) && prevLine . trim ( ) ) {
247+ // 跳过歌词行、注释行和空行
248+ if (
249+ ! / ^ \s * [ w W ] : / i. test ( prevLine ) &&
250+ ! / ^ \s * % / . test ( prevLine ) &&
251+ prevLine . trim ( )
252+ ) {
247253 const lineWithoutRepeats = prevLine
248254 . replace ( / \| : / g, '' )
249255 . replace ( / : \| / g, '' ) ;
Original file line number Diff line number Diff line change @@ -101,6 +101,11 @@ export class AbcMeasureParser {
101101 measureMeta . voiceId = measureVoiceId ;
102102 }
103103
104+ // 将该小节的歌词信息添加到 meta 中(支持多段歌词)
105+ if ( lyricsForMeasure && lyricsForMeasure . length > 0 ) {
106+ measureMeta . lyrics = this . organizeLyricsByVerse ( lyricsForMeasure ) ;
107+ }
108+
104109 // 如果小节数据中包含行内调号标记,将其存储到小节的 props 中
105110 if ( keySignature ) {
106111 measure . props = {
@@ -218,6 +223,32 @@ export class AbcMeasureParser {
218223 return elements ;
219224 }
220225
226+ /**
227+ * 将歌词按 verse 组织(用于存储在小节 meta 中)
228+ *
229+ * @param lyricsForMeasure - 该小节的所有歌词信息
230+ * @returns 按 verse 分组的歌词数组
231+ */
232+ private organizeLyricsByVerse ( lyricsForMeasure : LyricInfo [ ] ) : Array < {
233+ verse : number ;
234+ syllables : string [ ] ;
235+ } > {
236+ // 按 verse 分组
237+ const verseMap = new Map < number , string [ ] > ( ) ;
238+
239+ for ( const lyric of lyricsForMeasure ) {
240+ if ( ! verseMap . has ( lyric . verse ) ) {
241+ verseMap . set ( lyric . verse , [ ] ) ;
242+ }
243+ verseMap . get ( lyric . verse ) ! . push ( lyric . syllable ) ;
244+ }
245+
246+ // 转换为数组并排序
247+ return Array . from ( verseMap . entries ( ) )
248+ . sort ( ( a , b ) => a [ 0 ] - b [ 0 ] )
249+ . map ( ( [ verse , syllables ] ) => ( { verse, syllables } ) ) ;
250+ }
251+
221252 /**
222253 * 将歌词附加到元素
223254 */
You can’t perform that action at this time.
0 commit comments