@@ -43,13 +43,14 @@ export class AbcElementParser {
4343 * @example
4444 * ```typescript
4545 * // 解析音符
46- * parser.parseElement('C'); // SNParserNote
46+ * parser.parseElement('C'); // SNParserNote (默认长度)
4747 * parser.parseElement('C#'); // SNParserNote (升C)
48- * parser.parseElement('C4'); // SNParserNote (四分音符C)
48+ * parser.parseElement('C4'); // SNParserNote (4倍默认长度,如默认为1/4时为全音符)
49+ * parser.parseElement('C/2'); // SNParserNote (1/2倍默认长度,如默认为1/4时为八分音符)
4950 *
5051 * // 解析休止符
51- * parser.parseElement('z'); // SNParserRest
52- * parser.parseElement('z4'); // SNParserRest (四分休止符 )
52+ * parser.parseElement('z'); // SNParserRest (默认长度)
53+ * parser.parseElement('z4'); // SNParserRest (4倍默认长度 )
5354 *
5455 * // 解析连音线
5556 * parser.parseElement('-'); // SNParserTie
@@ -99,8 +100,9 @@ export class AbcElementParser {
99100 }
100101
101102 // 4. 解析音符(带装饰符)
103+ // 支持整数(C4)、分数(C/2, C3/2)和简写(C/)三种时值表示
102104 const noteMatch = noteStr . match (
103- / ^ ( \^ + \/ ? | _ + \/ ? | = ? ) ( [ A - G a - g ] ) ( [ , ' ] * ) ( \d * ) ( \. * ) $ / ,
105+ / ^ ( \^ + \/ ? | _ + \/ ? | = ? ) ( [ A - G a - g ] ) ( [ , ' ] * ) ( \d + \/ \d + | \/ \d * | \d * ) ( \. * ) $ / ,
104106 ) ;
105107 if ( noteMatch ) {
106108 const note = this . parseNote (
@@ -159,12 +161,14 @@ export class AbcElementParser {
159161
160162 if ( timeUnit ) {
161163 const restStr = trimmed . slice ( 1 ) ;
162- const durationStr = restStr . match ( / ^ ( \d + ) / ) ?. [ 1 ] ;
164+ // 支持整数(z4)、分数(z/2, z3/2)和简写(z/)三种时值表示
165+ const durationStr = restStr . match ( / ^ ( \d + \/ \d + | \/ \d * | \d + ) / ) ?. [ 1 ] ;
163166 const dotCount = ( restStr . match ( / \. / g) || [ ] ) . length ;
164167
165- const noteValue = durationStr
166- ? 1 / parseInt ( durationStr , 10 )
167- : defaultNoteLength || 1 / 4 ;
168+ const noteValue = this . parseDurationString (
169+ durationStr ,
170+ defaultNoteLength ,
171+ ) ;
168172
169173 const dottedNoteValue = this . calculateDottedNoteValue (
170174 noteValue ,
@@ -206,9 +210,10 @@ export class AbcElementParser {
206210 // 解析时值
207211 let duration : number ;
208212 if ( timeUnit ) {
209- const noteValue = durationStr
210- ? 1 / parseInt ( durationStr , 10 )
211- : defaultNoteLength || 1 / 4 ;
213+ const noteValue = this . parseDurationString (
214+ durationStr ,
215+ defaultNoteLength ,
216+ ) ;
212217
213218 const dotCount = ( trimmed . match ( / \. / g) || [ ] ) . length ;
214219 const dottedNoteValue = this . calculateDottedNoteValue (
@@ -257,6 +262,54 @@ export class AbcElementParser {
257262 }
258263 }
259264
265+ /**
266+ * 解析时值字符串
267+ *
268+ * 支持三种格式:
269+ * - 整数:4 表示 4倍默认长度(如 C4 表示全音符)
270+ * - 分数:3/2 表示 3/2倍默认长度
271+ * - 简写分数:/ 或 /2 表示 1/2倍默认长度
272+ *
273+ * @param durationStr - 时值字符串(如 "4", "/2", "3/2", "/")
274+ * @param defaultNoteLength - 默认音符长度
275+ * @returns 音符时值(相对于全音符的比例)
276+ */
277+ private parseDurationString (
278+ durationStr : string | undefined ,
279+ defaultNoteLength ?: number ,
280+ ) : number {
281+ const defaultLength = defaultNoteLength || 1 / 4 ;
282+
283+ if ( ! durationStr ) {
284+ return defaultLength ;
285+ }
286+
287+ // 处理分数形式:3/2, /2, /
288+ if ( durationStr . includes ( '/' ) ) {
289+ if ( durationStr === '/' ) {
290+ // / 简写表示 1/2
291+ return defaultLength * 0.5 ;
292+ }
293+
294+ const parts = durationStr . split ( '/' ) ;
295+ if ( parts [ 0 ] === '' ) {
296+ // /2 形式:表示默认长度的 1/2
297+ // 例如:L:1/4 时,/2 = (1/4) × (1/2) = 1/8(八分音符)
298+ const denominator = parseInt ( parts [ 1 ] , 10 ) ;
299+ return defaultLength / denominator ;
300+ } else {
301+ // 3/2 形式表示 3/2
302+ const numerator = parseInt ( parts [ 0 ] , 10 ) ;
303+ const denominator = parseInt ( parts [ 1 ] , 10 ) ;
304+ return defaultLength * ( numerator / denominator ) ;
305+ }
306+ }
307+
308+ // 处理整数形式:4 表示 4倍默认长度
309+ const multiplier = parseInt ( durationStr , 10 ) ;
310+ return defaultLength * multiplier ;
311+ }
312+
260313 /**
261314 * 计算带附点的音符时值
262315 *
0 commit comments