Skip to content
SheetToriPublic

About

コダーイメソッドに基づく移動ド階名唱トレーニングアプリ

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

SolfaGym

コダーイメソッドに基づく、移動ド階名唱のトレーニングアプリ。

楽譜を見た瞬間に固定ドの音名で読んでしまい、調の中での機能(do からの距離)として音を捉えられない——という問題を、音高情報を意図的に隠したリズム譜+階名から始めて段階的に音・伴奏・実音譜を足していくことで解く。「階名 → 音」の回路を先に作るのが狙い。

サーバを持たない静的サイト。GitHub Pages で公開する。

学習の 5 ステップ

Step 見るもの 聞こえるもの ねらい
0 — 主和音をアルペジオ(do-mi-so)→ 1拍空け → 三音同時 調のセンターを耳に置く
1 リズム譜+階名 メトロノームのみ 無伴奏で自力で歌う(訓練の本体)
2 リズム譜+階名 旋律 + カーソル 答え合わせ
3 リズム譜+階名 伴奏のみ + カーソル 和声の支えだけで歌う
4 実音の五線譜+階名 旋律 + 伴奏 + カーソル 五線と階名を結びつける

ステップ順は強制せず、自由に行き来できる。

リズム譜は符頭をすべて五線第2間(ト音記号の A)に固定し、音高の手がかりを消したもの。

キーは曲を開くたびにランダムに変わる(歌唱音域に収まるものから、直前と同じキーを除いて選ぶ)。絶対音高の記憶が固定されるのを防ぐため、キー名は Step 4 の調号で初めて分かる。

