@onjmin/dtm 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -48,6 +48,43 @@ unitsToMidiDetune(1968); // { midi: 63, detuneCents: 48.39 } … 31平均
48
48
  > **`PlayDrumEvent.pitch` と `DRUM_KEYS` は変更していません。** これらは GM 打楽器のキー番号であって
49
49
  > 音高ではないため、units へ変換すると全ドラムが壊れます。
50
50
 
51
+ ### 単位はブランド型で区別されます
52
+
53
+ `pitchUnits` の型は素の `number` ではなく **`Units`** です。MIDI ノート番号は **`MidiNote`** で、両者は互いに代入できません。
54
+
55
+ ```ts
56
+ import { units, midiNote, pitchV1ToUnits, type Units } from "@onjmin/dtm";
57
+
58
+ // ノートを手で組むとき
59
+ const note = {
60
+ id: 0,
61
+ startStep: 0,
62
+ durationSteps: 48,
63
+ pitchUnits: pitchV1ToUnits(60), // MIDI 60 (中央ド) から作る
64
+ velocity: 100,
65
+ };
66
+
67
+ // units を直接指定するとき
68
+ const c4: Units = units(1860); // 60 × 31
69
+
70
+ // これはコンパイルエラーになる
71
+ const bad: Units = 1860; // Type 'number' is not assignable to type 'Units'
72
+ ```
73
+
74
+ 素の数値からは `units()` / `midiNote()` を通してください。これは手間ではなく、**「この数値の単位を確認した」という宣言**として機能します。
75
+
76
+ なぜこうしたかというと、`Note.pitch` → `pitchUnits` の改名時に**単位の取り違えを 12 件作り込んだ**からです。すべて型チェックを通過していました。半音のつもりの閾値が units と比較される、units が SoundFont へ MIDI ノート番号として渡されて**楽器音が無音になる**、オクターブユニゾンに半音の 12 が足されて 0.4 半音ずれる、といった不具合が、目視の監査を 3 回重ねても毎回新しく見つかりました。型が同じ `number` である限りコンパイラは単位を一切検証しないためです。
77
+
78
+ ブランド型はこれらを検出します。
79
+
80
+ ```
81
+ units を SoundFont の pitch へ → Units is not assignable to MidiNote
82
+ units に半音の 12 を足す → number is not assignable to Units
83
+ MidiNote を units の関数へ → MidiNote is not assignable to Units
84
+ ```
85
+
86
+ ただし**比較演算だけは防げません**。`pitchUnits < 48` のような式は `number` 同士の比較として通ります。ピッチと数値を比べる箇所は、引き続き単位を目で確認してください。
87
+
51
88
  ### その他の破壊的変更
52
89
 
53
90
  | 1.x | 2.0.0 |
@@ -511,6 +548,45 @@ const cp = mountChordPlayer(document.getElementById("chord-app"), "| C | G | Am
511
548
  @@0 tsukuyomi どんぐりころころどんぐりこ;
512
549
  ```
513
550
 
551
+ ### 歌詞の制御記号
552
+
553
+ 歌詞は **1 文字(拗音は 2 文字)= ノート 1 つ**で対応します。ブレス `、` だけはノートを消費しません。
554
+
555
+ | 記号 | 意味 | 例 |
556
+ | --- | --- | --- |
557
+ | `ー` | **継続**。言い直さずに音を保ち、ピッチだけを階段状に切り替える | `あーーーー` |
558
+ | `〜` | **継続(ポルタメント)**。`ー` と同じだがピッチを滑らかに繋ぐ(しゃくり・スラー) | `あ〜〜` |
559
+ | `っ` | **促音**。ノートを消費し、無音の閉鎖として間を作る | `がっこう` |
560
+ | `_` | **休符**。ノートを消費するが歌わない(そのノートは無音) | `あ_い` |
561
+ | `、` | **ブレス**。ノートは消費せず、直前ノートの尻を削って息継ぎを差し込む | `あー、いー` |
562
+
563
+ `〜` は `~`(全角チルダ)でも、`、` は `,` でも書けます。
564
+
565
+ 継続の要点は「**同じモーラをピッチ違いで続けるとき、区切って発音するか否かを書き分けられる**」ことです。
566
+
567
+ ```
568
+ @0 t120 o4 c8 d8 e8 f8 g8;
569
+ @@0 tsukuyomi あああああ; ← 5 回それぞれ言い直す(従来どおり)
570
+ @@0 tsukuyomi あーーーー; ← 1 つの「あ」を保ったままピッチだけ動く
571
+ ```
572
+
573
+ 継続は**隙間なく続くノートを 1 音へ結合**し、区間ごとのピッチ推移として 1 回で合成します
574
+ (`buildStreamVoiceNotes` / `StreamVoiceNote.pitchSegments`)。ノートの間に休符があるとき、
575
+ 結合後が `TIE_MERGE_MAX_SEC`(4 秒)を超えるとき、シークで先頭が切り落とされたときは結合をやめ、
576
+ 先行母音を切って直前ノートへクロスフェードする「継続ノート」として繋ぎます。
577
+
578
+ 継続が引き継ぐ母音は直前の音節のものです。`きょー` は `きょ` + `お`、`んー` は `ん` を伸ばした
579
+ ハミングになります。休符 `_` とブレス `、` は母音の文脈を切るので、その次の音節は語頭
580
+ (連続音の `- か`)として歌われます。
581
+
582
+ > **`ー` `っ` の扱いが 2.0 系から変わりました。**
583
+ > 以前は「音源に該当する音声サンプルが無い」として**歌詞から丸ごと除去**していたため、
584
+ > `きょーと` は `きょ` `と` の 2 音節(=ノート 2 つ)でした。現在は `きょ` `ー` `と` の
585
+ > 3 音節になり、**同じ歌詞でもノートとの対応が 1 つずつずれます**。
586
+ > `ー` `っ` を含む既存の曲は歌詞かノートの調整が必要です。
587
+
588
+ ### モデルと音源
589
+
514
590
  - モデルに `klatt` を指定すると内蔵フォルマント合成(音源ロード不要)。
515
591
  - 内蔵 UTAU 音源(@onjmin/koe)キーワード:
516
592
  `tsukuyomi` / `rino` / `roze` / `uc` / `ruko_male` / `ruko_female` / `teto` / `shiyo` / `rei` / `mgroid` / `motroid` / `nynroid`。