diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..03eebc9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,45 @@ +name: Publish release + +on: + push: + tags: ['v*'] + +permissions: + contents: read + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +jobs: + validate: + uses: ./.github/workflows/validate.yml + publish: + needs: validate + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: write + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: npm run build + - run: npm run release:pack + - name: Publish tested build and checksums + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + version=$(node -p "require('./package.json').version") + test "$GITHUB_REF_NAME" = "v$version" + gh release create "$GITHUB_REF_NAME" \ + "release/harpscore-$version-web.tar.gz" release/SHA256SUMS.txt \ + --verify-tag --title "HarpScore $GITHUB_REF_NAME" \ + --notes-file "docs/releases/$GITHUB_REF_NAME.md" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..8ebef0b --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,35 @@ +name: Validate scores and application + +on: + push: + branches: [main, 'codex/**'] + pull_request: + workflow_dispatch: + workflow_call: + +permissions: + contents: read + +jobs: + validate: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: npm test + - run: npm run scores:validate + - run: npm run scores:build + - name: Generated scores must be committed + run: git diff --exit-code -- src/data + - run: npm run build diff --git a/.gitignore b/.gitignore index 996cd33..d826713 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules dist dist-ssr +release/ *.local *.tsbuildinfo .DS_Store diff --git a/README.md b/README.md index 5af6366..8880c0f 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,17 @@ ## 功能 - **专业琴谱展示**:上行简谱(数字 + 八度点 + 升降号 + 附点 + 节奏下划线 + 延音线 + 歌词),下行口琴演奏标记(吹吸方向 + 孔号 + 压音级别 + 超吹 + 技巧),小节线 / 反复记号 / 双纵线完整刻画 -- **吹吸配色**:`+N` 吹气(暖红)、`-N` 吸气(深蓝)、`'` 压音(每个符号半音)、`°` 超吹、`~` 颤音 -- **曲谱管理**:内置曲谱 + 个人曲谱(浏览器本地持久化),搜索、查看、删除、JSON 导入 +- **吹吸配色**:`+N` 吹气(暖红)、`-N` 吸气(深蓝)、`'` 压音(每个符号半音)、`°` 超吹、`~` 振音 +- **曲谱管理**:内置曲谱 + 个人曲谱(浏览器本地持久化),多词搜索、难度/标签/压音筛选、排序、本地收藏、删除、JSON 导入 - **导出**:PNG(高清位图)、SVG(矢量)、打印 / PDF、JSON 源文件 -- **可吹性分析**:自动标注每个音的孔位,统计音域、压音数量,超出 C 调十孔口琴能力的音即时提示 +- **可吹性分析**:按指定标准 Richter 调性自动标注孔位,统计音域、压音数量,超出当前口琴能力的音即时提示(不含 Low/High 特殊型号) +- **手机阅读**:按可用宽度自动重新分行,保留长音、多八度点和跨行延音;导出使用完整页面布局 +- **变拍曲谱**:支持小节级拍号变化,自动沿用后续小节的拍号;谱面及 JSON / SVG 导出保留变拍记号 +- **前倚音**:支持 1–4 个短倚音的小号谱面与孔位,音域、可吹性和筛选均计入倚音;倚音不增加小节拍数 +- **单顺/逆波音**:显式保存邻音,校验相邻音级、指法和音域;谱下的“辅”标出辅助音孔位 +- **三十二分音符**:支持短音与附点时值、三道减时线;密集小节按拍点续行,虚线与“续”字表示同一小节 +- **显式弱起与延长记号**:允许来源明确的短首节搭配足拍末节;自由延长记号保留书面时值和谱面提示 +- **重音与保持音**:保留强调发音与保持音值的记号,支持与倚音、延音、延长叠加;导出和打印同步 - **跨平台**:纯 Web 应用,浏览器即开即用,桌面 / 手机 / 平板通用(PWA 友好) ## 内置曲谱 @@ -19,21 +26,81 @@ |---|---|---|---|---| | 小星星 | C | 第一把位 | ★ | 全自然音,入门 | | 欢乐颂 | C | 第一把位 | ★ | 全自然音,入门 | -| 爱尔兰画眉 | C | 第一把位 | ★★ | 十孔口琴经典 | +| 爱尔兰画眉 | C | 第一把位 | ★★★ | 传统 ABC 单旋律版、弱起 | | 友谊地久天长 | C | 第一把位 | ★★ | 附点节奏练习 | | 送别 | C | 第一把位 | ★★ | 教材 24 小节完整展开版 | | 12 小节布鲁斯练习曲 | G | 第二把位 | ★★★★ | 压音强化(3' 3'' 4') | | 铃儿响叮当 | C | 第一把位 | ★ | 全自然音,吐音练习 | | 压音入门练习曲 | C | 第一把位 | ★★★ | 原创压音练习 | | 两只老虎 | C | 第一把位 | ★ | 连续八分音符、轮唱旋律 | -| 玛丽有只小羊羔 | C | 第一把位 | ★ | 相邻音与重复吐音 | -| 祝你生日快乐 | C | 第一把位 | ★ | 3/4 拍弱起 | +| 玛丽有只小羊羔 | C | 第一把位 | ★ | 1927 年歌集版、附点与延音 | +| 祝你生日快乐 | C | 第一把位 | ★ | 3/4 拍弱起、双结尾展开 | | 西班牙划船歌 | C | 第一把位 | ★★ | 2/4 拍二八节奏 | -| 平安夜 | C | 第一把位 | ★★ | 3/4 拍附点节奏 | -| 奇异恩典 | C | 第一把位 | ★★★ | 弱起、附点与较宽音域 | +| 平安夜 | C | 第一把位 | ★★ | 1927 年歌集版、6/8 拍 | +| 奇异恩典 | C | 第一把位 | ★★★ | G4–G5、一拍弱起与低音压音 | +| 阿里郎 | C | 第一把位 | ★★★ | 完整 16 小节,附点与低音 la 压音 | +| 丰收之歌 | C | 第一把位 | ★★ | 完整 24 小节,自然音与高音跳进 | +| 小白菜 | C | 第二把位 | ★★★ | 5/4 转 4/4,含结束句与反复 | +| 伦敦桥 | C | 第一把位 | ★ | 1927 歌集 4/4 版,附点与延音 | +| 哦,苏珊娜 | C | 第一把位 | ★★ | 主歌反复展开,十六分弱起 | +| 走绛州 | C | 第二把位 | ★★★ | 完整 23 小节,短音与休止交替 | +| 扬基歌 | C | 第一把位 | ★★★ | 含低音副歌与附点弱起 | +| 我的邦妮 | C | 第一把位 | ★★★ | 完整主副歌,九度跳进与跨小节延音 | +| 凤阳花鼓 | C | 第一把位 | ★★★ | 教材 15 小节版,五声旋律与低音 la | +| 剪羊毛 | C | 第一把位 | ★★ | 附点八分、十六分与高音区跳进 | +| 桑塔·露琪亚 | C | 第一把位 | ★★★ | 3/8 船歌,升降还原与反复结尾 | +| 渴望春天 | C | 第一把位 | ★★★ | 完整教材旋律,6/8 弱起、半音与前倚音 | +| 二把位呼应练习 | G | 第二把位 | ★★ | 原创 16 小节自然音与呼吸停顿练习 | +| 低音区蓝调音阶练习 | G | 第二把位 | ★★★ | 原创 8 小节,Bb / Db 半音压音定位 | +| 无锡景 | C | 第一把位 | ★★★ | 完整 16 小节,单顺波音与两处前倚音 | +| 春游 | C | 第一把位 | ★★★ | 李叔同完整主旋律,6/8 与升 fa | +| 布谷鸟 | C | 第一把位 | ★ | 1927 歌集版,自然音与三拍休止 | +| 摇篮曲(Rock-a-bye Baby) | C | 第一把位 | ★★★ | 6/8 拍附点短音,E4–F5 音区 | +| 盼红军 | C | 第四把位 | ★★★ | 四川民歌,低 la 压音与跨小节延音 | +| 孟姜女,哭长城 | C | — | ★★★★ | 江苏民歌,三十二分、前倚音及加花旋律 | + +| 你可曾见过小姑娘 | C | 第一把位 | ★ | 3/4 问答游戏曲,一拍弱起 | +| 金色梦乡 | C | 第一把位 | ★★★ | 传统摇篮曲,低音区压音与三拍长音 | +| 斑鸠调 | C | — | ★★ | 江西民歌完整演唱主旋律,短音鸟鸣 | +| 放纸鹞 | C | — | ★★★ | 台湾民谣旋律,两次反复结尾完整展开 | + +| 茉莉花 | C | — | ★★ | 江苏民歌28小节,完整后段低音句 | +| 什么船儿 | C | — | ★★ | 山西民歌曲调,3/8 拍完整问答 | +| 牧场上的家 | C | 第一把位 | ★★★ | 完整主副歌,弱起和跨小节延音 | +| 红河谷 | C | 第一把位 | ★★★ | 显式一拍弱起,足拍尾节和低音半音下降 | +| 鸿雁 | C | 第四把位 | ★★★ | 反复主体与结束句,多音倚音和延长记号 | +| 桑树丛 | C | 第一把位 | ★★ | 1927歌集游戏曲,6/8 短音和低音句 | +| 轻轻划 | C | 第一把位 | ★ | 16 小节歌集版本,mi 收尾和休止 | +| 滴答滴答钟声响 | C | 第一把位 | ★★ | Elliott 版本,六八拍休止练习 | +| 跷跷板 | C | 第一把位 | ★★ | 六八拍,保留低 sol 和低 si | +| 露西的口袋 | C | 第一把位 | ★ | 四分/二分节奏与低音跳进 | +| 勤劳的小蜜蜂 | C | 第一把位 | ★★ | 英国古调,一拍弱起与末句八分音 | +| 快乐的磨坊主 | C | 第一把位 | ★★★ | 英国游戏歌,低 fa/la 压音和附点 | +| 好伙伴 | C | 第一把位 | ★★ | 歌集版本,段落弱起衔接 | +| 白蜡树林 | G | 第二把位 | ★★★ | 威尔士民歌,完整展开反复与变化音 | +| 大家一起拍拍手 | C | 第一把位 | ★ | 传统动作歌,弱起和段落衔接 | +| 叮咚铃声 | C | 第一把位 | ★★ | 10 小节反复,含一个低 la 压音 | +| 小河淌水 | C | 第四把位 | ★★★ | 云南民歌,变拍、反复、延音与自由延长 | +| 波兰圆舞曲 | C | 第一把位 | ★★ | 教材完整32小节主旋律,高音区自然音 | +| 左右齐步走 | C | 第一把位 | ★ | 16小节二拍子,低si与高re自然音 | +| 雪鸟 | C | 第一把位 | ★ | 16小节三拍子,C5–C6自然音 | +| 小小航船 | C | 第一把位 | ★★★ | 英格兰旧歌,六八拍、低音压音与延音 | +| 我们去狩猎 | C | 第一把位 | ★★ | 英格兰传统曲,附点与自由延长 | + +目前 64 项,其中 60 首歌曲、4 首原创练习。持续扩曲进度与来源核对记录见 [Stage 5 来源台账](docs/sources/stage-5-audit.md)、[Stage 7 均衡扩曲台账](docs/sources/stage-7-library.md) 与 [Stage 9 逐首核对台账](docs/sources/stage-9-library.md)。 + +## 下载与发布 + +[GitHub Releases](https://github.com/ax2/harpscore/releases) 提供已构建的Web版本、启动器和SHA256校验文件,具体使用见[v0.2.0说明](docs/releases/v0.2.0.md)。下载包需要Node.js 24或更新版本,无需安装npm依赖。个人数据按浏览器网址保存,升级前请备份。 + +维护者发布流程:更新package.json/package-lock.json版本与 `docs/releases/vX.Y.Z.md`,完成测试并提交代码,再推送同名 `vX.Y.Z` 标签。Release工作流会先运行Windows/Linux验证,再构建和执行 `npm run release:pack`,成功后上传两个下载资产并发布。打包要求干净工作区,版本与标签必须一致;已有公开版本不会被覆盖,失败时先检查Actions日志再重试。 ## 本地开发 +打印 / PDF 使用 A4 纵向整行分页,续页重复曲名、调号和图例;跨页延音保留,主孔位字号约 8.5 pt。导出时建议保留 A4 纵向设置。普通 SVG/PNG 仍为完整长图。验证记录见 [Stage 6 打印与把位校正](docs/stages/stage-6-print.md)。 + +工具栏的「小节号」可在当前会话显示书写小节编号,SVG、PNG和打印同步采用该选择。编号从1开始,弱起计入;反复段不按演奏次数重新编号,同一小节在手机上拆成续行时沿用原号并标「续」。编号单独留出空间,可能增加打印页数;该显示选项不写入曲谱JSON,也不改变音乐内容。 + ```bash npm install npm run dev # 开发服务器 @@ -47,16 +114,42 @@ npm run scores:validate # 校验全部内置谱 ## 曲谱数据格式 +完整 JSON 的 `techniques` 可包含 `vibrato`(振音)、`bend-release`(压音释放)、`slap`(舌拍)、`gliss`(滑音)、`trill`(颤音)。这些是文字提示,不包含交替音、过程音或和声音的完整信息;谱面和导出会保留标记,并提示需要核对的音高。分析中的“已记音高均有孔位”仅说明谱中明确音高的定位结果,节拍与技巧提示仍需检查。详见 [技巧验证](docs/stages/stage-6-techniques.md)。 + +个人曲谱保存在当前浏览器。曲库侧栏的「备份个人曲库」可下载完整原始数据;「恢复个人曲库」支持上传备份数组,整批检查后合并或替换。替换需确认,并先保存最近一份恢复前副本,可从恢复窗口导出;副本保存失败或检测到其他标签页修改时取消替换。收藏列表独立于曲谱备份;支持 Web Locks 的浏览器会协调多标签页收藏写入并同步显示,损坏收藏数据不会被普通操作覆盖。其他标签页修改后可点击「重新读取曲库」。导入同 ID 曲谱保留两首;损坏库会阻止普通导入/删除,仍可查看有效条目、导出原文,或用有效备份替换。详见 [存储验证](docs/stages/stage-6-storage.md) 和 [备份恢复](docs/stages/stage-6-restore.md)。 + 曲谱为 JSON(见 `src/data/scores/*.json`):既可导入包含 `lines` 的完整 HarpScore JSON,也可导入包含 `melody` 的旋律 DSL JSON。导入器会校验嵌套结构、规范化八度、避免 ID 覆盖,并重新推算口琴孔位。 -批量制谱使用配套导入工具:`tools/import/*.json` 为旋律 DSL 输入(空格分词、`|` 小节线、`/` 八分、`//` 十六分、`'` 高音点、`,` 低音点、`#`/`b` 升降、`~` 延音、`=字` 歌词、`-` 延长一拍),运行 `npm run scores:build` 完成“深层校验 → 八度规范化 → 可吹性验证 → 孔位推算 → 写入内置曲谱 → 重建索引”全流程。 +导入窗口支持 Tab 循环、Escape 关闭及焦点返回;读取文件期间显示进度并暂停提交,过期读取不会覆盖新草稿。短横屏可滚动访问按钮。详见 [导入交互验证](docs/stages/stage-6-import.md)。 + +批量制谱使用配套导入工具:`tools/import/*.json` 为旋律 DSL 输入(空格分词、`|` 小节线、`/` 八分、`//` 十六分、`///` 三十二分、`'` 高音点、`,` 低音点、`#`/`b` 升降、`~` 延音、`=字` 歌词、`-` 延长一拍),运行 `npm run scores:build` 完成“深层校验 → 八度规范化 → 可吹性验证 → 孔位推算 → 写入内置曲谱 → 重建索引”全流程。 + +三十二分音符在完整 JSON 中使用 `beats: 0.125`。如 `3//. 5///` 为附点十六分加三十二分,合计半个四分拍;64 分音符与 32 分形状的倚音暂不支持。手机阅读时过密小节会续行,虚线不是小节线,拍数与临时升降号沿用;数据及宽版导出不增加实际小节。 非原创内置谱优先以文字简谱或清晰数字简谱逐音核对,并记录 `sourceUrl`、`sourceFormat` 与 `sourceCheckedAt`;五线谱不作为图片识别输入。`npm run scores:validate` 会同时阻止节拍、可吹性或来源字段不完整的曲谱进入发布。 +可追踪的 ABC / LilyPond 音符文本采用独立的 `music-text` 来源类型,保留版本、转调和反复处理说明。小节变拍在 DSL 中写为 `[M:3/4] 1 2 3`(记号必须位于小节开头);完整 JSON 在小节对象中使用 `timeSignature`。该拍号持续生效,直到下一次变拍。变拍曲暂不自动豁免首尾弱起,请检查其节拍提示。 + +数据中的临时升降号逐音指定,不隐式沿用前一音:例如 `#4 4` 表示升 fa 后还原为自然 fa,谱面会自动补出还原号。同一小节内若需连续升 fa,写作 `#4 #4`。 + +前倚音写在主音前的花括号里,如 `{5}4/` 或 `{6,;1'}2`,分号分隔,最多 4 个。默认显示十六分小音符,`{5/}4` 显示八分小音符;完整 JSON 用主音的 `graceNotes` 数组,每项 `beats` 只能是 `0.25` 或 `0.5`。该值表示符形,不作为独立拍数相加;实际演奏需快速连入主音。倚音不能为休止、嵌套,或携带歌词、附点、延音、技巧。当前不支持把延音线跨过另一音的前倚音。 + +单顺波音写作 `[U:3']2'/`,表示高音 re 的八分音符,快速奏 re–mi–re;逆波音 `[L:1]2` 表示 re–do–re。方括号内只写邻音音高,必须是正确方向的相邻音级,可以显式带升降号。完整 JSON 示例:`"ornament": {"kind": "upper-mordent", "auxiliary": {"degree": 3, "octave": 1}}`。辅助音没有独立时值,分析总数中单独注明其数量,不把返回主音重复计数;谱下“辅 +8”意为辅助音吹 8 孔。当前单波音不与同一主音的倚音/其他技巧叠加,不支持延音跨入波音,也不等于已实现 tr 颤音、复波音或回音。 + +显式弱起:在顶层 JSON 声明 `"pickupBeats": 1`,以四分音符为一拍。数值须大于零、小于首小节有效拍号的时长,并与首小节实际音符时长完全一致;弱起后至少有一个小节。它只放行首小节,不掩盖中间或末尾缺拍,适用于《红河谷》这类末小节足拍的版本。未声明时仍沿用原有首尾互补识别。 + +延长记号:DSL 写 `3!`、`0!` 或 `{6,/}3!`;同音延音同时存在时写 `3!~`。完整 JSON 使用音符的 `fermata: true`。半圆加点表示按乐句自由延长,不自动加拍,不承诺固定播放时值;倚音和波音辅助音上不接受该属性。 + +重音与保持音:音符后加 `>` 或 `_`,例如 `1>`、`2/_`、`{2}1>!~`。标记顺序为时值/附点、重音或保持音、延长、延音。完整 JSON 使用 `articulation: "accent"` 或 `"tenuto"`。重音强调发音,保持音提示保持音值;二者不改变已记音高或拍数,SVG/PNG/打印保留同一标记。休止符、倚音和波音辅助音不接受这些标记;暂不支持两种标记叠加在同一音符上。 + +2026-10-23 对旧曲进行了来源复核,修正八度、弱起、延音与反复,并为失效来源选定明确的替代版本。见 [逐曲台账](docs/sources/stage-6-legacy-audit.md) 与 [验证记录](docs/stages/stage-6-legacy.md);《西班牙划船歌》现明确采用湖南教材可完整核对的8小节活动短版,此前16小节编配保留在Git历史中。 + ## 技术栈 Vite + React + TypeScript,乐谱为纯 SVG 渲染,无重型依赖。 +内置库在构建时合并重复音符记录,加载后恢复为独立的完整曲谱对象;不改变JSON导出或个人曲库格式。60项曲库的主JS从544.31kB降为294.29kB(gzip93.62kB→87.94kB),详见[曲库传输与等价验证](docs/stages/stage-8-library-payload.md)。 + ## 相关文章 - [HarpScore:我做了一个开源的布鲁斯口琴乐谱软件](https://zicode.com/blog/harpscore/) —— 从 Prompt 到实现的全过程记录,含内置曲谱的音高校验与来源标注说明。 @@ -64,3 +157,7 @@ Vite + React + TypeScript,乐谱为纯 SVG 渲染,无重型依赖。 ## License MIT © 2026 ax2 + +Firefox 153 与 Chromium 均完成关键导入、收藏并发、曲库恢复和谱面/导出检查;四标签页各20轮保留80条收藏及80首个人曲谱。浏览器范围及已发现的存储可见性问题见 [跨浏览器验证](docs/stages/stage-6-firefox.md)。 + +诊断提示采用与谱面一致的连续书写小节号,弱起计入,手机续行沿用原号;音符序号包含休止符,倚音和波音辅助音注明所属主音。在分析面板点击“显示小节号”可开启标记。压音/进阶技巧位置超过12处时折叠显示,展开后可查看全部位置;源文件语法错误仍按输入位置报告。 diff --git a/docs/releases/v0.2.0.md b/docs/releases/v0.2.0.md new file mode 100644 index 0000000..0c60bc3 --- /dev/null +++ b/docs/releases/v0.2.0.md @@ -0,0 +1,31 @@ +# HarpScore v0.2.0 + +本阶段将内置曲库从14项扩充至64项:60首歌曲、4首原创练习,并修复音高、时值、指法、导入和打印中的多项问题。 + +## 新功能与修复 + +- 逐首记录选定曲谱的来源、移调方式和核对范围;按传统曲与入门曲均衡扩充。最新加入《康定情歌》《一起来学唱》《苏武牧羊》《小蜜蜂》。 +- 支持变拍、前倚音、带明确辅助音的波音、三十二分音符、显式弱起、自由延长、重音和保持音;保留跨小节/跨行同音延音。 +- 修正标准Richter口琴的音高与孔位,明确低音压音及超吹提示;诊断位置与可选小节编号一致。 +- 增加多条件曲库筛选、排序、收藏、个人库备份与预检恢复;修复存储并发、慢文件读取和键盘焦点问题。 +- 手机按实际可用宽度重排,密集小节保留续行关系;PNG/SVG/JSON导出及A4分页打印同步。 +- 内置谱的传输数据去重,保留完整音乐内容及公开JSON格式。 +- 新增标签触发的GitHub Release工作流:Windows/Linux Node24验证通过后构建、打包并上传Web版本及SHA256校验文件。 + +## 下载和使用 + +下载 `harpscore-0.2.0-web.tar.gz` 与 `SHA256SUMS.txt`,解压后安装Node.js 24或更新版本,无需运行npm install。 + +- Windows:双击 `START.cmd`,在浏览器打开 `http://127.0.0.1:4178`。 +- macOS/Linux:在解压目录运行 `node serve.mjs`,打开同一地址。 +- 静态网站部署:上传包内 `dist/` 内容;不要直接双击其中的HTML。 + +Windows可用 `tar -xzf harpscore-0.2.0-web.tar.gz` 解压,`Get-FileHash -Algorithm SHA256 harpscore-0.2.0-web.tar.gz` 校验文件。此包是Web应用,不是Windows EXE。 + +个人曲谱和收藏保存在浏览器,绑定网站地址和端口。升级前导出个人库备份,升级后沿用原地址与浏览器;如改了地址,请从备份恢复。 + +## 验证范围 + +150项自动测试覆盖来源旋律对照、节拍/延音/孔位门禁、导入和存储、记号与打印布局、数据打包隔离及本地启动器。64项曲库验证和生产构建通过。Chromium/Firefox检查64曲的三种谱宽;新曲经过实际搜索、导出、手机及打印DOM检查。新两曲Chromium实际PDF分别2页和1页,组合标记经过导入、重载和导出验证。 + +当前没有音频播放。音乐核对针对注明的来源版本,尚未经过独立音乐专家、实体口琴、真实手机和打印机的验收;Firefox原生PDF与WebKit未验。不将自动测试声称为专业音乐审校,也不将体积减少声称为启动速度必然提高。 diff --git a/docs/sources/stage-5-audit.md b/docs/sources/stage-5-audit.md new file mode 100644 index 0000000..57fd49f --- /dev/null +++ b/docs/sources/stage-5-audit.md @@ -0,0 +1,88 @@ +# Stage 5 transcription ledger + +2026-10-23 metadata correction: Xiao Bai Cai and Zou Jiang Zhou use G as the Zhi-mode tonic after transposition to a C jianpu reference, so both use second position on a C harp. Their melody/fingering arrays are unchanged. Sources: Music Appreciation printed p.5/PDF p.12 for Zou Jiang Zhou's original D Zhi cadence; Basic Music Theory revised edition printed p.85/PDF p.102, ex.7-3(d), for Xiao Bai Cai's mode classification. See [Stage 6 evidence and links](../stages/stage-6-print.md). The theory example was used only for the written mode label, not as a melody transcription source. + +All dates use Asia/Shanghai. Only melodic note/rhythm data are incorporated. Source page artwork, lyrics and accompaniments are excluded. A source-check date denotes an actual comparison with the specified version, not a universal claim across folk variants. + +| ID | Primary source and location | Comparison and adaptation | +| --- | --- | --- | +| arirang | School-hosted Guangdong Education/Huacheng grade 3 music textbook, printed p.53 / PDF p.54 | Visually inspected complete numbered notation on 2026-10-22. 16 bars of 3/4; dotted quarter + three eighths in opening. First eight bars and second eight bars have different endings. F tonic transposed to C; source octaves then raised one octave. Low la uses draw bend. Slurs/lyrics omitted explicitly. | +| harvest-song | Same textbook, printed pp.50–51 / PDF pp.51–52 | Both pages visually inspected on 2026-10-22. 24 bars of 2/4, including the continuation after p.50. Repeated section and upper-register middle section retained; three phrase-ending quarter rests. Bb tonic transposed to C, then up one octave. | +| xiao-bai-cai | Guo Liping / Wang Limin / Ma Zhifei, Music Appreciation, Xinhua Publishing, printed p.17 / PDF p.24, example 1-7 | Visually inspected 2026-10-22. Four 5/4 main-verse bars and two 4/4 coda bars, repeat markers retained. This edition uses do–low la in bar 4; another theory textbook uses low ti–low la, so variants must not be mixed. Ab to C, up one octave. Different-pitch slurs and lyrics omitted. | +| zou-jiang-zhou | Same Music Appreciation, printed p.6 / PDF p.13, example 1-1 | Complete 23 bars visually read 2026-10-22, with enlarged inspection of first two rows. Bar 2 is eighth + two sixteenths + quarter; bar 3 jumps to upper do, not middle do. Bars 6–7 have alternating sixteenth notes/rests. G to C, up one octave. Only main notated rhythm retained; tiny alternative syllable rhythms for later verses and slurs omitted. | +| london-bridge | The Everyday Song Book (1927), No.18; John Chambers ABC transcription (2015) | Direct ABC file read 2026-10-22. Top melody of two-voice chords, 8 bars in common time; dotted quarter opening, tied quarters, final quarter rest. F to C, up one octave. No lower voice/lyrics incorporated. | +| oh-susanna | Frank Nordberg / musicaviva ABC transcription, John Chambers mirror | Direct ABC file read 2026-10-22. Half-beat pickup, two passes through the eight-bar verse, eight-bar chorus. First verse ending merges with next pickup; second ending includes eighth rest. Final dotted quarter complements pickup. Source C5 register preserved; chords omitted. 25 written bars including pickup. | +| yankee-doodle | The Everyday Song Book (1927), No.3; John Chambers ABC (2015) | Direct ABC read 2026-10-22. Complete verse and lower-register chorus, eighth pickup and dotted rhythms. A4 tonic becomes C5. Independent ABC event decoding matches all 61 note/rest events against generated score pitches and durations. | +| my-bonnie | Same song book, No.6 | Direct ABC read 2026-10-22; source note documents repaired flag in bar 5. Complete 32-bar verse/chorus plus pickup; middle two-beat ending joined to the next phrase pickup. Bb4 tonic becomes C5. Draft main-verse octave error corrected during source review; independent ABC decoding then matches all 67 note/rest events. Fermata omitted explicitly; written durations retained. | +| fengyang-flower-drum | PEP Music, Grade 7 lower, numbered notation, printed p.31 / PDF p.36 | Full 15 bars visually inspected 2026-10-22. Main-melody low la marks agree with Guangdong/Huacheng grade 3 book, resolving the earlier candidate concern without guessing a different register. C key, up one octave. Two eighth rests in rhythmic coda retained. Lyrics/accompaniment/slurs omitted. | +| click-go-the-shears | Same PEP book, printed p.46 / PDF p.51 | 16-bar displayed melody visually inspected 2026-10-22. Dotted eighth/sixteenth rhythms, four quarter rests and the last phrase's middle-to-upper do leap retained. C key, up one octave. Lyrics and different-pitch slurs omitted. | +| santa-lucia | Same PEP book, printed p.41 / PDF p.46 | Visually inspected and enlarged on 2026-10-22. 3/8, original C4 tonic register retained so F#4 is a draw bend, not F#5 overblow. First eight bars retain repeat signs; second section is expanded with both alternate endings (16 bars). Bar 6 F#–F cancellation is represented by explicit per-note pitches and an automatically printed natural sign. | + +Source: https://lywgyxx.szftedu.cn/xkjc/yinyue/202209/P020220910586729125995.pdf + +Additional direct sources: +- https://backend.sjhtbook.com/upload/file/2025/04/80fb4b7923894770828045c9e613e236.pdf +- https://trillian.mit.edu/~jc/music/book/EverydaySongBook/018_London_Bridge.abc +- https://trillian.mit.edu/~jc/music/abc/mirror/kirby98.fsnet.co.uk/oh/Oh_Susanna_1.abc +- https://trillian.mit.edu/~jc/music/book/EverydaySongBook/003_Yankee_Doodle.abc +- https://trillian.mit.edu/~jc/music/book/EverydaySongBook/006_My_Bonnie.abc +- https://dev98.oa.henanyuexue.com/static/szjc-resources/pdf/q/f/2/7.pdf + +Suggested practice tempi are editorial choices unless the source gives a numerical tempo. Automated checks establish data/rhythm/range consistency; they do not independently prove a transcription matches every folk variant. + +## Grace-note batch — 2026-10-22 + +- **longing-for-spring**: PEP grade 7 lower, printed pp.4–5 / PDF pp.9–10, both pages visually compared again. 6/8, 17 written bars including the eighth pickup; final 2.5 quarter-note beats complement the pickup. Original C4–C5 register retained. C#4 / F#4 remain playable draw bends. The penultimate bar has a small sixteenth G4 before F4, represented as a grace note rather than an extra rhythmic event. Same-pitch ties retained; lyrics and different-pitch slurs omitted explicitly. Regression fixtures cover the chromatic bridge, last two bars, pickup and grace. +- **cross-harp-call-response**: newly authored 16-bar G/C-harp exercise, four four-bar question/answer groups with phrase-ending rests. Natural reeds only; no claimed folk-song source or 12-bar chord form. Straight eighths, suggested practice tempo. +- **low-blues-scale-study**: newly authored eight-bar G blues-scale exercise. Pitch classes G/Bb/C/Db/D/F, including octave G; Bb4 is 3-draw half-step bend and Db5 is 4-draw half-step bend. Eleven written bend notes. Straight eighths, with C–Db–D and reverse comparisons. Exercises are counted separately from the song expansion target. + +## Mordent batch — 2026-10-22 + +- **wuxi-jing**: Music Appreciation, printed p.18 / PDF p.25, example 1-8. All three notation rows enlarged and compared. Sixteen bars in 2/4, B tonic changed to C and source-relative octaves raised one octave. Opening high re has a single upper mordent, with high mi auxiliary (D6–E6–D6 on the adapted score); bars 4 and 8 retain pre-graces. The short wave was identified against *Basic Music Theory Course* (revised edition, People's Music Publishing House, 2006), printed p.183 / PDF p.200, example 13-5: its upper-mordent glyph matches the source. The earlier tentative “trill” description was corrected. [Theory scan](https://www.whyx.net.cn/uploads/media/files/gkzp/fj6.pdf#page=200). No performance-timing allocation is asserted. Lyrics and different-pitch slurs omitted; suggested tempo is editorial. +- **spring-excursion**: PEP grade 7 lower, printed pp.6–7 / PDF pp.11–12. Both complete pages visually compared; only the upper female voice (main melody) is incorporated, as a 16-bar melody. Original C4–C5 register retained, including F#4 in bar 12 so ordinary draw bending suffices. The first four-bar phrase ends on E4; second and final phrases end on C4. Lower voices, lyrics, dynamics and ritardando omitted explicitly. Suggested quarter-note tempo is editorial. + +## Fourth batch — 2026-10-22 + +- **cuckoo-song**: *The Everyday Song Book* (1927), No.117, John Chambers ABC (2017). Twelve bars, 3/4; Bb4 tonic transposed to C5. Independent ABC decoding matches all 30 note/rest events, including the four quarter rests. Dynamics and lyrics omitted; tempo is editorial. [ABC](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/117_Cuckoo_You_Sing_So_Clear.abc). +- **rock-a-bye-baby**: Same book, No.9, ABC (2015). Eight bars, 6/8; Bb4 tonic transposed to C5 while retaining the E4–F5 adapted range. Independent ABC decoding matches all 38 events. Dotted eighth/sixteenth opening, differing second and sixth bar endings, and final eighth rest retained. Lyrics omitted; tempo is editorial. [ABC](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/009_Rock-a-bye_Baby.abc). +- **pan-hong-jun**: Guangdong/Huacheng grade 6 upper numbered-notation textbook, printed p.47 / PDF p.48. Complete twelve-bar Sichuan folk melody visually compared. 1=D changed to 1=C, up one octave; low la closing note is tied across two bars for four quarter beats. A tonal centre on a C harp is labelled fourth position, checked against the publisher's [position chart, p.14](https://www.melbay.com/Content/Site161/Basics/328094347DVDpdf_00000020928.pdf). Lyrics and different-pitch slurs omitted; tempo editorial. +- **meng-jiang-nv**: Same grade 6 book, printed p.49 / PDF p.50. All four rows enlarged and visually compared. Eight bars in 4/4, 1=F changed to 1=C and raised one octave, E4–A5 adapted range. Five dotted-sixteenth/thirty-second pairs, four eighth-shaped pre-graces and four same-pitch tie starts retained. Last note is low sol; no automatic first-position label inferred merely from 1=C. Lyrics and different-pitch slurs omitted; tempo editorial. Dense mobile bars now wrap as labelled continuations without altering musical measures. + +Grade 6 source: https://lywgyxx.szftedu.cn/xkjc/yinyue/202209/P020220910588732922659.pdf + +## Fifth batch — 2026-10-22 + +- **did-you-ever-see-a-lassie**: *The Everyday Song Book* (1927), No.10, John Chambers ABC (2015). Seventeen written bars including a one-quarter-beat pickup, with a complementary two-beat last bar; 3/4. G4 tonic becomes C5. Independent ABC decoding matches all 53 note/rest events. The middle rest and extended question/answer section are retained. [ABC](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/010_Did_You_Ever_See_a_Lassie.abc). +- **golden-slumbers**: Same book, No.175, ABC (2017). Sixteen written bars, first eight repeated; 3/4. Bb4 tonic becomes C5, adapted E4–G5 range. Independent ABC decoding matches all 35 events; repeat boundaries separately verified. This is the historical lullaby tune in that edition. Lyrics and different-pitch slurs omitted, tempo editorial. [ABC](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/175_Golden_Slumbers.abc). +- **spotted-dove-song**: Guangdong/Huacheng grade 6 upper, printed p.38 / PDF p.39. Enlarged comparison of the complete nine-bar vocal melody beginning at 回原速. The preceding bird-dialogue introduction and two-part instrumental passage are expressly outside this solo version. 1=C retained, up one octave, E5–E6 natural reeds. Eighth/sixteenth calls, dotted rhythm in bar 8, two-beat ending and repeat retained. +- **flying-kites**: Same book, printed p.15 / PDF p.16, traditional Taiwanese 一只鸟仔 tune; modern replacement lyrics excluded. All four rows visually compared, with endings enlarged. 1=A becomes 1=C, up one octave. Eleven common bars plus each of two alternate endings expanded to 24 bars. The first ending's response has middle re, while the second has upper re: they must not be copied as identical endings. Instrumental responses in parentheses and every rest retained. D5–F6 adapted natural-reed range. Lyrics/different-pitch slurs omitted, tempo editorial. + +## Sixth batch — 2026-10-23 + +The school books were initially inspected late on October 22 and transcriptions rechecked after midnight on October 23 (Asia/Shanghai). + +- **jasmine-flower**: Guangdong/Huacheng grade 4 lower, printed p.15 / PDF p.16. Selected the complete 28-bar simpler Jiangsu version; do not mix it with the more ornamented version on printed p.12. 1=D to 1=C, raised one octave, including the low sol/la in the final two rows. All phrase-ending rests retained; lyrics and different-pitch slurs omitted. +- **what-boat**: Same book, printed p.46 / PDF p.47. Complete twelve-bar Shanxi folk tune in 3/8, 1=F to 1=C, raised one octave. The response's low sol/la/ti and dotted-quarter whole-bar endings retained. Modern game lyrics excluded. +- **home-on-the-range**: Same book, printed p.35 / PDF p.36. Complete verse and chorus, 33 written bars including a one-quarter-beat pickup; the two-beat final bar complements it. 1=Eb to 1=C, up one octave, G4–G5 adapted range. Five cross-bar tie starts retained. Translation, dynamics and different-pitch slurs excluded. +- **red-river-valley**: Same book, printed p.39 / PDF p.40. One-beat pickup plus eight complete 4/4 bars. The source has a full final bar, so it is explicitly declared with pickupBeats=1; no final rest is deleted to satisfy a complementary-pickup heuristic. In complete bar 6, low la descends to flat low la (A4–Ab4 in the adapted version). Three same-pitch ties retained. 1=F to 1=C, up one octave. Translated lyrics and different-pitch slurs excluded. +- **hong-yan**: Guangdong/Huacheng grade 5 upper, printed p.40 / PDF p.41, identified on the page as an Urad Mongolian folk tune. The main eight-bar melody repeats, followed by the full eight-bar ending; 16 written bars. All source rows enlarged. 1=D to 1=C, up one octave, G4–C6. Three single pre-graces use eighth-shaped low la; three groups use sixteenth-shaped do–re–do. Same-pitch ties across bars 3–4 and inside bar 4 retained. The last coda's mi has a fermata; its written quarter duration remains unchanged. Modern lyrics and accompaniment arrangement are excluded. Numerical practice tempo is editorial. +- **mulberry-bush**: *The Everyday Song Book* (1927), No.38, John Chambers ABC (2017), eight bars in 6/8. A4 tonic transposed to C5; source F#/G#/C# follow the A-major signature. Independent ABC decoding, including slash durations, matches all 38 note/rest events. Sixteenth repeated notes, low descending phrase and two eighth rests retained. [ABC](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/038_the_Mulberry_Bush.abc). + +Grade 4 source: https://lywgyxx.szftedu.cn/xkjc/yinyue/202209/P020220910587435677420.pdf + +Grade 5 source: https://lywgyxx.szftedu.cn/xkjc/yinyue/202209/P020220910587728087297.pdf + +## Pending / rejected sources (current) + +- Fengyang Flower Drum concern resolved in batch 3 using the PEP edition; see its accepted ledger entry above. +- There's a Hole in the Bucket in the same book: modern introduction/interlude and alternate endings intertwined with the folk tune; seek a simpler original melody source rather than copying an arrangement. +- The school PDF exceeds the web reader's size limit. Downloaded normally, rendered with Poppler; JP2 fallback warnings occurred but page images and notation were visually legible. Detail crop used to investigate ambiguous octave marks. +- Existing source policy's statement that octaveShift does not affect displayed octave dots is obsolete. Stage 2 hotfix/current implementation bakes all shifts into note octaves for consistent notation and pitch. +- Wuxi Jing's ornament dependency is resolved in the mordent batch above. It is a single upper mordent, not a verified trill. +- Deck the Hall ABC in Everyday Song Book: final bar appears half a beat short; needs original-edition comparison before admission. +- Longing for Spring's grace-note dependency is resolved; admitted in the grace-note batch above. +- PEP grade 7 PDF download initially stopped partway; resumed successfully, then verified as 79 readable pages before inspection. National platform PDF was unavailable (403); it was not treated as read. +- All Through the Night No.108 ABC has a five-quarter-beat bar in 4/4; Lavender's Blue No.126 has suspect bar divisions and a short last bar. Both require original scan comparison before admission. +- Grade 6 月亮月光光 is credited to composer 钟哲亮; it was not treated as an anonymous traditional melody. Grade 5 Old MacDonald is a two-part teaching arrangement with melody transferred between parts; the student part alone is not a complete source tune. + +- Good King Wenceslas No.153 ABC has a suspect relationship between key signature and melody; pending original edition comparison. Grade 6 金瓶似的小山 additionally credits 冰河曲, so it was not treated as an anonymous traditional composition. diff --git a/docs/sources/stage-6-legacy-audit.md b/docs/sources/stage-6-legacy-audit.md new file mode 100644 index 0000000..28db4a1 --- /dev/null +++ b/docs/sources/stage-6-legacy-audit.md @@ -0,0 +1,28 @@ +# Stage 6 — legacy score source audit + +2026-10-23, Asia/Shanghai. Rechecks below are based on source images/text actually inspected in this stage. Earlier check dates are not fresh verification. Practice tempi are editorial; only solo melodic data are included. + +| Score | Source inspected | Findings and changes | +| --- | --- | --- | +| Amazing Grace | [Public-domain numbered soprano source](https://commons.wikimedia.org/wiki/File:AmazingGraceNumberedMusicalNotation.png), complete image | Corrected initial sol to the source middle register, and the high sol in complete bars 7–8. Kept source G4–G5 with no octave shift. Restored one-beat pickup and two-beat ending; removed artificial opening rests. Joined the source line-split bar 7. All 15 written measures compared. | +| Ode to Joy | [School examination score](https://www.xnyesz.com/uploads/1/file/public/202311/20231115213157_md3qv5o7db.pdf), printed p.105 / PDF p.107 | Full 16 bars compared, with enlarged inspection. Corrected bar 12 low sol, restored the same-pitch tie from bar 12 to 13 and repeat around bars 9–16. D to C plus one octave; different-pitch slurs/lyrics excluded. | +| Happy Birthday | [University-hosted guitar textbook](https://music.hgnu.edu.cn/_upload/article/files/09/d4/faf43ffb4e08b61cf64ee0adaf16/ec5df379-7cae-4b24-9ece-880a54ee478b.pdf), printed p.28 / PDF p.32 | Removed invented opening rests. Expanded both endings: first has two pickup eighths, second is a three-beat tonic. 17 bars including pickup, source register raised one octave. | +| Twinkle | Same guitar textbook p.28 / PDF p.32 | All 12 bars agree with existing melody/rhythm. Replaced the weak previous text-page attribution with this fresh complete numbered-source comparison. Source register raised one octave. | +| Jingle Bells | Same guitar textbook p.40 / PDF p.44 | All 16 bars agree with the simplified chorus version. Metadata now says this is the chorus, not the complete verse-plus-chorus song. Register raised one octave. | +| Auld Lang Syne | [Yinban guitar course](https://www.yinban.com/a/yinban_Guitar_Course_20210412.pdf), PDF p.20 | Complete pickup and 16 full bars compared. Removed three artificial opening rests; retained the full final bar including its quarter rest. All melodic pitches and remaining durations unchanged, source register raised one octave. | +| Farewell | [School-hosted Chinese Poetry and Music: Twenty Pieces](https://cxqx.jyc.edu.cn/__local/3/D5/71/B7A1BD225359A39D710C8C3021A_A9C90F62_167DC01.pdf), printed p.222 / PDF p.238 | All 24 bars of numbered notation agree. Corrected the source-key description from E to Eb; target pitches/rhythm unchanged. The adjacent staff notation was not used as transcription input. | +| Frere Jacques | [Everyday Song Book No.104](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/104_Round_Are_You_Sleeping.abc) | The old maker PDF returned an HTML browser check. Adopted the readable ABC version: same note/duration stream, different bar divisions with two-beat pickup and ending. G4 tonic to C5. Independent ABC decoding matches all 32 events. Round entrances omitted in this solo version. | +| Mary | [Everyday Song Book No.77](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/077_Mary_Had_a_Little_Lamb.abc) | Replaced the old pitch-only source (which gave no rhythm) with the 1927 version. Nine bars including eighth pickup; dotted rhythm, tied high sol and ending rests retained. F4 to C5, all 30 events independently matched. | +| Silent Night | [Everyday Song Book No.57](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/057_Holy_Night.abc) | Old website text disagrees with the old score and was not retained as proof. Adopted the complete 12-bar 6/8 version, C4 tonic raised to C5, four eighth rests, bar 3 dotted-sixteenth/thirty-second pair and final tonic tie retained. Final tonic is C5. All 53 events independently matched. | +| Sally Gardens | [Thornton Rose ABC, John Chambers mirror](https://trillian.mit.edu/~jc/music/abc/mirror/mindspring.com/~thornton.rose/SallyGardens.abc), X:2 | Old piano page returned a server-error HTML document. Adopted the clearly identified traditional single-melody version, not the X:1 setting in the same file. 17 bars including pickup, internal phrase pickups joined to preceding bars, final repeat retained. D4 tonic to C5. All 73 events independently matched; this is a deliberate version change from the former 25-bar piano expansion. | +| Bend Etude | Original repository DSL | 15 complete bars; C4–A4, every written F4 is 2-draw whole-step bend and A4 is 3-draw whole-step bend. Added regression checks; no changed melody. Removed unsupported performance-process claims from guidance. | +| 12-Bar Blues | Original repository DSL | Twelve complete bars, G on C harp. Verified A4/Bb4/Db5 as 3-draw whole/half-step and 4-draw half-step bends. Clarified that straight written eighths do not encode shuffle timing; no chord accompaniment is supplied. No changed melody. | + +Pending: Spanish Rowing Song full 16-bar source. The old endpoint fails TLS hostname validation; its HTTP endpoint also returns an explicit storage error. A readable Hunan textbook, PDF p.49, corroborates only the first eight bars, so the second half is still unverified in this stage. Existing source-check date remains unchanged. Grade 5's Ugandan Rowing Song is a different tune. The Everyday Song Book No.112 similarly uses an unrelated Elliott Twinkle tune and must not validate the familiar French melody. + +Automated checks verify internal consistency and selected source landmarks. They are not an independent professional performance review. + +Follow-up: the Hunan Grade 1 lower-volume contents entry at p.46 is a different Lu Jingshan tune (opening 2–5–3), not the Spanish melody (opening 5–3–3). The physical-education teacher book p.199 / PDF p.208 uses that other tune as well. Neither supplies the missing eight bars. The known partial corroboration remains Hunan p.49 only. + + +Final source decision at 01:57: adopt the clearly labeled **eight-bar classroom short version** printed in the Hunan Grade 1 lower-volume p.49 activity. The page shows a final double bar after bar 8. Its full sequence is now transcribed and checked, original C raised one octave for C harp. The old sixteen-bar version remains in Git history but is no longer the active builtin. Subtitle and performance notes explicitly distinguish the short version; this does not validate or reconstruct the old missing second half. A complete pitch/duration regression covers all eight bars. The p.46 Lu Jingshan tune remains excluded. diff --git a/docs/sources/stage-7-library.md b/docs/sources/stage-7-library.md new file mode 100644 index 0000000..edf43a2 --- /dev/null +++ b/docs/sources/stage-7-library.md @@ -0,0 +1,63 @@ +# Stage 7:传统曲与入门曲均衡扩充 + +核对日期:2026-10-23,北京时间。本批加入 8 首歌曲,分为 4 首入门选曲和 4 首传统曲选曲,曲库累计 48 首歌曲、4 条原创练习。“入门”表示本批的练习用途,不排除其本身也采用传统曲调;原始歌集的具体署名保留,不把所有童谣都改称匿名民歌。 + +来源为 [The Everyday Song Book(1927,第12版)的 John Chambers ABC 转录](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/)。此次直接核对的是 ABC 音符文本,不声称已经检查原书扫描件。每首曲谱的 `sourceUrl` 指向具体文件。歌词、伴奏与异音连奏线不录入,速度为编辑建议。曲名相同不表示所有流传版本都相同。 + +| 本批用途 | ID / 曲名 | 文件与核对要点 | +| --- | --- | --- | +| 入门 | lightly-row / 轻轻划 | `097_Lightly_Row.abc`,2/4,16 小节。G4 主音移到 C5;保留第 8、16 小节 mi + 四分休止。与现有《西班牙划船歌》8 小节短版的末句不同,两个版本不合并。 | +| 入门 | hickory-dickory-dock / 滴答滴答钟声响 | `019_Dickory_Dickory_Dock.abc`,Elliott 版本,6/8,8 小节。F4 主音移到 C5;保留全部独立休止,第 6 小节附点四分 la。 | +| 入门 | see-saw / 跷跷板 | `008_See-saw_Margery_Daw.abc`,Elliott 版本,6/8,8 小节。Ab4 主音移到 C5;第 5 小节原文本跨行,按实际小节线连接;低 sol、低 si 及结尾休止均保留。 | +| 入门 | lucy-locket / 露西的口袋 | `067_Lucy_Locket.abc`,G. M. S. 署名版本,2/4,16 小节。G4 主音移到 C5;仅四分/二分时值,第 14 小节低 si 来自原调 F#4。 | +| 传统曲 | busy-bee / 勤劳的小蜜蜂 | `034_How_Doth_the_Little_Busy_Bee.abc`,署名 Old English,4/4,9 个书写小节。Eb4 主音移到 C5;一拍弱起、三拍尾节、末句 fa–mi 两个八分音保留。不是常见德国童谣《小蜜蜂》的旋律。 | +| 传统曲 | jolly-miller / 快乐的磨坊主 | `098_Jolly_is_the_Miller.abc`,英国游戏歌,4/4,8 小节。A4 主音移到 C5;保留四组附点八分/十六分与低音句。F4 为 2 孔吸气全音压音,A4 为 3 孔吸气全音压音,标为进阶而非无压音入门。 | +| 传统曲 | good-comrade / 好伙伴 | `148_the_Good_Comrade.abc`,歌集署名 Folksong,4/4,13 个书写小节。G4 主音移到 C5;原文本第一段三拍末节与第二段一拍起拍合成同一小节,音符和休止均未增删。 | +| 传统曲 | ash-grove / 白蜡树林 | `131_the_Ash_Grove.abc`,威尔士民歌,3/4,33 个书写小节。Ab 调整体下移半音为 G,C 调口琴第二把位。首段反复完整展开,重复段弱起和下一段弱起接入前小节;第 24 书写小节原还原 D5 对应 C#5(4 吸半音压音),不能按原调号误读成 Db5。 | + +## 可重复的来源对照 + +`tests/fixtures/stage7-sources/` 保存这 8 份 ABC 的音符和归属元数据摘录;只去掉歌词、注释及非音符说明。`manifest.json` 明确每首的原调与半音移调量。 + +`tests/stage7Sources.test.ts` 使用独立于简谱导入器的严格 ABC 解码,比较每一小节的绝对 MIDI 音高与四分拍时长。它拒绝不支持的语法,显式处理《白蜡树林》的反复展开和《好伙伴》的弱起衔接;另检查全小节拍数及首尾互补。生成器仍执行来源字段、节拍、延音和标准 Richter 孔位门禁。自动检查用于防止转录和后续修改出错,不替代音乐专业复核或实体口琴试奏。 + +## 暂缓的候选 + +- `090_Lazy_Sheep.abc`:2/4 中出现 `c B c2` 四拍小节;不猜测补写小节线。 +- `094_Oats_and_Beans.abc`:6/8 中出现 `B2 B G3 E` 七个八分单位的小节;待原书对照。 +- `134-O_No_John-V1.abc`:4/4 中有七个八分单位的小节,尚未找到可解释的拍号变更;暂缓。 +- `021-Jolly_Old_St_Nicholas-V1.abc`:缺少明确小节线且含多声部和弦;本批不自动修补转录。 + +这些问题针对所读 ABC 版本,不表示曲目本身有误,也不以耳熟的旋律替代证据。 + +## 第二批:数字简谱与短曲(2026-10-23) + +再加入传统曲、入门曲各2首,累计56项(52歌曲、4原创练习)。本阶段共12首新增选曲,按本批用途为传统曲6首、入门6首。 + +| 本批用途 | ID / 曲名 | 来源与逐首核对 | +| --- | --- | --- | +| 入门 | clap-hands / 大家一起拍拍手 | [ABC No.7](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/007_Well_all_Clap_Hands_Together.abc),G4主音移为C5。9个书写小节,一拍弱起、三拍尾节;原文本两段的三拍结束与一拍起拍接成一个小节。28个事件独立解码比对。 | +| 入门 | ding-dong-bell / 叮咚铃声 | [ABC No.32](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/032_Ding_Dong_Bell.abc),F4主音移为C5。10小节,首尾反复保留,40个事件独立比对。第6小节低la为3吸全音压音,明确标注“压音”,不列入无需压音结果。 | +| 传统曲 | flowing-stream / 小河淌水 | [《音乐鉴赏》印刷p.15 / PDF p.22,谱例1-6](https://backend.sjhtbook.com/upload/file/2025/04/80fb4b7923894770828045c9e613e236.pdf#page=22)。整页及4行放大图目视核对。原1=降E改为1=C并提高八度,A羽主音,C口琴第四把位。23个书写小节,前21小节反复;9处自由延长、9次显式拍号变更、2处同音延音、低la与高mi均保留。71个事件另以绝对音高/时值表对照。书面拍数不等于自由延长后的实奏长度。 | +| 传统曲 | polish-waltz / 波兰圆舞曲 | [人教七年级下册简谱,印刷p.48 / PDF p.53](https://dev98.oa.henanyuexue.com/static/szjc-resources/pdf/q/f/2/7.pdf#page=53)。全页与放大谱面目视核对。32小节:前16小节单旋律,后16小节每对谱行只取上声部,不把下声部当成旋律续行。1=C保留并提高八度,D5–F6自然音;第7→8、11→12、15→16小节同音延音保留。72个事件以绝对音高/时值表对照。 | + +第二批共211个音符事件,测试位于 `tests/stage7Batch2.test.ts`。ABC参照解码器移至 `tests/helpers/abcReference.ts`,首批8曲的对照仍运行。第二批摘录位于 `tests/fixtures/stage7-batch2/`。 + +制作摘录时曾发现脚本把行首 `|:` 当作头字段而丢弃整行;10小节断言检出参考文件只有7小节。已修正为仅识别字母开头的头字段,并重新从原始ABC生成摘录。未修改来源旋律或用调整预期数量掩盖该错误。 + +新增可选小节号按这里的“书写小节”计数,不是演奏展开后的次数。数字谱的来源对照仍为本次作者目视核对加回归表,不冒充第二位音乐专业人士的独立审校。 + +## 第三批:二拍、三拍入门与六八拍传统曲(2026-10-23) + +再加入入门/传统曲各2首;当前60项(56歌曲、4原创练习),Stage 7累计新增16首,用途分类各8首。以下核对针对链接中的单旋律ABC文本,未声称原书扫描件复核。 + +| 用途 | ID / 曲名 | 来源与核对 | +| --- | --- | --- | +| 入门 | left-right / 左右齐步走 | [No.26 Left Right](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/026_Left_Right.abc),降E4主音移至C5(+9半音)。16小节2/4,28事件,低si至高re自然音。歌集未署曲作者,不擅自归给某作者。 | +| 入门 | snowbirds / 雪鸟 | [No.29 The Snowbirds](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/029_the_Snowbirds.abc),E4主音移至C5(+8)。16小节3/4,42事件,C5–C6自然音;保留歌集B. J. Relemann署名。 | +| 传统曲 | little-ship / 小小航船 | [No.85 The Little Ship](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/085_the_Little_Ship.abc),A4主音移至C5(+3)。17书写小节6/8,68事件,其中1休止;首半拍和末2.5拍互补。6处同音延音,第13小节第3事件的自由延长保留。音域E4–G5,4个低fa/低la压音音符,不纳入无需压音筛选。 | +| 传统曲 | hunting-we-will-go / 我们去狩猎 | [No.188 A Hunting We Will Go](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/188_A_Hunting_We_Will_Go.abc),C4主音提高八度(+12)。21书写小节6/8,89事件;半拍弱起与2.5拍尾节互补。6处同音延音和第11、19小节的自由延长保留,C5–F6自然音。保留附点八分/十六分,未抹平为均分八分音。 | + +227个音符/休止事件逐小节对照音高、时值、延音和延长记号。严格参照解码器扩展了C/E调号与H/横线记号,另有固定样例检验十六分同音延音和延长落点;所有先前来源对照仍运行。见 tests/stage7Batch3.test.ts 和 tests/fixtures/stage7-batch3/。歌词及异音连奏线不收录;演奏速度是编辑建议。 + +本次候选复查仍暂缓 Lazy Sheep、Oats and Beans 的异常小节。All Through the Night 第7小节 E3 F>F 为5拍;Good-Night Ladies 含转录者明确注明的小节线问题;Harp That Once Through Tara's Halls 含 B c d zB 五拍小节,均待进一步来源证据。Polly's Piano 的第5小节有转录者对音符/休止的推断,本批不作为无疑义入门谱纳入。以上是对特定转录的限制,不是对曲目本身的否定。 diff --git a/docs/sources/stage-9-library.md b/docs/sources/stage-9-library.md new file mode 100644 index 0000000..3f943db --- /dev/null +++ b/docs/sources/stage-9-library.md @@ -0,0 +1,32 @@ +# Stage 9 逐首核对台账 + +2026-10-23,第一批新增传统曲与入门曲各1首;内置库60→62项(58歌曲、4原创练习)。本批116个音符/休止事件逐小节对照,未把歌曲长度补齐成常见的16小节。 + +| 用途 | 曲目 | 来源与核对 | +| --- | --- | --- | +| 传统曲 | 康定情歌 / kangding-love-song | [香港中乐团《唢呐初级教程》第29页](https://ynyqxg.hkco.org/uploads/docs/5bd9828a7a7201.pdf#page=29),原页署四川民歌。8小节4/4、63事件;原页未单列1=调号,按音级移写1=C并提高八度,不声称原调为某一绝对调性。A羽主音、C口琴第四把位,A4–A5,七个低la均为3吸全音压音。六处同音延音位置为小节/音符:2/7、4/8、5/2、5/5、6/3、7/2。十六分、末小节的八分/四分/八分/二分组合均保留。 | +| 入门 | 一起来学唱 / learn-to-sing | [The Everyday Song Book No.25,John Chambers 2017 ABC](https://trillian.mit.edu/~jc/music/book/EverydaySongBook/025_Come_Let_Us_Learn_to_Sing.abc)。原F4主音移为C5(+7半音),15小节4/4、53事件含3休止,C5–C6均为自然音。第4、8、15小节末尾各四分休止。中文名为编辑译名;原转录未署曲作者。 | + +《康定情歌》由原页放大图另录绝对MIDI音高与四分拍时长,固定在 `tests/stage9Library.test.ts`;《一起来学唱》由独立ABC解码器逐事件比较,音乐及来源字段摘录位于 `tests/fixtures/stage9/`。这不是第二位音乐专家的独立审校。未收录歌词和异音连奏线,编辑速度不声称来自原谱。 + +## 待补齐表达能力的候选 + +- 同一本香港中乐团教程第33页《苏武牧羊》含重音、保持音、前倚音及末尾延长加跨小节延音。当前重音/保持音未建模,暂缓收录;后续不能把谱面上方的保持音短线误作八度标记。 +- [世教宏图公开教材节选,印刷第21页/PDF第29页《绣荷包》](https://www.sjhtbook.com/upload/file/2021/03/7b45ed52434b4c1aac635368236c2bde.pdf#page=29)含滑音箭头,需要明确处理后再录入。 +- The Farmer No.5 有分段弱起与末尾反复,需单独核对段落边界。 +- 已读教材《虹彩妹妹》《郊游》页为分声部/回声训练,不直接充作完整旋律;《月亮月光光》《金瓶似的小山》有明确作曲署名,不列为匿名民歌。 + +## 本批验证 + +145测试、62项曲库来源/节拍/延音/孔位门禁及生产构建通过。Chromium与Firefox分别检查两首在420/900/1080宽度的小节编号和谱面不相交;实际1440×1000桌面及390×844手机交互通过。两首各自JSON、SVG、PNG导出,JSON旋律字段与生成文件深层相等;打印行及编号完整。Chromium实际PDF均1页,Firefox只验证打印DOM。专业复核、实体口琴试奏与实体打印未执行。 + +## 第二批:补齐演奏记号后收录(2026-10-23) + +新增 `articulation: accent/tenuto`,DSL音符后缀 `>` / `_`;主音可与倚音、波音、延音和延长叠加,辅助音与休止符拒绝此属性。书面音高/时值不变,SVG/PNG/打印同源渲染,编号与演奏标记分层。 + +| 用途 | 曲目 | 来源及核对 | +| --- | --- | --- | +| 传统曲 | 苏武牧羊 / su-wu-herding-sheep | [香港中乐团教程第33页](https://ynyqxg.hkco.org/uploads/docs/5bd9828a7a7201.pdf#page=33)。29个书写小节(末行5小节),77个主音事件、3个八分前倚音;15处重音、29处保持音。末两全音符同音延音,前一音自由延长。G4–D6,3个低la压音。选用学堂乐歌的所列器乐版本;原页未署曲作者,不推定为匿名作品。 | +| 入门 | 小蜜蜂 / little-bee | [同教程第26页](https://ynyqxg.hkco.org/uploads/docs/5bd9828a7a7201.pdf#page=26)。8小节4/4,51事件含两处四分休止,C5–G5全部自然音。与既有英国旧歌《勤劳的小蜜蜂》旋律不同,未按同曲重复计数。 | + +两曲原页均未单列1=调号;按音级移写1=C并提高八度。只转录旋律和已支持演奏记号,不收录歌词、异音连奏线,速度为编辑建议。高清原页另录为绝对MIDI/时值表,逐音及逐记号检查见tests/stage9Batch2.test.ts。Stage 9至此传统/入门各2首,曲库64项(60歌曲、4原创练习)。《绣荷包》滑音及《农夫》段落/反复仍未处理,不计入本批。 diff --git a/docs/stages/stage-4.md b/docs/stages/stage-4.md new file mode 100644 index 0000000..4f97c02 --- /dev/null +++ b/docs/stages/stage-4.md @@ -0,0 +1,32 @@ +# Stage 4 — notation correctness and validation + +Date: 2026-10-22 (Asia/Shanghai). Baseline: 9078226. + +## User-visible changes + +- Sustained notes display duration dashes; long rests display repeated zeroes. Multiple octave dots and ties crossing line breaks are retained. +- Dense rows wrap within the page; mobile notation reflows into readable short rows instead of shrinking the entire desktop sheet. +- Standard Richter keys G–B use their normal third-octave roots; C–F# use fourth-octave roots. C-harp 10 blow B6 is one semitone bent, Bb6 two. Blow bends are labeled as blowing. The engine also covers standard overdraw pitches when its advanced mode is explicitly enabled; the current UI remains in normal/bend mode. +- Imports reject zero meter, impossible dates, unrenderable note lengths, invalid octave ranges and malformed source formats. Invalid ties and beat mismatches are visible beside the score. A single incomplete measure no longer qualifies as a complementary pickup pair. +- Failed local storage writes retain the import dialog and JSON. Export errors are visible. Printed titles are assigned as text instead of interpolated into HTML. +- Score generation checks rhythm, ties, provenance fields and duplicate IDs before writing. A regression check compares all generated scores with their source DSL. + +## Evidence + +- `npm test`: 24/24 passed. +- `npm run scores:build` and `npm run scores:validate`: 14/14 passed; source melodies unchanged. +- `npm run build` and `git diff --check`: passed. +- Chromium interaction checks at 1440×1000 and 390×844, dark UI: search/select, import, invalid meter, quota failure, SVG content, PNG dimensions (2160 px wide), cross-row ties, multiple octaves and print-title injection passed. No page errors or console warnings/errors. +- Browser plugin was not available; regular Playwright used through the locally installed runtime. UI screenshots were inspected. Mobile screenshot capture waits for the drawer transition to finish. + +## References and limits + +- [HOHNER Standard Richter tables](https://www.hohner-cshop.de/out/media/HOHNER%20DATASHEET%20for%2010-hole%20Standard%20Richter.pdf), accessed 2026-10-22. +- [Steve Baker: Bending & Overblowing](https://my.hohner.de/t/harmonica-terminology-3-bending-overblowing/1377), accessed 2026-10-22. +- Source URLs/dates are provenance metadata, not proof of melody correctness. This stage did not freshly transcribe or independently recheck every historical melody. +- Standard Richter only; Low/High and alternate tunings are not modeled. Triplets remain unsupported. Physical printer pagination, real-device Safari/Firefox and listening/human performance verification remain outside this stage's evidence. +- Local stage completion; no remote release or ZIP created. Long-term archive: `C:\Users\ax2\Documents\技术博客\harpscore`. + +## Next + +Stage 5: expand the traceable library to at least 40 distinct scores, balancing Chinese familiar/introductory material with blues exercises and Western traditional tunes as requested on 2026-10-22. Original exercises and transposed copies are counted separately. Continue until the daily 02:00 Beijing pause boundary, then save handoff and pause the goal without automatic resumption. diff --git a/docs/stages/stage-5-batch-2.md b/docs/stages/stage-5-batch-2.md new file mode 100644 index 0000000..d9d9804 --- /dev/null +++ b/docs/stages/stage-5-batch-2.md @@ -0,0 +1,11 @@ +# Stage 5 batch 2 — 2026-10-22 + +Added four source-checked melodies: Xiao Bai Cai, London Bridge, Oh Susanna and Zou Jiang Zhou. Library grows from 16 to 20 entries (18 songs, 2 original exercises). This remains a partial batch toward the 40-song expansion goal. + +Xiao Bai Cai requires 5/4 followed by 4/4. Measure-level `timeSignature` now imports, persists and renders with reserved horizontal space. DSL syntax is `[M:4/4]` at a measure start; later measures inherit it. Invalid meters and misplaced tokens are rejected. Pickup suppression is conservative for pieces with meter changes. JSON/SVG export preserves the change. + +ABC/LilyPond source text now has the explicit `music-text` source format and a reader-facing label. This is source metadata, not a general-purpose ABC importer. Source edition, transposition and repeat handling are recorded in each score and the transcription ledger. + +Validation: 33 tests pass, all 20 scores pass the rhythm/source/playability gate, production build succeeds. Chromium at 1440×1100 opened every entry with no notation warnings or browser errors; 390×844 mobile search/reflow and full 420px sheet checked visually. Xiao Bai Cai JSON and SVG downloads preserve its 4/4 change. Screenshots retained in the project archive and task outputs. + +Limits: no physical harmonica performance, real mobile-device test, or human second-reader sign-off; phrase slurs, grace notes and triplets are not yet supported. Unverified candidates remain outside the built-in library. No release/push in this batch. diff --git a/docs/stages/stage-5-batch-3.md b/docs/stages/stage-5-batch-3.md new file mode 100644 index 0000000..e32d1dd --- /dev/null +++ b/docs/stages/stage-5-batch-3.md @@ -0,0 +1,11 @@ +# Stage 5 batch 3 — 2026-10-22 + +Library grows from 20 to 25 entries: 23 songs and 2 original exercises. Added Yankee Doodle (verse + chorus), My Bonnie (full 32-bar form plus pickup), Fengyang Flower Drum (15-bar PEP edition), Click Go the Shears (16 bars) and Santa Lucia (repeat and alternate endings retained/expanded). + +Sources and adaptations are recorded in the transcription ledger. An independent one-off ABC decoder compared all note/rest events for Yankee Doodle (61) and My Bonnie (67) against generated JSON, including pitch register and duration. It does not reuse the application's import/notation code. My Bonnie's draft register was corrected before the final match; source traceability is not a substitute for checking octaves. + +Santa Lucia exposed a printed-notation bug: a natural note following a sharp of the same degree/octave in one bar had the correct calculated pitch but no cancellation sign. The SVG renderer now prints the natural sign. Alterations remain explicit per note in data/DSL; state resets at each bar. Tests cover sharp/flat cancellation, octave separation and bar resets. + +Validation: 41 tests pass; all 25 scores pass rhythm, tie, source and playability gates; production build passes. Chromium opens every exact title with no notation warnings or browser errors. Desktop 1440×1100 verifies the Chinese-folk filter and SVG download with natural sign; mobile 390×844 verifies search/select/reflow. The full 1080px Santa Lucia SVG was rendered separately for uncropped visual inspection. + +No human second-reader, physical-instrument or real-device sign-off is claimed. Grace-note and free-rhythm candidates remain pending. Work is locally committed and archived; no release or remote push in this batch. The broader expansion goal remains active. diff --git a/docs/stages/stage-5-grace.md b/docs/stages/stage-5-grace.md new file mode 100644 index 0000000..b3fb7fa --- /dev/null +++ b/docs/stages/stage-5-grace.md @@ -0,0 +1,13 @@ +# Stage 5: grace notes and blues exercises — 2026-10-22 + +The library grows from 25 to 28 entries: 24 songs and four original exercises. Longing for Spring now retains the PEP edition's small G before F in the penultimate bar. The new call-response and low blues-scale studies are explicitly original exercises, not additions to the target of at least 40 distinct songs. + +The importer accepts one to four pre-grace notes in JSON or `{5}4` / `{6;1'}2` DSL. Small eighth/sixteenth shapes carry pitch/register but do not add independent measure beats. Nested graces, rests, lyrics, ties and additional techniques on graces are rejected. Octave shifting is baked into both principal and grace notes. A tie crossing an intervening grace group is flagged as unsupported. + +SVG and PNG exports show small raised digits, octave dots, accidentals, beams, connecting curves and fingering. Grace pitches are included in range, bend counts and playability; natural-note filtering cannot hide a difficult grace. Accidentals on graces participate in subsequent cancellation signs. JSON exports preserve the structure. + +Validation: 49 tests pass; 28 built-ins pass source/rhythm/tie/playability gates; production build passes. Chromium opens every exact title without notation warnings or console/page errors. Desktop 1440×1100 checks analysis and SVG/JSON/PNG downloads; mobile 390×844 checks reflow and the grace note in context. Full 1080px Mozart/blues SVGs and 420px mobile Mozart sheet were rendered for inspection. The first mobile capture caught sidebar animation and was replaced after waiting for the sidebar to finish closing. + +Source review covers Mozart's two complete textbook pages, chromatic bridge, pickup/rest complement, ties and grace. The two original exercises have targeted checks for natural reeds/breathing rests and the G blues pitch set with Bb4/Db5 half-step draw bends. An initial test incorrectly expected a rest or long note in every call-response bar; the intended musical requirement is rest at each four-bar phrase ending, and the assertion was corrected accordingly. + +Limits: no grace playback/timing allocation, trill/mordent realization or second-reader/physical-instrument acceptance is claimed. Wuxi Jing remains pending: the first-bar wavy ornament needs identification and auxiliary-pitch verification. Code and evidence are local; this batch does not constitute a release. The wider expansion goal remains active. diff --git a/docs/stages/stage-5-library.md b/docs/stages/stage-5-library.md new file mode 100644 index 0000000..f9ed797 --- /dev/null +++ b/docs/stages/stage-5-library.md @@ -0,0 +1,9 @@ +# Stage 5 library usability — 2026-10-22 + +The 20-score library now supports combined search terms (title, subtitle, composer and tags), maximum difficulty, exact tag, natural-note/bend filters, difficulty/title ordering and local favorites. Counts show visible/total entries and an empty result explains how to reset. The currently open score remains readable when it no longer matches the filter. + +The natural-note filter uses prepared fingering and excludes unplayable notes and overblows as well as bends. Favorite IDs persist separately from imported scores; a failed write leaves the visible favorite state unchanged and reports the error. Filters reset on reload; favorites persist in the current browser, without account sync. + +Validation: 35 automated tests and production build pass. Chromium desktop 1440×1000 and mobile 390×844 exercise combined filters, empty results, reset, favorite/unfavorite, reload persistence and forced storage rejection. The first browser run exposed an ambiguous accessible name on the sort selector; explicit labels fix it. A screenshot taken during sidebar animation was replaced after waiting for its final position; final screenshots were visually inspected. + +This increment adds no new melodies. Source expansion remains active and daily 02:00 Beijing pause remains configured. diff --git a/docs/stages/stage-5-mordent.md b/docs/stages/stage-5-mordent.md new file mode 100644 index 0000000..8646a3d --- /dev/null +++ b/docs/stages/stage-5-mordent.md @@ -0,0 +1,11 @@ +# Stage 5: single mordents and two Chinese melodies — 2026-10-22 + +The library grows to 30 entries (26 songs, four original exercises) with Wuxi Jing and Li Shutong's Spring Excursion. Both retain the complete selected 16-bar main melodies. The source ledger records edition, page, register, ornament and omitted accompaniment/expression details. + +Wuxi Jing's first-bar ornament was identified as a single upper mordent by comparing the source's short wave with the notation/playback examples in Basic Music Theory Course, p.183. The implementation stores an explicit neighboring pitch instead of treating the symbol as a cosmetic trill. DSL `[U:3']2'/` and `[L:1]2` represent upper/lower mordents. JSON `ornament` contains `kind` and `auxiliary`; the auxiliary has no independent duration. Direction, adjacent scale degree, octave limits and unsupported nested decoration are checked. + +Auxiliary pitches participate in fingering/range/bend analysis and library filters. Each is counted once and labelled separately in the UI. The SVG draws the wave (with a vertical stroke for lower mordents), explicit auxiliary alterations/cancellations and a small auxiliary fingering row. Extra space separates it from lyrics. Unsupported auxiliary pitches show `辅 ?` and a located analysis error. Ties crossing into a mordent are flagged. + +Validation: 56 tests pass, all 30 built-ins pass the source/rhythm/tie/playability gate, production build passes. Chromium opens all exact titles; no notation warnings, page errors or console errors. SVG/JSON/PNG exports preserve the Wuxi ornament and both graces. An imported F#5 auxiliary is correctly rejected for the normal C-harp mode and has no stale fingering. Desktop 1440×1100, mobile 390×844 and complete 1080px/420px score images inspected. Spring Excursion's complete exported SVG was compared to the two source pages. + +No audio playback, exact ornament timing, compound mordents, turns or trill realization is added. No physical-instrument, real-device or independent human second-reader sign-off is claimed. Work and evidence remain local, not a release; the wider expansion goal continues. diff --git a/docs/stages/stage-6-ci.md b/docs/stages/stage-6-ci.md new file mode 100644 index 0000000..a5e7c98 --- /dev/null +++ b/docs/stages/stage-6-ci.md @@ -0,0 +1,14 @@ +# Stage 6 — CI configuration and delivery state + +2026-10-23, Asia/Shanghai. + +The validation workflow runs on pushes to main/codex branches, pull requests and manual dispatch. Its Windows/Linux matrix uses Node 24 and read-only repository permissions. It installs the lockfile, runs tests and score gates, regenerates the library and rejects generated differences, then builds the application. Official checkout/setup-node v7.0.0 tags were resolved with `git ls-remote` and pinned to full commits; credentials are not persisted. + +Local Windows validation: Node 26.10.0 passes all 112 tests, 44 score checks and production build. A separate Node 24.22.1 executable also passes all 112 tests, TypeScript compilation and Vite production build. The initial `npx --package=node@24` attempt still selected Node 26 and is not counted as Node 24 evidence. A subsequent explicit executable invocation confirms the correct version. + +Remote delivery: [draft PR #1](https://github.com/ax2/harpscore/pull/1), branch `codex/stage-4-score-correctness`. No merge or release. GitHub confirms the workflow file exists on the branch, repository Actions is enabled, and the PR is mergeable. However, the Actions runs/workflows APIs returned empty results after the initial push and PR creation. Manual dispatch returned HTTP 404 because the workflow was not found on the default branch. The reason automatic events have not registered a run remains unconfirmed. No remote CI success is claimed, and the default branch was not modified merely to force a run. + +Remaining review boundaries include the Spanish Rowing Song source gap, Linux/remote CI execution, other browser engines, physical instrument/printer testing and independent music review. Source details are recorded in the [legacy audit](../sources/stage-6-legacy-audit.md). + + +Update at 01:50 Beijing: the remote API now reports successful push runs for 5b9b439 and 8cc0c1c. [Run 39823095925](https://github.com/ax2/harpscore/actions/runs/39823095925) confirms both Ubuntu and Windows jobs passed dependency installation, tests, score validation, regeneration consistency and build for **8cc0c1c** (112-test revision). Its timestamps are 17:29:08–17:29:50 UTC; the reason earlier queries returned no runs remains unknown. The branch ref has since advanced, while the PR API still reports head 8cc0c1c. New import/favorites/Firefox changes have local validation but are not covered by this older run. Latest local suites now contain 116 passing tests on both Node 24 and Node 26; see the Firefox note. The historical empty-run observations above are retained as history, not current status. diff --git a/docs/stages/stage-6-favorites.md b/docs/stages/stage-6-favorites.md new file mode 100644 index 0000000..eb363a6 --- /dev/null +++ b/docs/stages/stage-6-favorites.md @@ -0,0 +1,11 @@ +# Stage 6 — favorites across tabs + +2026-10-23, Asia/Shanghai. + +Favorites previously saved a whole list from the page's stale state, overwriting another tab's additions. Each click now applies an explicit add/remove choice to freshly read storage inside a Web Lock. Repeating the same choice is idempotent. Tabs refresh favorites on storage changes, and manual library reload refreshes them too. Malformed/unreadable data blocks writing; quota failures leave the stored list and displayed pressed state intact. A pending write disables the favorite button. Browsers without Web Locks use a synchronous fallback without a simultaneous cross-tab guarantee. + +A related responsive check found that resizing an open mobile drawer to desktop left the page covered by its backdrop. The backdrop is now displayed only at the mobile breakpoint; desktop controls remain usable. + +Validation: 116 tests and production build pass. Four new tests cover stale state, idempotence, corrupt/unreadable data, quota failure and reading after lock acquisition. Chromium uses three pages in one browser context to hold a real lock and queue two user clicks. Both different additions survive; duplicate removals remain removed; both filtered lists update, and storage failure and manual refresh behave correctly. Desktop 1440×1000 and mobile 390×844 screenshots inspected. The first responsive script attempted a desktop click with the stale drawer backdrop still present; the behavior was reproduced and fixed before rerunning successfully. + +During Node 26 testing, synchronous throwing lock callbacks stalled the malformed-data test. Returning an async callback, as the personal-library store already does, makes rejection explicit; all 116 tests then finish. No unrelated process or test failure was hidden. diff --git a/docs/stages/stage-6-firefox.md b/docs/stages/stage-6-firefox.md new file mode 100644 index 0000000..936dd6f --- /dev/null +++ b/docs/stages/stage-6-firefox.md @@ -0,0 +1,23 @@ +# Stage 6 — Firefox and storage visibility + +2026-10-23, Asia/Shanghai. + +Firefox 153.0 was added to local headless verification. Its initial localhost request failed under the default proxy configuration; an isolated test launch with `network.proxy.type=0` reaches the existing local development server. No user browser profile or system proxy setting was changed. + +The Firefox favorites test twice reproduced a lost update: two tabs queued different additions under the same Web Lock, but only the second item remained in all three pages. Chromium passed that scenario. Keeping the lock until a subsequent task after writing fixed this reproduction. Storage writes now use one shared helper that also starts the mutation in a new task after acquiring the lock. Both favorites and personal scores use it, so reads and writes do not rely on a localStorage snapshot surviving the current task boundary. The existing personal-library Firefox scenario passed before the helper change; this pass does not claim it independently reproduced the same loss there. + +The snapshot explanation is an inference consistent with the reproduction and [Mozilla's discussion of task-scoped storage values](https://bugzilla.mozilla.org/show_bug.cgi?id=1378716). The [HTML storage specification](https://html.spec.whatwg.org/multipage/webstorage.html) warns that localStorage alone does not provide locking. This helper is a tested compatibility measure, not a claim of transactional guarantees across arbitrary browser versions, suspended processes or browsers without Web Locks. + +Validation on the final helper: + +- Node 26.10.0 and an explicit Node 24.22.1 executable each pass 117 tests. Production build succeeds. +- Chromium 151.0.7922.34 and Firefox 153.0 each run four tabs through 20 concurrent rounds. Each finishes with all 80 favorites and 80 personal scores, including 80 unique IDs despite simultaneous same-ID imports. +- Firefox import, restore, personal-library corruption/stale deletion, favorites/resize and technique/export suites pass with no page errors. All 44 builtin scores open with their expected pitch-analysis status; SVG, PNG and JSON exports retain technique information. Print DOM annotations are checked, but Firefox's native PDF pagination is not verified. +- Firefox 390×844 import and 420-wide technique screenshots inspected. Desktop/mobile evidence is retained with the browser scripts and results. + +Remote evidence is tracked separately in [the CI note](stage-6-ci.md); successful older revisions do not certify this newer helper. + + +Final checkpoint at 01:54: a regression now verifies that a queued publication happens before lock release on both success and error. The helper keeps its post-write task checkpoint in `finally`, because a restore may have written its recovery copy before a later write fails. Node 24/26 pass all 117 tests; production asset is index-Dm19vT0d.js. + +A repeated stress run made alongside the build reported 51 favorites instead of 52 in Chromium at round 12. That browser context was not retained, so delayed observation versus persistent loss cannot be determined from that run. The stress script now records immediate counts and waits up to three seconds for both data sets to become visible before checking IDs. A subsequent 20-round run passed; a 100-round run then passed on each browser, with 400 favorites and 400 personal scores/unique IDs per browser and no immediate-count discrepancies recorded. These later successes do not erase the isolated failure. Reproduction under simultaneous load remains a follow-up; do not describe this as a universal storage-atomicity guarantee. diff --git a/docs/stages/stage-6-import.md b/docs/stages/stage-6-import.md new file mode 100644 index 0000000..b41ddf6 --- /dev/null +++ b/docs/stages/stage-6-import.md @@ -0,0 +1,11 @@ +# Stage 6 — import dialog interaction + +2026-10-23, Asia/Shanghai. + +The import window previously left focus behind the modal and did not contain keyboard navigation. It now focuses the JSON field, wraps Tab/Shift+Tab through enabled controls, closes on Escape when idle, and returns focus to the import action. The empty and selected toolbars reuse DOM nodes for different actions, so focus restoration finds the semantic import trigger instead of a stale button reference. + +File reading reports its pending state and disables submission until complete. Typing, a newer selection, closing the window or starting a save invalidates an earlier read. A stale read therefore cannot overwrite a newer draft or a reopened window. Errors are associated with the field and announced as alerts. During saving, focus moves to the dialog, Tab stays there, and Escape/backdrop clicks cannot close it or move focus behind it. + +Modal overlays now sit above the mobile sidebar, including during its transition. A short landscape viewport can scroll the dialog to reach its footer buttons. + +Validation: production build and 112 tests pass. Chromium checks initial focus, both Tab boundaries, invalid JSON alerts, Escape/cancel, retained drafts, delayed file read races, a real shared-context storage lock, successful save, simulated quota failure, and focus return after the empty-to-selected toolbar change. Desktop 1440×1000, phone 390×844 and landscape 844×390 screenshots were inspected. The first test lock used separate browser contexts and did not actually block the save; it was corrected to use two pages in one context. The first phone capture overlapped the responsive sidebar transition; final captures wait for it to settle. No physical keyboard, screen-reader, device or other browser-engine acceptance is claimed. diff --git a/docs/stages/stage-6-legacy.md b/docs/stages/stage-6-legacy.md new file mode 100644 index 0000000..7a7905e --- /dev/null +++ b/docs/stages/stage-6-legacy.md @@ -0,0 +1,17 @@ +# Stage 6 — legacy melody audit and import boundaries + +2026-10-23, Asia/Shanghai. + +Old metadata and passing rhythm checks concealed incorrect octaves, missing ties and omitted repeats. This pass freshly compares eleven legacy songs with complete readable sources and checks the two original exercises. The remaining legacy song, Spanish Rowing Song, still needs a readable source for its second half. See the [per-score audit](../sources/stage-6-legacy-audit.md). + +- Amazing Grace: restore source pickup, two high-sol notes, two-beat ending and G4–G5 register. +- Ode to Joy: restore low sol, cross-bar same-pitch tie and second-section repeat. +- Happy Birthday: expand both endings; remove artificial opening rests. Auld Lang Syne also keeps its actual pickup instead of padded rests. +- Mary, Frere Jacques, Silent Night and Sally Gardens: adopt explicitly identified, readable ABC versions. Version differences are documented, including the 6/8 Silent Night and 17-bar Sally Gardens; no mixture of incompatible arrangements is claimed. +- Twinkle, Jingle Bells and Farewell: fresh complete numbered-source comparisons. Melody/rhythm unchanged; improve source attribution and distinguish the Jingle Bells chorus from a full song. Farewell's original tonic is Eb, not E. +- Two original exercises: check their declared bar counts and bend targets, distinguish written straight eighths from an optional shuffle interpretation. +- Imports reject inherited property names as keys. Truncation and duplicate suffixes preserve Unicode code points and stable canonical IDs across JSON export/reimport and storage reload. Minimum corpus checks now require 40 songs and four original exercises. + +Validation: 112 tests, 44 score checks and production build; independent ABC decoding matches 188 events across the four adopted sources. Thirteen actual A4 PDFs preserve every rendered row and tie. Four desktop (1440×1000) and mobile (390×844) views captured, with representative views and PDF pages inspected. Browser import tests reject `__proto__`, save colliding supplementary-plane Unicode IDs, then reload without changing their identities. No browser page errors. + +The first mobile capture occurred during the sidebar's responsive transition and was discarded as acceptance evidence; the script now waits for the sidebar to be fully off-screen and the compact SVG layout to settle. Source tables and selected regression landmarks are not an independent professional performance review. Spanish Rowing Song's second half, other browser engines and real instruments/devices remain outside this pass's verification. diff --git a/docs/stages/stage-6-print.md b/docs/stages/stage-6-print.md new file mode 100644 index 0000000..acb833f --- /dev/null +++ b/docs/stages/stage-6-print.md @@ -0,0 +1,30 @@ +# Stage 6:完整乐谱行分页与把位校正 + +2026-10-23,北京时间。 + +## 打印问题与修复 + +旧实现把整张长 SVG 交给浏览器连续裁页。12 行、带五种技巧和歌词的测试谱实际 PDF 共 3 页:第 6 行的简谱留在第 1 页,孔位和歌词落入第 2 页;第 12 行的技巧文字又被第 2/3 页截断。普通内置长谱在原字号下恰好能放一页,不能据此证明分页正确。 + +打印现在采用 A4 纵向、12 mm 页边距,先按 900 单位宽重新排谱,再测量每行实际 SVG 绘制范围。以完整行分配页面,页间保留原来的延音入/出段,重复曲名、调号、口琴信息和图例,并加入页码。末页仅一行且空间允许时,移入上一页末行,避免孤行。无法放入的单行明确报错,不裁掉内容。 + +900 单位打印布局使主孔位约为 8.5 pt、简谱数字约 12.3 pt;不改变屏幕自适应布局或普通 SVG/PNG 导出。打印页高额外留出 1 mm,避免浏览器小数取整多出空页。谱面测量容器在完成后移除。 + +## 把位依据 + +- 《走绛州》所用[《音乐鉴赏》](https://backend.sjhtbook.com/upload/file/2025/04/80fb4b7923894770828045c9e613e236.pdf#page=12)印刷第 5 页(PDF 12)对上、下句落音作 A 商与 D 徵分析。原谱 1=G,移为 1=C 后徵主音相应为 G。 +- [《基本乐理教程》修订版](https://www.whyx.net.cn/uploads/media/files/gkzp/fj6.pdf#page=102)印刷第 85 页、例 7-3(d) 将河北民歌《小白菜》列为徵调式。本次只据此核对调式分类;旋律仍采用《音乐鉴赏》印刷第 17 页的简谱版本,没有混用该乐理书五线谱中的旋律异文。 +- [Harmonica Dictionary 的第二把位定义](https://www.harmonica.com/harmonica-dictionary/)以高于口琴标称调性纯五度的音为主音。因此本软件这两个 1=C、G 徵主音的版本,使用 C 调口琴应标第二把位。 +- 两首导入源、生成曲谱、README 已更正并附解释。逐对象比较确认所有旋律、节拍、装饰、指法数据 `lines` 完全未变;未以末音列表自动改写其他曲目的把位。 + +## 验证 + +- 100 项自动测试、44 项曲谱门禁、生产构建通过。本批新增 4 项分页测试与 1 项有来源依据的把位回归。 +- 44 项内置谱均经界面打印入口生成真实 A4 PDF,另生成 12 行装饰/歌词谱、12 行跨页延音谱,共 46 文件。 +- 每行恰好出现一次、顺序和行文本完全保留;每页实际绘制区域在页头与图例之间;跨页延音路径总数保持不变。 +- PyMuPDF 逐文件核对物理 A4 尺寸、页数、页码。长谱每行的全部歌词标识只出现在同一页;两首校正曲的 PDF 文本含第二把位。 +- 当前爱尔兰画眉、我的邦妮、牧场上的家、鸿雁各 2 页,其余内置谱各 1 页;两份长谱夹具各 3 页。PDF 整页栅格预览检查了装饰谱、跨页延音及我的邦妮分页。最终我的邦妮为 6+2 行,无孤行尾页。 + +## 范围 + +验证环境为 Windows 上的 Chromium 无头 PDF 引擎;未做实体打印机、其他浏览器内核、用户改选 Letter/横向纸张的验收。其他曲目的未确认调式继续留空把位,不依据简谱 key 自动补写。源码台账、第二读谱者和实体乐器验证仍是整体正确性评估的一部分。 diff --git a/docs/stages/stage-6-restore.md b/docs/stages/stage-6-restore.md new file mode 100644 index 0000000..a1a6946 --- /dev/null +++ b/docs/stages/stage-6-restore.md @@ -0,0 +1,30 @@ +# Stage 6:个人曲库备份恢复 + +2026-10-23,北京时间。补齐上一批原始备份只能人工处理的缺口。 + +## 使用流程 + +曲库侧栏选择「恢复个人曲库」,上传下载的备份 JSON 或粘贴数组。界面先列出可读取曲谱、错误条目和记谱提示。任何条目结构错误都会阻止整批恢复,不部分写入。 + +- 默认合并:读取最新个人曲库,保留现有曲谱。同 ID 曲谱分配新 ID,两首都保留。 +- 可选替换:确认后完整替换个人曲库,可用于修复损坏数据。替换前先把当前原始数据保存到恢复副本,副本写入失败就取消替换;检测到其他标签页修改时要求重新读取确认。内置曲谱不参与替换。 +- 「导出恢复前副本」下载最近一次替换前的原始数据。副本只保留一代,再次替换会更新它;需要长期保留的副本应下载到本地。损坏的副本仍保留原文,需要修复错误后才能恢复。 +- 空数组仅允许在明确选择替换并确认后还原为空库;合并空备份不可执行。 + +## 实现与数据边界 + +恢复和单曲导入共用同源 Web Lock。所有备份条目先经过同一曲谱导入校验,再一次写入最终库;派生孔位重新分析,已有 updatedAt 保留。恢复前副本保存原始字符串,不经过曲谱规范化。当前库写入失败时旧值保持不变,副本可能已成功保存,界面不会报告恢复成功。 + +备份仅包含个人曲谱;收藏列表不包含在备份中,恢复时保留当前收藏列表。恢复并非原始 JSON 字段顺序或格式的保真编辑,支持的曲谱字段经过正常导入校验。结构合法但节拍/延音存在问题的曲谱会显示记谱提示,不能把“可读取”理解为旋律已经来源核对。 + +## 验证 + +- 新增 6 项回归:整批预检、合并去冲突、损坏当前库恢复、完整副本往返、过期替换、恢复副本/主数据写入失败。91 项测试通过,生产构建通过。 +- 真实 Chromium 流程通过:下载曲库备份→文件上传→合并→取消替换→确认替换→导出恢复前副本,恢复数据与最初保存的两首测试谱深度一致,包含时间戳和装饰音。 +- 浏览器实测损坏条目禁用按钮、损坏当前库修复、过期替换拒绝、恢复副本配额失败保留原库与输入。副本下载与原文逐字对比通过。 +- 桌面 1440×1000、移动 390×844 截图人工查看通过;移动文件上传、合并、Tab 焦点循环及 Escape 关闭并返回曲库按钮通过。 +- 上一批存储浏览器回归重新执行通过。曲谱内容未更改;完整测试继续覆盖全部 44 项内置谱。 + +## 未完成范围 + +尚未验证真实手机、其他浏览器内核及无 Web Locks 浏览器的跨窗口写入。恢复副本消耗额外浏览器空间;保存副本失败时保留当前库,不执行替换。把位标签、未绘制的技巧和长谱打印仍在 Stage 6 后续审查中。 diff --git a/docs/stages/stage-6-rowing.md b/docs/stages/stage-6-rowing.md new file mode 100644 index 0000000..fdd6e82 --- /dev/null +++ b/docs/stages/stage-6-rowing.md @@ -0,0 +1,5 @@ +# Stage 6 — Spanish Rowing Song source version + +2026-10-23 01:58 Beijing. The Hunan Grade 1 lower-volume p.49 image has a final double bar after eight measures. Adopt this complete classroom short version explicitly, instead of retaining the old sixteen-bar expansion with an unverified second half. Preserve the old version in Git history; keep the builtin ID and title stable and mark the eight-bar scope in subtitle, source and notes. Original C, raised one octave for C harp. All eight measures independently read from the numbered source image and covered by a complete pitch/duration regression. + +118 tests pass on Node 24/26; 44 score checks and production build pass. The actual A4 PDF preserves all rows; desktop 1440×1000 and mobile 390×844 screenshots captured and mobile inspected. This version selection does not establish the correctness of the former sixteen-bar expansion or of all song arrangements. diff --git a/docs/stages/stage-6-storage.md b/docs/stages/stage-6-storage.md new file mode 100644 index 0000000..b6cccf8 --- /dev/null +++ b/docs/stages/stage-6-storage.md @@ -0,0 +1,29 @@ +# Stage 6:个人曲库完整性修复 + +2026-10-23,北京时间。Stage 6 整体验收中的第一批修复,未发布。 + +## 已复现的问题 + +旧版本四项针对性测试全部失败:连续导入使旧曲谱孔位消失;损坏存储被当作空库覆盖;过期标签页导入同 ID 覆盖先前曲谱;保存时间在重载时丢失。其原因分别是重新解析后的旧曲谱未分析、读取异常被吞掉、新增使用替换语义、导入器未保留 updatedAt。 + +## 实现 + +- 所有加载、导入和删除返回的个人曲谱都重新计算孔位,技巧筛选与谱面保持一致。 +- 导入只追加。保存时读取最新库、分配唯一 ID,返回实际保存的曲谱供界面选择;64 字符 ID 留出后缀空间,避免重载截断又发生碰撞。 +- 旧数据中的重复 ID 或与内置谱相同的 ID,在视图中分配独立 ID;下一次正常保存持久化修复,不混淆删除对象。 +- 支持 Web Locks 的浏览器使用同源锁串行化导入与删除。删除还要求原始存储快照一致,过期页面需先重新读取。 +- 无法解析、根结构错误、部分条目无效或存储不可读时,禁止保存/删除;仍能打开有效条目。原始内容通过「备份个人曲库」逐字导出,「重新读取曲库」可读取外部修复或其他标签页的修改。 +- 写入失败保留导入弹窗和输入,不修改调用者数据;写入期间禁用重复提交。updatedAt 经完整 JSON 再导入和重载仍保留。 + +## 验证 + +- 85 项测试通过,含新增 8 项存储回归;44 项内置谱节拍、延音、来源和音高门禁通过;生产构建通过。 +- Chromium 真实界面:连续导入与自然音/压音筛选、删除后指法、两个标签页同时导入同 ID、过期删除拦截、重新读取、部分损坏数据保护、原文备份、配额失败保留输入均通过。 +- 桌面 1440×1000、移动尺寸 390×844 截图已检查:损坏提示可读,备份/重新读取按钮可从移动曲库抽屉访问;移动备份文件也逐字对比通过。 +- 备份原文为测试夹具,不含真实个人曲谱。截图和浏览器结果位于本任务 outputs/stage-6-storage,副本同步到技术博客档案。 + +## 限制与后续 + +- 不支持 Web Locks 的旧浏览器或非安全上下文退化为同步读写,不能保证多个窗口同时写入的原子性;同源旧版应用也不受新锁约束。 +- 本批完成时原始曲库备份只能保全/人工修复;后续已补齐经过预检、一次写入的批量恢复流程,见 [备份恢复验证](stage-6-restore.md)。 +- 尚未完成技巧标记、把位说明与长谱打印的整体验证;85 测试不代表所有音乐版本、实体口琴及真实手机验收完成。 diff --git a/docs/stages/stage-6-techniques.md b/docs/stages/stage-6-techniques.md new file mode 100644 index 0000000..dcdac90 --- /dev/null +++ b/docs/stages/stage-6-techniques.md @@ -0,0 +1,30 @@ +# Stage 6:技巧标记与音高结论修正 + +2026-10-23,北京时间。 + +## 问题与修复 + +原导入器接受 vibrato、bend-release、slap、gliss、trill 五种技巧,但谱面仅在存在孔位时附加振音波浪号,其他四种被静默遗漏。现在五种均以独立文字标记显示在孔位下方,同音多个标记纵向排列,歌词随行内最大标记数下移;与倚音、波音、自由延长记号共同排版。即使音高暂无孔位或技巧错误地附在休止符上,文字也保留,休止符情况另给提示。 + +这些旧技巧字段只有名称,没有颤音交替音、滑音起止及过程音、压音释放过程、舌拍和声音的数据。导入/恢复预检和谱下分析现说明每处缺失信息;导出谱尾注明“技巧文字不展开过程音;孔位仅校验已记音高”。没有自行补猜音高,也没有把旧字段当作完整技巧音符模型。 + +分析结论改为“已记音高均有孔位”,替代范围过大的“可完整演奏”。纯休止谱显示“无有声音符”;不可定位的音高说明当前模式不含超吹/超吸。节拍、延音和技巧提示单独显示,不被音高检查成功掩盖。 + +“无需压音”筛选暂排除缺少交替音、过程音或和声音的四种技巧,界面说明排除原因;显式压音释放标记列入“需要压音”。振音按已记主音判断。内置 44 项目前没有这些旧技巧标记,本批未改其旋律。 + +## 概念核对 + +- [Harmonica Dictionary](https://www.harmonica.com/harmonica-dictionary/) 的 Shake/Trill、Scooping、Tongue Slap 条目确认:这些技法可能涉及交替音、压音变化或和弦。仅有主音和一个字符串字段不足以校验全部参与音高。本批据此保留文字并明确分析范围。 +- [David Barrett:Tongue Blocking Study 2 – Slaps](https://www.bluesharmonica.com/lessons/tongue_blocking_2) 将 slap 明确归入舌堵技巧。本批未转载其练习谱和音频。 + +## 验证 + +- 新增 4 项针对性测试,验证所有标记往返、独立/紧凑谱面可见、缺孔位和休止符反例、五标记叠放与歌词/装饰排版、技巧筛选边界。总计 95 项测试通过,生产构建通过。 +- Chromium 逐一打开 44 项内置谱,均显示准确音高结论且无记谱提示;本批全部自动测试继续核对全库。 +- 导入故意含短拍、休止符技巧及同音五标记的排版夹具:10 处技巧全部保留,缺失音高和短拍提示可见;SVG、PNG、JSON 导出与打印窗口 DOM 检查通过。 +- 桌面 1440×1050 与移动谱面 420 宽截图人工检查,技巧文字和歌词 SVG bounding box 无交叠;纯休止/不可定位音高的界面结论验证通过。 +- 初版浏览器反例使用 C7(MIDI 96),它是标准 C 调口琴 10 孔吹音,不能作为越界反例;改用 D7(MIDI 98)后验证通过。没有为适配错误测试修改音高引擎。 + +## 继续事项 + +当前仅修复文字标记遗漏和结论范围;完整颤音/滑音过程音模型仍未实现。打印窗口含标记不等于长谱分页通过,长谱打印与把位标签继续单独审查。 diff --git a/docs/stages/stage-8-library-payload.md b/docs/stages/stage-8-library-payload.md new file mode 100644 index 0000000..cf24613 --- /dev/null +++ b/docs/stages/stage-8-library-payload.md @@ -0,0 +1,13 @@ +# Stage 8 内置曲库传输 + +60项曲库的3358个音符仅有203种完整记录。原生产主JS为544,314字节,超过默认500kB建议值;其中大量重复的孔位、时值、音高及默认字段会被重复解析。 + +构建工具仍按原流程生成和验证完整 `src/data/scores/*.json`,另将完整音符按JSON值去重,生成 `builtin.packed.json`。运行时的 `builtin.ts` 只导入该传输数据,并还原标准 `Score[]`。来源说明、音高、时值、反复、装饰音、歌词、派生孔位、字段是否存在等均保留。公开导入、导出、个人曲库和备份格式不变,不引入网络延迟加载或新的依赖。 + +每次展开都独立复制曲谱元数据、音符及嵌套倚音/波音对象,避免后续指法分析原地更新时串改别的音符或曲谱。构建内部版本及字典索引异常时明确失败,不能默默替换音符。 + +验证包含全库完整JSON深层等价、来源DSL重建一致性、可重复生成,以及跨曲谱/重复音符/多次展开的可变对象隔离。两内核生产构建中实际逐首导出60份JSON,与完整文件逐项相等;另往返导入包含倚音、波音、变拍与延长的三曲。 + +生产主JS减至294,293字节,减少45.93%;Vite构建报告的gzip约93.62kB→87.94kB。未改500kB阈值,原建议警告消失。体积缩小不等于启动时延必然缩短:运行时增加了字典展开和对象复制。本机各5轮冷上下文观察中,Chromium曲库就绪中位数约130→140ms、Firefox约192→192ms;测试期间存在其他自动测试负载,不能将这些小样本当作移动设备或真实网络性能结论。 + +音乐内容没有因本轮修改而重新选版;曲库仍为56歌曲与4原创练习,专业审校和实体演奏边界沿用来源台账。 diff --git a/package-lock.json b/package-lock.json index 8b3739c..347152f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "harpscore", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "harpscore", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "dependencies": { "react": "^18.3.1", diff --git a/package.json b/package.json index 9db4f60..ca747c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "harpscore", - "version": "0.1.0", + "version": "0.2.0", "description": "HarpScore - 跨平台布鲁斯口琴乐谱软件:琴谱展示、管理与导出", "license": "MIT", "type": "module", @@ -10,7 +10,8 @@ "preview": "vite preview", "test": "tsx --test tests/*.test.ts", "scores:build": "tsx tools/build-scores.ts", - "scores:validate": "tsx tools/validate-scores.ts" + "scores:validate": "tsx tools/validate-scores.ts", + "release:pack": "node tools/package-release.mjs" }, "dependencies": { "react": "^18.3.1", diff --git a/src/App.tsx b/src/App.tsx index cc3b4f7..21fc63f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,14 +1,19 @@ -import { useMemo, useRef, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import type { Score } from './types/score'; import { analyzeScore } from './lib/harmonica'; +import { formatScoreLocation } from './lib/scoreLocation'; import { loadLibrary, allScores, findScore, saveUserScore, deleteUserScore, - importScoreJson, uniqueScoreId, type Library, + importScoreJson, userLibraryBackup, type Library, } from './lib/store'; -import { exportSvg, exportPng, exportPdf, exportJson } from './lib/export'; +import { exportSvg, exportPng, exportPdf, exportJson, exportLibraryBackup } from './lib/export'; import ScoreSheet from './components/ScoreSheet'; +import LibraryRestoreDialog from './components/LibraryRestoreDialog'; +import ImportScoreDialog from './components/ImportScoreDialog'; +import { scoreFromObject } from './lib/scoreImport'; +import { DEFAULT_FILTERS, FAVORITES_KEY, filterScores, loadFavorites, setFavorite, type LibraryFilters } from './lib/libraryView'; -function ScoreItem({ s, active, onClick }: { s: Score; active: boolean; onClick: () => void }) { +function ScoreItem({ s, active, favorite, onClick }: { s: Score; active: boolean; favorite: boolean; onClick: () => void }) { return ( ); } -function AnalysisPanel({ score }: { score: Score }) { +function AnalysisPanel({ score, showMeasureNumbers, onShowMeasureNumbers }: { + score: Score; showMeasureNumbers: boolean; onShowMeasureNumbers: () => void; +}) { const a = useMemo(() => analyzeScore(score, false), [score]); const warns = a.issues.filter((i) => i.severity === 'warning'); const errs = a.issues.filter((i) => i.severity === 'error'); + const notationWarnings = useMemo(() => scoreFromObject(score).warnings, [score]); const sourceFormat = score.sourceFormat === 'jianpu-text' ? '文字简谱' : score.sourceFormat === 'jianpu-image' ? '数字简谱' + : score.sourceFormat === 'music-text' + ? '音符文本(ABC / LilyPond)' : score.sourceFormat === 'original' ? '原创' : ''; return (
{score.title} 音符 {a.totalNotes} 个  + {a.graceNotes > 0 && (含 {a.graceNotes} 个前倚音) } + {a.ornamentNotes > 0 && (含 {a.ornamentNotes} 个波音辅助音) } 音域 {a.minMidi ?? '—'} ~ {a.maxMidi ?? '—'}(MIDI)  - 压音音 {a.bendNotes} 个  + 压音 {a.bendNotes} 个  {a.overblowNotes > 0 && <>超吹 {a.overblowNotes} 个 } - {errs.length === 0 - ? · 可完整演奏 - : · {errs.length} 个音超出能力范围} + {a.totalNotes === 0 ? · 无有声音符 : errs.length === 0 + ? · 已记音高均有孔位 + : · {errs.length} 个已记音高暂无孔位(当前不含超吹/超吸)} + {(notationWarnings.length > 0 || errs.length > 0 || warns.length > 0) && ( +
以下按书写小节编号,弱起计入;音符序号包含休止符。 + {!showMeasureNumbers && } +
+ )} + {notationWarnings.length > 0 &&
记谱提示:{notationWarnings.join(';')}
} + {errs.length > 0 &&
{errs.map((e, i) => + {formatScoreLocation(score, e)}:{e.reason};)}
} {score.notes &&
演奏提示:{score.notes}
} {score.source && (
@@ -53,12 +74,13 @@ function AnalysisPanel({ score }: { score: Score }) { )}
)} - {warns.length > 0 && warns.length <= 12 && ( -
+ {warns.length > 0 && ( +
+ 压音与进阶技巧位置({warns.length} 处) {warns.map((w, i) => ( - 第{w.measureIndex + 1}小节 {w.reason}; + {formatScoreLocation(score, w)}:{w.reason}; ))} -
+ )}
); @@ -68,41 +90,61 @@ export default function App() { const [lib, setLib] = useState(() => loadLibrary()); const [currentId, setCurrentId] = useState(null); const [sidebarOpen, setSidebarOpen] = useState(false); - const [query, setQuery] = useState(''); + const [showMeasureNumbers, setShowMeasureNumbers] = useState(false); + const [filters, setFilters] = useState(DEFAULT_FILTERS); + const [favorites, setFavorites] = useState(loadFavorites); + const [favoriteSaving, setFavoriteSaving] = useState(false); const [importOpen, setImportOpen] = useState(false); + const [restoreOpen, setRestoreOpen] = useState(false); const [importText, setImportText] = useState(''); const [importError, setImportError] = useState(''); + const [importSaving, setImportSaving] = useState(false); const [notice, setNotice] = useState(''); - const fileRef = useRef(null); + const [actionError, setActionError] = useState(''); + useEffect(() => { + const refreshFavorites = (event: StorageEvent) => { + if (event.key === FAVORITES_KEY || event.key === null) setFavorites(loadFavorites()); + }; + window.addEventListener('storage', refreshFavorites); + return () => window.removeEventListener('storage', refreshFavorites); + }, []); + const runAction = async (action: () => void | Promise) => { + setActionError(''); + try { await action(); } catch (error) { + setActionError(error instanceof Error ? error.message : '操作失败,请重试'); + } + }; - const scores = useMemo(() => { - const all = allScores(lib); - if (!query.trim()) return all; - const q = query.trim().toLowerCase(); - return all.filter((s) => - s.title.toLowerCase().includes(q) || - (s.subtitle ?? '').toLowerCase().includes(q) || - s.tags.some((t) => t.toLowerCase().includes(q)) - ); - }, [lib, query]); + const scores = useMemo(() => filterScores(allScores(lib), filters, favorites), [lib, filters, favorites]); + const tags = useMemo(() => [...new Set(allScores(lib).flatMap((s) => s.tags))].sort((a, b) => a.localeCompare(b, 'zh-CN')), [lib]); + const changeFilter = (key: K, value: LibraryFilters[K]) => + setFilters((old) => ({ ...old, [key]: value })); const current = currentId ? findScore(lib, currentId) : undefined; - const doImport = (text: string) => { + const doImport = async (text: string) => { + if (importSaving) return; const { score: parsedScore, error, warnings } = importScoreJson(text); if (error || !parsedScore) { setImportError(error ?? '导入失败'); return; } - const score = uniqueScoreId(parsedScore, allScores(lib).map((item) => item.id)); - const newLib = saveUserScore(lib, score); - setLib(newLib); + setImportSaving(true); + let saved: Awaited>; + try { saved = await saveUserScore(lib, parsedScore); } catch (error) { + setImportError(`未保存:${error instanceof Error ? error.message : '保存失败,请保留曲谱 JSON 后重试'}`); + return; + } finally { + setImportSaving(false); + } + const score = saved.savedScore; + setLib(saved.library); setCurrentId(score.id); setImportOpen(false); setImportText(''); setImportError(''); setNotice(warnings.length - ? `已导入《${score.title}》,另有 ${warnings.length} 个节拍提示` + ? `已导入《${score.title}》,另有 ${warnings.length} 个记谱提示,详见谱面下方` : `已导入《${score.title}》`); }; @@ -114,17 +156,53 @@ export default function App() {
布鲁斯口琴乐谱 · 十孔口琴专用
- setQuery(e.target.value)} /> + changeFilter('query', e.target.value)} /> +
+
+ + + + + + +
找到 {scores.length} / {lib.builtin.length + lib.user.length} 首曲谱
+ {filters.technique === 'natural' &&
未明确交替音、过程音或和声音的技巧曲谱暂不列入。
} +
+
+ + +
-
内置曲谱({lib.builtin.length})
+ {scores.length === 0 &&

没有符合条件的曲谱。试试清除筛选,或打开一首曲谱后点击「收藏」。

} +
内置曲谱({scores.filter((s) => s.builtin).length} / {lib.builtin.length})
{scores.filter((s) => s.builtin).map((s) => ( - { setCurrentId(s.id); setSidebarOpen(false); }} /> ))} -
我的曲谱({lib.user.length})
+
我的曲谱({scores.filter((s) => !s.builtin).length} / {lib.user.length})
{scores.filter((s) => !s.builtin).map((s) => ( - { setCurrentId(s.id); setSidebarOpen(false); }} /> ))} {lib.user.length === 0 && ( @@ -136,45 +214,47 @@ export default function App() {
- { - const file = e.target.files?.[0]; - if (file) { - setImportText(await file.text()); - setImportError(''); - } - e.target.value = ''; - }} /> {notice &&
{notice}
} + {actionError &&
{actionError}
} + {lib.storageIssue &&
{lib.storageIssue}
} {current ? ( <>
- - - - + + + + + +
- + {!current.builtin && ( )}
- - + + setShowMeasureNumbers(true)} /> ) : ( <>
- +
🎵
@@ -191,33 +271,17 @@ export default function App() {
setSidebarOpen(false)} /> )} - {importOpen && ( -
setImportOpen(false)}> -
e.stopPropagation()}> -

导入曲谱

-

支持 HarpScore 完整 JSON,以及含 melody 字段的旋律 DSL JSON。

-