tests: replace copyrighted lyrics with placeholder text

The align, transcribe, lyrics and notes tests quoted lines from published
songs, and the UltraStar parser fixtures were complete community charts of
commercial songs. Replace them with counting words and two hand-written
charts that keep what the parser tests rely on: CRLF line endings,
Latin-1 bytes, a decimal-comma BPM, freestyle/golden/rap notes, negative
pitch and a two-number line break.
This commit is contained in:
2026-10-09 17:17:02 -07:00
parent 7a5696a306
commit 766c9f0d98
9 changed files with 107 additions and 1016 deletions
+36
View File
@@ -0,0 +1,36 @@
#TITLE:Placeholder Chart
#ARTIST:Test Artist
#LANGUAGE:English
#YEAR:2000
#CREATOR:songtrace tests
#GENRE:Test
#COVER:Test Artist - Placeholder Chart [CO].jpg
#MP3:Test Artist - Placeholder Chart.mp3
#VIDEO:Test Artist - Placeholder Chart.mp4
#BPM:217,98
#GAP:4880
#MEDLEYSTARTBEAT:113
#MEDLEYENDBEAT:240
: 0 3 16 ah
: 5 3 20 ah
: 11 3 23 la
: 17 3 16 la
: 22 28 15 ah
: 51 2 16 doo
: 54 31 16 ~
- 95
F 113 3 18 la
F 117 3 21 la
F 121 3 21 la
* 125 3 21 doo
* 129 12 19 ~
- 150
: 155 4 12 Na
R 160 4 0 na
G 165 4 0 na
: 170 6 14 la
- 190
: 194 3 16 La
: 198 3 18 la
* 202 10 20 ah
E
+21
View File
@@ -0,0 +1,21 @@
#TITLE:Café Placeholder
#ARTIST:Tést Artist
#LANGUAGE:English
#GENRE:Test
#YEAR:2000
#MP3:Tést Artist - Café Placeholder.mp3
#CREATOR:songtrace tests
#BPM:256
#GAP:49673
: 0 4 2 Doo
: 6 4 4 doo
: 12 8 5 laé
- 28 30
: 32 4 0 Na
: 38 4 -2 na
* 44 12 -9 ah
- 64
F 70 4 3 La
: 76 6 -5 la
: 84 10 -9 ~
E
-472
View File
@@ -1,472 +0,0 @@
#TITLE:Sanctuary
#ARTIST:Utada Hikaru
#LANGUAGE:English
#YEAR:2009
#CREATOR:Subkei
#GENRE:J-Pop
#COVER:Utada Hikaru - Sanctuary [CO].jpg
#MP3:Utada Hikaru - Sanctuary.mp3
#VIDEO:Utada Hikaru - Sanctuary.mp4
#BPM:217,98
#GAP:4880
#MEDLEYSTARTBEAT:2471
#MEDLEYENDBEAT:2990
: 0 3 16 ah
: 5 3 20 ah
: 11 3 23 ah
: 17 3 16 ah
: 22 28 15 ah
: 51 2 16 ah
: 54 31 16 ~
- 95
F 113 3 18 wonk
F 117 3 21 uoy
F 121 3 21 naht
F 125 3 21 snoit
F 129 3 21 cef
F 133 3 18 fa
F 137 3 18 erom
F 144 2 18 deen
F 147 4 18 I
- 180
: 425 3 16 In
: 429 3 21 you
: 433 2 20 ~
: 436 4 16 ~
: 441 2 18 and
: 445 4 20 I
: 451 4 18 ~
- 459
: 461 3 16 there's
: 465 3 16 a
: 469 5 20 new
: 475 4 23 land
: 481 4 16 ~
: 487 27 15 ~
: 515 4 16 ~
: 521 17 16 ~
: 539 1 18 ~
: 541 3 20 ~
- 547
: 549 1 16 An
: 551 2 18 ~
: 556 3 18 gels
: 560 4 18 in
: 565 7 20 flight
: 573 2 18 ~
- 576
F 578 2 18 wonk
F 581 3 21 uoy
F 585 3 21 naht
F 589 3 21 snoit
F 593 3 21 cef
F 597 3 18 fa
F 601 3 18 erom
F 608 2 18 deen
F 611 3 18 I
- 615
: 617 1 16 My
: 620 1 21 sanc
: 624 2 20 tu
: 628 4 16 a
: 634 8 18 ry
- 646
: 648 3 16 my
: 653 1 21 sanc
: 657 2 20 tu
: 661 3 16 a
: 665 4 16 ry
: 670 2 18 ~
F 674 4 13 yeah
- 680
: 682 2 16 Where
: 686 1 21 fears
: 689 1 20 ~
: 691 5 16 ~
: 697 1 18 and
* 701 4 20 lies
* 707 4 18 ~
- 715
: 717 3 16 melt
: 721 3 16 a
: 725 4 20 way
: 732 4 23 ~
: 737 3 16 ~
: 742 28 15 ~
: 771 3 16 ~
: 775 19 16 ~
: 795 1 18 ~
: 797 3 20 ~
- 804
: 806 1 16 Mu
: 808 2 18 ~
: 812 3 18 sic
: 817 3 18 will
: 822 7 20 tie
: 830 2 18 ~
- 833
F 835 1 18 wonk
F 837 3 21 uoy
F 841 3 21 naht
F 845 3 21 snoit
F 849 3 21 cef
F 853 3 18 fa
F 857 3 18 erom
F 864 2 18 deen
F 867 3 18 I
- 871
: 873 2 16 What's
: 877 1 21 left
: 881 1 20 of
: 884 3 16 me
: 888 1 16 ~
: 890 7 18 ~
- 903
: 905 2 16 What's
: 909 1 21 left
: 913 1 20 of
: 916 3 16 me
: 921 2 16 ~
: 925 3 18 ~
: 933 24 13 now?
- 986
: 1069 3 13 I
: 1074 6 16 watch
: 1082 3 16 you
: 1087 9 18 ~
- 1102
: 1105 3 18 fast
: 1109 3 20 ~
* 1117 2 13 a
* 1122 5 16 sleep
: 1129 3 18 ~
: 1133 15 16 ~
- 1162
: 1189 3 20 All
: 1193 6 21 ~
: 1201 3 18 I
: 1205 6 20 ~
: 1213 5 16 fear
: 1220 20 13 ~
- 1246
: 1249 2 11 means
: 1255 7 13 no
: 1266 5 18 thing
: 1273 41 20 ~
- 1319
: 1321 2 16 In
: 1325 2 21 you
: 1328 2 20 ~
: 1332 3 16 ~
: 1337 2 18 and
: 1341 4 20 I
: 1347 4 18 ~
- 1355
: 1357 3 16 there's
: 1361 3 16 a
* 1365 5 20 new
: 1371 4 23 land
: 1377 4 16 ~
: 1383 27 15 ~
: 1411 4 16 ~
: 1417 17 16 ~
: 1435 1 18 ~
: 1437 3 20 ~
- 1442
: 1444 1 16 An
: 1446 2 18 ~
: 1452 3 18 gels
: 1457 3 18 in
: 1462 6 20 flight
: 1469 2 18 ~
- 1472
F 1474 1 18 wonk
F 1476 3 21 uoy
F 1480 3 21 naht
F 1484 3 21 snoit
F 1488 3 21 cef
F 1492 3 18 fa
F 1496 3 18 erom
F 1503 2 18 deen
F 1506 3 18 I
- 1511
: 1513 1 16 My
: 1516 1 21 sanc
: 1520 2 20 tu
: 1524 4 16 a
: 1530 8 18 ry
- 1541
: 1543 3 16 my
: 1548 1 21 sanc
: 1552 2 20 tu
: 1556 3 16 a
: 1560 4 16 ry
: 1565 2 18 ~
F 1570 3 13 yeah
- 1575
: 1577 2 16 Where
* 1581 1 21 fears
* 1584 1 20 ~
: 1586 5 16 ~
: 1592 1 18 and
: 1596 4 20 lies
: 1602 4 18 ~
- 1611
: 1613 3 16 melt
: 1617 3 16 a
: 1621 4 20 way
: 1628 4 23 ~
: 1633 3 16 ~
: 1638 27 15 ~
: 1666 4 16 ~
: 1672 18 16 ~
: 1691 1 18 ~
: 1693 3 20 ~
- 1699
: 1701 1 16 Mu
: 1703 2 18 ~
: 1707 3 18 sic
: 1712 3 18 will
: 1717 7 20 tie
: 1725 2 18 ~
- 1728
F 1730 2 18 wonk
F 1733 3 21 uoy
F 1737 3 21 naht
F 1741 3 21 snoit
F 1745 3 21 cef
F 1749 3 18 fa
F 1753 3 18 erom
F 1760 2 18 deen
F 1763 3 18 I
- 1767
: 1769 2 16 What's
: 1773 1 21 left
: 1777 1 20 of
: 1780 3 16 me
: 1785 1 16 ~
: 1787 7 18 ~
- 1798
: 1800 2 16 What's
: 1804 1 21 left
* 1808 1 20 of
* 1811 3 16 me
: 1815 1 16 ~
: 1817 7 18 ~
- 1831
F 1834 2 21 snwod
F 1837 2 20 dna
F 1841 3 18 spu
F 1845 3 20 y
F 1849 3 20 nam
F 1853 3 18 os
- 1870
: 1897 3 20 My
* 1901 2 20 heart's
: 1904 1 18 a
: 1906 3 20 bat
: 1910 3 21 tle
: 1914 5 21 ground
- 1926
F 1929 2 16 snoit
F 1932 6 16 om
F 1941 2 18 e
F 1945 2 18 ~
F 1948 2 18 eurt
F 1952 2 18 deen
F 1955 2 16 I
- 1971
F 1987 3 18 wonk
F 1991 3 21 uoy
F 1995 3 21 naht
F 1999 3 21 snoit
F 2003 3 21 cef
F 2007 3 18 fa
F 2011 3 18 erom
F 2018 2 18 deen
F 2021 3 18 I
- 2038
F 2059 2 16 snoit
F 2062 6 16 om
F 2070 3 18 e
F 2075 2 18 ~
F 2078 2 18 eurt
F 2082 2 18 deen
F 2085 2 16 I
- 2116
: 2219 3 13 You
: 2224 6 16 show
: 2232 3 16 me
: 2237 7 18 ~
- 2252
: 2255 3 18 how
: 2259 3 20 ~
: 2267 2 13 to
: 2272 5 16 see
- 2282
* 2284 3 13 That
* 2289 6 16 no
* 2297 3 16 thing
* 2302 5 18 ~
- 2313
: 2316 3 13 is
: 2321 3 16 whole
: 2325 2 15 ~
: 2328 2 13 and
: 2331 1 13 ~
: 2333 2 15 ~
- 2338
: 2340 3 20 no
: 2344 6 21 ~
: 2352 3 18 ~
: 2356 6 20 ~
: 2364 5 16 thing
: 2371 12 13 ~
- 2395
: 2399 2 11 is
: 2403 9 13 bro
: 2416 5 18 ken
: 2423 41 20 ~
- 2470
: 2472 2 16 In
* 2476 2 21 you
* 2479 2 20 ~
* 2483 3 16 ~
: 2488 2 18 and
: 2492 4 20 I
: 2499 3 18 ~
- 2506
: 2508 3 16 There's
: 2512 3 16 a
: 2516 5 20 new
: 2522 4 23 land
: 2528 4 16 ~
: 2534 27 15 ~
: 2562 4 16 ~
: 2568 17 16 ~
: 2586 1 18 ~
: 2588 3 20 ~
- 2593
: 2595 1 16 An
: 2597 2 18 ~
: 2603 3 18 gels
: 2608 3 18 in
: 2613 6 20 flight
: 2620 2 18 ~
- 2623
F 2625 2 18 wonk
F 2628 3 21 uoy
F 2632 3 21 naht
F 2636 3 21 snoit
F 2640 3 21 cef
F 2644 3 18 fa
F 2648 3 18 erom
F 2655 2 18 deen
F 2658 2 18 I
- 2661
: 2663 2 16 My
: 2668 1 21 sanc
: 2672 1 20 tu
: 2675 4 16 a
: 2681 7 18 ry
- 2693
: 2695 3 16 My
: 2700 1 21 sanc
: 2704 2 20 tu
: 2708 3 16 a
: 2712 4 16 ry
: 2717 2 18 ~
F 2721 4 13 yeah
- 2726
: 2728 2 16 Where
: 2732 1 21 fears
: 2735 1 20 ~
: 2737 5 16 ~
: 2743 1 18 and
: 2747 4 20 lies
: 2754 3 18 ~
- 2761
: 2763 3 16 melt
: 2767 3 16 a
: 2771 4 20 way
: 2778 4 23 ~
: 2783 4 16 ~
: 2789 27 15 ~
: 2817 4 16 ~
: 2823 17 16 ~
: 2841 1 18 ~
: 2843 3 20 ~
- 2849
: 2851 1 16 Mu
: 2853 2 18 ~
: 2857 3 18 sic
* 2862 3 18 will
* 2868 7 20 tie
: 2877 2 18 ~
- 2880
F 2882 1 18 wonk
F 2884 3 21 uoy
F 2888 3 21 naht
F 2892 3 21 snoit
F 2896 3 21 cef
F 2900 3 18 fa
F 2904 3 18 erom
F 2911 2 18 deen
F 2914 2 18 I
- 2917
: 2919 2 16 What's
: 2923 1 21 left
: 2927 1 20 of
: 2930 3 16 me
: 2935 1 16 ~
: 2937 7 18 ~
- 2949
: 2951 2 16 What's
: 2955 1 21 left
* 2959 1 20 of
* 2962 3 16 me
* 2967 2 16 ~
: 2971 3 18 ~
: 2979 12 13 now?
- 3005
: 3026 2 11 uh
: 3029 2 13 ~
: 3032 3 16 ~
: 3036 8 15 ~
: 3045 4 18 ~
: 3051 4 16 ~
: 3056 3 15 ~
: 3060 3 13 ~
- 3066
: 3068 1 16 My
: 3070 2 18 ~
: 3074 12 18 fears
- 3096
: 3100 1 16 my
: 3102 2 18 ~
: 3107 3 18 lies
: 3111 2 16 ~
: 3114 1 13 ~
- 3129
* 3164 3 16 melt
* 3169 3 16 a
* 3173 4 20 way
* 3179 4 23 ~
* 3185 3 16 ~
* 3190 27 15 ~
* 3218 3 16 ~
* 3223 19 16 ~
- 3248
: 3250 4 18 ah
: 3256 126 20 ~
- 3392
F 3410 1 18 wonk
F 3412 3 21 uoy
F 3416 3 21 naht
F 3420 3 21 snoit
F 3424 3 21 cef
F 3428 3 18 fa
F 3432 3 18 erom
F 3439 2 18 deen
F 3442 3 18 I
E
-494
View File
@@ -1,494 +0,0 @@
#TITLE:Sweet Child O´Mine
#ARTIST:Guns N' Roses
#LANGUAGE:English
#GENRE:Rock
#YEAR:1987
#MP3:Guns N' Roses - Sweet Child O´Mine.mp3
#COVER:Guns N' Roses - Sweet Child O´Mine.jpg
#VIDEO:Guns N' Roses - Sweet Child O´Mine.webm
#CREATOR:sanicsip
#FIXER:Pacayaka
#BPM:256
#GAP:49673
#VIDEOGAP:0,3
#START:13
#MEDLEYSTARTBEAT:1564
#MEDLEYENDBEAT:2057
: 0 3 8 She's
: 5 2 8 got
: 8 1 8 a
: 13 2 6 smile
: 16 8 8 ~
: 25 1 6 that
: 27 2 5 it
: 32 7 6 seems
: 41 1 8 to
: 45 1 5 me
: 47 5 4 ~
: 53 1 3 ~
- 55
: 57 1 1 re
: 60 7 6 minds
: 69 2 5 me
: 73 1 1 of
: 76 1 1 child
: 79 9 6 ~
: 91 5 6 hood
- 99
: 101 3 6 mem
: 105 4 5 o
: 111 6 3 ries
: 118 1 1 ~
- 120
: 122 3 1 where
: 126 2 4 eve
: 129 5 6 ~
: 135 2 5 ~
: 138 1 1 ry
: 142 2 4 thing
: 145 4 6 ~
: 150 2 5 was
: 154 1 1 as
: 158 2 5 fresh
: 161 4 6 ~
: 166 3 5 as
: 171 1 1 the
: 174 5 6 bright
: 181 5 6 blue
: 190 8 6 sky
: 200 5 5 ~
- 222
: 258 4 8 Now
: 264 4 8 and
: 271 10 8 then
: 283 2 6 when
: 287 2 5 I
: 291 3 6 see
: 295 1 5 her
: 297 1 6 ~
: 299 2 8 ~
: 304 8 5 face
- 313
: 315 3 6 she
: 319 3 8 ~
: 324 3 6 takes
: 330 2 5 me
: 334 1 1 a
: 338 2 3 way
: 341 3 6 ~
: 345 2 5 to
: 349 3 1 that
- 354
: 356 3 6 spe
: 360 3 6 cial
: 364 2 8 ~
: 369 1 6 place
: 371 4 5 ~
: 377 2 5 and
: 381 1 6 if
: 384 2 5 I
: 389 3 6 stare
: 393 2 5 ~
: 398 3 6 too
: 402 2 5 ~
: 405 4 6 long
: 410 1 5 ~
- 415
: 417 1 1 I'd
: 420 1 1 prob
: 423 1 6 a
: 425 2 6 bly
: 429 5 6 break
: 436 5 6 down
: 442 2 6 and
* 445 1 8 ~
: 450 7 6 cry
: 458 8 5 ~
- 483
: 512 3 6 Whoah
: 517 24 8 ~
: 543 3 10 oh
: 548 2 8 ooh
: 551 3 8 whoah
: 555 1 6 ~
: 559 3 6 sweet
: 563 1 5 ~
: 567 2 5 child
: 570 3 6 ~
: 575 1 6 o'
: 578 8 6 mine
: 587 12 5 ~
- 616
: 643 1 6 Whoah
: 645 11 8 ~
: 659 7 6 oh
: 668 7 5 oh
: 677 2 5 oh
: 680 3 6 ~
: 684 3 5 ~
: 689 5 1 sweet
: 697 7 3 love
: 706 2 5 of
: 711 26 1 mine
- 771
: 1041 5 8 She's
: 1048 6 8 got
: 1056 2 6 eyes
: 1059 7 8 ~
: 1068 3 6 of
: 1073 1 5 the
: 1076 3 6 blu
: 1080 3 6 est
: 1084 2 8 ~
: 1089 11 3 skies
: 1102 5 1 as
: 1110 5 6 if
: 1118 7 6 they
- 1128
: 1130 4 6 thought
: 1135 2 5 of
: 1140 2 5 rain
: 1143 4 6 ~
: 1148 4 5 ~
- 1162
* 1171 4 8 I
: 1176 2 5 hate
: 1179 3 6 ~
: 1184 2 6 to
: 1187 3 5 ~
: 1192 2 6 look
: 1196 10 6 in
: 1208 3 6 to
: 1213 6 5 those
: 1221 2 5 eyes
: 1224 7 6 ~
: 1233 4 6 and
- 1239
: 1241 4 6 see
: 1246 1 5 ~
: 1249 3 1 an
: 1253 1 3 ounce
: 1256 5 6 ~
: 1262 1 5 ~
: 1265 2 1 of
: 1269 15 1 pain
- 1294
: 1301 3 6 Her
: 1305 2 6 hair
: 1308 5 8 ~
: 1314 2 8 re
: 1318 6 8 minds
: 1326 4 8 me
: 1331 3 6 of
: 1335 2 5 a
: 1339 6 6 warm
: 1347 2 6 safe
: 1350 3 8 ~
: 1355 6 5 place
- 1362
: 1364 6 1 where
: 1372 3 6 as
: 1376 2 5 ~
: 1380 2 1 a
: 1384 2 5 child
: 1387 7 6 ~
* 1395 3 8 I'd
: 1399 5 6 hide
: 1405 1 5 ~
: 1408 11 1 ~
- 1428
: 1431 1 6 and
: 1433 2 8 ~
: 1437 2 5 pray
: 1440 3 6 ~
: 1445 3 6 for
: 1449 2 5 the
: 1453 7 6 thun
: 1461 6 6 der
- 1471
: 1473 4 6 and
: 1478 2 5 the
: 1482 1 5 rain
: 1484 6 6 ~
- 1491
: 1493 1 5 to
: 1496 8 6 qui
: 1505 2 5 et
: 1509 2 1 ly
: 1514 1 5 pass
: 1516 6 6 ~
* 1526 2 8 me
: 1529 5 6 by
: 1535 1 5 ~
: 1538 10 1 ~
- 1558
: 1566 3 6 Whoah
: 1570 19 8 ~
: 1591 3 10 oh
: 1595 3 8 ooh
: 1600 2 8 whoah
: 1603 1 6 ~
: 1607 4 6 sweet
: 1612 1 5 ~
: 1615 3 5 child
: 1620 2 6 ~
: 1623 1 6 o'
: 1629 7 6 mine
: 1637 11 5 ~
- 1658
: 1675 2 12 Whoah
: 1678 20 13 ~
: 1699 1 12 ~
: 1701 5 13 ~
: 1711 1 8 oh
: 1713 4 10 ~
: 1719 7 8 oh
: 1727 3 8 oh
: 1731 3 10 ~
: 1736 1 8 ~
- 1737
: 1739 3 6 Sweet
: 1743 1 5 ~
: 1748 7 6 love
* 1757 2 8 of
: 1761 4 6 mine
: 1766 1 5 ~
: 1770 16 1 ~
- 1803
* 1829 8 8 Oh
* 1842 4 10 oh
* 1850 4 13 oh
* 1858 2 13 oh
* 1861 5 15 ~
* 1867 1 13 ~
- 1869
: 1871 3 13 Sweet
: 1875 1 10 ~
: 1880 1 10 child
: 1882 3 13 ~
: 1886 1 10 ~
: 1888 2 8 o'
: 1892 2 8 mine
: 1895 5 10 ~
: 1901 2 13 ~
: 1905 2 10 ~
: 1909 9 8 ~
- 1924
: 1926 2 8 Ooh
: 1929 4 10 ~
: 1934 2 8 ~
: 1937 3 5 ~
: 1942 3 8 yeah
: 1951 1 8 yeah
: 1953 5 10 ~
: 1959 2 8 ~
- 1962
: 1964 1 8 Ooh
: 1966 7 10 ~
: 1974 7 8 ~
: 1982 7 6 ~
: 1991 2 5 ooh
: 1994 4 6 ~
: 1999 1 5 ~
- 2002
: 2004 3 1 sweet
: 2010 9 3 love
: 2021 1 5 of
: 2025 1 3 mine
: 2027 1 5 ~
: 2029 2 3 ~
: 2032 25 1 ~
- 2091
: 3176 4 3 Where
: 3181 3 1 do
: 3185 2 -2 we
: 3190 8 -4 go?
- 3204
: 3207 1 1 Where
: 3209 4 3 ~
: 3214 2 1 do
: 3217 2 -2 we
: 3221 6 -4 go
: 3229 8 -6 now?
- 3239
: 3241 3 3 Where
: 3245 3 1 do
: 3249 2 -2 we
: 3253 11 -4 go?
- 3274
: 3282 1 -4 Ooh
: 3284 4 -2 ~
: 3289 3 -4 oh
: 3293 3 -2 ~
: 3297 3 -4 oh
: 3301 1 -6 ~
- 3304
: 3306 2 -2 Where
: 3309 2 -4 do
: 3312 2 -6 we
: 3316 4 -2 go
: 3321 4 -4 ~
: 3326 6 -2 ~?
- 3342
: 3355 5 -4 Oh
: 3362 7 -2 ~
: 3370 2 -2 where
: 3373 2 -2 do
: 3376 2 -2 we
: 3380 3 -4 go
: 3384 1 -6 ~
: 3387 13 -9 now?
- 3410
: 3430 1 -6 Where
: 3433 1 -2 ~
: 3435 2 -2 do
: 3439 2 1 we
: 3443 3 -4 go
: 3447 8 -2 ~?
- 3465
: 3474 7 3 Ooh
: 3483 12 1 oh
: 3497 2 3 where
: 3500 2 1 do
: 3503 2 -2 we
: 3507 6 -4 go
: 3514 13 -6 now?
- 3537
: 3558 1 -4 ay
: 3560 6 -2 ~
: 3567 1 -4 ay
: 3569 5 -2 ~
: 3575 1 -4 ay
: 3577 5 -2 ~
: 3583 1 -4 ay
: 3585 5 -2 ~
: 3591 1 -4 ay
: 3593 6 -2 ~
: 3600 1 -4 ay
: 3602 5 -2 ~
: 3608 2 -4 ay
: 3611 2 -2 ~
- 3614
: 3616 4 10 Oh
: 3623 1 13 where
: 3625 1 15 ~
: 3627 3 13 do
: 3631 3 10 we
: 3635 6 8 go
: 3643 5 6 now?
- 3649
* 3651 2 6 ah
* 3655 2 10 ah
* 3659 2 13 ah
* 3663 2 10 ah
* 3666 1 8 ah
* 3668 2 10 ah
* 3673 2 8 ah
* 3676 2 6 oh
* 3680 2 8 ah
* 3683 1 6 oh
- 3684
: 3686 1 8 where
: 3688 2 10 ~
: 3691 2 8 do
: 3695 2 6 we
: 3699 1 8 go
: 3701 11 10 ~?
- 3719
* 3722 2 13 ah
* 3725 12 15 ~
* 3738 8 13 ~
* 3748 1 10 oh
* 3752 2 15 where
* 3756 2 13 do
* 3760 2 10 we
* 3764 7 8 go
* 3772 10 6 now?
- 3792
: 3816 3 10 Where
: 3821 2 10 do
: 3825 2 13 we
: 3829 1 8 go
: 3831 4 10 ~
: 3837 1 13 ~
: 3841 2 10 ~
: 3845 1 13 ~
: 3849 1 10 ~
: 3851 1 8 ~
: 3853 1 10 ~
: 3855 1 8 ~
: 3857 1 10 ~
: 3859 1 8 ~
: 3861 2 10 ~
: 3865 2 8 ~
: 3868 3 6 ~
: 3872 3 8 ~
: 3876 2 6 ~?
- 3879
: 3881 2 10 where
: 3884 1 8 do
: 3886 1 10 ~
: 3888 2 10 we
: 3892 3 8 go
: 3897 1 6 ~
: 3901 11 3 now?
- 3922
: 3943 1 8 Where
: 3945 2 10 ~
: 3949 2 8 do
: 3953 2 6 we
: 3957 2 10 go
: 3960 2 8 ~
: 3963 10 10 ~?
- 3983
: 3990 8 10 Whoah
: 4000 3 13 ~
: 4004 3 10 ~
: 4008 3 10 where
: 4012 3 13 do
: 4017 2 10 we
: 4021 5 8 go
: 4028 10 6 now?
- 4039
* 4041 3 15 no
* 4046 3 13 no
* 4051 4 10 no
* 4057 3 9 no
* 4062 3 8 no
* 4067 3 6 no
* 4072 9 3 no
- 4087
: 4090 3 8 sweet
: 4094 2 10 ~
: 4101 3 8 child
: 4105 2 6 ~
: 4109 7 3 ~
- 4122
: 4124 3 10 sweet
: 4128 1 8 ~
: 4130 3 10 ~
: 4137 3 10 child
: 4142 2 13 ~
: 4145 4 10 ~
: 4150 2 13 ~
: 4153 4 10 ~
: 4158 1 8 ~
: 4160 2 10 ~
: 4163 2 8 ~
: 4166 3 6 ~
: 4171 2 10 ~
: 4175 2 13 ~
: 4179 3 10 ~
: 4183 4 13 ~
: 4188 4 15 ~
: 4194 2 13 ~
: 4199 1 10 ~
- 4200
: 4202 1 8 o'
: 4204 2 10 ~
: 4208 2 8 mine
: 4211 2 6 ~
: 4214 46 3 ~
: 4262 33 1 ~
: 4297 49 3 ~
E
+17 -17
View File
@@ -7,7 +7,7 @@ from songtrace.lyrics import normalize_lyrics
from songtrace.pipeline import Ctx, PipelineError, SongInput
from songtrace.stages import align
LYRICS = "she's got a smile\nthat it seems to me"
LYRICS = "it's one two three\nfour five six seven eight"
def make_ctx(tmp_path, lyrics=LYRICS, transcript=None):
@@ -15,7 +15,7 @@ def make_ctx(tmp_path, lyrics=LYRICS, transcript=None):
work.mkdir(exist_ok=True)
if transcript is None:
transcript = [{"start": 1.0, "end": 4.0,
"text": "she's got a smile that it seems to me"}]
"text": "it's one two three four five six seven eight"}]
(work / "transcript.json").write_text(json.dumps(transcript))
inp = SongInput(title="T", artist="A", lyrics_text=lyrics)
return Ctx(inp=inp, cfg=Config(whisper_url="u", device="cpu"),
@@ -49,7 +49,7 @@ def check_invariants(rows, lyrics):
def test_alignment_covers_all_words(tmp_path, monkeypatch):
rows = run_align(tmp_path, monkeypatch)
assert [r["word"] for r in rows] == \
["she's", "got", "a", "smile", "that", "it", "seems", "to", "me"]
["it's", "one", "two", "three", "four", "five", "six", "seven", "eight"]
assert rows[0]["line_index"] == 0 and rows[-1]["line_index"] == 1
starts = [r["start"] for r in rows]
assert starts == sorted(starts)
@@ -63,18 +63,18 @@ def test_mismatched_lyrics_fail(tmp_path, monkeypatch):
def test_unalignable_word_interpolated(tmp_path, monkeypatch):
lyrics = "she's got ... smile\nthat it seems to me"
lyrics = "it's one ... three\nfour five six seven eight"
rows = run_align(tmp_path, monkeypatch, lyrics)
dots = next(r for r in rows if r["word"] == "...")
assert dots["score"] == 0.0
prev = next(r for r in rows if r["word"] == "got")
nxt = next(r for r in rows if r["word"] == "smile")
prev = next(r for r in rows if r["word"] == "one")
nxt = next(r for r in rows if r["word"] == "three")
assert prev["end"] <= dots["start"] <= dots["end"] <= nxt["start"] + 1e-6
check_invariants(rows, lyrics)
def test_leading_unalignable_word(tmp_path, monkeypatch):
lyrics = "... got a smile\nthat it seems to me"
lyrics = "... one two three\nfour five six seven eight"
rows = run_align(tmp_path, monkeypatch, lyrics)
assert rows[0]["word"] == "..." and rows[0]["score"] == 0.0
assert rows[0]["start"] >= 0.0
@@ -83,7 +83,7 @@ def test_leading_unalignable_word(tmp_path, monkeypatch):
def test_trailing_unalignable_word(tmp_path, monkeypatch):
lyrics = "she's got a smile\nthat it seems to ..."
lyrics = "it's one two three\nfour five six seven ..."
rows = run_align(tmp_path, monkeypatch, lyrics)
assert rows[-1]["word"] == "..." and rows[-1]["score"] == 0.0
assert rows[-2]["end"] <= rows[-1]["start"]
@@ -91,7 +91,7 @@ def test_trailing_unalignable_word(tmp_path, monkeypatch):
def test_consecutive_unalignable_words(tmp_path, monkeypatch):
lyrics = "she's got ... ... smile\nthat it seems to me"
lyrics = "it's one ... ... three\nfour five six seven eight"
rows = run_align(tmp_path, monkeypatch, lyrics)
dots = [r for r in rows if r["word"] == "..."]
assert len(dots) == 2
@@ -113,8 +113,8 @@ def test_all_words_unalignable_in_section(tmp_path, monkeypatch):
def test_non_chronological_transcript(tmp_path, monkeypatch):
transcript = [{"start": 30.0, "end": 33.0, "text": "that it seems to me"},
{"start": 1.0, "end": 4.0, "text": "she's got a smile"}]
transcript = [{"start": 30.0, "end": 33.0, "text": "four five six seven eight"},
{"start": 1.0, "end": 4.0, "text": "it's one two three"}]
rows = run_align(tmp_path, monkeypatch, transcript=transcript)
check_invariants(rows, LYRICS)
assert len({r["start"] for r in rows}) == len(rows)
@@ -131,8 +131,8 @@ def test_inverted_section_does_not_collapse(tmp_path, monkeypatch):
def test_words_split_across_sections(tmp_path, monkeypatch):
transcript = [{"start": 1.0, "end": 4.0, "text": "she's got a smile"},
{"start": 30.0, "end": 33.0, "text": "that it seems to me"}]
transcript = [{"start": 1.0, "end": 4.0, "text": "it's one two three"},
{"start": 30.0, "end": 33.0, "text": "four five six seven eight"}]
rows = run_align(tmp_path, monkeypatch, transcript=transcript)
check_invariants(rows, LYRICS)
assert rows[3]["end"] <= 5.0
@@ -140,9 +140,9 @@ def test_words_split_across_sections(tmp_path, monkeypatch):
def test_align_drops_lines_never_sung(tmp_path, monkeypatch):
lyrics = "she's got a smile\nthat it seems to me\nbut then it fades away"
lyrics = "it's one two three\nfour five six seven eight\nnine ten eleven twelve"
transcript = [{"start": 1.0, "end": 4.0,
"text": "she's got a smile that it seems to me"}]
"text": "it's one two three four five six seven eight"}]
rows = run_align(tmp_path, monkeypatch, lyrics, transcript)
assert {r["line_index"] for r in rows} == {0, 1}
meta = json.loads((tmp_path / "w" / "align_meta.json").read_text())
@@ -151,9 +151,9 @@ def test_align_drops_lines_never_sung(tmp_path, monkeypatch):
def test_align_flags_missing_span_for_unmatched_singing(tmp_path, monkeypatch):
lyrics = "she's got a smile\nthat it seems to me"
lyrics = "it's one two three\nfour five six seven eight"
transcript = [
{"start": 1.0, "end": 4.0, "text": "she's got a smile that it seems to me"},
{"start": 1.0, "end": 4.0, "text": "it's one two three four five six seven eight"},
{"start": 20.0, "end": 23.0, "text": "completely unrelated ad lib bridge"},
]
run_align(tmp_path, monkeypatch, lyrics, transcript)
+7 -7
View File
@@ -1,22 +1,22 @@
from songtrace.lyrics import normalize_lyrics, alignment_token
RAW = """[Verse 1]
She's got a smile
It's one two three
[Chorus]
Whoa, sweet child o' mine
Take me down to 7 streets
Whoa, la la o' la
Count me up to 7 times
"""
def test_normalize_strips_sections_and_blanks():
ref = normalize_lyrics(RAW)
assert ref.lines == [
["She's", "got", "a", "smile"],
["Whoa,", "sweet", "child", "o'", "mine"],
["Take", "me", "down", "to", "7", "streets"],
["It's", "one", "two", "three"],
["Whoa,", "la", "la", "o'", "la"],
["Count", "me", "up", "to", "7", "times"],
]
assert ref.words[0] == "She's"
assert ref.words[0] == "It's"
assert ref.word_line_index == [0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2]
+9 -9
View File
@@ -78,7 +78,7 @@ def test_syllabify():
assert syllabify("reminds", "English") == ["re", "minds"]
assert syllabify("a", "English") == ["a"]
assert syllabify("xyzzy", None) == ["xyzzy"]
assert syllabify("She's", "English") == ["She's"]
assert syllabify("It's", "English") == ["It's"]
def test_syllabify_preserves_word_characters():
@@ -96,12 +96,12 @@ def test_syllabify_language_without_pyphen_dict_is_not_mishandled_as_english():
def test_build_notes_basic_line():
times, f0, conf = flat_pitch()
rows = [
{"word": "She's", "start": 1.0, "end": 1.5, "score": 0.9, "line_index": 0},
{"word": "It's", "start": 1.0, "end": 1.5, "score": 0.9, "line_index": 0},
{"word": "got", "start": 1.6, "end": 2.0, "score": 0.9, "line_index": 0},
{"word": "Now", "start": 5.0, "end": 5.5, "score": 0.9, "line_index": 1},
]
notes, breaks, warnings = build_notes(rows, times, f0, conf, "English")
assert notes[0].text == "She's" # line-initial: no leading space
assert notes[0].text == "It's" # line-initial: no leading space
assert notes[1].text == " got" # mid-line: leading space
assert notes[2].text == "Now" # new line: no leading space
assert len(breaks) == 1 and 2.0 <= breaks[0] <= 5.0
@@ -225,8 +225,8 @@ def test_build_notes_short_multisyllable_word_does_not_overlap():
def test_build_notes_never_overlaps_on_dense_input():
rng = np.random.default_rng(11)
times = np.arange(0, 40.0, 0.01)
words = ["She's", "got", "a", "beautiful", "smile", "reminds", "me",
"everything", "I", "lost", "yesterday", "oh"]
words = ["We", "count", "a", "beautiful", "number", "reminds", "us",
"everything", "we", "listed", "yesterday", "oh"]
for _ in range(8):
f0 = 220.0 * 2 ** (rng.integers(0, 12, len(times)) // 3 / 12.0)
conf = np.where(rng.random(len(times)) < 0.9, 0.9, 0.1)
@@ -244,8 +244,8 @@ def test_build_notes_starts_are_non_decreasing():
times = np.arange(0, 12.0, 0.01)
f0 = 220.0 * 2 ** (np.floor(times * 2) % 5 / 12.0) # stepped melody
conf = np.where((times % 3.0) < 2.6, 0.9, 0.1) # unvoiced gaps
words = ["She's", "got", "a", "beautiful", "smile", "that",
"reminds", "me", "of", "everything", "I", "lost"]
words = ["We", "count", "a", "beautiful", "number", "that",
"reminds", "us", "of", "everything", "we", "list"]
rows = []
for i, w in enumerate(words):
dur = 0.8 if i % 3 else 0.07 # every third word sung very fast
@@ -278,7 +278,7 @@ def test_notes_stage_writes_json(tmp_path):
times, f0, conf = flat_pitch()
np.savez(work / "pitch.npz", times=times, f0=f0, conf=conf)
(work / "alignment.json").write_text(json.dumps([
{"word": "She's", "start": 1.0, "end": 1.5, "score": 0.9, "line_index": 0},
{"word": "It's", "start": 1.0, "end": 1.5, "score": 0.9, "line_index": 0},
{"word": "gone", "start": 3.0, "end": 3.5, "score": 0.9, "line_index": 1},
]))
ctx = Ctx(inp=SongInput(title="T", artist="A", lyrics_text="x", language="English"),
@@ -287,7 +287,7 @@ def test_notes_stage_writes_json(tmp_path):
assert stage.outputs(ctx) == [work / "notes_sec.json"]
stage.run(ctx)
data = json.loads((work / "notes_sec.json").read_text())
assert [n["text"] for n in data["notes"]] == ["She's", "gone"]
assert [n["text"] for n in data["notes"]] == ["It's", "gone"]
assert data["breaks"] == [2.25]
assert data["warnings"] == []
assert TimedNote(**data["notes"][0]).pitch == 0
+12 -12
View File
@@ -18,35 +18,35 @@ def test_vocal_sections_keeps_contained_segment():
assert sections == [(0.0, 11.0)]
def test_similarity_high_for_matching_lyrics():
ref = normalize_lyrics("she's got a smile\nthat it seems to me")
segs = [seg(0, 2, "She's got a smile"), seg(2, 4, "that it seems to me")]
ref = normalize_lyrics("it's one two three\nfour five six seven eight")
segs = [seg(0, 2, "It's one two three"), seg(2, 4, "four five six seven eight")]
assert lyrics_similarity(segs, ref) > 0.9
def test_similarity_low_for_wrong_lyrics():
ref = normalize_lyrics("completely different words here entirely")
segs = [seg(0, 2, "she's got a smile that it seems to me")]
segs = [seg(0, 2, "it's one two three four five six seven eight")]
assert lyrics_similarity(segs, ref) < 0.4
def test_classify_lyrics_match_flags_extra_reference_line():
ref = normalize_lyrics("she's got a smile\nthat it seems to me\nbut then it fades")
segs = [seg(1, 4, "she's got a smile that it seems to me")]
ref = normalize_lyrics("it's one two three\nfour five six seven eight\nnine ten eleven")
segs = [seg(1, 4, "it's one two three four five six seven eight")]
dropped, missing = classify_lyrics_match(segs, ref)
assert dropped == [2]
assert missing == []
def test_classify_lyrics_match_flags_unmatched_singing():
ref = normalize_lyrics("she's got a smile\nthat it seems to me")
segs = [seg(1, 4, "she's got a smile that it seems to me"),
ref = normalize_lyrics("it's one two three\nfour five six seven eight")
segs = [seg(1, 4, "it's one two three four five six seven eight"),
seg(20, 23, "completely unrelated ad lib bridge")]
dropped, missing = classify_lyrics_match(segs, ref)
assert dropped == []
assert missing == [(19.0, 24.0)]
def test_classify_lyrics_match_keeps_partially_matched_lines():
ref = normalize_lyrics("she's got a smile\nthat it seems to me")
# line 1 heard with one word garbled ("it" -> "bit"): still a replace/equal
ref = normalize_lyrics("it's one two three\nfour five six seven eight")
# line 1 heard with one word garbled ("five" -> "fife"): still a replace/equal
# match, so the line stays matched rather than dropped
segs = [seg(1, 4, "she's got a smile that bit seems to me")]
segs = [seg(1, 4, "it's one two three four fife six seven eight")]
dropped, _ = classify_lyrics_match(segs, ref)
assert dropped == []
@@ -54,8 +54,8 @@ def test_classify_lyrics_match_scopes_missing_span_to_adlib_not_whole_section():
# matched line and an unmatched ad-lib close enough together that
# vocal_sections would merge them into one section; missing_spans must
# stay tight around the ad-lib so the review marks the right place.
ref = normalize_lyrics("she's got a smile\nthat it seems to me")
segs = [seg(1, 4, "she's got a smile that it seems to me"),
ref = normalize_lyrics("it's one two three\nfour five six seven eight")
segs = [seg(1, 4, "it's one two three four five six seven eight"),
seg(6, 8, "completely unrelated ad lib")]
sections = vocal_sections(segs)
assert sections == [(0.0, 9.0)] # confirms the two segments do merge
+5 -5
View File
@@ -10,8 +10,8 @@ def load(name):
return (FIXTURES / name).read_text(encoding="utf-8", errors="replace")
def test_parse_headers_and_notes():
song = parse_ultrastar(load("sanctuary.txt"))
assert get_header(song, "artist") == "Utada Hikaru"
song = parse_ultrastar(load("chart_crlf.txt"))
assert get_header(song, "artist") == "Test Artist"
assert parse_bpm(get_header(song, "BPM")) == 217.98
first = song.body[0]
assert first == Note(kind=":", beat=0, length=3, pitch=16, text="ah")
@@ -19,16 +19,16 @@ def test_parse_headers_and_notes():
assert any(n.kind == "F" for n in song.body if isinstance(n, Note))
def test_note_text_keeps_leading_space():
song = parse_ultrastar(load("sanctuary.txt"))
song = parse_ultrastar(load("chart_crlf.txt"))
notes = [n for n in song.body if isinstance(n, Note)]
assert notes[1].text == " ah"
def test_negative_pitch():
song = parse_ultrastar(load("sweet_child.txt"))
song = parse_ultrastar(load("chart_latin1.txt"))
assert any(n.pitch == -9 for n in song.body if isinstance(n, Note))
def test_round_trip():
for name in ("sweet_child.txt", "sanctuary.txt"):
for name in ("chart_latin1.txt", "chart_crlf.txt"):
song = parse_ultrastar(load(name))
again = parse_ultrastar(serialize_ultrastar(song))
assert again == song