階名の規則

  • 長調は do 基準、短調は la 基準(la-ti-do-re-mi-fa-so)
  • 派生音: Do#→Di / Re#→Ri / Re♭→Ra / Mi♭→Ma / Fa#→Fi / So#→Si / So♭→Sa / La#→Li / La♭→Lo / Ti♭→Ta
  • 表に無い変化(Do♭, Mi#, Fa♭, Ti#)はデータ検証で例外にする。該当する曲を足す段階で表を広げる

階名は音高クラスではなく音名の文字+変化記号から導く。そうしないと Do#(Di) と Re♭(Ra) を区別できない。

開発

npm install
npm run dev          # http://localhost:5173/SolfaGym/
npm run test         # ロジック層のユニットテスト
npm run build        # 曲インデックス生成 → 型チェック → 本番ビルド
npm run lint

Node は 22.22.0 以上を推奨(react-router@8 と jsdom@30 がそれを要求する。22.15 でも動くが npm が EBADENGINE 警告を出す)。

技術スタック

Vite 8(Rolldown 既定)/ React 19 / TypeScript 6 / Tailwind v4 / Vitest 4 / oxlint / abcjs / Tone.js

ネット記事と食い違う点が4つあるので注意:

  1. Vite は 8 系。Rolldown が既定バンドラ。移行用の rolldown-vite は入れない
  2. Tailwind v4 は tailwind.config.js を作らない。@tailwindcss/vite プラグイン + CSS に @import "tailwindcss"; だけ。postcss.config.js / autoprefixer は不要
  3. Linter は oxlint(create-vite の React テンプレートが 2026-06 に ESLint から切り替えた)
  4. tone は 15.5.36 に厳密ピン。npm の latest タグは 15.1.22(2025-04)で止まっており、^15.1.22 と書くと1年以上前のビルドが入る

構成

public/songs/
  index.json          # 一覧用メタ。scripts/build-songs.ts が自動生成する
  {id}.json           # 曲本体(ABC・原調・コード進行・出典)
src/core/             # 純粋関数層。Vitest の対象
  pitch.ts            # 音高の表現(音名+変化記号+オクターブ)
  solfa.ts            # 音高 + 調 → 階名
  transpose.ts        # 綴りを保つ移調、音域からのキー選択
  level.ts            # 階名の集合 → レベル自動算出
  abc.ts              # ABC の解析と、リズム譜/実音譜の生成
  chords.ts           # 度数表記 → MIDI
  playback.ts         # 再生の「いつ・どの音を」を拍で決める
  song.ts / schema.ts # 曲データの導出と Zod スキーマ
src/audio/engine.ts   # Tone.js を包む薄いファサード
src/score/AbcScore.tsx# abcjs 描画 + カーソル

設計上の要点

  • 時計は AudioContext ただ1つ。 カーソルは Tone.js の getDraw() が駆動する。abcjs の TimingCallbacks は使わない——あれは繰り返しを時間方向に展開するので、譜面上の要素と1対1にならない(7要素の楽譜に対して 11 イベントが出る)。代わりに描画後の SVG を DOM 順に引く
  • レベルも階名も音域も、曲データから計算する。 手でタグ付けしない
  • 和音は度数表記(ローマ数字)で持つ。 音名で持つと移調が効かない
  • Tone.js はモジュールスコープの単一実体。 React の useEffect の cleanup で dispose() しない(StrictMode の二重マウントで無音になる)
  • Tone.js の取得は練習画面のマウント時に先行して行う。 クリックしてから取得を始めると、遅い回線で取得中にユーザー操作のアクティベーションが切れて無音になる

曲データを追加する

public/songs/<id>.json を足して npm run build:songs。レベルと階名は自動で計算され、index が更新される。

ABC は意図的に狭い範囲だけを扱う——単旋律のみ、和音なし、連符なし、繰り返し記号なし。範囲外の記法は例外になり、npm run test の全曲検証が捕まえる。

収録するのはパブリックドメイン確実な曲のみ。ABC は既存の符号化データを流用せず新規に書き起こす(Essen Folksong の **kern 版は「教材への埋め込み禁止」、The Session は「LLM による処理の禁止」がライセンスに明記されている)。

デプロイ

GitHub Pages。main への push で GitHub Actions がビルドして公開する。

初回だけリポジトリの Settings → Pages → Source を「GitHub Actions」に設定すること(Vite はビルドが必要なため)。

公開 URL がサブパス(https://<user>.github.io/SolfaGym/)になるので、vite.config.ts の base: '/SolfaGym/' は必須。外すとアセットが 404 になる。

ルーティングは HashRouter。GitHub Pages はサーバ側リライトができず、SPA リライトのネイティブサポートも無い。404.html トリックは初回レスポンスが HTTP 404 のまま返り一部のブラウザが警告を出すので採らない。ハッシュはサーバに送られないので 404 問題が原理的に起きない。

手動での確認項目

音とカーソルは自動テストで押さえきれないので、変更したら以下を耳と目で確認する。

  1. 曲一覧がレベル順に並び、日本の音階が別セクションになっている
  2. Step 0 で do-mi-so が1音ずつ鳴り、1拍おいて三音同時に響く(短調は la-do-mi)
  3. Step 1 でリズム譜の符頭がすべて同じ高さに並び、下に階名が付く。メトロノームの ON/OFF が効く
  4. Step 2 で旋律が鳴り、カーソルが音符を正確に追う
  5. 別タブに切り替えて戻してもカーソルが飛ばない(Tone.js Draw の expiration の確認)
  6. テンポを 50% / 120% に変えて、再生中でも音とカーソルがずれない
  7. Step 3 で旋律が消え、伴奏だけになる
  8. Step 4 で調号付きの実音五線譜になり、鳴っている音と譜面が一致する
  9. 同じ曲を閉じて開き直すとキーが変わる(かつ音域から外れない)
  10. 「できた」を押す → 一覧にチェック → リロードしても残っている
  11. スマホ幅で楽譜が折り返し、横スクロールが発生しない

注意: ブラウザ自動操作(ヘッドレス/バックグラウンドのタブ)では 4〜8 を確認できない。隠れたタブでは requestAnimationFrame が発火せず、Draw に依存するカーソルが動かないため。必ず前面のタブで確認すること。

About

コダーイメソッドに基づく移動ド階名唱トレーニングアプリ

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages