@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/dist/index.d.ts CHANGED
@@ -57,6 +57,151 @@ type ChordPlayerInstance = {
57
57
  */
58
58
  declare const mountChordPlayer: (target: HTMLElement, chords: string, options?: MountChordPlayerOptions) => ChordPlayerInstance;
59
59
 
60
+ /**
61
+ * 音律(12平均律 / 31平均律)と、ピッチの内部表現。
62
+ *
63
+ * ## 単位
64
+ *
65
+ * ノートのピッチは **1/372オクターブの整数** で持つ。372 = 12 × 31 で、12と31は
66
+ * 互いに素なので最小公倍数がこれになり、**12平均律と31平均律の両方が誤差ゼロで
67
+ * 同じ数直線に乗る**。
68
+ *
69
+ * 12平均律 1半音 = 31 units
70
+ * 31平均律 1度 = 12 units
71
+ * 1 unit = 1200/372 ≒ 3.2258 セント
72
+ *
73
+ * 整数のままなので `a.pitchUnits === b.pitchUnits` の同値判定が全部生き残る
74
+ * (重複判定・当たり判定・協調編集の (startStep, pitchUnits) キー)。小数にすると
75
+ * ここが軒並み壊れるため、細かい整数単位を選んでいる。
76
+ *
77
+ * ## 音律は音声経路に入らない
78
+ *
79
+ * ピッチが絶対値なので、シーケンサも発音器も音律を知らなくてよい。音律は
80
+ * 「格子・記譜・表示」だけに効く編集上の概念で、`RenderConfig.edo` に置く。
81
+ */
82
+ /**
83
+ * ピッチの単位を型で区別するためのブランド。
84
+ *
85
+ * `Units` と `MidiNote` はどちらも実体は number だが、互いに代入できない。
86
+ * これは「半音の数値を units のつもりで使う」「units を SoundFont の
87
+ * MIDIノート番号として渡す」といった取り違えを**コンパイラに検出させる**ため。
88
+ * 単位の取り違えは型が同じ number である限り一切検出できず、実際に
89
+ * 「楽器音が無音」「オクターブユニゾンが0.4半音ずれる」「多音階音源のサンプル
90
+ * 選択が常に最高音になる」といった不具合を作り込んだ。
91
+ *
92
+ * 生の number から作るときは {@link units} / {@link midiNote} を通す。
93
+ * 変換は {@link midiToUnits} / {@link unitsToMidi} など、この module の関数に集約する。
94
+ */
95
+ declare const UNITS_BRAND: unique symbol;
96
+ declare const MIDI_BRAND: unique symbol;
97
+ /** ピッチ。1/372オクターブの整数。 */
98
+ type Units = number & {
99
+ readonly [UNITS_BRAND]: true;
100
+ };
101
+ /** MIDIノート番号(半音)。0-127。SoundFont のゾーン選択やMIDI入出力で使う。 */
102
+ type MidiNote = number & {
103
+ readonly [MIDI_BRAND]: true;
104
+ };
105
+ /** 生の数値を units として扱う。単位が units であると確信できる箇所だけで使う。 */
106
+ declare const units: (n: number) => Units;
107
+ /** 生の数値をMIDIノート番号として扱う。MIDI入出力の境界だけで使う。 */
108
+ declare const midiNote: (n: number) => MidiNote;
109
+ /** units 同士・units と生の差分の加算。結果も units。 */
110
+ declare const addUnits: (a: Units, delta: number) => Units;
111
+ /** 1オクターブあたりの units。12 × 31。 */
112
+ declare const UNITS_PER_OCTAVE = 372;
113
+ /** 12平均律の1半音あたりの units。 */
114
+ declare const UNITS_PER_SEMITONE: number;
115
+ /** 31平均律の1度あたりの units。 */
116
+ declare const UNITS_PER_EDO31_DEGREE: number;
117
+ /** 1 unit のセント値。 */
118
+ declare const CENTS_PER_UNIT: number;
119
+ /** A4 (MIDI 69) の units。周波数計算の基準。 */
120
+ declare const A4_UNITS: Units;
121
+ /** A4 の周波数(Hz)。 */
122
+ declare const A4_HZ = 440;
123
+ /** 対応する音律(1オクターブの分割数)。 */
124
+ type Edo = 12 | 31;
125
+ /** 既定の音律。宣言のない曲はすべてこれ(=既存データは素通り)。 */
126
+ declare const DEFAULT_EDO: Edo;
127
+ /** その音律の1格子ステップが何 units か。 */
128
+ declare const unitsPerStep: (edo: Edo) => number;
129
+ /** MIDIノート番号 → units。 */
130
+ declare const midiToUnits: (midi: MidiNote | number) => Units;
131
+ /**
132
+ * units → MIDIノート番号(小数)。
133
+ * 歌声合成(koe)は Hz を受けるためこの小数値をそのまま渡してよい。
134
+ * SoundFont のように整数ゾーンしか持たない発音器は {@link unitsToMidiDetune} を使う。
135
+ */
136
+ declare const unitsToMidi: (u: Units) => number;
137
+ /** units → 周波数(Hz)。 */
138
+ declare const unitsToHz: (u: Units) => number;
139
+ /**
140
+ * units → 「最寄りの整数MIDIノート番号 + セント補正」。
141
+ *
142
+ * SoundFont は音高ごとに整数キーでゾーンを持つため小数ピッチを直接鳴らせない。
143
+ * 最寄りのゾーンを鳴らして残差を `AudioBufferSourceNode.detune` で補正する。
144
+ * 31平均律での残差は最大 ±48.4 セントで、detune の可動域に十分収まる。
145
+ */
146
+ declare const unitsToMidiDetune: (u: Units) => {
147
+ midi: MidiNote;
148
+ detuneCents: number;
149
+ };
150
+ /**
151
+ * 臨時記号1つあたりの格子ステップ数。
152
+ *
153
+ * - `#` / `+`(12平均律) … クロマチック半音上げ
154
+ * - `-` … クロマチック半音下げ
155
+ * - `+`(31平均律) … 格子1ステップ上げ(微分音)
156
+ * - `_` … 格子1ステップ下げ(微分音)
157
+ *
158
+ * 12平均律ではクロマチック半音=1格子ステップなので4記号すべてが従来の意味に潰れ、
159
+ * 既存MMLの解釈は1文字も変わらない。31平均律でのみ `#`/`-`(±2度)と
160
+ * `+`/`_`(±1度)が分岐する。
161
+ */
162
+ declare const chromaticStep: (edo: Edo) => number;
163
+ /** 微分音記号 `+` / `_` 1つあたりの格子ステップ数。常に1。 */
164
+ declare const MICRO_STEP = 1;
165
+ /** 幹音の文字か。 */
166
+ declare const isNaturalLetter: (ch: string) => boolean;
167
+ /** 幹音の文字 → オクターブ内の格子ステップ。未知の文字は null。 */
168
+ declare const naturalStep: (ch: string, edo: Edo) => number | null;
169
+ /**
170
+ * 音名・オクターブ・臨時記号から units を組み立てる。
171
+ *
172
+ * `octave` はMMLの `o` 指定(MIDI慣習で o4 の c が中央ド = MIDI 60)。
173
+ * 臨時記号でオクターブを跨ぐ綴り(`b##` が次オクターブへ、`c--` が前オクターブへ)も
174
+ * そのまま加算されるので、度数を 0〜edo-1 にクランプしてはいけない。
175
+ */
176
+ declare const spellingToUnits: (letter: string, octave: number, chromatic: number, micro: number, edo: Edo) => number | null;
177
+ /**
178
+ * 五度圏インデックス(C=0, G=1, F=-1, C#=7, Db=-5)→ オクターブ内の格子ステップ。
179
+ *
180
+ * ミーントーン系の音律はすべて「五度を何ステップとするか」だけで決まるため、
181
+ * 綴りを持つ音(chord-parser の `rootFifth` / `noteFifths` など)はこの一本で
182
+ * どちらの音律へも正確に写せる。12平均律では C# と Db が同じ値に潰れ、
183
+ * 31平均律では 2 と 3 に分かれる。
184
+ */
185
+ declare const fifthToStep: (fifthIndex: number, edo: Edo) => number;
186
+ /** 五度圏インデックス → オクターブ内の units。 */
187
+ declare const fifthToUnits: (fifthIndex: number, edo: Edo) => number;
188
+ /**
189
+ * ピッチ表現のバージョン。
190
+ *
191
+ * - v1 … `pitch` が半音(MIDIノート番号)。dtm 1.x
192
+ * - v2 … `pitchUnits` が 1/372オクターブ単位。dtm 2.x
193
+ *
194
+ * dtm 自身は通信路を持たず `onNotesPatch` / `applyPatch` のフックを出すだけなので、
195
+ * バージョンの突き合わせは利用側アプリのハンドシェイクの責務になる。変換の知識だけを
196
+ * ここに置く。v1→v2 は無損失だが、**v2→v1 は12平均律の曲でしか成立しない**
197
+ * (31平均律を半音へ丸めると最大48.4セント動く)。
198
+ */
199
+ declare const PITCH_ENCODING_VERSION = 2;
200
+ /** v1(半音)→ v2(units)。無損失。 */
201
+ declare const pitchV1ToUnits: (pitch: number) => Units;
202
+ /** v2(units)→ v1(半音)。12平均律の曲でのみ無損失。 */
203
+ declare const unitsToPitchV1: (u: Units) => number;
204
+
60
205
  /**
61
206
  * コード進行文字列を伴奏トラックのノート配置へ展開する。
62
207
  *
@@ -64,6 +209,7 @@ declare const mountChordPlayer: (target: HTMLElement, chords: string, options?:
64
209
  * 旧 demo/index.html の applyChordProgression を移植・整理し、
65
210
  * 実際のノート追加を行わず配置(placement)の配列を返す純関数にした。
66
211
  */
212
+
67
213
  type ChordPatternType = "block" | "arpeggio" | "arpeggio-fast" | "offbeat" | "yatsume" | "alternating";
68
214
  type ChordPlacement = {
69
215
  startStep: number;
@@ -71,7 +217,7 @@ type ChordPlacement = {
71
217
  * 1/372オクターブ単位。chord-parser が返すのは12平均律の半音なので、
72
218
  * ここへ入れる時点で音律に応じた格子へ写している。
73
219
  */
74
- pitchUnits: number;
220
+ pitchUnits: Units;
75
221
  durationSteps: number;
76
222
  velocity: number;
77
223
  };
@@ -85,6 +231,28 @@ type ApplyChordOptions = {
85
231
  bpm: number;
86
232
  stepsPerBar: number;
87
233
  };
234
+ /**
235
+ * 伴奏の基準音(C3)に移調量を足した位置を units で返す。
236
+ *
237
+ * 移調(`rootShift`)は12平均律の半音で指定されるので、31平均律ではミーントーンの
238
+ * 度数({@link MEANTONE_STEP_BY_SEMITONE})へ写してから足す。
239
+ */
240
+ /**
241
+ * 12平均律の半音で表した移調量を、その音律の units へ写す。
242
+ *
243
+ * 31平均律では半音をそのまま足すと格子から外れるので、ミーントーンの度数
244
+ * ({@link MEANTONE_STEP_BY_SEMITONE})を経由する。曲全体を同じ量だけずらす用途
245
+ * (自動作曲の調決め)と、和音の基準位置を出す用途の両方で使う。
246
+ */
247
+ declare const semitonesToUnits: (semitones: number, edo: 12 | 31) => number;
248
+ /**
249
+ * 綴りを保った音(絶対半音+五度圏インデックス)を units へ写す。
250
+ *
251
+ * 作曲マクロ(`compose.ts`)がメロディ・ベースの音を作るときにも使う。伴奏トラックと
252
+ * **同じ写像を通す**ことで、31平均律で `D7` の増4度(15ステップ)に対しメロディが
253
+ * 減5度(16ステップ)を鳴らして半端にぶつかる、といった食い違いを構造的に防ぐ。
254
+ */
255
+ declare const spelledToUnits: (semitone: number, fifth: number, edo: 12 | 31) => Units;
88
256
  declare const buildChordPlacements: (options: ApplyChordOptions) => ChordPlacement[];
89
257
 
90
258
  type SongDrumInstruction = {
@@ -132,6 +300,548 @@ declare const resolveDrumPattern: (name: string, dict: Record<string, DrumPatter
132
300
  declare const DRUM_PATTERNS: Record<string, DrumPatternDef<DrumPattern>>;
133
301
  declare const getDrumPatternKeys: (name: string, dict: Record<string, AnyDrumPattern | DrumPatternDef>) => number[];
134
302
 
303
+ /**
304
+ * 自動作曲のドラム編曲。16小節ぶんのドラムを、セクションに合わせて組み立てる。
305
+ *
306
+ * ## なぜ要るか
307
+ *
308
+ * 作曲マクロは長らく**ドラムに一切触っていなかった**。メロディ・サブメロ・ベース・伴奏の
309
+ * ノートだけを書き、ドラムは既定の `dance`(1小節10ヒットのループ)が16小節そのまま
310
+ * 鳴り続ける、という状態だった。
311
+ *
312
+ * 参考にした人間の曲13本を測ると、**全曲がドラムに2〜31種(平均5.9種)の小節パターンを
313
+ * 持っている**。イントロ、サビ頭のクラッシュ、4小節ごとのフィル、セクションごとの
314
+ * ハイハットの開け閉め——曲の「進行している感じ」の大半はここが作っている。
315
+ * 同じ1小節を16回鳴らすと、上物が何をしていても打ち込みらしさが残る。
316
+ *
317
+ * ## 作り方
318
+ *
319
+ * 曲の骨格(A / A' / B / A'')に対して**強度**を割り当て、その強度でキック・スネア・
320
+ * ハイハットの刻みを選び、4小節ごとの区切りにフィルを置く。フィルは毎回違う形を引く。
321
+ *
322
+ * ```
323
+ * 1 2 3 4 | 5 6 7 8 | 9 10 11 12 | 13 14 15 16
324
+ * A(抑えめ) A'(標準) B(サビ・最大) A''(標準→締め)
325
+ * ↑クラッシュ ↑クラッシュ ↑クラッシュ ↑クラッシュ
326
+ * ↑フィル ↑フィル ↑フィル ↑フィル
327
+ * ```
328
+ *
329
+ * フィルは4小節の切れ目に置くが、**最終小節の1つ手前**にも置く。フィルは「次の小節へ
330
+ * 入る助走」なので、次が無い16小節目に置いても行き場が無い。16小節目はクラッシュと
331
+ * キックを1発ずつ置いて鳴らし切る。
332
+ *
333
+ * ## グリッド
334
+ *
335
+ * `shuffle` だけは**16分の格子から外れる**。1小節192ステップは3で割り切れるので、
336
+ * 8分三連(16ステップ)でハネを表現できる。参考曲は平均24%の音が16分格子の外に
337
+ * あり、生成物は0%だった——グルーヴを作るには格子から出る必要がある。
338
+ */
339
+
340
+ /** 曲全体のドラムの性格。曲ごとに1つ引く。 */
341
+ type DrumStyle = "eight" | "sixteen" | "four" | "shuffle" | "ballad" | "rock";
342
+ type ComposedDrums = {
343
+ style: DrumStyle;
344
+ def: DrumPatternDef<SongDrumPattern>;
345
+ /** 小節パターンの種類数。単調さの検算に使う。 */
346
+ variety: number;
347
+ };
348
+
349
+ /**
350
+ * 生成した曲の「良さ」を測るための指標。
351
+ *
352
+ * ## なぜ分けたか
353
+ *
354
+ * 初期の受け入れ基準(音価のエントロピー・休符率・跳躍率・音域)は、**7項目のうち5つが
355
+ * 順序非依存**だった。実際に16小節の順番をシャッフルして曲を破壊しても、
356
+ *
357
+ * 原曲 kinds:5 entropy:1.925 rest:0.039 range:15
358
+ * 小節混ぜ kinds:5 entropy:1.925 rest:0.039 range:15
359
+ *
360
+ * と、ほぼ全部の値が動かず基準を通過する。残る2つ(最大跳躍・跳躍率)も隣接2音しか
361
+ * 見ておらず、**小節をまたぐ構造を測る指標が1つも無かった**。80回引き直しても、
362
+ * モチーフの配置・盛り上がりの位置・フレーズの呼応は評価対象外なので改善しようがない。
363
+ *
364
+ * この module は「順序に依存する指標」を足すためにある。指標は
365
+ * {@link StructureFeatures}(コードを知らなくても測れる=人間の曲から較正できる)と
366
+ * {@link TensionFeatures}(和音が要る=音楽理論から決める)に分けてあり、前者は
367
+ * `scripts/calibrate-corpus.ts` が人間の曲から採った実測値
368
+ * ({@link file://./compose-corpus.ts})を目標値に使う。
369
+ *
370
+ * ## 単位
371
+ *
372
+ * MIDI取り込みの曲と生成物を**同じ関数で測る**ため、入力は units ではなく
373
+ * 半音({@link MetricNote.pitchSemi})とステップで受ける。
374
+ */
375
+ /** 測定対象の1音。MIDIから作るときも生成物から作るときも、この形へ落としてから測る。 */
376
+ type MetricNote = {
377
+ startStep: number;
378
+ /** MIDIノート番号相当の半音。31平均律では小数になるが、指標の計算には影響しない。 */
379
+ pitchSemi: number;
380
+ durationSteps: number;
381
+ };
382
+ type MetricOptions = {
383
+ stepsPerBar: number;
384
+ bars: number;
385
+ };
386
+ type StructureFeatures = {
387
+ /**
388
+ * 自己相似プロファイル。lag 小節だけ離れた小節どうしの平均類似度。
389
+ *
390
+ * 良い曲は「lag4 と lag8 が高く、lag1 は低い」という山型になる——4小節・8小節の
391
+ * フレーズで同じ形が戻ってくる一方、隣り合う小節は違う形をしている、ということ。
392
+ * 平坦だと「全部同じ」か「全部バラバラ」のどちらかで、どちらも曲に聞こえない。
393
+ */
394
+ sim1: number;
395
+ sim2: number;
396
+ sim4: number;
397
+ sim8: number;
398
+ /** フレーズの切れ目(2小節ごと)で息継ぎ(ロングトーンか休符)があった比率。 */
399
+ phraseBreath: number;
400
+ /** 最高音が現れる位置。曲頭が0、曲末が1。 */
401
+ climaxPosition: number;
402
+ /** 最高音の2半音以内まで達する「頂点」の数。1〜2なら単峰。 */
403
+ climaxPeaks: number;
404
+ /** メロディが鳴っていないステップに、サブメロの音が入っている比率(コール&レスポンス)。 */
405
+ complementarity: number;
406
+ };
407
+ /**
408
+ * コール&レスポンス。サブメロの**音の入り(オンセット)**のうち、メロディが休んで
409
+ * いる瞬間に入るものの割合。
410
+ *
411
+ * 最初は「サブメロが鳴っているステップのうちメロディが休んでいる割合」で測っていたが、
412
+ * それだと**伸ばしている音が一方的に不利になる**。メロディの隙間で入ってそのまま
413
+ * 伸びるパッドや保続音は対旋律として正しい書き方なのに、伸びた分だけ重なりとして
414
+ * 数えられ、書法を変えても値がほとんど動かなかった(実測 0.15 前後で頭打ち)。
415
+ *
416
+ * 測りたいのは「サブメロはメロディが息継ぎしたときに喋るか」なので、
417
+ * **入りの瞬間だけ**を見る。持続の長さは {@link StructureFeatures.phraseBreath} や
418
+ * 音価の指標が別に見ている。
419
+ *
420
+ * ハモリはメロディと同時に入るのが正しい書法なので、1を目指す指標ではない。
421
+ */
422
+ declare const complementarity: (melody: MetricNote[], submelody: MetricNote[], opts: MetricOptions) => number;
423
+ declare const structureFeatures: (melody: MetricNote[], submelody: MetricNote[], opts: MetricOptions) => StructureFeatures;
424
+ /**
425
+ * メロディがどれくらい細かく動いているか。
426
+ *
427
+ * **これが無かったせいで、生成物は人間の曲のちょうど半分の速さで止まっていた。**
428
+ * 参考曲を測ると 1小節あたり 9.3音(p25〜p75 で 4.4〜10.0)・音の 93% が8分以下
429
+ * なのに対し、生成物は 4.4音・49% で、支配的な音価は4分音符だった。
430
+ *
431
+ * 既存の指標ではこの差が1つも見えない——音価エントロピーも種類数も**分布の散らばり**
432
+ * しか見ておらず、「4分中心に散らばった曲」と「16分中心に散らばった曲」を区別しない。
433
+ * 休符率も自己相似も同じ。結果、`groove: "sixteenth"` を50%の確率で引いていたのに、
434
+ * 採点を通ると16分の曲は8%(60曲中5曲)まで落ちていた。
435
+ *
436
+ * メロディの速さは曲の性格そのものなので、採点項目として持つ。
437
+ */
438
+ type DensityFeatures = {
439
+ /** 1小節あたりの音数。 */
440
+ notesPerBar: number;
441
+ /** 8分音符以下の短い音が占める比率。曲の「刻みの細かさ」。 */
442
+ shortNoteRatio: number;
443
+ };
444
+ type TensionFeatures = {
445
+ /** B部(9〜12小節)がA部(1〜4小節)より緊張しているか。0〜1。 */
446
+ rise: number;
447
+ /** 終止(16小節目)で緊張が解けているか。0〜1。 */
448
+ resolve: number;
449
+ };
450
+ /**
451
+ * 小節ごとの緊張度から、曲の緊張カーブが「B部で上がって終止で解ける」形かを測る。
452
+ *
453
+ * `barTension` は各小節の不協和度({@link file://./compose.ts} の `toneWeight` を
454
+ * 音価で重み付けして集計したもの)。**この関数の存在意義は「1音ごとに使い捨てていた
455
+ * 不協和度を時系列に積む」こと**で、材料は前からあった。
456
+ */
457
+ declare const tensionFeatures: (barTension: number[]) => TensionFeatures;
458
+ /**
459
+ * 台形の当てはめ。`[idealLo, idealHi]` の内側なら1、`lo`/`hi` の外側で0、間は線形。
460
+ *
461
+ * **しきい値の合否ではなく連続値にする**のが要点。初期実装は全部を1つの真偽値へ
462
+ * 潰していたので、「構造は良いのに休符率が0.09だったので捨てられた曲」が
463
+ * 大量に発生していた。
464
+ */
465
+ declare const band: (v: number, lo: number, idealLo: number, idealHi: number, hi: number) => number;
466
+ /** 目標帯。`scripts/calibrate-corpus.ts` が人間の曲から採った値をこの形で出す。 */
467
+ type Band = [lo: number, idealLo: number, idealHi: number, hi: number];
468
+ /**
469
+ * 曲の特徴ベクトル。各成分はおおよそ 0〜1 に収まるよう正規化してある。
470
+ * 直近に作った曲との距離を採点へ入れることで、**指標に最適化した結果として
471
+ * 全曲が同じ統計値へ寄る**のを防ぐ。
472
+ */
473
+ declare const featureVector: (f: {
474
+ entropy: number;
475
+ restRatio: number;
476
+ leapRatio: number;
477
+ melodyRange: number;
478
+ density: DensityFeatures;
479
+ structure: StructureFeatures;
480
+ }) => number[];
481
+ /** 特徴ベクトル間のユークリッド距離。 */
482
+ declare const featureDistance: (a: number[], b: number[]) => number;
483
+
484
+ /**
485
+ * 曲のセクション(イントロ・Aメロ・Bメロ・サビ・間奏・アウトロ)。
486
+ *
487
+ * ## なぜ要るか
488
+ *
489
+ * 作曲マクロは長らく**16小節を A-A'-B-A'' という抽象的な形でしか作っていなかった**。
490
+ * 「いま作っているのが曲のどこなのか」を持っていないので、どの小節も同じ密度・同じ音域で
491
+ * 鳴り、イントロもサビも区別が付かない。人が聴いて最初に分かるのは和音や音程ではなく
492
+ * **「ここはAメロだ」「サビに入った」というセクションの切り替わり**なので、そこが
493
+ * 無いまま細部を詰めても「曲の一部を切り出したもの」にしかならない。
494
+ *
495
+ * ## セクションごとに何を変えるか
496
+ *
497
+ * | | メロディ | 音域 | 密度 | ドラム | 終わり方 |
498
+ * |---|---|---|---|---|---|
499
+ * | イントロ | 無し | — | — | 抑えめ | — |
500
+ * | Aメロ | 有り | 低め | 控えめ | 抑えめ | 半終止 |
501
+ * | Bメロ | 有り | 中 | 上げる | 標準 | ドミナントで宙吊り |
502
+ * | サビ | 有り | 高い | 最大 | 最大 | 主音へ全終止 |
503
+ * | 間奏 | 無し | — | — | 標準 | — |
504
+ * | アウトロ | 有り | 低め | 薄い | 薄い | 主音へ全終止 |
505
+ *
506
+ * メロディを書かないセクション(イントロ・間奏)でも、伴奏・ベース・ドラムは鳴る。
507
+ * ここを「メロディが無いだけの同じ小節」にすると、結局のっぺりしたままになるので、
508
+ * ドラムの強度と伴奏の奏法で差を付ける。
509
+ */
510
+ /** セクションの種類。 */
511
+ type SectionKind = "intro" | "verse" | "prechorus" | "chorus" | "interlude" | "outro";
512
+ declare const SECTION_LABELS: Record<SectionKind, string>;
513
+ /** セクションの並び順(UIの並びと、指定が無いときの既定の順序)。 */
514
+ declare const SECTION_ORDER: SectionKind[];
515
+ /**
516
+ * 既定で作るセクション。イントロ→Aメロ→Bメロ→サビ の、いちばん短い「1コーラス」。
517
+ * 全部入れると長くなりすぎるので、間奏とアウトロは既定では作らない。
518
+ */
519
+ declare const DEFAULT_SECTIONS: SectionKind[];
520
+ type SectionSpec = {
521
+ /** 小節数。 */
522
+ bars: number;
523
+ /** メロディを書くか。イントロと間奏は伴奏だけ。 */
524
+ melody: boolean;
525
+ /**
526
+ * メロディの音域の中心をどれだけずらすか(半音)。
527
+ * サビが高く、Aメロが低いのが、セクションの差として最も分かりやすい。
528
+ */
529
+ registerShift: number;
530
+ /**
531
+ * 音数の傾き。1より小さいと休符寄りの薄いセクション、大きいと詰める。
532
+ * リズム型を選ぶときの「休符を含む型」の引きやすさに効く。
533
+ */
534
+ density: number;
535
+ /** ドラムの強度。0=抑えめ 1=標準 2=最大。 */
536
+ drumLevel: 0 | 1 | 2;
537
+ /**
538
+ * セクションの終わりの着地音(主音からの音階度数)。
539
+ * `null` はメロディが無いセクション。
540
+ */
541
+ landing: number | null;
542
+ /** 伴奏に使うコード進行の役割。 */
543
+ progression: "a" | "b";
544
+ };
545
+ declare const SECTION_SPECS: Record<SectionKind, SectionSpec>;
546
+ /** 曲の中に置かれた1つのセクション。 */
547
+ type PlacedSection = {
548
+ kind: SectionKind;
549
+ /** 開始小節(0始まり)。 */
550
+ startBar: number;
551
+ bars: number;
552
+ spec: SectionSpec;
553
+ /**
554
+ * セクション内の転調量(ハ長調基準からの半音シフト)。
555
+ * ラスサビで +1/+2 半音上がるなど、曲中の転調を小節単位で表現する。
556
+ */
557
+ keyShift: number;
558
+ };
559
+ /**
560
+ * 選ばれたセクションを並べて、曲の設計図にする。
561
+ *
562
+ * 指定が空のときは {@link DEFAULT_SECTIONS} を使う——チェックを全部外した状態で
563
+ * 「何も作られない」より、既定の構成が出たほうが押した意味がある。
564
+ */
565
+ declare const buildSectionPlan: (kinds: SectionKind[]) => PlacedSection[];
566
+
567
+ /**
568
+ * 自動作曲マクロ。シンプルモードの4トラック(メロディ/サブメロ/ベース/伴奏)へ
569
+ * 16小節の曲を丸ごと組み立てる。
570
+ *
571
+ * ## 設計の根拠
572
+ *
573
+ * 「和音構成音を均等音価で並べる」素朴な自動生成は、**リズムが単調で機械的に聞こえる**。
574
+ * この実装は、実際に人が聴いて評価した結果から逆算した次の基準を満たすように作ってある
575
+ * ({@link ComposeStats} で毎回検算し、満たさない draw は引き直す)。
576
+ *
577
+ * - 音価は4種類以上を混在させる。メロディの音価のシャノンエントロピーが
578
+ * {@link MIN_ENTROPY_BITS} を下回るものは「単調」とみなして採用しない。
579
+ * - 休符を全体の {@link MIN_REST_RATIO}〜{@link MAX_REST_RATIO} 程度入れる。
580
+ * - **順番が本質**: ①コード進行を決める →②先に各小節のリズム型を、隣接小節で変化を
581
+ * つけて設計する →③その上に音を乗せる。
582
+ * - **短いモチーフを1つ作り、変形で展開する**(反復/セクエンツ/オクターブ上げ)。
583
+ * - **緩急**をつける。休符・ロングトーンの「緩」と16分音符の「急」を対比させる。
584
+ *
585
+ * ## 「どの曲も似ている」を潰すための設計
586
+ *
587
+ * 初版は品質基準こそ満たしていたが、**曲どうしの違い**が出ていなかった。300曲を生成して
588
+ * 測ったところ、ベースの配置は1種類(全曲同一)、サブメロの配置も1種類、メロディの
589
+ * 隣接音程は77%が2半音以内、16小節の役割配置は定数で固定——つまり「同じ設計図の上で
590
+ * 音名だけが違う曲」を量産していた。そこで次を曲ごとの引きに変えてある。
591
+ *
592
+ * - **曲の骨格をランダムに組む**。{@link SECTION_FORMS} から4セクションぶんを引く。
593
+ * 固定するのは「1小節目はモチーフ提示」「B部にクライマックスが1回」「16小節目は終止」
594
+ * の3点だけで、残りの配置は曲ごとに変わる。
595
+ * - **メロディの書法を曲ごとに引く**({@link MelodyStyle})。走句の形・つなぎの形・
596
+ * ロングトーンの形・終止形・モチーフの原型・跳躍の混ぜ具合を、曲ごとに選び直す。
597
+ * - **跳躍を積極的に使う**。順次進行だけのメロディは「歌いやすい」が「印象に残らない」。
598
+ * 跳躍が隣接音程に占める比率を {@link MIN_LEAP_RATIO}〜{@link MAX_LEAP_RATIO} に収め、
599
+ * **跳躍の直後は反行の順次進行で埋める**(gap fill)という古典的な定石で歌える形に保つ。
600
+ * - **サブメロを対旋律にする**。初版は和音構成音を1小節に1〜2個置くだけで、旋律と
601
+ * 呼べる形をしていなかった。メロディと反行(contrary motion)する輪郭を持たせ、
602
+ * 置き方も曲ごとに変える。ただし**単音・低密度・長音価**という形は崩さない——
603
+ * おまかせマスタリングの役割推定がこの3点でサブメロを判定するため。
604
+ * - **ベースに奏法を持たせる**。ルート4分打ち固定をやめ、オルタネイト/ウォーキング/
605
+ * 8分ドライブ/シンコペ等から曲ごとに引く。
606
+ *
607
+ * ## 強拍に何を置くか(重み3階層)
608
+ *
609
+ * 和音の構成音には重みがある(ルート・5度=最重要/3度・7度=重要/それ以外=経過音)。
610
+ * 強拍(小節頭と3拍目)では重みの高い音へ着地させ、弱拍は隣接音でつなぐ。
611
+ * さらに**その瞬間の和音によって、同じ音でも重要度が変わる**。半音上に和音構成音が
612
+ * 来るスケール音は「アボイドノート」として重み0とし、強拍・長い音価では使わない
613
+ * (弱拍の経過音としてだけ通す)。{@link toneWeight} 参照。
614
+ *
615
+ * ## 「基準を満たす」から「候補から選ぶ」へ
616
+ *
617
+ * ここまでの設計でも、**受け入れ基準そのものが曲の構造を測っていなかった**。
618
+ * 16小節の順番をシャッフルして曲を破壊しても、7項目のうち5つは値が1つも動かない。
619
+ * 順序を見ていたのは隣接2音までの2項目だけで、モチーフの配置も盛り上がりの位置も
620
+ * フレーズの呼応も評価対象外だった。しきい値をいくら引き直しても改善しようがない。
621
+ *
622
+ * そこで次の3点を変えてある。
623
+ *
624
+ * - **順序に依存する指標を作った**({@link file://./compose-metrics.ts})。
625
+ * 小節どうしの自己相似プロファイル、フレーズの息継ぎ、クライマックスの単峰性、
626
+ * 緊張カーブ、メロディとサブメロのコール&レスポンス。採点の重みは
627
+ * {@link WEIGHTS} のとおり構造側へ半分以上を配分している。
628
+ * - **合否ではなく点数にした**。ハード制約({@link HARD})に触れたものだけを捨て、
629
+ * 残りは {@link DRAW_COUNT} 本の候補から**点数最大のものを選ぶ**。初版は
630
+ * 「しきい値を全部満たした最初の1本」を返していたので、構造が良くても休符率が
631
+ * 0.09なら捨てるし、しきい値さえ満たせば1本目で確定していた。
632
+ * - **しきい値を人間の曲から採った**({@link CORPUS_BANDS})。手で決めた定数をやめ、
633
+ * 実際のMIDIから同じ指標を抽出した分布の中央50%を満点にしている。
634
+ * 較正は `scripts/calibrate-corpus.ts`。
635
+ *
636
+ * さらに、指標に最適化した結果として全曲が同じ統計値へ寄るのを防ぐため、直近に作った
637
+ * 曲との距離を加点している({@link ComposeOptions.recent})。
638
+ *
639
+ * ## 「調が固定に聞こえる」を潰したこと
640
+ *
641
+ * 曲の調({@link ROOT_SHIFTS})は曲ごとに12調へ散らしてあるのに、続けて聴くと
642
+ * どれも同じ調子に聞こえる、という状態が長く残っていた。参考曲91本と生成物を
643
+ * **同じ物差しで測って**原因を特定した(`scripts/compare-corpus.ts`)。
644
+ *
645
+ * - **メロディが調の外の音を1音も出せなかった。** 音の並びは最後まで音階の度数
646
+ * ({@link MAJOR_SCALE})で持っていて、{@link nearestChordTone} が `E7` の `G#` を
647
+ * 返しても `semitoneToDegree` がその場で `G` へ丸めていた。実測で非ダイアトニック音は
648
+ * 40曲・約4000音を測って**0音**(参考曲は音数比で中央値4%・p75で11%)。
649
+ * 進行にセカンダリドミナントを入れておきながら、旋律だけが調に留まっていた。
650
+ * {@link applyChromatic} と、アボイドノートを半音上の和音構成音へ解決させる分岐
651
+ * ({@link shapeBar})で、和音の変化音・半音の経過音を通すようにしてある。
652
+ * - **和音の側にも調の外が無かった。** 進行の候補が全部ハ長調/イ短調の
653
+ * ダイアトニックだったので、借用和音(サブドミナントマイナー・bVII・裏コード)を
654
+ * 候補に足した。
655
+ *
656
+ * ## 帯の内側で、どこに居るか
657
+ *
658
+ * 目標帯({@link CORPUS_BANDS})は指標ごとに独立して採った**周辺分布**なので、
659
+ * p25〜p75 を一律に満点にすると**全部の帯の端に同時に居座る曲**も満点を取れる。
660
+ * 実測がまさにそれで、参考曲と生成物の中央値を並べると
661
+ *
662
+ * 1小節の音数 6.3 / 5.2  順次進行 0.50 / 0.36  休符率 0.07 / 0.18
663
+ * 跳躍率 0.37 / 0.51  音域 19 / 23半音
664
+ *
665
+ * と、**どの項目も帯の内側なのに揃って同じ側へ寄っていた**——「スカスカで跳ねて
666
+ * ばかりの曲」は、周辺分布だけ見れば全項目が人間の範囲に収まる。帯の内側にも
667
+ * 中央値へ向かう傾斜を付け(`centeredBand`)、測っていたのに採点していなかった
668
+ * 密度({@link file://./compose-metrics.ts} の `DensityFeatures`)と順次進行の比率を
669
+ * 採点項目に足した。
670
+ *
671
+ * ただし**採点だけでは密度は動かない**。候補40本はどれも同じ分布から引いた
672
+ * ものなので、選抜しても中央値は1音も動かなかった。曲ごとに狙いの密度と休符率を
673
+ * 先に決めて、それに近いリズム型を引くようにしてある(`targetNotesPerBar` /
674
+ * `targetRestRatio`)。
675
+ *
676
+ * ## サブメロの制約が外れたこと
677
+ *
678
+ * サブメロは長らく「単音・低密度・長音価」の形しか書けなかった。おまかせマスタリングの
679
+ * 役割推定がその3点でサブメロを判定していたため、外すと伴奏と誤判定されて楽器が
680
+ * 変わってしまうからで、**ツールの都合が音楽を縛っていた**。`daw.ts` が simple モードで
681
+ * トラックIDをそのまま役割として使うようになった(推定はMIDI取り込みと advanced モード
682
+ * 専用に退いた)ので、この制約は無くなった。合いの手・ハモリ・対旋律・保続音を
683
+ * {@link SubStyle} として持たせてある。
684
+ */
685
+
686
+ type ComposedNote = {
687
+ startStep: number;
688
+ pitchUnits: Units;
689
+ durationSteps: number;
690
+ velocity: number;
691
+ };
692
+ /** 生成結果の検算値。{@link composeSong} が基準を満たすまで引き直すのに使う。 */
693
+ type ComposeStats = {
694
+ /** メロディの音価の種類数。 */
695
+ valueKinds: number;
696
+ /** メロディの音価のシャノンエントロピー(bit)。 */
697
+ entropy: number;
698
+ /** 休符が占めるステップの比率。 */
699
+ restRatio: number;
700
+ /** クライマックスを除く隣接音の最大跳躍(半音)。 */
701
+ maxLeapSemitones: number;
702
+ /** 隣接音程のうち跳躍(3半音以上)が占める比率。低すぎると「のっぺり」。 */
703
+ leapRatio: number;
704
+ /** 隣接音程のうち順次進行(1〜2半音)が占める比率。歌いやすさの側。 */
705
+ stepRatio: number;
706
+ /** 調の外の音(変化音)が音数に占める比率。0が続くと「調が固定」に聞こえる。 */
707
+ chromaticRatio: number;
708
+ /** メロディの密度(1小節あたりの音数・短い音の比率)。 */
709
+ density: DensityFeatures;
710
+ /** メロディが使った音域(半音)。 */
711
+ melodyRange: number;
712
+ /** サブメロが使った音域(半音)。小さいと旋律になっていない。 */
713
+ submelodyRange: number;
714
+ /** 順序に依存する構造の指標。{@link file://./compose-metrics.ts} 参照。 */
715
+ structure: StructureFeatures;
716
+ /** 緊張カーブの指標(B部で上がるか・終止で解けるか)。 */
717
+ tension: TensionFeatures;
718
+ /** 採点の総合点(0〜1)。{@link WEIGHTS} の加重和を重みの合計で割ったもの。 */
719
+ score: number;
720
+ /** 項目ごとの得点(0〜1)。どこで点を落としたかを調べるために持つ。 */
721
+ scoreBreakdown: Record<string, number>;
722
+ /** 引いた候補の数。 */
723
+ attempts: number;
724
+ /** ハード制約で捨てられた候補の数。 */
725
+ rejected: number;
726
+ /**
727
+ * この曲の特徴ベクトル。次に作曲するとき {@link ComposeOptions.recent} へ
728
+ * 渡すと、「前と似た曲」が出にくくなる。
729
+ */
730
+ fingerprint: number[];
731
+ };
732
+ type ComposeOptions = {
733
+ /** 1小節のステップ数。DAW の renderConfig.stepsPerBar をそのまま渡す。 */
734
+ stepsPerBar: number;
735
+ /** 曲の音律。省略時は12平均律。 */
736
+ edo?: number;
737
+ /** 乱数源。テストから決定的な値を注入するために差し替えられる。 */
738
+ random?: () => number;
739
+ /**
740
+ * 直近に作った曲の特徴ベクトル({@link ComposeStats.fingerprint})。
741
+ *
742
+ * 指標に最適化すると、**基準を満たすことに最適化された結果として全曲が同じ
743
+ * 統計値へ寄る**。実際、初版は品質基準こそ満たしていたのに「同じ設計図の上で
744
+ * 音名だけが違う曲」を量産していた。ここへ直近の曲を渡すと、それらから離れている
745
+ * 候補に加点する({@link WEIGHTS.novelty})。
746
+ */
747
+ recent?: number[][];
748
+ /** 引く候補の数。既定 {@link DRAW_COUNT}。 */
749
+ drawCount?: number;
750
+ /**
751
+ * 作るセクション(イントロ・Aメロ・Bメロ・サビ・間奏・アウトロ)。
752
+ * 省略時は {@link DEFAULT_SECTIONS}。並び順は指定によらず
753
+ * `SECTION_ORDER` に従う。
754
+ */
755
+ sections?: SectionKind[];
756
+ };
757
+ type ComposeResult = {
758
+ /** 伴奏トラック用のコード進行文字列。`buildChordPlacements` へそのまま渡せる。 */
759
+ chordProgression: string;
760
+ /** 伴奏の奏法。 */
761
+ chordPattern: ChordPatternType;
762
+ /**
763
+ * 曲の調。ハ長調からの移調量(半音)。メロディ・サブメロ・ベースの音は
764
+ * **既にこの量だけ移調済み**なので、呼び出し側は伴奏トラックの `rootShift` へ
765
+ * 同じ値を渡すだけでよい。
766
+ */
767
+ rootShift: number;
768
+ /** 曲のテンポ(BPM)。 */
769
+ bpm: number;
770
+ /** 曲の設計図。どの小節がどのセクションかを表す。 */
771
+ sections: PlacedSection[];
772
+ /** 曲の長さ(小節)。セクションの選び方で変わる。 */
773
+ bars: number;
774
+ /**
775
+ * 16小節ぶんのドラム編曲。
776
+ *
777
+ * **作曲マクロは長らくドラムに一切触っていなかった**。既定の1小節ループが16小節
778
+ * そのまま鳴り続けるので、上物をどれだけ作り分けても「同じ伴奏の上で違うメロディを
779
+ * 鳴らしただけ」に聞こえていた。参考にした人間の曲は全曲がドラムに2〜31種の小節
780
+ * パターン(フィル・クラッシュ・セクションごとの刻みの差)を持っている。
781
+ */
782
+ drums: ComposedDrums;
783
+ melody: ComposedNote[];
784
+ submelody: ComposedNote[];
785
+ bass: ComposedNote[];
786
+ stats: ComposeStats;
787
+ };
788
+ /** 音価のシャノンエントロピー(bit)。全部同じ音価なら 0 になる。 */
789
+ declare const durationEntropy: (durations: number[]) => number;
790
+ /**
791
+ * 16小節の曲を組み立てる。
792
+ *
793
+ * {@link DRAW_COUNT} 本の候補を引き、**ハード制約({@link HARD})に触れたものだけを捨てて、
794
+ * 残りから一番点数の高いものを返す**。初版は「しきい値を全部満たした最初の1本」を
795
+ * 返していたが、それでは
796
+ *
797
+ * - 構造が良くても休符率が0.09なら捨てる(好みを表現できない)
798
+ * - しきい値さえ満たせば1本目で確定する(より良い候補を探しに行かない)
799
+ *
800
+ * という2つの問題があった。全滅した場合も点数最大のものを返すので、
801
+ * 「ボタンを押して何も起きない」状態にはならない。
802
+ *
803
+ * 較正済みの目標帯({@link CORPUS_BANDS}、人間の曲 {@link CORPUS_SIZE} 本から実測)を
804
+ * 使うので、点数は「人間の曲として普通の範囲にどれだけ収まっているか」を意味する。
805
+ */
806
+ declare const composeSong: (options: ComposeOptions) => ComposeResult;
807
+
808
+ /**
809
+ * **自動生成ファイル。手で編集しないこと。**
810
+ *
811
+ * npx tsx scripts/calibrate-corpus.ts --dir <MIDIのフォルダ>
812
+ * npx tsx scripts/calibrate-corpus.ts --api --limit 200
813
+ *
814
+ * 人間が書いた曲 91本 から `src/compose-metrics.ts` と同じ指標を抽出し、
815
+ * その分布の中央50%(p25〜p75)を満点、p05〜p95 の外側を0点とする目標帯にしたもの。
816
+ * 手で決めた定数の代わりにこれを使うことで、受け入れ基準が
817
+ * 「勘で決めた代理指標」から「人間の曲から採った代理指標」になる。
818
+ *
819
+ * コーパスを増やして測り直したら、このファイルごと差し替える。
820
+ */
821
+
822
+ /** 較正に使った曲数。少ないほど帯が狭く・偏る点に注意。 */
823
+ declare const CORPUS_SIZE = 91;
824
+ declare const CORPUS_BANDS: {
825
+ entropy: Band;
826
+ valueKinds: Band;
827
+ restRatio: Band;
828
+ leapRatio: Band;
829
+ stepRatio: Band;
830
+ chromaticRatio: Band;
831
+ maxLeap: Band;
832
+ melodyRange: Band;
833
+ notesPerBar: Band;
834
+ shortNoteRatio: Band;
835
+ sim1: Band;
836
+ sim2: Band;
837
+ sim4: Band;
838
+ sim8: Band;
839
+ phraseBreath: Band;
840
+ climaxPosition: Band;
841
+ climaxPeaks: Band;
842
+ complementarity: Band;
843
+ };
844
+
135
845
  type ClipMeter = {
136
846
  /** クリップ状態(ON/OFF)が変化するたびに呼ばれる。戻り値は購読解除関数。 */
137
847
  onClipChange: (cb: (clipping: boolean) => void) => () => void;
@@ -154,6 +864,29 @@ type ClipMeter = {
154
864
  /** 選べる音価。value は保存用の短いキー、beats は四分音符を1とした拍数。 */
155
865
  type DelayDivision = "4" | "8" | "8d" | "16";
156
866
 
867
+ /**
868
+ * 継続記号(`ー` / `〜`)で結合したノート内のピッチ推移を、koe の `CurveInput`
869
+ * (1フレームごとに評価される関数)へ変換する。
870
+ *
871
+ * メインスレッド({@link file://./lyrics.ts} の local backend)とWorker
872
+ * ({@link file://./voice-worker.ts})の双方から使う純ロジックなのでここへ切り出す。
873
+ * {@link file://./vibrato.ts} と同じ方針で、「何が歌唱表現か」を決めるのは dtm 側の責務。
874
+ *
875
+ * 副作用ゼロ・DOM非依存なので、Worker の別バンドルからも安全に import できる。
876
+ */
877
+ /**
878
+ * 継続記号で結合されたノートの、2区間目以降のピッチ推移。
879
+ * Worker へも postMessage で渡すため、素の JSON で表せる形にしてある。
880
+ */
881
+ type PitchSegment = {
882
+ /** この区間のピッチ。単位は units(1/372オクターブの整数)。 */
883
+ pitch: number;
884
+ /** ノートの母音オンセットからの相対秒(区間の開始位置)。 */
885
+ atSec: number;
886
+ /** 直前区間から滑らかに繋ぐ(`〜`)。false なら階段状に切り替える(`ー`)。 */
887
+ portamento: boolean;
888
+ };
889
+
157
890
  /**
158
891
  * 自動ビブラート(ロングトーンに自動で掛けるピッチLFO)の定数と曲線生成。
159
892
  *
@@ -180,7 +913,8 @@ declare const VIBRATO_MIN_SEC = 0.35;
180
913
  * 例: `@@2 klatt どはどなつのど`
181
914
  * `@@3 external_engine きょー`
182
915
  *
183
- * 区切りは半角スペース(引用符不要)。歌詞内のひらがな・カタカナ・長音記号(ー)以外は破棄する。
916
+ * 区切りは半角スペース(引用符不要)。歌詞内のひらがな・カタカナと、
917
+ * 制御記号 `ー` `〜`(継続)・`っ`(促音)・`_`(休符)・`、`(ブレス)以外は破棄する。
184
918
  *
185
919
  * 演奏データ(テンポ・休符・音符長)とは完全に分離されており、
186
920
  * Note On のタイミングで音節を1つずつ消費して歌わせる。
@@ -189,8 +923,52 @@ declare const VIBRATO_MIN_SEC = 0.35;
189
923
  * オプトインのフォルマント合成ヘルパ(createKlattVoice / createVoiceRegistry)を分離して提供する。
190
924
  */
191
925
 
192
- /** 歌詞文字列(かな)を正規化済み音節列へ変換する */
926
+ /** 継続記号(階段)。直前の音を言い直さずに保ち、ピッチだけを切り替える。 */
927
+ declare const TIE_MARK = "\u30FC";
928
+ /** 継続記号(ポルタメント)。{@link TIE_MARK} と同じだが、ピッチを滑らかに繋ぐ。 */
929
+ declare const PORTAMENTO_MARK = "\u301C";
930
+ /** 促音。ノートを消費し、無音の閉鎖として間を作る。 */
931
+ declare const STOP_MARK = "\u3063";
932
+ /** 明示的な休符。ノートを消費するが歌わない(そのノートは無音になる)。 */
933
+ declare const REST_MARK = "_";
934
+ /** ブレス。ノートは消費せず、直前ノートの尻を削って息継ぎを差し込む。 */
935
+ declare const BREATH_MARK = "\u3001";
936
+ /**
937
+ * デクレッシェンド。ノートは消費せず、直前の音(継続で結合されていればその全体)を
938
+ * 歌いながら声量0へ落とす。`あーーーーー↓` で「あ」を伸ばしたまま消えていく。
939
+ */
940
+ declare const FADE_OUT_MARK = "\u2193";
941
+ /**
942
+ * クレッシェンド。ノートは消費せず、直前の音(継続で結合されていればその全体)を
943
+ * 歌いながら声量を上げていく。{@link FADE_OUT_MARK} と併用するとスウェルになる。
944
+ */
945
+ declare const FADE_IN_MARK = "\u2191";
946
+ /**
947
+ * 歌詞文字列(かな+制御記号)を正規化済み音節列へ変換する。
948
+ *
949
+ * 出力の1要素=ノート1つぶん。ブレス(`、`)だけはノートを消費せず、
950
+ * 直前の音節の {@link LyricSyllable.breathAfter} へ畳まれる。
951
+ *
952
+ * 継続記号(`ー` / `〜`)は直前の音節の母音を焼き込んで返す(例: きょ + ー → きょ + お)。
953
+ * `kind: "tie"` は残るので、発音側は「言い直さない」判断ができ、表示側は `ー` へ戻せる。
954
+ *
955
+ * - 撥音(ん)の直後の継続は「ん」を伸ばす(ハミング)。
956
+ * - 促音(`っ`)は母音の文脈を切らない(語中の詰まりなので直前母音を保つ)。
957
+ * - 休符(`_`)は歌わず、ブレス(`、`)ともども母音の文脈を切る。次の音節は語頭として歌われる。
958
+ * - 引き継ぐ母音が無い継続(行頭・ブレス直後)は意味を持たないので捨てる。
959
+ */
193
960
  declare const normalizeLyrics: (text: string) => LyricSyllable[];
961
+ /**
962
+ * 音節を歌詞テキストの1文字(表示・MMLへの書き戻し用)へ戻す。
963
+ * 継続・休符は元の記号へ、ブレスは直後に `、` を付けて返す。
964
+ *
965
+ * {@link LyricSyllable.kana} は継続のとき「引き継いだ母音のかな」なので、
966
+ * これをそのまま繋ぐと `あーー` が `あああ` になって継続の意図が失われる。
967
+ * 表示と往復(MML ⇄ エディタ)には必ずこちらを使うこと。
968
+ */
969
+ declare const displayKana: (syl: LyricSyllable) => string;
970
+ /** 音節列を歌詞テキストへ戻す({@link displayKana} の連結)。 */
971
+ declare const syllablesToText: (syllables: LyricSyllable[]) => string;
194
972
  /**
195
973
  * 歌唱の声量の上限(%)。100=等倍。100超は合成音声をブースト(増幅)する。
196
974
  * 100では音量が足りないケース向けに大きめのヘッドルームを確保する。
@@ -315,12 +1093,26 @@ type VoiceModel = {
315
1093
  * (モデル内部状態に依存しないので、同一モデルを複数トラックで共有しても干渉しない)。
316
1094
  * 合成不能(該当音素なし・無声)なら null。klatt 等の軽量モデルは未実装でよい。
317
1095
  */
318
- renderToCache?: (syllable: LyricSyllable, prevVowel: string, pitch: number, durationMs: number, vibrato?: boolean, expr?: VoiceExpression) => Promise<string | null>;
1096
+ renderToCache?: (syllable: LyricSyllable, prevVowel: string, pitch: number, durationMs: number, vibrato?: boolean, expr?: VoiceExpression,
1097
+ /**
1098
+ * 継続記号(`ー` / `〜`)で結合されたノート内のピッチ推移(2区間目以降)。
1099
+ * 先頭区間は `pitch`、`durationMs` は結合後の全長。未指定なら単一ピッチ。
1100
+ */
1101
+ pitchSegments?: PitchSegment[]) => Promise<string | null>;
319
1102
  /**
320
1103
  * {@link renderToCache} 済みのバッファを絶対時刻 t0(AudioContextクロック秒)へスケジュールする。
321
1104
  * t0 は未来の任意時刻でよく、再生はオーディオスレッドが担うのでメインスレッドのもたつきに影響されない。
322
1105
  */
323
- scheduleCached?: (key: string, t0: number, peak: number, pan: number, reverbSend?: number, delaySend?: number, destination?: AudioNode) => void;
1106
+ scheduleCached?: (key: string, t0: number, peak: number, pan: number, reverbSend?: number, delaySend?: number, destination?: AudioNode,
1107
+ /**
1108
+ * 直前ノートからの継続として鳴らす(継続記号がノート結合できなかったときの分割再生)。
1109
+ * 先行母音を切り、立ち上がりを直前ノートの減衰へ被せて言い直し感を消す。
1110
+ */
1111
+ continuation?: boolean,
1112
+ /** 歌いながら声量0へ落とす(`↓`)。{@link LyricSyllable.fadeOut} を引き回したもの。 */
1113
+ fadeOut?: boolean,
1114
+ /** 歌いながら声量を上げる(`↑`)。両方立てるとスウェルになる。 */
1115
+ fadeIn?: boolean) => void;
324
1116
  /** スケジュール済みの発音をすべて即停止する(停止・一時停止・シーク時)。 */
325
1117
  stopAll?: () => void;
326
1118
  };
@@ -418,13 +1210,80 @@ type StreamVoiceNote = {
418
1210
  /**
419
1211
  * ピッチ。単位は units(1/372オクターブ)。koe は Hz を受けるので、
420
1212
  * ここから直接 Hz へ変換して歌わせる(整数MIDIノートに丸めない)。
1213
+ * 継続記号で複数ノートが結合されている場合は**先頭区間**のピッチ。
421
1214
  */
422
- pitch: number;
1215
+ pitch: Units;
423
1216
  /** アンカー(再生開始時刻)からの相対秒。実発音時刻 = anchorTime + startSec。 */
424
1217
  startSec: number;
425
- /** ゲート適用済みの発音長(秒)。 */
1218
+ /** ゲート適用済みの発音長(秒)。結合されている場合は結合後の全長。 */
426
1219
  durationSec: number;
1220
+ /**
1221
+ * 継続記号(`ー` / `〜`)で結合された2区間目以降のピッチ推移。
1222
+ * これがあるノートは「1回の合成で歌い切る長い1音」で、区間の境界では
1223
+ * 言い直さずにピッチだけが動く。
1224
+ */
1225
+ pitchSegments?: PitchSegment[];
1226
+ /**
1227
+ * 直前ノートからの継続として鳴らす(結合できなかった `ー` / `〜`)。
1228
+ * 発音側は先行母音を切り、立ち上がりを直前ノートへ被せて言い直し感を消す。
1229
+ */
1230
+ continuation?: boolean;
1231
+ /**
1232
+ * このノートの直後にブレス(`、`)を入れる。
1233
+ * `durationSec` は既にブレスぶん切り詰めてある。
1234
+ */
1235
+ breath?: boolean;
1236
+ /**
1237
+ * このノートを歌いながら声量0へ落とす(`↓`)。継続記号で結合されたグループの
1238
+ * どこに `↓` が付いていても、**結合後の1音全体**に掛かる。
1239
+ */
1240
+ fadeOut?: boolean;
1241
+ /** このノートを歌いながら声量を上げていく(`↑`)。{@link StreamVoiceNote.fadeOut} の対。 */
1242
+ fadeIn?: boolean;
427
1243
  };
1244
+ /**
1245
+ * 継続記号でノートを結合できる上限(秒)。これを超える長さは1回の合成に載せず、
1246
+ * 分割して継続ノート({@link StreamVoiceNote.continuation})として繋ぐ。
1247
+ *
1248
+ * WORLD再合成のコストは音価にほぼ比例するので、際限なく結合すると先読みが
1249
+ * 間に合わず「遅延スキップ」で歌が抜ける。
1250
+ * ロングトーンとして実用になる長さを確保しつつ、合成が破綻しない上限。
1251
+ */
1252
+ declare const TIE_MERGE_MAX_SEC = 4;
1253
+ /** {@link buildStreamVoiceNotes} が受け取る演奏ノート(startStep 昇順で渡すこと)。 */
1254
+ type TieSourceNote = {
1255
+ startStep: number;
1256
+ durationSteps: number;
1257
+ pitchUnits: Units;
1258
+ };
1259
+ /** {@link buildStreamVoiceNotes} のタイミング・移調パラメータ。 */
1260
+ type StreamVoiceNoteOptions = {
1261
+ /** シーク開始位置(ステップ)。これより前に始まるノートは切り落とす。 */
1262
+ fromStep: number;
1263
+ /** 1ステップの実時間(秒)。 */
1264
+ secondsPerStep: number;
1265
+ /** ゲートタイム係数 0-1(歌詞トラックの `q` を正規化したもの)。 */
1266
+ gate: number;
1267
+ /** 歌唱ピッチのオクターブシフト(units)。 */
1268
+ octaveShiftUnits: number;
1269
+ };
1270
+ /**
1271
+ * 音節列と演奏ノート列を突き合わせ、ストリーミング用のノート列を組み立てる。
1272
+ *
1273
+ * 音節とノートは**発音順(startStep昇順)の index で1:1**に対応する。これは
1274
+ * ピアノロールの歌詞表示(`renderer.drawNoteLyrics`)とも共通の規則。
1275
+ *
1276
+ * 継続記号(`ー` / `〜`)はここでノートへ畳まれる:
1277
+ * - 直前ノートと**隙間なく続いている**なら1音へ結合し、{@link StreamVoiceNote.pitchSegments}
1278
+ * としてピッチ推移だけを持たせる(=1回の合成で歌い切る)。
1279
+ * - 隙間がある/{@link TIE_MERGE_MAX_SEC} を超えるなら結合をやめ、
1280
+ * {@link StreamVoiceNote.continuation} を立てた別ノートとして繋ぐ。
1281
+ * - シークで先頭が切り落とされた継続(結合相手が居ない)も同じく継続ノートになる。
1282
+ *
1283
+ * 隙間の判定は**ゲート適用前のステップ**で行う。ゲートを短くすると全ノートの間に
1284
+ * 隙間ができるが、それは発音長の設定であって「音が途切れている」ことではないため。
1285
+ */
1286
+ declare const buildStreamVoiceNotes: (syllables: LyricSyllable[], sorted: TieSourceNote[], o: StreamVoiceNoteOptions) => StreamVoiceNote[];
428
1287
  /** ストリーミング再生する歌詞トラック1本。 */
429
1288
  type StreamVoiceTrack = {
430
1289
  /**
@@ -681,7 +1540,7 @@ type Note = {
681
1540
  * 12平均律の1半音 = 31、31平均律の1度 = 12。MIDIノート番号ではないので注意。
682
1541
  * dtm 1.x の `pitch`(半音)から意味が変わっている。
683
1542
  */
684
- pitchUnits: number;
1543
+ pitchUnits: Units;
685
1544
  velocity?: number;
686
1545
  };
687
1546
  type RenderConfig = {
@@ -736,7 +1595,7 @@ type LyricSyncData = {
736
1595
  type NoteData = {
737
1596
  startStep: number;
738
1597
  /** 1/372オクターブ単位。{@link Note.pitchUnits} と同じ。 */
739
- pitchUnits: number;
1598
+ pitchUnits: Units;
740
1599
  durationSteps: number;
741
1600
  velocity?: number;
742
1601
  };
@@ -744,7 +1603,7 @@ type NoteData = {
744
1603
  type NoteRemove = {
745
1604
  startStep: number;
746
1605
  /** 1/372オクターブ単位。{@link Note.pitchUnits} と同じ。 */
747
- pitchUnits: number;
1606
+ pitchUnits: Units;
748
1607
  };
749
1608
  type PianoRollOptions = {
750
1609
  mountTarget: HTMLElement;
@@ -790,7 +1649,7 @@ type PlayNoteEvent = {
790
1649
  * `unitsToMidiDetune` を使う。{@link PlayDrumEvent.pitch} はGM打楽器のキー番号で
791
1650
  * 音高ではないため、こちらとは別物。
792
1651
  */
793
- pitchUnits: number;
1652
+ pitchUnits: Units;
794
1653
  /** 元ノートのvelocity (0-127) */
795
1654
  velocity: number;
796
1655
  /** トラックvolume×velocityを反映した 0-1 程度の音量係数 */
@@ -799,6 +1658,12 @@ type PlayNoteEvent = {
799
1658
  when: number;
800
1659
  /** 秒 */
801
1660
  duration: number;
1661
+ /**
1662
+ * 継続記号(`ー` / `〜`)で結合されたノート内のピッチ推移(2区間目以降)。
1663
+ * 先頭区間のピッチは {@link PlayNoteEvent.pitchUnits}、`duration` は結合後の全長。
1664
+ * 未指定なら従来どおり単一ピッチの1音として鳴らせばよい。
1665
+ */
1666
+ pitchSegments?: PitchSegment[];
802
1667
  /**
803
1668
  * ステレオ定位 -1(完全左)〜+1(完全右)、0が中央。未指定は中央扱い。
804
1669
  * 歌詞トラックに p<n>(0-127, 既定64=中央) があれば正規化した値が載る。
@@ -832,6 +1697,17 @@ type PlayNoteEvent = {
832
1697
  */
833
1698
  destination?: AudioNode;
834
1699
  };
1700
+ /**
1701
+ * 音節の種別。省略時(undefined)は通常のかな音節。
1702
+ *
1703
+ * - `"tie"` : 継続記号(`ー` / `〜`)。ノートは消費するが**言い直さない**。
1704
+ * 直前の母音をそのまま引き継ぎ、ピッチだけを動かす。
1705
+ * - `"stop"` : 促音(`っ`)。ノートを消費し、無音の閉鎖として間を作る。
1706
+ * - `"rest"` : 明示的な休符(`_`)。ノートを消費するが歌わない(そのノートは無音)。
1707
+ * 歌詞トラックは楽器音も鳴らさないので、`_` はそのまま「歌の休み」になる。
1708
+ * 直前母音の文脈もここで切れる(次の音節は語頭として扱われる)。
1709
+ */
1710
+ type LyricSyllableKind = "tie" | "stop" | "rest";
835
1711
  type LyricSyllable = {
836
1712
  /** 表示用かな("きょ" 等。長音は置換後の母音かな) */
837
1713
  kana: string;
@@ -839,6 +1715,38 @@ type LyricSyllable = {
839
1715
  consonant: string;
840
1716
  /** 母音 "a"|"i"|"u"|"e"|"o"、撥音 "N"、促音 "" */
841
1717
  vowel: string;
1718
+ /**
1719
+ * 音節の種別。省略時は通常のかな音節。
1720
+ * 発音・表示・MMLへの書き戻しの分岐に使う({@link LyricSyllableKind})。
1721
+ */
1722
+ kind?: LyricSyllableKind;
1723
+ /**
1724
+ * `kind === "tie"` のとき、直前ピッチから滑らかに繋ぐ(`〜`)。
1725
+ * false(`ー`)なら階段状に切り替える。
1726
+ */
1727
+ portamento?: boolean;
1728
+ /**
1729
+ * この音節の直後にブレス(息継ぎ)を入れる(`、`)。
1730
+ * ブレス自体はノートを消費しない — 直前ノートの尻を削って息を差し込み、
1731
+ * 次の音節を語頭(連続音の `- か`)として歌わせる。
1732
+ */
1733
+ breathAfter?: boolean;
1734
+ /**
1735
+ * この音節を歌いながら声量を0へ落としていく(`↓`)。
1736
+ *
1737
+ * 記号自体はノートを消費しない({@link LyricSyllable.breathAfter} と同じ扱い)。
1738
+ * 継続記号で1音へ結合されたグループに付いた場合は、**結合後の音全体**に掛かる——
1739
+ * `あーーーーー↓` は「あ」を伸ばしたまま最後まで滑らかに消えていく、という意味になる。
1740
+ */
1741
+ fadeOut?: boolean;
1742
+ /**
1743
+ * この音節を歌いながら声量を上げていく(`↑`)。{@link LyricSyllable.fadeOut} の対。
1744
+ *
1745
+ * 両方立っている場合(`あーーー↑↓`)は**スウェル**——小さく入って中央で最大、
1746
+ * そこから消えていく。声楽の messa di voce にあたる形で、`↑↓` `↓↑` どちらの
1747
+ * 順で書いても同じ意味になる。
1748
+ */
1749
+ fadeIn?: boolean;
842
1750
  };
843
1751
  /**
844
1752
  * オクターブユニゾンの重ね方。
@@ -1250,7 +2158,7 @@ type DawInstance = {
1250
2158
  * 画面外の場合は onScreen=false と方角 side を返す。
1251
2159
  * 協力DAWでのカーソル表示に使う。
1252
2160
  */
1253
- noteToCanvas: (step: number, pitch: number) => {
2161
+ noteToCanvas: (step: number, pitch: Units) => {
1254
2162
  x: number;
1255
2163
  y: number;
1256
2164
  onScreen: boolean;
@@ -1404,7 +2312,8 @@ declare const playPlacements: (placements: Array<{
1404
2312
  trackIndex: number;
1405
2313
  startStep: number;
1406
2314
  durationSteps: number;
1407
- pitchUnits: number;
2315
+ /** ピッチ。単位は units(1/372オクターブ)。 */
2316
+ pitchUnits: Units;
1408
2317
  velocity: number;
1409
2318
  }>, options: PlayPlacementsOptions) => MmlPlayback;
1410
2319
  /**
@@ -1415,7 +2324,7 @@ declare const playMML: (mml: string, options?: PlayMmlOptions) => MmlPlayback;
1415
2324
  type PlayNoteOptions = {
1416
2325
  audioContext?: AudioContext;
1417
2326
  destination?: AudioNode;
1418
- pitchUnits: number;
2327
+ pitchUnits: Units;
1419
2328
  volume?: number;
1420
2329
  duration?: number;
1421
2330
  };
@@ -1574,10 +2483,10 @@ declare class MMLCore {
1574
2483
  * @param pitch ピッチ番号
1575
2484
  * @param options ノート長などの設定
1576
2485
  */
1577
- addNote(step: number, pitch: number, options: AddNoteOptions): void;
2486
+ addNote(step: number, pitch: Units, options: AddNoteOptions): void;
1578
2487
  deleteNoteById(noteId: number): void;
1579
2488
  private getMaxStep;
1580
- moveNote(noteId: number, startStep: number, pitch: number): void;
2489
+ moveNote(noteId: number, startStep: number, pitch: Units): void;
1581
2490
  moveNoteEnd(_: number): void;
1582
2491
  resizeNote(noteId: number, durationSteps: number): void;
1583
2492
  resizeNoteEnd(_: number): void;
@@ -1875,7 +2784,7 @@ type MMLNotePlacement = {
1875
2784
  trackIndex: number;
1876
2785
  startStep: number;
1877
2786
  /** 1/372オクターブ単位({@link Note.pitchUnits} と同じ)。 */
1878
- pitchUnits: number;
2787
+ pitchUnits: Units;
1879
2788
  durationSteps: number;
1880
2789
  /** v コマンドで指定されたベロシティ(0-127、既定100) */
1881
2790
  velocity: number;
@@ -2069,11 +2978,12 @@ type Renderer = {
2069
2978
  getXY: (e: MouseEvent | PointerEvent) => [number, number, number];
2070
2979
  getGridPosition: (e: MouseEvent | PointerEvent) => {
2071
2980
  step: number;
2072
- pitch: number;
2981
+ /** ピッチ。単位は units(1/372オクターブ)。 */
2982
+ pitch: Units;
2073
2983
  x: number;
2074
2984
  y: number;
2075
2985
  };
2076
- onClick: (callback: (step: number, pitch: number) => void) => void;
2986
+ onClick: (callback: (step: number, pitch: Units) => void) => void;
2077
2987
  setDrawOffset: (x: number, y: number) => void;
2078
2988
  /** Canvasをマウント先から取り外す。 */
2079
2989
  destroy: () => void;
@@ -2134,6 +3044,17 @@ type Sequencer = {
2134
3044
  declare const resolveLoopPoint: (point: LoopPoint, _bpm: number, stepsPerBar: number, sps: number) => number;
2135
3045
  declare const createSequencer: (options: SequencerOptions) => Sequencer;
2136
3046
 
3047
+ /**
3048
+ * トラックへ挿す音作りの種別。GMプログラム番号から決める({@link toneForProgram})。
3049
+ *
3050
+ * - `bass` … パラレル・サチュレーション。
3051
+ * - `guitar-clean` … キャビネットのみ(クリーン系エレキ。歪ませない)。
3052
+ * - `guitar-drive` … 軽いドライブ+キャビネット(オーバードライブ/ディストーション)。
3053
+ * - `acoustic` … 生の撥弦楽器。プレゼンス+エアのEQだけ。
3054
+ * - `none` … 何も挿さない(鍵盤・管弦・シンセなど)。
3055
+ */
3056
+ type InstrumentTone = "bass" | "guitar-clean" | "guitar-drive" | "acoustic" | "none";
3057
+
2137
3058
  type SoundFontInstance = {
2138
3059
  play: (o: {
2139
3060
  ctx: AudioContext;
@@ -2148,6 +3069,12 @@ type SoundFontInstance = {
2148
3069
  when: number;
2149
3070
  duration: number;
2150
3071
  }) => void;
3072
+ /**
3073
+ * この音源に合った音作りの種別(ベース/ギター等)。チャンネルストリップへ挿す
3074
+ * 段(サチュレーション・キャビネット)の選択に使う。外部から差し替えたエンジンが
3075
+ * 持たない場合は "none" 扱い。
3076
+ */
3077
+ tone?: InstrumentTone;
2151
3078
  };
2152
3079
  type SoundFontEngine = {
2153
3080
  load: (o: {
@@ -2397,7 +3324,7 @@ type DtmStudio = {
2397
3324
  /** SoundFontを用いた単音再生を行う */
2398
3325
  playNote: (options: {
2399
3326
  /** ピッチ。単位は units(1/372オクターブ)。`pitchV1ToUnits` でMIDI番号から変換できる。 */
2400
- pitchUnits: number;
3327
+ pitchUnits: Units;
2401
3328
  volume?: number;
2402
3329
  duration?: number;
2403
3330
  instrument?: string;
@@ -2458,7 +3385,7 @@ declare const createDtmStudio: (options?: DtmStudioOptions) => Promise<DtmStudio
2458
3385
  * ドット絵・レトロゲーム風ピクセルアートデザイン(美咲フォント・角なし・ハードシャドウ)。
2459
3386
  * モバイルファースト(狭幅基準)で設計し、min-width メディアクエリで広幅に拡張する。
2460
3387
  */
2461
- declare const DAW_CSS = "\n@font-face {\n font-family: 'k8x12';\n src: url('https://db.onlinewebfonts.com/t/777630d46640dc5a928ea833c2fcb875.woff2') format('woff2'),\n url('https://db.onlinewebfonts.com/t/777630d46640dc5a928ea833c2fcb875.woff') format('woff'),\n url('https://db.onlinewebfonts.com/t/777630d46640dc5a928ea833c2fcb875.ttf') format('truetype');\n font-weight: normal;\n font-style: normal;\n}\n\n/* ====================================================\n PIXEL MUSIC STUDIO \u2014 \u30C9\u30C3\u30C8\u7D75UI\u30B7\u30B9\u30C6\u30E0\n PICO-8\u30AB\u30E9\u30FC\u30D1\u30EC\u30C3\u30C8\u30FB\u7F8E\u54B2\u30D5\u30A9\u30F3\u30C8\u30FB\u30B2\u30FC\u30E0\u30A6\u30A3\u30F3\u30C9\u30A6\u67A0\n ==================================================== */\n\n/* \u30C7\u30B6\u30A4\u30F3\u30C8\u30FC\u30AF\u30F3\u306F\u7DE8\u96C6UI\u672C\u4F53\uFF08.dtm-daw\uFF09\u306B\u52A0\u3048\u3001\u305D\u306E\u5916\u5074\u306B\u5DEE\u3057\u8FBC\u307E\u308C\u308B\n \u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u30D0\u30FC\uFF08.dtm-controlbar\uFF09\u306B\u3082\u4F9B\u7D66\u3059\u308B\u3002mountPresetSelect /\n mountModeSwitch \u306EUI\u306F .dtm-daw \u306E\u5144\u5F1F\u3068\u3057\u3066\u7F6E\u304B\u308C\u308B\u305F\u3081\u3001\u3053\u3053\u3067\u914D\u3089\u306A\u3044\u3068\n var(--dtm-*) \u304C\u89E3\u6C7A\u3067\u304D\u305A\u7121\u88C5\u98FE\uFF08\u767D\u5730\u30FB\u65E2\u5B9A\u30D5\u30A9\u30F3\u30C8\uFF09\u306B\u306A\u3063\u3066\u3057\u307E\u3046\u3002\n \u518D\u751F\u5C02\u7528\u30D3\u30E5\u30FC\u306E\u30E2\u30FC\u30C0\u30EB\uFF0F\u5229\u7528\u898F\u7D04\u30AB\u30D0\u30FC\u3001\u6B4C\u58F0\u30E2\u30C7\u30EB\u540D\u306E\u5439\u304D\u51FA\u3057(.dtm-player-balloon)\n \u306F document.body \u76F4\u4E0B\u3078\u91CD\u306D\u308B\u305F\u3081\u3001.dtm-daw \u306E\u5916\u306B\u51FA\u308B\u3002\u3053\u308C\u3089\u3082\u540C\u69D8\u306B\u30C8\u30FC\u30AF\u30F3\u3092\n \u4F9B\u7D66\u3057\u306A\u3044\u3068\u7121\u88C5\u98FE\uFF08\u67A0\u7DDA\u306A\u3057\u30FB\u6587\u5B57\u8272\u4E0D\u660E\u306E\u767D\u3044\u7BB1\uFF09\u306B\u306A\u308B\u3002 */\n.dtm-daw,\n.dtm-controlbar,\n.dtm-modal-overlay,\n.dtm-consent-overlay,\n.dtm-chord-player,\n.dtm-player-balloon {\n /* PICO-8 16\u8272\u30D1\u30EC\u30C3\u30C8\u3088\u308A */\n --c-black: #000000;\n --c-navy: #1d2b53;\n --c-purple: #7e2553;\n --c-dkgreen: #008751;\n --c-brown: #ab5236;\n --c-dkgray: #5f574f;\n --c-gray: #c2c3c7;\n --c-white: #fff1e8;\n --c-red: #ff004d;\n --c-orange: #ffa300;\n --c-yellow: #ffec27;\n --c-green: #00e436;\n --c-cyan: #29adff;\n --c-lavend: #83769c;\n --c-pink: #ff77a8;\n --c-peach: #ffccaa;\n\n /* \u30BB\u30DE\u30F3\u30C6\u30A3\u30C3\u30AF\u30C8\u30FC\u30AF\u30F3 */\n --dtm-bg: var(--c-black);\n --dtm-surface: var(--c-navy);\n --dtm-deep: #0a0f1f;\n --dtm-border: var(--c-cyan);\n --dtm-border2: var(--c-dkgray);\n --dtm-text: var(--c-white);\n --dtm-muted: var(--c-lavend);\n --dtm-primary: var(--c-cyan);\n --dtm-pfg: var(--c-black);\n --dtm-danger: var(--c-red);\n --dtm-success: var(--c-green);\n --dtm-accent: var(--c-pink);\n --dtm-gold: var(--c-yellow);\n --dtm-warn: var(--c-orange);\n --dtm-tap: 40px;\n --dtm-gap: 6px;\n --dtm-font: 'k8x12',ui-monospace,monospace;\n}\n\n.dtm-daw {\n box-sizing: border-box;\n font-family: var(--dtm-font);\n font-size: 14px;\n line-height: 1.6;\n letter-spacing: .06em;\n color: var(--dtm-text);\n background: var(--dtm-bg);\n width: 100%;\n display: flex;\n flex-direction: column;\n gap: var(--dtm-gap);\n padding: 0;\n image-rendering: pixelated;\n -webkit-font-smoothing: none;\n -moz-osx-font-smoothing: unset;\n font-smooth: never;\n -webkit-tap-highlight-color: transparent;\n}\n.dtm-daw *,\n.dtm-daw *::before,\n.dtm-daw *::after { box-sizing: border-box; }\n\n/* \u2500\u2500\u2500 \u30B2\u30FC\u30E0\u30A6\u30A3\u30F3\u30C9\u30A6\u5171\u901A\u67A0 \u2500\u2500\u2500 */\n/* \u5916\u67A0(\u9ED22px) \u2192 \u8272\u4ED8\u304D2px border \u2192 \u5185\u67A0(\u9ED2inset2px) \u306E3\u91CD\u69CB\u9020 */\n.dtm-win {\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-primary),\n 4px 4px 0 var(--c-black);\n background: var(--dtm-surface);\n}\n\n/* \u2500\u2500\u2500 \u5171\u901A\u30DC\u30BF\u30F3 \u2500\u2500\u2500 */\n.dtm-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n min-height: var(--dtm-tap);\n min-width: var(--dtm-tap);\n padding: 0 10px;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-surface);\n color: var(--dtm-text);\n font-family: var(--dtm-font);\n font-size: 13px;\n text-transform: uppercase;\n letter-spacing: .12em;\n cursor: pointer;\n user-select: none;\n white-space: nowrap;\n box-shadow: 3px 3px 0 var(--c-black);\n transition: none;\n}\n.dtm-btn:active { transform: translate(3px,3px); box-shadow: none; }\n.dtm-btn:disabled { opacity: .3; cursor: default; box-shadow: none; }\n.dtm-btn--ghost { background: transparent; border-color: var(--dtm-border2); }\n.dtm-btn--primary { border-color: var(--dtm-primary); background: var(--dtm-primary); color: var(--dtm-pfg); }\n.dtm-btn--success { border-color: var(--dtm-success); background: var(--dtm-success); color: var(--c-black); }\n.dtm-btn--danger { border-color: var(--dtm-danger); background: var(--dtm-danger); color: var(--c-white); }\n.dtm-btn--accent { border-color: var(--dtm-accent); background: var(--dtm-accent); color: var(--c-black); }\n.dtm-btn--icon { padding: 0; }\n\n/* \u2500\u2500\u2500 \u30A2\u30A4\u30B3\u30F3\u30DC\u30BF\u30F3 \u2500\u2500\u2500 */\n.dtm-iconbtn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--dtm-tap);\n height: var(--dtm-tap);\n flex: 0 0 auto;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-surface);\n color: var(--dtm-text);\n font-size: 16px;\n cursor: pointer;\n box-shadow: 3px 3px 0 var(--c-black);\n}\n.dtm-iconbtn:active { transform: translate(3px,3px); box-shadow: none; }\n.dtm-iconbtn:disabled { opacity: .3; cursor: default; box-shadow: none; }\n\n/* \u2500\u2500\u2500 \u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u30D0\u30FC\uFF08HUD\u30B9\u30BF\u30A4\u30EB\uFF09 \u2500\u2500\u2500 */\n.dtm-topbar {\n position: sticky;\n top: 0;\n z-index: 20;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--dtm-gap);\n padding: 6px;\n background: var(--dtm-deep);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-success),\n 4px 4px 0 var(--c-black);\n}\n.dtm-topbar-row1 {\n display: flex;\n align-items: center;\n gap: var(--dtm-gap);\n flex-basis: 100%;\n min-width: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.dtm-topbar-row1::-webkit-scrollbar { display: none; }\n.dtm-topbar-row1 > * { flex-shrink: 0; }\n.dtm-topbar-row1 > .dtm-grow { flex-shrink: 1; }\n\n/* PLAY\u30DC\u30BF\u30F3 \u2014 \u30B2\u30FC\u30E0\u306E\u300C\u6C7A\u5B9A\u30DC\u30BF\u30F3\u300D\u7684\u5B58\u5728\u611F */\n.dtm-play {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--dtm-tap);\n height: var(--dtm-tap);\n flex: 0 0 auto;\n border: 2px solid var(--c-black);\n background: var(--dtm-success);\n color: var(--c-black);\n cursor: pointer;\n box-shadow: 0 0 0 2px var(--dtm-success), 4px 4px 0 var(--c-black);\n}\n.dtm-play:active { transform: translate(4px,4px); box-shadow: none; }\n.dtm-play:disabled { opacity: .35; cursor: default; box-shadow: none; }\n.dtm-play--stop {\n background: var(--dtm-danger);\n box-shadow: 0 0 0 2px var(--dtm-danger), 4px 4px 0 var(--c-black);\n}\n.dtm-rec { color: var(--dtm-danger); }\n\n/* BPM \u2014 \u30C7\u30B8\u30BF\u30EB\u30AB\u30A6\u30F3\u30BF\u30FC\u98A8 */\n.dtm-label {\n font-family: var(--dtm-font);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .14em;\n color: var(--dtm-muted);\n white-space: nowrap;\n}\n.dtm-checkbox-label {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-family: var(--dtm-font);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .12em;\n color: var(--dtm-muted);\n cursor: pointer;\n user-select: none;\n margin-top: 4px;\n}\n.dtm-checkbox-label:hover { color: var(--dtm-text); }\n.dtm-checkbox-label--sub { margin-left: 20px; font-size: 10px; }\n.dtm-checkbox {\n width: 14px;\n height: 14px;\n accent-color: var(--dtm-success);\n cursor: pointer;\n flex-shrink: 0;\n}\n\n.dtm-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-family: var(--dtm-font);\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: .1em;\n color: var(--dtm-muted);\n cursor: pointer;\n}\n.dtm-toggle input { width: 16px; height: 16px; accent-color: var(--dtm-accent); }\n\n/* \u2500\u2500\u2500 \u30C4\u30FC\u30EB\u30C9\u30C3\u30AF\uFF08\u88C5\u5099\u30B9\u30ED\u30C3\u30C8\u98A8\uFF09 \u2500\u2500\u2500 */\n.dtm-tooldock {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--dtm-gap);\n padding: 6px;\n background: var(--dtm-deep);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-border2),\n 4px 4px 0 var(--c-black);\n}\n.dtm-sep {\n width: 2px; align-self: stretch;\n background: var(--dtm-border2); margin: 2px;\n}\n.dtm-row .dtm-label[data-dtm] { min-width: 48px; text-align: center; }\n\n/* \u2500\u2500\u2500 \u30BB\u30B0\u30E1\u30F3\u30C8\uFF08\u30A2\u30A4\u30C6\u30E0\u30B9\u30ED\u30C3\u30C8\uFF09 \u2500\u2500\u2500 */\n.dtm-seg {\n display: inline-flex;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-deep);\n box-shadow: 3px 3px 0 var(--c-black);\n}\n.dtm-segbtn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--dtm-tap);\n height: var(--dtm-tap);\n border: none;\n border-right: 2px solid var(--dtm-border2);\n background: transparent;\n color: var(--dtm-muted);\n cursor: pointer;\n}\n.dtm-segbtn:last-child { border-right: none; }\n.dtm-segbtn--active {\n background: var(--dtm-gold);\n color: var(--c-black);\n}\n.dtm-segbtn:not(.dtm-segbtn--active):active { background: var(--dtm-border2); }\n\n/* \u2500\u2500\u2500 \u30D5\u30A9\u30FC\u30E0\u8981\u7D20 \u2500\u2500\u2500 */\n.dtm-select, .dtm-input, .dtm-textarea {\n min-height: var(--dtm-tap);\n padding: 4px 8px;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-deep);\n color: var(--dtm-text);\n font-family: var(--dtm-font);\n font-size: 13px;\n letter-spacing: .06em;\n box-shadow: inset 2px 2px 0 var(--c-black);\n}\n.dtm-select:focus, .dtm-input:focus, .dtm-textarea:focus {\n outline: none;\n border-color: var(--dtm-primary);\n}\n.dtm-input--num { width: 64px; text-align: center; font-size: 16px; }\n.dtm-textarea { width: 100%; min-height: 56px; resize: vertical; line-height: 1.7; }\n.dtm-textarea.dtm-grow { width: 0; }\n.dtm-range { height: var(--dtm-tap); accent-color: var(--dtm-primary); }\n.dtm-range:disabled { opacity: .35; cursor: default; }\n\n/* \u2500\u2500\u2500 \u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u30D0\u30FC\uFF08\u697D\u5668\u30D7\u30EA\u30BB\u30C3\u30C8 / \u30E2\u30FC\u30C9\u5207\u66FF\u306A\u3069\u306E\u5DEE\u3057\u8FBC\u307FUI\uFF09 \u2500\u2500\u2500 */\n.dtm-controlbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--dtm-gap);\n padding: 6px 8px;\n background: var(--dtm-deep);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-border2),\n 4px 4px 0 var(--c-black);\n margin-bottom: var(--dtm-gap);\n}\n.dtm-controlbar-label {\n font-family: var(--dtm-font);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .14em;\n color: var(--dtm-accent);\n white-space: nowrap;\n flex-shrink: 0;\n}\n.dtm-controlbar .dtm-select { flex: 1 1 160px; }\n\n/* \u30E2\u30FC\u30C9\u5207\u66FF\uFF08\u30C6\u30AD\u30B9\u30C8\u7248\u30BB\u30B0\u30E1\u30F3\u30C8\uFF09 */\n.dtm-modeseg {\n display: inline-flex;\n border: 2px solid var(--dtm-border2);\n box-shadow: 3px 3px 0 var(--c-black);\n}\n.dtm-modebtn {\n min-height: var(--dtm-tap);\n padding: 0 14px;\n border: none;\n border-right: 2px solid var(--dtm-border2);\n background: var(--dtm-deep);\n color: var(--dtm-muted);\n font-family: var(--dtm-font);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .12em;\n cursor: pointer;\n}\n.dtm-modebtn:last-child { border-right: none; }\n.dtm-modebtn--active { background: var(--dtm-primary); color: var(--dtm-pfg); }\n.dtm-modebtn:not(.dtm-modebtn--active):active { background: var(--dtm-border2); }\n\n/* \u2500\u2500\u2500 \u30C8\u30E9\u30C3\u30AF\u30D4\u30EB\uFF08\u756A\u53F7\u30DC\u30BF\u30F3\u3001\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u30D0\u30FC2\u884C\u76EE\uFF09 \u2500\u2500\u2500 */\n.dtm-tracks {\n flex-basis: 100%;\n display: flex;\n flex-wrap: nowrap;\n gap: 4px;\n align-items: flex-end;\n}\n.dtm-pill {\n --dtm-pill-color: var(--dtm-primary);\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 1 1 0;\n min-width: 0;\n height: 24px;\n padding: 0;\n border: 2px solid var(--c-black);\n background: color-mix(in srgb, var(--dtm-pill-color) 22%, #0e121e);\n color: color-mix(in srgb, var(--c-white) 60%, transparent);\n font-family: var(--dtm-font);\n font-size: 11px;\n font-weight: bold;\n cursor: pointer;\n box-shadow: 1px 1px 0 var(--c-black);\n opacity: 0.55;\n transition: opacity 120ms ease, background 120ms ease, transform 120ms ease;\n}\n.dtm-pill:hover:not(.dtm-pill--active) {\n opacity: 0.85;\n background: color-mix(in srgb, var(--dtm-pill-color) 45%, #0e121e);\n color: var(--c-white);\n}\n/* \u30A2\u30AF\u30C6\u30A3\u30D6\u9078\u629E = \u9BAE\u660E\u306A\u30C8\u30E9\u30C3\u30AF\u8272 + \u30B4\u30FC\u30EB\u30C9\u67A0 + \u30B0\u30ED\u30FC + \u4E0A\u6607\u7ACB\u4F53\u30BF\u30D6\u8868\u73FE */\n.dtm-pill--active {\n opacity: 1;\n height: 28px;\n background: var(--dtm-pill-color);\n color: #ffffff;\n text-shadow: 1px 1px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000;\n border-color: var(--dtm-gold);\n box-shadow:\n 0 0 0 1.5px var(--dtm-gold),\n 0 0 10px color-mix(in srgb, var(--dtm-pill-color) 70%, transparent),\n 2px 2px 0 var(--c-black);\n z-index: 1;\n transform: translateY(-2px);\n}\n.dtm-pill--active::before {\n content: '';\n position: absolute;\n top: -2px;\n left: 2px;\n right: 2px;\n height: 2px;\n background: #ffffff;\n border-radius: 1px;\n opacity: 0.85;\n}\n.dtm-pill:not(.dtm-pill--active):active { transform: translate(1px,1px); box-shadow: none; }\n/* \u518D\u751F\u4E2D\u3001\u5B9F\u969B\u306B\u767A\u97F3\u3057\u305F\u77AC\u9593\u3060\u3051\u70B9\u706F\uFF08\u3069\u306E\u30BF\u30D6\u304C\u4ECA\u9CF4\u3063\u3066\u3044\u308B\u304B\u8996\u899A\u7684\u306B\u5206\u304B\u308B\u3088\u3046\u306B\uFF09 */\n.dtm-pill--sounding {\n filter: brightness(1.6);\n transition: filter 30ms linear;\n}\n\n/* \u2500\u2500\u2500 \u30D4\u30A2\u30CE\u30ED\u30FC\u30EB\uFF08\u30C8\u30E9\u30C3\u30AB\u30FC\u98A8\uFF09 \u2500\u2500\u2500 */\n.dtm-roll-wrap { display: flex; gap: var(--dtm-gap); }\n.dtm-roll {\n position: relative;\n flex: 1 1 auto;\n height: 32vh;\n max-height: 32vh;\n background: var(--dtm-deep);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-border2),\n 4px 4px 0 var(--c-black);\n overflow: hidden;\n}\n/* \u30AB\u30B9\u30BF\u30E0\u80CC\u666F\u753B\u50CF\u30EC\u30A4\u30E4\u30FC\uFF08\u753B\u50CF\u81EA\u4F53\u3092\u534A\u900F\u660E\u5316\u3057\u3001\u30AD\u30E3\u30F3\u30D0\u30B9\u5074\u306E\u5857\u308A\u306F\u7DAD\u6301\uFF09 */\n.dtm-roll::before {\n content: '';\n position: absolute;\n inset: 0;\n background-image: var(--dtm-roll-bg-image, none);\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n opacity: var(--dtm-roll-bg-opacity, 0.4);\n pointer-events: none;\n}\n.dtm-vscroll {\n position: relative;\n width: 20px;\n background: var(--dtm-deep);\n border: 2px solid var(--dtm-border2);\n cursor: pointer;\n flex: 0 0 auto;\n touch-action: none;\n}\n.dtm-vscroll-thumb, .dtm-hscroll-thumb {\n position: absolute;\n background: var(--dtm-primary);\n min-width: 20px;\n min-height: 20px;\n}\n.dtm-vscroll-thumb { left: 0; width: 100%; }\n.dtm-hscroll {\n position: relative;\n width: 100%; height: 20px;\n background: var(--dtm-deep);\n border: 2px solid var(--dtm-border2);\n cursor: pointer;\n touch-action: none;\n}\n.dtm-hscroll-thumb { top: 0; height: 100%; }\n\n/* \u2500\u2500\u2500 \u30D1\u30CD\u30EB\uFF08RPG\u30C0\u30A4\u30A2\u30ED\u30B0\u30A6\u30A3\u30F3\u30C9\u30A6\uFF09 \u2500\u2500\u2500 */\n.dtm-panel {\n background: var(--dtm-surface);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-primary),\n 4px 4px 0 var(--c-black);\n overflow: hidden;\n}\n.dtm-panel > summary {\n list-style: none;\n cursor: pointer;\n padding: 0 12px;\n font-family: var(--dtm-font);\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: .14em;\n display: flex;\n align-items: center;\n min-height: var(--dtm-tap);\n background: var(--dtm-deep);\n border-bottom: 2px solid var(--dtm-border2);\n color: var(--dtm-primary);\n gap: 8px;\n}\n.dtm-panel:not([open]) > summary { border-bottom: none; }\n.dtm-panel > summary::-webkit-details-marker { display: none; }\n/* \u5DE6\u7AEF\u30E9\u30A4\u30F3\uFF08\u30B2\u30FC\u30E0UI\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u8272\u5206\u3051\uFF09 */\n.dtm-panel > summary::before {\n content: '';\n display: block;\n width: 4px;\n height: 20px;\n background: var(--dtm-accent);\n flex: 0 0 auto;\n}\n.dtm-panel[open] > summary::before { background: var(--dtm-primary); }\n/* \u6298\u308A\u305F\u305F\u307F\u77E2\u5370 */\n.dtm-panel > summary::after {\n content: \"\u25B6\";\n margin-left: auto;\n color: var(--dtm-muted);\n font-size: 10px;\n}\n.dtm-panel[open] > summary::after { content: \"\u25BC\"; }\n.dtm-panel-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 10px; }\n.dtm-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }\n.dtm-track-body { display: flex; flex-direction: column; gap: 10px; }\n\n/* \u2500\u2500\u2500 \u30A2\u30AF\u30C6\u30A3\u30D6\u30C8\u30E9\u30C3\u30AF\u60C5\u5831\u30D0\u30CA\u30FC \u2500\u2500\u2500 */\n.dtm-active-track-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 10px;\n background: color-mix(in srgb, var(--dtm-track-color, var(--dtm-primary)) 16%, var(--dtm-deep));\n border: 2px solid var(--c-black);\n border-left: 5px solid var(--dtm-track-color, var(--dtm-primary));\n box-shadow: 2px 2px 0 var(--c-black);\n margin-bottom: 2px;\n}\n.dtm-active-track-badge {\n font-family: var(--dtm-font);\n font-size: 11px;\n font-weight: bold;\n background: var(--dtm-track-color, var(--dtm-primary));\n color: #ffffff;\n text-shadow: 1px 1px 0 #000, -1px -1px 0 #000;\n padding: 2px 6px;\n border: 1px solid var(--c-black);\n border-radius: 2px;\n letter-spacing: .06em;\n}\n.dtm-active-track-name {\n font-family: var(--dtm-font);\n font-size: 12px;\n font-weight: bold;\n color: var(--c-white);\n flex: 1 1 auto;\n}\n.dtm-active-track-pill {\n font-family: var(--dtm-font);\n font-size: 9px;\n font-weight: bold;\n letter-spacing: .08em;\n padding: 2px 5px;\n color: var(--dtm-gold);\n border: 1px solid var(--dtm-gold);\n background: rgba(0, 0, 0, 0.45);\n border-radius: 2px;\n}\n\n/* \u2500\u2500\u2500 MML\u51FA\u529B\uFF08CRT\u30BF\u30FC\u30DF\u30CA\u30EB\uFF09 \u2500\u2500\u2500 */\n.dtm-output {\n background: var(--c-black);\n color: var(--dtm-success);\n border: 2px solid var(--dtm-success);\n padding: 10px;\n box-shadow: 0 0 0 2px var(--c-black), 4px 4px 0 var(--c-black);\n}\n.dtm-output::before {\n content: \"C:\\> MML OUTPUT\";\n display: block;\n font-size: 11px;\n color: var(--dtm-muted);\n letter-spacing: .14em;\n margin-bottom: 6px;\n padding-bottom: 6px;\n border-bottom: 1px solid var(--dtm-border2);\n}\n.dtm-output pre {\n margin: 0;\n background: transparent;\n padding: 0;\n overflow-x: auto;\n font-family: var(--dtm-font);\n font-size: 12px;\n line-height: 1.8;\n color: var(--dtm-success);\n}\n.dtm-output-label {\n font-size: 11px;\n color: var(--dtm-muted);\n font-family: var(--dtm-font);\n margin-top: 10px;\n}\n.dtm-output-label:first-of-type {\n margin-top: 0;\n}\n.dtm-output-row { display: flex; gap: 8px; align-items: flex-start; margin-top: 6px; }\n.dtm-output-row pre { flex: 1; }\n.dtm-output-scroll { max-height: 240px; overflow-y: auto; }\n\n/* \u2500\u2500\u2500 \u30ED\u30FC\u30C7\u30A3\u30F3\u30B0\u30AA\u30FC\u30D0\u30FC\u30EC\u30A4 \u2500\u2500\u2500 */\n.dtm-overlay {\n position: absolute; inset: 0; z-index: 10;\n background: rgba(0,0,0,.92);\n display: flex; align-items: center; justify-content: center;\n flex-direction: column; gap: 14px;\n pointer-events: auto;\n cursor: wait;\n}\n.dtm-overlay[hidden] { display: none; }\n.dtm-overlay::before {\n content: '\u30ED\u30FC\u30C9\u4E2D';\n font-family: var(--dtm-font);\n font-size: 13px;\n color: var(--dtm-primary);\n text-transform: uppercase;\n letter-spacing: .25em;\n animation: dtm-blink 1s steps(1) infinite;\n}\n/* 8\u30D6\u30ED\u30C3\u30AF\u523B\u307F\u3067\u57CB\u307E\u308B\u30D4\u30AF\u30BB\u30EB\u30D0\u30FC */\n.dtm-spinner {\n width: 96px; height: 12px;\n position: relative;\n background: var(--c-navy);\n border: 2px solid var(--dtm-primary);\n box-shadow: 0 0 0 2px var(--c-black), 4px 4px 0 var(--c-black);\n}\n.dtm-spinner::after {\n content: '';\n position: absolute;\n left: 0; top: 0; height: 100%;\n background: var(--dtm-primary);\n animation: dtm-load 1.6s steps(8) infinite;\n}\n@keyframes dtm-load { 0%{width:0} 100%{width:100%} }\n/* \u9032\u6357\u304C\u78BA\u5B9A\u3057\u305F\u3089\u7121\u9650\u30EB\u30FC\u30D7\u6F14\u51FA\u3092\u6B62\u3081\u3001\u5B9F\u6E2C\u5024\u3067\u5857\u308A\u3064\u3076\u3059 */\n.dtm-spinner--determinate::after { display: none; }\n.dtm-spinner-fill {\n position: absolute;\n left: 0; top: 0; height: 100%;\n width: 0;\n background: var(--dtm-primary);\n transition: width .12s steps(8);\n}\n.dtm-loading-label {\n font-family: var(--dtm-font);\n font-size: 11px;\n color: var(--dtm-primary);\n letter-spacing: .15em;\n min-height: 1em;\n}\n.dtm-overlay-skip-btn {\n margin-top: 12px;\n min-height: 32px;\n font-size: 11px;\n font-family: var(--dtm-font);\n padding: 0 12px;\n background: var(--dtm-surface);\n border: 2px solid var(--dtm-border2);\n color: var(--dtm-muted);\n box-shadow: 2px 2px 0 var(--c-black);\n cursor: pointer;\n pointer-events: auto;\n}\n.dtm-overlay-skip-btn:hover {\n color: var(--dtm-text);\n border-color: var(--dtm-primary);\n}\n.dtm-overlay-skip-btn:active {\n transform: translate(2px, 2px);\n box-shadow: none;\n}\n.dtm-overlay-skip-btn:disabled {\n opacity: .3;\n cursor: default;\n box-shadow: none;\n transform: none;\n}\n.dtm-topbar-loading {\n display: none;\n font-family: var(--dtm-font);\n font-size: 11px;\n color: var(--dtm-primary);\n margin-left: 12px;\n letter-spacing: .15em;\n align-self: center;\n}\n.dtm-topbar.is-loading .dtm-topbar-loading {\n display: inline-block;\n}\n.dtm-topbar.is-loading {\n pointer-events: none;\n opacity: 0.7;\n}\n\n@keyframes dtm-blink { 0%,100%{opacity:1} 50%{opacity:0} }\n.dtm-blink { animation: dtm-blink 1s steps(1) infinite; }\n\n/* \u2500\u2500\u2500 \u97F3\u5272\u308C\u691C\u77E5\u30D0\u30C3\u30B8 \u2500\u2500\u2500 \u30AF\u30EA\u30C3\u30D7\u767A\u751F\u4E2D\u3060\u3051\u8868\u793A\u3055\u308C\u308B\u8B66\u544A\u30DC\u30BF\u30F3\u3002\u30AF\u30EA\u30C3\u30AF\u3067\u6D88\u305B\u308B\u3002 */\n.dtm-clip-badge {\n flex: 0 0 auto;\n min-height: 24px;\n padding: 0 8px;\n border: 2px solid var(--c-black);\n background: var(--dtm-danger);\n color: var(--c-white);\n font-family: var(--dtm-font);\n font-size: 10px;\n letter-spacing: .1em;\n cursor: pointer;\n box-shadow: 0 0 0 2px var(--dtm-danger), 2px 2px 0 var(--c-black);\n animation: dtm-blink .5s steps(1) infinite;\n}\n.dtm-clip-badge:active { transform: translate(2px,2px); box-shadow: none; }\n\n/* \u2500\u2500\u2500 \u30A4\u30F3\u30D5\u30A9\u30DC\u30BF\u30F3 \u2500\u2500\u2500 */\n.dtm-infobtn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n flex: 0 0 auto;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-surface);\n color: var(--dtm-muted);\n cursor: pointer;\n box-shadow: 1px 1px 0 var(--c-black);\n padding: 0;\n margin: 0;\n}\n.dtm-infobtn:hover {\n color: var(--dtm-primary);\n border-color: var(--dtm-primary);\n}\n.dtm-infobtn:active {\n transform: translate(1px, 1px);\n box-shadow: none;\n}\n\n/* \u2500\u2500\u2500 \u89E3\u8AAC\u30E2\u30FC\u30C0\u30EB \u2500\u2500\u2500 */\n.dtm-modal-overlay {\n position: fixed;\n inset: 0;\n z-index: 10000;\n background: rgba(0, 0, 0, 0.7);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n backdrop-filter: blur(2px);\n /* body\u76F4\u4E0B\u306B\u91CD\u306D\u305F\u5834\u5408\uFF08\u518D\u751F\u5C02\u7528\u30D3\u30E5\u30FC\uFF09\u3067\u3082\u6587\u5B57\u8272\u30FB\u30D5\u30A9\u30F3\u30C8\u304C\n .dtm-daw \u304B\u3089\u7D99\u627F\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u3053\u3053\u3067\u660E\u793A\u3059\u308B\u3002 */\n color: var(--dtm-text);\n font-family: var(--dtm-font);\n}\n.dtm-modal-overlay[hidden] {\n display: none !important;\n}\n\n/* \u2500\u2500\u2500 \u5229\u7528\u898F\u7D04\u540C\u610F\u30AB\u30D0\u30FC \u2500\u2500\u2500 */\n.dtm-consent-overlay {\n position: fixed;\n inset: 0;\n z-index: 10100;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n backdrop-filter: blur(2px);\n /* body\u76F4\u4E0B\u306B\u91CD\u306D\u308B\u305F\u3081 .dtm-daw \u304B\u3089\u7D99\u627F\u3067\u304D\u306A\u3044\u6587\u5B57\u8272\u30FB\u30D5\u30A9\u30F3\u30C8\u3092\u660E\u793A\u3002 */\n color: var(--dtm-text);\n font-family: var(--dtm-font);\n}\n.dtm-consent-overlay[hidden] {\n display: none !important;\n}\n.dtm-consent-modal {\n max-width: 450px;\n width: 100%;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n background: var(--dtm-surface);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-primary),\n 4px 4px 0 var(--c-black);\n overflow-y: auto;\n}\n.dtm-consent-header {\n background: var(--dtm-deep);\n color: var(--dtm-text);\n padding: 8px 12px;\n border-bottom: 2px solid var(--c-black);\n font-weight: bold;\n text-align: center;\n font-size: 14px;\n}\n.dtm-consent-body {\n padding: 12px 16px;\n font-size: 13px;\n line-height: 1.6;\n}\n.dtm-consent-body a {\n color: var(--dtm-primary);\n text-decoration: underline;\n}\n.dtm-consent-body a:hover {\n color: var(--dtm-accent);\n}\n.dtm-consent-footer {\n padding: 8px;\n border-top: 2px solid var(--c-black);\n background: var(--dtm-deep);\n display: flex;\n justify-content: center;\n}\n\n.dtm-confirm-footer {\n padding: 8px 12px;\n border-top: 2px solid var(--c-black);\n background: var(--dtm-deep);\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n}\n\n.dtm-modal {\n max-width: 500px;\n width: 100%;\n max-height: 80vh;\n display: flex;\n flex-direction: column;\n background: var(--dtm-surface);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-primary),\n 4px 4px 0 var(--c-black);\n overflow: hidden;\n}\n.dtm-modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: var(--dtm-deep);\n padding: 8px 12px;\n border-bottom: 2px solid var(--c-black);\n}\n.dtm-modal-title {\n font-family: var(--dtm-font);\n font-size: 14px;\n color: var(--dtm-gold);\n font-weight: bold;\n}\n.dtm-modal-close {\n background: transparent;\n border: none;\n color: var(--dtm-text);\n font-size: 20px;\n cursor: pointer;\n padding: 0;\n line-height: 1;\n}\n.dtm-modal-close:hover {\n color: var(--dtm-danger);\n}\n.dtm-modal-body {\n padding: 12px;\n overflow-y: auto;\n overflow-x: hidden;\n font-size: 13px;\n line-height: 1.6;\n}\n.dtm-modal-body * {\n max-width: 100%;\n box-sizing: border-box;\n}\n.dtm-modal-body a {\n color: var(--dtm-primary);\n text-decoration: underline;\n}\n.dtm-modal-body a:hover {\n color: var(--dtm-accent);\n}\n.dtm-modal-body h4 {\n margin: 12px 0 6px 0;\n color: var(--dtm-primary);\n font-size: 13px;\n}\n.dtm-modal-body h4:first-child {\n margin-top: 0;\n}\n.dtm-modal-body p {\n margin: 0 0 8px 0;\n}\n.dtm-modal-body ul {\n margin: 0 0 8px 0;\n padding-left: 16px;\n}\n.dtm-modal-body li {\n margin-bottom: 4px;\n}\n.dtm-modal-body code {\n background: var(--dtm-deep);\n color: var(--dtm-accent);\n padding: 1px 4px;\n font-family: var(--dtm-font);\n font-size: 12px;\n}\n.dtm-modal-body pre {\n background: var(--dtm-deep);\n color: var(--dtm-success);\n padding: 8px;\n border: 1px solid var(--dtm-border2);\n margin: 6px 0;\n overflow-x: auto;\n font-family: var(--dtm-font);\n font-size: 12px;\n}\n\n.dtm-modal-sample-box {\n background: var(--dtm-deep);\n border: 1px solid var(--dtm-border2);\n border-radius: 4px;\n padding: 8px 10px;\n margin-bottom: 12px;\n}\n.dtm-modal-sample-box:last-child {\n margin-bottom: 0;\n}\n.dtm-modal-sample-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 6px;\n}\n.dtm-modal-sample-tag {\n font-family: var(--dtm-font);\n font-size: 11px;\n font-weight: bold;\n color: var(--dtm-accent);\n}\n.dtm-modal-sample-desc {\n margin: 6px 0 0 0;\n font-size: 11px;\n color: var(--dtm-muted);\n}\n.dtm-modal-sample-player-container {\n margin-top: 8px;\n}\n.dtm-modal-sample-player-container:empty {\n margin-top: 0;\n}\n.dtm-modal-sample-player-container .dtm-player {\n border: 1px solid var(--dtm-border2);\n box-shadow: none;\n background: rgba(0, 0, 0, 0.3);\n}\n.dtm-modal-sample-player-container .dtm-player-body {\n max-height: 100px;\n overflow-y: auto;\n}\n\n.dtm-hidden { display: none !important; }\n/* \u8AAD\u8FBC\u6642\u306E\u8B66\u544A\u304A\u77E5\u3089\u305B\uFF08\u4F8B: \u30B7\u30F3\u30D7\u30EB\u30E2\u30FC\u30C9\u3067\u306E\u30C8\u30E9\u30C3\u30AF\u5408\u7B97\uFF09\u3002 */\n.dtm-load-note {\n margin: 6px 0 0;\n padding: 0 2px;\n font-family: var(--dtm-font);\n font-size: 11px;\n line-height: 1.5;\n letter-spacing: .04em;\n color: var(--dtm-warn); /* \u8B66\u544A\u8272\uFF08\u30AA\u30EC\u30F3\u30B8\uFF09 */\n font-weight: bold;\n opacity: 1.0;\n}\n.dtm-load-note::before { content: \"\u26A0 \"; }\n.dtm-grow { flex: 1 1 auto; }\n.dtm-lyric-icon {\n flex: 0 0 auto;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n object-fit: cover;\n image-rendering: pixelated;\n}\n/* \u6B4C\u8A5E\u30C8\u30E9\u30C3\u30AF\u306E\u300C\u8A73\u7D30\u8A2D\u5B9A\u300D\u2014 \u5E38\u7528\u3057\u306A\u3044\u30D1\u30E9\u30E1\u30FC\u30BF\uFF08\u30AA\u30AF\u30BF\u30FC\u30D6/\u5B9A\u4F4D/\u30EA\u30D0\u30FC\u30D6\u9001\u308A/\n \u30B8\u30A7\u30F3\u30C0\u30FC/\u30D6\u30EC\u30B7\u30CD\u30B9/\u30D3\u30D6\u30E9\u30FC\u30C8\uFF09\u3092\u7573\u3093\u3067\u304A\u304F\u8EFD\u91CF\u306A details\u3002dtm-panel \u307B\u3069\n \u4EF0\u3005\u3057\u304F\u305B\u305A\u3001\u63A7\u3048\u3081\u306A\u4ED5\u5207\u308A\u7DDA\uFF0B\u5C0F\u3055\u3044\u25B6\u30DE\u30FC\u30AB\u30FC\u3060\u3051\u4ED8\u3051\u308B\u3002 */\n.dtm-advanced {\n border-top: 1px dashed var(--dtm-border2);\n padding-top: 6px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.dtm-advanced > summary {\n list-style: none;\n cursor: pointer;\n font-size: 11px;\n letter-spacing: .08em;\n color: var(--dtm-text);\n display: flex;\n align-items: center;\n gap: 6px;\n min-height: 28px;\n padding: 0 8px;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-deep);\n box-shadow: 2px 2px 0 var(--c-black);\n width: fit-content;\n}\n.dtm-advanced > summary:active { transform: translate(2px,2px); box-shadow: none; }\n.dtm-advanced > summary::-webkit-details-marker { display: none; }\n.dtm-advanced > summary::before { content: \"\u25B6\"; font-size: 9px; color: var(--dtm-accent); }\n.dtm-advanced[open] > summary::before { content: \"\u25BC\"; }\n.dtm-advanced[open] > summary { border-color: var(--dtm-primary); color: var(--dtm-primary); }\n\n/* \u2500\u2500\u2500 \u5E83\u5E45\u62E1\u5F35 \u2500\u2500\u2500 */\n@media (min-width: 768px) {\n .dtm-daw { gap: 8px; padding: 10px; }\n .dtm-roll { height: 420px; }\n}\n\n/* ====================================================\n MML PLAYER \u2014 \u518D\u751F\u5C02\u7528\u30D3\u30E5\u30FC\uFF08mountMmlPlayer\uFF09\n ==================================================== */\n.dtm-player {\n display: flex;\n flex-direction: column;\n gap: var(--dtm-gap);\n padding: var(--dtm-gap);\n background: var(--dtm-deep);\n border: 2px solid var(--dtm-border2);\n box-shadow: 4px 4px 0 var(--c-black);\n}\n.dtm-player-message {\n padding: 4px 8px;\n background: var(--c-purple);\n color: var(--c-yellow);\n font-size: 11px;\n border: 2px solid var(--c-black);\n box-shadow: inset 0 -2px 0 rgba(0,0,0,0.2);\n font-family: var(--dtm-font);\n text-align: center;\n width: 100%;\n box-sizing: border-box;\n}\n.dtm-player-head {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n.dtm-player-play {\n flex: 0 0 auto;\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--dtm-primary);\n color: var(--dtm-pfg);\n border: 2px solid var(--c-black);\n box-shadow: 2px 2px 0 var(--c-black);\n cursor: pointer;\n padding: 0;\n}\n.dtm-player-play:active { transform: translate(2px, 2px); box-shadow: none; }\n.dtm-player-play--stop { background: var(--dtm-danger); }\n.dtm-player-play:disabled { opacity: 0.4; cursor: default; }\n.dtm-player-beat-row {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n.dtm-player-beat-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--dtm-border2);\n transition: background 0.06s;\n}\n.dtm-player-beat-dot--on { background: var(--dtm-primary); }\n.dtm-player-bar {\n font-family: 'k8x12', monospace;\n font-size: 11px;\n color: var(--dtm-text);\n min-width: 2em;\n margin-left: 4px;\n}\n.dtm-player-chord {\n font-family: 'k8x12', monospace;\n font-size: 11px;\n color: var(--dtm-accent);\n min-width: 4em;\n margin-left: 8px;\n font-weight: bold;\n}\n.dtm-player-seek-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.dtm-player-seek {\n --fill: 0%;\n flex: 1 1 auto;\n min-width: 0;\n height: 12px;\n margin: 0;\n background: transparent;\n cursor: pointer;\n -webkit-appearance: none;\n appearance: none;\n}\n.dtm-player-seek:disabled { cursor: default; opacity: 0.4; }\n/* \u518D\u751F\u6E08\u307F\u533A\u9593\u3092YouTube\u98A8\u306B\u8272\u5857\u308A\u3059\u308B\uFF08--fill \u306F\u518D\u751F\u4F4D\u7F6E\u306B\u5FDC\u3058\u3066JS\u304B\u3089\u8A2D\u5B9A\uFF09 */\n.dtm-player-seek::-webkit-slider-runnable-track {\n height: 6px;\n background: linear-gradient(\n to right,\n var(--dtm-primary) var(--fill),\n var(--dtm-border2) var(--fill)\n );\n border: 2px solid var(--c-black);\n}\n.dtm-player-seek::-moz-range-track {\n height: 6px;\n background: var(--dtm-border2);\n border: 2px solid var(--c-black);\n}\n.dtm-player-seek::-moz-range-progress {\n height: 6px;\n background: var(--dtm-primary);\n border: 2px solid var(--c-black);\n border-right: none;\n}\n.dtm-player-seek::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 16px;\n margin-top: -7px;\n background: var(--dtm-primary);\n border: 2px solid var(--c-black);\n box-shadow: 2px 2px 0 var(--c-black);\n cursor: pointer;\n}\n.dtm-player-seek::-moz-range-thumb {\n width: 12px;\n height: 16px;\n background: var(--dtm-primary);\n border: 2px solid var(--c-black);\n box-shadow: 2px 2px 0 var(--c-black);\n cursor: pointer;\n border-radius: 0;\n}\n.dtm-player-time {\n flex: 0 0 auto;\n font-family: 'k8x12', monospace;\n font-size: 11px;\n color: var(--dtm-text);\n white-space: nowrap;\n}\n.dtm-player-dots {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.dtm-player-dot { width: 8px; height: 8px; display: inline-block; }\n.dtm-player-mml-header {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n.dtm-player-mml-link {\n font-family: 'k8x12', monospace;\n font-size: 10px;\n color: var(--dtm-muted);\n text-decoration: none;\n white-space: nowrap;\n}\n.dtm-player-mml-link:hover { color: var(--dtm-primary); }\n.dtm-player-more-container {\n position: relative;\n display: inline-flex;\n align-items: center;\n}\n.dtm-player-more-btn {\n background: transparent;\n border: none;\n color: var(--dtm-muted);\n cursor: pointer;\n padding: 2px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n width: 24px;\n height: 24px;\n transition: color 0.15s, background-color 0.15s;\n}\n.dtm-player-more-btn:hover,\n.dtm-player-more-btn.is-active {\n color: var(--dtm-text);\n background: var(--dtm-border2);\n}\n.dtm-player-menu {\n position: absolute;\n top: calc(100% + 4px);\n right: 0;\n background: var(--dtm-deep);\n border: 2px solid var(--dtm-border2);\n box-shadow: 4px 4px 0 var(--c-black);\n z-index: 200;\n display: flex;\n flex-direction: column;\n padding: 4px 0;\n min-width: 130px;\n font-family: var(--dtm-font);\n}\n.dtm-player-menu-item {\n background: transparent;\n border: none;\n color: var(--dtm-text);\n padding: 6px 12px;\n text-align: left;\n cursor: pointer;\n font-size: 11px;\n font-family: inherit;\n white-space: nowrap;\n width: 100%;\n box-sizing: border-box;\n transition: background-color 0.1s, color 0.1s;\n}\n.dtm-player-menu-item:hover {\n background: var(--dtm-primary);\n color: var(--dtm-pfg);\n}\n.dtm-player-emoji {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n font-size: 18px;\n line-height: 1;\n user-select: none;\n}\n.dtm-player-balloon {\n /* body \u76F4\u4E0B\u306B\u751F\u6210\u3055\u308C\u3001JS(positionBalloon)\u304C\u6BCE\u56DE left/top \u3092\u30A2\u30F3\u30AB\u30FC\u306E\u753B\u9762\u5EA7\u6A19\u3067\n \u8A2D\u5B9A\u3059\u308B\u3002overflow:hidden \u306A\u7956\u5148\uFF08\u6295\u7A3F\u30AB\u30FC\u30C9\u7B49\uFF09\u306B\u30AF\u30EA\u30C3\u30D7\u3055\u308C\u306A\u3044\u3088\u3046 fixed \u3067\u6D6E\u304B\u305B\u308B\u3002\n top \u306F\u30A2\u30F3\u30AB\u30FC\u4E0A\u7AEF-6px\u3001transform \u3067\u81EA\u8EAB\u306E\u5DE6\u4E0B\u89D2\u3092\u305D\u306E\u70B9\u306B\u5408\u308F\u305B\u308B\u3002 */\n position: fixed;\n left: 0;\n top: 0;\n transform: translate(-50%, -100%);\n z-index: 1000;\n display: none;\n pointer-events: none;\n font-family: var(--dtm-font);\n font-size: 9px;\n color: var(--c-black);\n background: var(--c-white);\n border: 2px solid var(--c-black);\n padding: 2px 4px;\n white-space: nowrap;\n box-shadow: 2px 2px 0 var(--c-black);\n}\n.dtm-player-balloon::after {\n content: \"\";\n position: absolute;\n bottom: -6px;\n left: 50%;\n transform: translateX(-50%) rotate(45deg);\n width: 8px;\n height: 8px;\n background: var(--c-white);\n border-right: 2px solid var(--c-black);\n border-bottom: 2px solid var(--c-black);\n}\n/* \u30A2\u30A4\u30B3\u30F3\u304C\u753B\u9762\u4E0A\u7AEF\u306B\u8FD1\u304F\u4E0A\u306B\u51FA\u3059\u4F59\u767D\u304C\u7121\u3044\u5834\u5408\u306F\u4E0B\u306B\u53CD\u8EE2\u8868\u793A\u3059\u308B\uFF08positionBalloon \u304C\u4ED8\u4E0E\uFF09 */\n.dtm-player-balloon--below {\n transform: translate(-50%, 0);\n}\n.dtm-player-balloon--below::after {\n bottom: auto;\n top: -6px;\n border-right: none;\n border-bottom: none;\n border-left: 2px solid var(--c-black);\n border-top: 2px solid var(--c-black);\n}\n.dtm-player-balloon--visible {\n display: block;\n animation: dtm-balloon-fade-in 0.1s steps(2);\n}\n.dtm-player-balloon--below.dtm-player-balloon--visible {\n animation-name: dtm-balloon-fade-in-below;\n}\n@keyframes dtm-balloon-fade-in {\n from { opacity: 0; transform: translate(-50%, -100%) translateY(4px); }\n to { opacity: 1; transform: translate(-50%, -100%) translateY(0); }\n}\n@keyframes dtm-balloon-fade-in-below {\n from { opacity: 0; transform: translate(-50%, 0) translateY(-4px); }\n to { opacity: 1; transform: translate(-50%, 0) translateY(0); }\n}\n@keyframes dtm-emoji-jump {\n 0% { transform: translateY(0); }\n 35% { transform: translateY(-5px); }\n 65% { transform: translateY(-5px); }\n 100% { transform: translateY(0); }\n}\n.dtm-player-emoji--jump {\n animation: dtm-emoji-jump 0.18s ease-out forwards;\n}\n.dtm-player-chip {\n font-family: 'k8x12', monospace;\n font-size: 9px;\n color: var(--dtm-text);\n background: var(--dtm-border2);\n padding: 2px 6px;\n white-space: nowrap;\n}\n.dtm-player-body {\n position: relative; /* \u30ED\u30FC\u30C7\u30A3\u30F3\u30B0\u30AA\u30FC\u30D0\u30FC\u30EC\u30A4\u306E\u57FA\u6E96\u3002\u30EC\u30FC\u30F3\u7FA4\u3060\u3051\u3092\u8986\u3046 */\n display: flex;\n flex-direction: column;\n gap: var(--dtm-gap);\n}\n.dtm-player-lane-row {\n position: relative;\n display: flex;\n align-items: stretch;\n gap: 6px;\n}\n.dtm-player-lane-label {\n position: relative;\n flex: 0 0 auto;\n width: 16px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n}\n.dtm-player-lane-label--btn {\n cursor: pointer;\n user-select: none;\n}\n.dtm-player-lane-label--btn:hover { opacity: 0.7; }\n.dtm-player-lane-label--muted { opacity: 0.3; }\n\n/* \u2500\u2500\u2500 \u30DF\u30E5\u30FC\u30C8\u8868\u793A\uFF08\u6392\u4ED6\u540C\u671F\uFF09 \u2500\u2500\u2500 */\n.dtm-player-emoji.is-muted,\n.dtm-player-lane-label.is-muted {\n position: relative;\n}\n\n/* \u30DF\u30E5\u30FC\u30C8\u6642\u306E\u300C\u00D7\u300D\u30DE\u30FC\u30AF\u91CD\u306D\u63CF\u304D */\n.dtm-player-emoji.is-muted::before,\n.dtm-player-lane-label.is-muted::before {\n content: \"\u00D7\";\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--dtm-danger);\n font-family: var(--dtm-font);\n font-size: 16px;\n font-weight: bold;\n z-index: 10;\n pointer-events: none;\n text-shadow: 1px 1px 0 var(--c-black);\n}\n\n.dtm-player-lane-label.is-muted::before {\n font-size: 14px;\n}\n\n/* \u30DF\u30E5\u30FC\u30C8\u6642\u306E\u30A2\u30A4\u30B3\u30F3\u3084\u8981\u7D20\u306E\u8584\u6697\u5316\uFF08\u5439\u304D\u51FA\u3057\u306F\u9664\u5916\uFF09 */\n.dtm-player-emoji.is-muted > img,\n.dtm-player-emoji.is-muted > span:not(.dtm-player-balloon) {\n opacity: 0.25;\n filter: grayscale(80%);\n}\n\n.dtm-player-lane-label.is-muted {\n opacity: 0.25;\n}\n\n/* \u30DF\u30E5\u30FC\u30C8\u6642\u306E\u30C8\u30E9\u30C3\u30AF\u30EC\u30FC\u30F3\uFF08\u30B9\u30AF\u30ED\u30FC\u30EB\u90E8\uFF09\u306E\u8584\u6697\u5316\u3068\u30C7\u30AB\u00D7\u30DE\u30FC\u30AF\uFF08\u8272\u5F31\u5BFE\u5FDC\uFF09 */\n.dtm-player-lane-row.is-muted::after {\n content: \"\u00D7\";\n position: absolute;\n top: 0;\n bottom: 0;\n left: 22px; /* label width (16px) + gap (6px) */\n right: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--dtm-danger);\n font-family: var(--dtm-font);\n font-size: 24px;\n font-weight: bold;\n background: rgba(0, 0, 0, 0.45);\n z-index: 10;\n pointer-events: none;\n text-shadow: 1px 1px 0 var(--c-black);\n}\n.dtm-player-lane-no {\n font-family: 'k8x12', monospace;\n font-size: 9px;\n color: var(--dtm-muted);\n}\n.dtm-player-lane {\n position: relative; /* \u30C8\u30FC\u30AF\u30F3\u306E offsetParent \u3092\u30EC\u30FC\u30F3\u306B\u56FA\u5B9A\u3057\u3001\u4E2D\u592E\u5BC4\u305B\u8A08\u7B97\u3092\u6B63\u3059 */\n flex: 1 1 auto;\n overflow-x: auto;\n overflow-y: hidden;\n white-space: nowrap;\n background: var(--c-black);\n border: none;\n padding: 0 6px;\n scrollbar-width: none;\n display: flex;\n align-items: center;\n}\n.dtm-player-lane::-webkit-scrollbar { display: none; }\n.dtm-tk {\n font-family: 'k8x12', monospace;\n font-size: 12px;\n color: var(--dtm-text);\n flex: 0 0 auto;\n}\n.dtm-tk--rest { color: var(--dtm-muted); }\n.dtm-tk--octave,\n.dtm-tk--shift,\n.dtm-tk--length,\n.dtm-tk--ctrl { color: var(--dtm-border2); }\n.dtm-tk--lyric { color: var(--dtm-text); letter-spacing: 1px; }\n.dtm-tk--break { color: var(--dtm-muted); opacity: 0.7; margin: 0 2px; }\n.dtm-tk--meta { color: var(--dtm-border2); margin-right: 4px; }\n.dtm-tk.is-active {\n background: var(--tk, var(--dtm-primary));\n color: var(--c-black);\n font-weight: bold;\n}\n\n/* \u2500\u2500\u2500 \u30B3\u30FC\u30C9\u9032\u884C\u30D7\u30EC\u30A4\u30E4\u30FC \u2500\u2500\u2500 */\n/* MML\u518D\u751F\u5C02\u7528\u30D7\u30EC\u30A4\u30E4\u30FC\uFF08.dtm-player\uFF09\u3068\u67A0\u30C7\u30B6\u30A4\u30F3\u3092\u63C3\u3048\u308B */\n.dtm-chord-player {\n display: flex;\n flex-direction: column;\n gap: var(--dtm-gap);\n padding: var(--dtm-gap);\n font-family: var(--dtm-font);\n font-size: 13px;\n letter-spacing: .06em;\n color: var(--dtm-text);\n background: var(--dtm-deep);\n border: 2px solid var(--dtm-border2);\n box-shadow: 4px 4px 0 var(--c-black);\n image-rendering: pixelated;\n -webkit-font-smoothing: none;\n}\n.dtm-chord-player *,\n.dtm-chord-player *::before,\n.dtm-chord-player *::after { box-sizing: border-box; }\n\n/* \u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u30D0\u30FC \u2014 MML\u518D\u751F\u5C02\u7528\u30D7\u30EC\u30A4\u30E4\u30FC\u306E .dtm-player-head \u3068\u63C3\u3048\u308B */\n.dtm-cp-ctrl {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n}\n.dtm-cp-ctrl .dtm-player-more-container {\n margin-left: auto;\n}\n\n/* \u30EB\u30FC\u30D7\u5207\u66FF\u30DC\u30BF\u30F3 */\n.dtm-cp-loop {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-bg);\n color: var(--dtm-muted);\n cursor: pointer;\n flex: 0 0 auto;\n}\n.dtm-cp-loop:active { transform: translate(1px,1px); }\n.dtm-cp-loop--on {\n border-color: var(--c-black);\n background: var(--dtm-gold);\n color: var(--c-black);\n box-shadow: 2px 2px 0 var(--c-black);\n}\n\n/* \u30E1\u30BF\u60C5\u5831 (BPM) */\n.dtm-cp-meta {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .12em;\n color: var(--dtm-muted);\n}\n\n/* \u30B9\u30AF\u30ED\u30FC\u30EB\u30A8\u30EA\u30A2 */\n.dtm-cp-scroll {\n position: relative;\n max-height: 180px;\n overflow-y: auto;\n padding: 8px 10px 10px;\n scrollbar-width: none;\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n.dtm-cp-scroll::-webkit-scrollbar { display: none; }\n\n/* \u30BB\u30AF\u30B7\u30E7\u30F3\u898B\u51FA\u3057 \u2014 \u30BB\u30AF\u30B7\u30E7\u30F3\u8272\u306E\u5E2F\u3068\u30D4\u30AF\u30BB\u30EB\u30DE\u30FC\u30AB\u30FC */\n.dtm-cp-section {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .14em;\n margin-top: 8px;\n padding: 1px 8px 1px 5px;\n color: var(--cp-fg, var(--dtm-text));\n border-left: 4px solid var(--cp-fg, var(--dtm-text));\n background: linear-gradient(90deg, var(--cp-bg, transparent), transparent);\n align-self: flex-start;\n}\n.dtm-cp-section::before {\n content: \"\u25A0\";\n margin-right: 5px;\n}\n\n/* \u5C0F\u7BC0\u884C */\n.dtm-cp-bar {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 2px;\n margin-top: 2px;\n}\n\n/* \u5C0F\u7BC0\u533A\u5207\u308A\u30D1\u30A4\u30D7 */\n.dtm-cp-pipe {\n font-size: 13px;\n color: rgba(255, 255, 255, 0.35);\n font-family: var(--dtm-font);\n font-weight: bold;\n margin: 0 4px;\n user-select: none;\n}\n\n/* \u30B3\u30FC\u30C9\u30BB\u30EB \u2014 \u30B2\u30FC\u30E0\u306E\u30DC\u30BF\u30F3\u98A8\u30C1\u30C3\u30D7\uFF08\u30BF\u30C3\u30D7\u3067\u305D\u3053\u304B\u3089\u518D\u751F\uFF09 */\n.dtm-cp-chord {\n display: inline-block;\n font-family: var(--dtm-font);\n font-size: 12px;\n padding: 2px 5px;\n border: 2px solid transparent;\n color: var(--cp-fg, var(--dtm-text));\n background: var(--cp-bg, transparent);\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n}\n.dtm-cp-chord:not(.dtm-cp-chord--dead):hover {\n border-color: var(--cp-fg, var(--dtm-text));\n}\n.dtm-cp-chord:not(.dtm-cp-chord--dead):active {\n transform: translate(1px,1px);\n}\n.dtm-cp-chord:focus-visible {\n outline: 2px solid var(--c-white);\n outline-offset: 0;\n}\n.dtm-cp-chord--dead { cursor: default; }\n.dtm-cp-chord--played { opacity: .45; }\n.dtm-cp-chord--active {\n border-color: var(--c-black);\n color: var(--c-black);\n background: var(--cp-fg, var(--dtm-primary));\n box-shadow: 2px 2px 0 var(--c-black);\n opacity: 1;\n}\n";
3388
+ declare const DAW_CSS = "\n@font-face {\n font-family: 'k8x12';\n src: url('https://db.onlinewebfonts.com/t/777630d46640dc5a928ea833c2fcb875.woff2') format('woff2'),\n url('https://db.onlinewebfonts.com/t/777630d46640dc5a928ea833c2fcb875.woff') format('woff'),\n url('https://db.onlinewebfonts.com/t/777630d46640dc5a928ea833c2fcb875.ttf') format('truetype');\n font-weight: normal;\n font-style: normal;\n}\n\n/* ====================================================\n PIXEL MUSIC STUDIO \u2014 \u30C9\u30C3\u30C8\u7D75UI\u30B7\u30B9\u30C6\u30E0\n PICO-8\u30AB\u30E9\u30FC\u30D1\u30EC\u30C3\u30C8\u30FB\u7F8E\u54B2\u30D5\u30A9\u30F3\u30C8\u30FB\u30B2\u30FC\u30E0\u30A6\u30A3\u30F3\u30C9\u30A6\u67A0\n ==================================================== */\n\n/* \u30C7\u30B6\u30A4\u30F3\u30C8\u30FC\u30AF\u30F3\u306F\u7DE8\u96C6UI\u672C\u4F53\uFF08.dtm-daw\uFF09\u306B\u52A0\u3048\u3001\u305D\u306E\u5916\u5074\u306B\u5DEE\u3057\u8FBC\u307E\u308C\u308B\n \u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u30D0\u30FC\uFF08.dtm-controlbar\uFF09\u306B\u3082\u4F9B\u7D66\u3059\u308B\u3002mountPresetSelect /\n mountModeSwitch \u306EUI\u306F .dtm-daw \u306E\u5144\u5F1F\u3068\u3057\u3066\u7F6E\u304B\u308C\u308B\u305F\u3081\u3001\u3053\u3053\u3067\u914D\u3089\u306A\u3044\u3068\n var(--dtm-*) \u304C\u89E3\u6C7A\u3067\u304D\u305A\u7121\u88C5\u98FE\uFF08\u767D\u5730\u30FB\u65E2\u5B9A\u30D5\u30A9\u30F3\u30C8\uFF09\u306B\u306A\u3063\u3066\u3057\u307E\u3046\u3002\n \u518D\u751F\u5C02\u7528\u30D3\u30E5\u30FC\u306E\u30E2\u30FC\u30C0\u30EB\uFF0F\u5229\u7528\u898F\u7D04\u30AB\u30D0\u30FC\u3001\u6B4C\u58F0\u30E2\u30C7\u30EB\u540D\u306E\u5439\u304D\u51FA\u3057(.dtm-player-balloon)\n \u306F document.body \u76F4\u4E0B\u3078\u91CD\u306D\u308B\u305F\u3081\u3001.dtm-daw \u306E\u5916\u306B\u51FA\u308B\u3002\u3053\u308C\u3089\u3082\u540C\u69D8\u306B\u30C8\u30FC\u30AF\u30F3\u3092\n \u4F9B\u7D66\u3057\u306A\u3044\u3068\u7121\u88C5\u98FE\uFF08\u67A0\u7DDA\u306A\u3057\u30FB\u6587\u5B57\u8272\u4E0D\u660E\u306E\u767D\u3044\u7BB1\uFF09\u306B\u306A\u308B\u3002 */\n.dtm-daw,\n.dtm-controlbar,\n.dtm-modal-overlay,\n.dtm-consent-overlay,\n.dtm-chord-player,\n.dtm-player-balloon {\n /* PICO-8 16\u8272\u30D1\u30EC\u30C3\u30C8\u3088\u308A */\n --c-black: #000000;\n --c-navy: #1d2b53;\n --c-purple: #7e2553;\n --c-dkgreen: #008751;\n --c-brown: #ab5236;\n --c-dkgray: #5f574f;\n --c-gray: #c2c3c7;\n --c-white: #fff1e8;\n --c-red: #ff004d;\n --c-orange: #ffa300;\n --c-yellow: #ffec27;\n --c-green: #00e436;\n --c-cyan: #29adff;\n --c-lavend: #83769c;\n --c-pink: #ff77a8;\n --c-peach: #ffccaa;\n\n /* \u30BB\u30DE\u30F3\u30C6\u30A3\u30C3\u30AF\u30C8\u30FC\u30AF\u30F3 */\n --dtm-bg: var(--c-black);\n --dtm-surface: var(--c-navy);\n --dtm-deep: #0a0f1f;\n --dtm-border: var(--c-cyan);\n --dtm-border2: var(--c-dkgray);\n --dtm-text: var(--c-white);\n --dtm-muted: var(--c-lavend);\n --dtm-primary: var(--c-cyan);\n --dtm-pfg: var(--c-black);\n --dtm-danger: var(--c-red);\n --dtm-success: var(--c-green);\n --dtm-accent: var(--c-pink);\n --dtm-gold: var(--c-yellow);\n --dtm-warn: var(--c-orange);\n --dtm-tap: 40px;\n --dtm-gap: 6px;\n --dtm-font: 'k8x12',ui-monospace,monospace;\n}\n\n.dtm-daw {\n box-sizing: border-box;\n font-family: var(--dtm-font);\n font-size: 14px;\n line-height: 1.6;\n letter-spacing: .06em;\n color: var(--dtm-text);\n background: var(--dtm-bg);\n width: 100%;\n display: flex;\n flex-direction: column;\n gap: var(--dtm-gap);\n padding: 0;\n image-rendering: pixelated;\n -webkit-font-smoothing: none;\n -moz-osx-font-smoothing: unset;\n font-smooth: never;\n -webkit-tap-highlight-color: transparent;\n}\n.dtm-daw *,\n.dtm-daw *::before,\n.dtm-daw *::after { box-sizing: border-box; }\n\n/* \u2500\u2500\u2500 \u30B2\u30FC\u30E0\u30A6\u30A3\u30F3\u30C9\u30A6\u5171\u901A\u67A0 \u2500\u2500\u2500 */\n/* \u5916\u67A0(\u9ED22px) \u2192 \u8272\u4ED8\u304D2px border \u2192 \u5185\u67A0(\u9ED2inset2px) \u306E3\u91CD\u69CB\u9020 */\n.dtm-win {\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-primary),\n 4px 4px 0 var(--c-black);\n background: var(--dtm-surface);\n}\n\n/* \u2500\u2500\u2500 \u5171\u901A\u30DC\u30BF\u30F3 \u2500\u2500\u2500 */\n.dtm-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n min-height: var(--dtm-tap);\n min-width: var(--dtm-tap);\n padding: 0 10px;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-surface);\n color: var(--dtm-text);\n font-family: var(--dtm-font);\n font-size: 13px;\n text-transform: uppercase;\n letter-spacing: .12em;\n cursor: pointer;\n user-select: none;\n white-space: nowrap;\n box-shadow: 3px 3px 0 var(--c-black);\n transition: none;\n}\n.dtm-btn:active { transform: translate(3px,3px); box-shadow: none; }\n.dtm-btn:disabled { opacity: .3; cursor: default; box-shadow: none; }\n.dtm-btn--ghost { background: transparent; border-color: var(--dtm-border2); }\n.dtm-btn--primary { border-color: var(--dtm-primary); background: var(--dtm-primary); color: var(--dtm-pfg); }\n.dtm-btn--success { border-color: var(--dtm-success); background: var(--dtm-success); color: var(--c-black); }\n.dtm-btn--danger { border-color: var(--dtm-danger); background: var(--dtm-danger); color: var(--c-white); }\n.dtm-btn--accent { border-color: var(--dtm-accent); background: var(--dtm-accent); color: var(--c-black); }\n.dtm-btn--icon { padding: 0; }\n\n/* \u2500\u2500\u2500 \u30A2\u30A4\u30B3\u30F3\u30DC\u30BF\u30F3 \u2500\u2500\u2500 */\n.dtm-iconbtn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--dtm-tap);\n height: var(--dtm-tap);\n flex: 0 0 auto;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-surface);\n color: var(--dtm-text);\n font-size: 16px;\n cursor: pointer;\n box-shadow: 3px 3px 0 var(--c-black);\n}\n.dtm-iconbtn:active { transform: translate(3px,3px); box-shadow: none; }\n.dtm-iconbtn:disabled { opacity: .3; cursor: default; box-shadow: none; }\n\n/* \u2500\u2500\u2500 \u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u30D0\u30FC\uFF08HUD\u30B9\u30BF\u30A4\u30EB\uFF09 \u2500\u2500\u2500 */\n.dtm-topbar {\n position: sticky;\n top: 0;\n z-index: 20;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--dtm-gap);\n padding: 6px;\n background: var(--dtm-deep);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-success),\n 4px 4px 0 var(--c-black);\n}\n.dtm-topbar-row1 {\n display: flex;\n align-items: center;\n gap: var(--dtm-gap);\n flex-basis: 100%;\n min-width: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.dtm-topbar-row1::-webkit-scrollbar { display: none; }\n.dtm-topbar-row1 > * { flex-shrink: 0; }\n.dtm-topbar-row1 > .dtm-grow { flex-shrink: 1; }\n\n/* PLAY\u30DC\u30BF\u30F3 \u2014 \u30B2\u30FC\u30E0\u306E\u300C\u6C7A\u5B9A\u30DC\u30BF\u30F3\u300D\u7684\u5B58\u5728\u611F */\n.dtm-play {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--dtm-tap);\n height: var(--dtm-tap);\n flex: 0 0 auto;\n border: 2px solid var(--c-black);\n background: var(--dtm-success);\n color: var(--c-black);\n cursor: pointer;\n box-shadow: 0 0 0 2px var(--dtm-success), 4px 4px 0 var(--c-black);\n}\n.dtm-play:active { transform: translate(4px,4px); box-shadow: none; }\n.dtm-play:disabled { opacity: .35; cursor: default; box-shadow: none; }\n.dtm-play--stop {\n background: var(--dtm-danger);\n box-shadow: 0 0 0 2px var(--dtm-danger), 4px 4px 0 var(--c-black);\n}\n.dtm-rec { color: var(--dtm-danger); }\n\n/* BPM \u2014 \u30C7\u30B8\u30BF\u30EB\u30AB\u30A6\u30F3\u30BF\u30FC\u98A8 */\n.dtm-label {\n font-family: var(--dtm-font);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .14em;\n color: var(--dtm-muted);\n white-space: nowrap;\n}\n.dtm-checkbox-label {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-family: var(--dtm-font);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .12em;\n color: var(--dtm-muted);\n cursor: pointer;\n user-select: none;\n margin-top: 4px;\n}\n.dtm-checkbox-label:hover { color: var(--dtm-text); }\n\n/* \u2500\u2500\u2500 \u4F5C\u66F2\u3059\u308B\u30BB\u30AF\u30B7\u30E7\u30F3\u306E\u9078\u629E\uFF08\u30A4\u30F3\u30C8\u30ED\u30FBA\u30E1\u30ED\u2026\u306E\u30C1\u30A7\u30C3\u30AF\uFF09 \u2500\u2500\u2500 */\n.dtm-checks {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 10px;\n align-items: center;\n}\n.dtm-check {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-family: var(--dtm-font);\n font-size: 11px;\n color: var(--dtm-muted);\n cursor: pointer;\n user-select: none;\n white-space: nowrap;\n}\n.dtm-check:hover { color: var(--dtm-text); }\n.dtm-check input {\n width: 13px;\n height: 13px;\n accent-color: var(--dtm-success);\n cursor: pointer;\n flex-shrink: 0;\n}\n.dtm-info-table {\n border-collapse: collapse;\n margin: 6px 0;\n font-size: 11px;\n width: 100%;\n}\n.dtm-info-table th,\n.dtm-info-table td {\n border: 1px solid var(--dtm-border);\n padding: 3px 6px;\n text-align: left;\n white-space: nowrap;\n}\n.dtm-info-table th { color: var(--dtm-muted); font-weight: normal; }\n.dtm-hint {\n font-family: var(--dtm-font);\n font-size: 10px;\n color: var(--dtm-muted);\n white-space: nowrap;\n}\n.dtm-checkbox-label--sub { margin-left: 20px; font-size: 10px; }\n.dtm-checkbox {\n width: 14px;\n height: 14px;\n accent-color: var(--dtm-success);\n cursor: pointer;\n flex-shrink: 0;\n}\n\n.dtm-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-family: var(--dtm-font);\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: .1em;\n color: var(--dtm-muted);\n cursor: pointer;\n}\n.dtm-toggle input { width: 16px; height: 16px; accent-color: var(--dtm-accent); }\n\n/* \u2500\u2500\u2500 \u30C4\u30FC\u30EB\u30C9\u30C3\u30AF\uFF08\u88C5\u5099\u30B9\u30ED\u30C3\u30C8\u98A8\uFF09 \u2500\u2500\u2500 */\n.dtm-tooldock {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--dtm-gap);\n padding: 6px;\n background: var(--dtm-deep);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-border2),\n 4px 4px 0 var(--c-black);\n}\n.dtm-sep {\n width: 2px; align-self: stretch;\n background: var(--dtm-border2); margin: 2px;\n}\n.dtm-row .dtm-label[data-dtm] { min-width: 48px; text-align: center; }\n\n/* \u2500\u2500\u2500 \u30BB\u30B0\u30E1\u30F3\u30C8\uFF08\u30A2\u30A4\u30C6\u30E0\u30B9\u30ED\u30C3\u30C8\uFF09 \u2500\u2500\u2500 */\n.dtm-seg {\n display: inline-flex;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-deep);\n box-shadow: 3px 3px 0 var(--c-black);\n}\n.dtm-segbtn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--dtm-tap);\n height: var(--dtm-tap);\n border: none;\n border-right: 2px solid var(--dtm-border2);\n background: transparent;\n color: var(--dtm-muted);\n cursor: pointer;\n}\n.dtm-segbtn:last-child { border-right: none; }\n.dtm-segbtn--active {\n background: var(--dtm-gold);\n color: var(--c-black);\n}\n.dtm-segbtn:not(.dtm-segbtn--active):active { background: var(--dtm-border2); }\n\n/* \u2500\u2500\u2500 \u30D5\u30A9\u30FC\u30E0\u8981\u7D20 \u2500\u2500\u2500 */\n.dtm-select, .dtm-input, .dtm-textarea {\n min-height: var(--dtm-tap);\n padding: 4px 8px;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-deep);\n color: var(--dtm-text);\n font-family: var(--dtm-font);\n font-size: 13px;\n letter-spacing: .06em;\n box-shadow: inset 2px 2px 0 var(--c-black);\n}\n.dtm-select:focus, .dtm-input:focus, .dtm-textarea:focus {\n outline: none;\n border-color: var(--dtm-primary);\n}\n.dtm-input--num { width: 64px; text-align: center; font-size: 16px; }\n/* \u4ED6\u306E\u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u3068\u540C\u3058\u884C\u306B\u7F6E\u304F\u30D7\u30EB\u30C0\u30A6\u30F3\uFF08\u697D\u5668\u30FB\u6B4C\u5531\u30E2\u30C7\u30EB\uFF09 */\n.dtm-select--half { flex: 1 1 120px; min-width: 0; max-width: 200px; }\n.dtm-textarea { width: 100%; min-height: 56px; resize: vertical; line-height: 1.7; }\n.dtm-textarea.dtm-grow { width: 0; }\n.dtm-range { height: var(--dtm-tap); accent-color: var(--dtm-primary); }\n.dtm-range:disabled { opacity: .35; cursor: default; }\n\n/* \u2500\u2500\u2500 \u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u30D0\u30FC\uFF08\u697D\u5668\u30D7\u30EA\u30BB\u30C3\u30C8 / \u30E2\u30FC\u30C9\u5207\u66FF\u306A\u3069\u306E\u5DEE\u3057\u8FBC\u307FUI\uFF09 \u2500\u2500\u2500 */\n.dtm-controlbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--dtm-gap);\n padding: 6px 8px;\n background: var(--dtm-deep);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-border2),\n 4px 4px 0 var(--c-black);\n margin-bottom: var(--dtm-gap);\n}\n.dtm-controlbar-label {\n font-family: var(--dtm-font);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .14em;\n color: var(--dtm-accent);\n white-space: nowrap;\n flex-shrink: 0;\n}\n.dtm-controlbar .dtm-select { flex: 1 1 160px; }\n\n/* \u30E2\u30FC\u30C9\u5207\u66FF\uFF08\u30C6\u30AD\u30B9\u30C8\u7248\u30BB\u30B0\u30E1\u30F3\u30C8\uFF09 */\n.dtm-modeseg {\n display: inline-flex;\n border: 2px solid var(--dtm-border2);\n box-shadow: 3px 3px 0 var(--c-black);\n}\n.dtm-modebtn {\n min-height: var(--dtm-tap);\n padding: 0 14px;\n border: none;\n border-right: 2px solid var(--dtm-border2);\n background: var(--dtm-deep);\n color: var(--dtm-muted);\n font-family: var(--dtm-font);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .12em;\n cursor: pointer;\n}\n.dtm-modebtn:last-child { border-right: none; }\n.dtm-modebtn--active { background: var(--dtm-primary); color: var(--dtm-pfg); }\n.dtm-modebtn:not(.dtm-modebtn--active):active { background: var(--dtm-border2); }\n\n/* \u2500\u2500\u2500 \u30C8\u30E9\u30C3\u30AF\u30D4\u30EB\uFF08\u756A\u53F7\u30DC\u30BF\u30F3\u3001\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u30D0\u30FC2\u884C\u76EE\uFF09 \u2500\u2500\u2500 */\n.dtm-tracks {\n flex-basis: 100%;\n display: flex;\n flex-wrap: nowrap;\n gap: 4px;\n align-items: flex-end;\n}\n.dtm-pill {\n --dtm-pill-color: var(--dtm-primary);\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 1 1 0;\n min-width: 0;\n height: 24px;\n padding: 0;\n border: 2px solid var(--c-black);\n background: color-mix(in srgb, var(--dtm-pill-color) 22%, #0e121e);\n color: color-mix(in srgb, var(--c-white) 60%, transparent);\n font-family: var(--dtm-font);\n font-size: 11px;\n font-weight: bold;\n cursor: pointer;\n box-shadow: 1px 1px 0 var(--c-black);\n opacity: 0.55;\n transition: opacity 120ms ease, background 120ms ease, transform 120ms ease;\n}\n.dtm-pill:hover:not(.dtm-pill--active) {\n opacity: 0.85;\n background: color-mix(in srgb, var(--dtm-pill-color) 45%, #0e121e);\n color: var(--c-white);\n}\n/* \u30A2\u30AF\u30C6\u30A3\u30D6\u9078\u629E = \u9BAE\u660E\u306A\u30C8\u30E9\u30C3\u30AF\u8272 + \u30B4\u30FC\u30EB\u30C9\u67A0 + \u30B0\u30ED\u30FC + \u4E0A\u6607\u7ACB\u4F53\u30BF\u30D6\u8868\u73FE */\n.dtm-pill--active {\n opacity: 1;\n height: 28px;\n background: var(--dtm-pill-color);\n color: #ffffff;\n text-shadow: 1px 1px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000;\n border-color: var(--dtm-gold);\n box-shadow:\n 0 0 0 1.5px var(--dtm-gold),\n 0 0 10px color-mix(in srgb, var(--dtm-pill-color) 70%, transparent),\n 2px 2px 0 var(--c-black);\n z-index: 1;\n transform: translateY(-2px);\n}\n.dtm-pill--active::before {\n content: '';\n position: absolute;\n top: -2px;\n left: 2px;\n right: 2px;\n height: 2px;\n background: #ffffff;\n border-radius: 1px;\n opacity: 0.85;\n}\n.dtm-pill:not(.dtm-pill--active):active { transform: translate(1px,1px); box-shadow: none; }\n/* \u518D\u751F\u4E2D\u3001\u5B9F\u969B\u306B\u767A\u97F3\u3057\u305F\u77AC\u9593\u3060\u3051\u70B9\u706F\uFF08\u3069\u306E\u30BF\u30D6\u304C\u4ECA\u9CF4\u3063\u3066\u3044\u308B\u304B\u8996\u899A\u7684\u306B\u5206\u304B\u308B\u3088\u3046\u306B\uFF09 */\n.dtm-pill--sounding {\n filter: brightness(1.6);\n transition: filter 30ms linear;\n}\n\n/* \u2500\u2500\u2500 \u30D4\u30A2\u30CE\u30ED\u30FC\u30EB\uFF08\u30C8\u30E9\u30C3\u30AB\u30FC\u98A8\uFF09 \u2500\u2500\u2500 */\n.dtm-roll-wrap { display: flex; gap: var(--dtm-gap); }\n.dtm-roll {\n position: relative;\n flex: 1 1 auto;\n height: 32vh;\n max-height: 32vh;\n background: var(--dtm-deep);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-border2),\n 4px 4px 0 var(--c-black);\n overflow: hidden;\n}\n/* \u30AB\u30B9\u30BF\u30E0\u80CC\u666F\u753B\u50CF\u30EC\u30A4\u30E4\u30FC\uFF08\u753B\u50CF\u81EA\u4F53\u3092\u534A\u900F\u660E\u5316\u3057\u3001\u30AD\u30E3\u30F3\u30D0\u30B9\u5074\u306E\u5857\u308A\u306F\u7DAD\u6301\uFF09 */\n.dtm-roll::before {\n content: '';\n position: absolute;\n inset: 0;\n background-image: var(--dtm-roll-bg-image, none);\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n opacity: var(--dtm-roll-bg-opacity, 0.4);\n pointer-events: none;\n}\n.dtm-vscroll {\n position: relative;\n width: 20px;\n background: var(--dtm-deep);\n border: 2px solid var(--dtm-border2);\n cursor: pointer;\n flex: 0 0 auto;\n touch-action: none;\n}\n.dtm-vscroll-thumb, .dtm-hscroll-thumb {\n position: absolute;\n background: var(--dtm-primary);\n min-width: 20px;\n min-height: 20px;\n}\n.dtm-vscroll-thumb { left: 0; width: 100%; }\n.dtm-hscroll {\n position: relative;\n width: 100%; height: 20px;\n background: var(--dtm-deep);\n border: 2px solid var(--dtm-border2);\n cursor: pointer;\n touch-action: none;\n}\n.dtm-hscroll-thumb { top: 0; height: 100%; }\n\n/* \u2500\u2500\u2500 \u30D1\u30CD\u30EB\uFF08RPG\u30C0\u30A4\u30A2\u30ED\u30B0\u30A6\u30A3\u30F3\u30C9\u30A6\uFF09 \u2500\u2500\u2500 */\n.dtm-panel {\n background: var(--dtm-surface);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-primary),\n 4px 4px 0 var(--c-black);\n overflow: hidden;\n}\n.dtm-panel > summary {\n list-style: none;\n cursor: pointer;\n padding: 0 12px;\n font-family: var(--dtm-font);\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: .14em;\n display: flex;\n align-items: center;\n min-height: var(--dtm-tap);\n background: var(--dtm-deep);\n border-bottom: 2px solid var(--dtm-border2);\n color: var(--dtm-primary);\n gap: 8px;\n}\n.dtm-panel:not([open]) > summary { border-bottom: none; }\n.dtm-panel > summary::-webkit-details-marker { display: none; }\n/* \u5DE6\u7AEF\u30E9\u30A4\u30F3\uFF08\u30B2\u30FC\u30E0UI\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u8272\u5206\u3051\uFF09 */\n.dtm-panel > summary::before {\n content: '';\n display: block;\n width: 4px;\n height: 20px;\n background: var(--dtm-accent);\n flex: 0 0 auto;\n}\n.dtm-panel[open] > summary::before { background: var(--dtm-primary); }\n/* \u6298\u308A\u305F\u305F\u307F\u77E2\u5370 */\n.dtm-panel > summary::after {\n content: \"\u25B6\";\n margin-left: auto;\n color: var(--dtm-muted);\n font-size: 10px;\n}\n.dtm-panel[open] > summary::after { content: \"\u25BC\"; }\n.dtm-panel-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 10px; }\n.dtm-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }\n.dtm-track-body { display: flex; flex-direction: column; gap: 10px; }\n\n/* \u2500\u2500\u2500 \u30A2\u30AF\u30C6\u30A3\u30D6\u30C8\u30E9\u30C3\u30AF\u8272\uFF08\u500B\u5225\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A\u30D1\u30CD\u30EB\u306E\u5DE6\u7AEF\u30E9\u30A4\u30F3\uFF09 \u2500\u2500\u2500 */\n.dtm-panel--track > summary::before,\n.dtm-panel--track[open] > summary::before {\n background: var(--dtm-track-color, var(--dtm-primary));\n}\n\n/* \u2500\u2500\u2500 MML\u51FA\u529B\uFF08CRT\u30BF\u30FC\u30DF\u30CA\u30EB\uFF09 \u2500\u2500\u2500 */\n.dtm-output {\n background: var(--c-black);\n color: var(--dtm-success);\n border: 2px solid var(--dtm-success);\n padding: 10px;\n box-shadow: 0 0 0 2px var(--c-black), 4px 4px 0 var(--c-black);\n}\n.dtm-output::before {\n content: \"C:\\> MML OUTPUT\";\n display: block;\n font-size: 11px;\n color: var(--dtm-muted);\n letter-spacing: .14em;\n margin-bottom: 6px;\n padding-bottom: 6px;\n border-bottom: 1px solid var(--dtm-border2);\n}\n.dtm-output pre {\n margin: 0;\n background: transparent;\n padding: 0;\n overflow-x: auto;\n font-family: var(--dtm-font);\n font-size: 12px;\n line-height: 1.8;\n color: var(--dtm-success);\n}\n.dtm-output-label {\n font-size: 11px;\n color: var(--dtm-muted);\n font-family: var(--dtm-font);\n margin-top: 10px;\n}\n.dtm-output-label:first-of-type {\n margin-top: 0;\n}\n.dtm-output-row { display: flex; gap: 8px; align-items: flex-start; margin-top: 6px; }\n.dtm-output-row pre { flex: 1; }\n.dtm-output-scroll { max-height: 240px; overflow-y: auto; }\n\n/* \u2500\u2500\u2500 \u30ED\u30FC\u30C7\u30A3\u30F3\u30B0\u30AA\u30FC\u30D0\u30FC\u30EC\u30A4 \u2500\u2500\u2500 */\n.dtm-overlay {\n position: absolute; inset: 0; z-index: 10;\n background: rgba(0,0,0,.92);\n display: flex; align-items: center; justify-content: center;\n flex-direction: column; gap: 14px;\n pointer-events: auto;\n cursor: wait;\n}\n.dtm-overlay[hidden] { display: none; }\n.dtm-overlay::before {\n content: '\u30ED\u30FC\u30C9\u4E2D';\n font-family: var(--dtm-font);\n font-size: 13px;\n color: var(--dtm-primary);\n text-transform: uppercase;\n letter-spacing: .25em;\n animation: dtm-blink 1s steps(1) infinite;\n}\n/* 8\u30D6\u30ED\u30C3\u30AF\u523B\u307F\u3067\u57CB\u307E\u308B\u30D4\u30AF\u30BB\u30EB\u30D0\u30FC */\n.dtm-spinner {\n width: 96px; height: 12px;\n position: relative;\n background: var(--c-navy);\n border: 2px solid var(--dtm-primary);\n box-shadow: 0 0 0 2px var(--c-black), 4px 4px 0 var(--c-black);\n}\n.dtm-spinner::after {\n content: '';\n position: absolute;\n left: 0; top: 0; height: 100%;\n background: var(--dtm-primary);\n animation: dtm-load 1.6s steps(8) infinite;\n}\n@keyframes dtm-load { 0%{width:0} 100%{width:100%} }\n/* \u9032\u6357\u304C\u78BA\u5B9A\u3057\u305F\u3089\u7121\u9650\u30EB\u30FC\u30D7\u6F14\u51FA\u3092\u6B62\u3081\u3001\u5B9F\u6E2C\u5024\u3067\u5857\u308A\u3064\u3076\u3059 */\n.dtm-spinner--determinate::after { display: none; }\n.dtm-spinner-fill {\n position: absolute;\n left: 0; top: 0; height: 100%;\n width: 0;\n background: var(--dtm-primary);\n transition: width .12s steps(8);\n}\n.dtm-loading-label {\n font-family: var(--dtm-font);\n font-size: 11px;\n color: var(--dtm-primary);\n letter-spacing: .15em;\n min-height: 1em;\n}\n.dtm-overlay-skip-btn {\n margin-top: 12px;\n min-height: 32px;\n font-size: 11px;\n font-family: var(--dtm-font);\n padding: 0 12px;\n background: var(--dtm-surface);\n border: 2px solid var(--dtm-border2);\n color: var(--dtm-muted);\n box-shadow: 2px 2px 0 var(--c-black);\n cursor: pointer;\n pointer-events: auto;\n}\n.dtm-overlay-skip-btn:hover {\n color: var(--dtm-text);\n border-color: var(--dtm-primary);\n}\n.dtm-overlay-skip-btn:active {\n transform: translate(2px, 2px);\n box-shadow: none;\n}\n.dtm-overlay-skip-btn:disabled {\n opacity: .3;\n cursor: default;\n box-shadow: none;\n transform: none;\n}\n.dtm-topbar-loading {\n display: none;\n font-family: var(--dtm-font);\n font-size: 11px;\n color: var(--dtm-primary);\n margin-left: 12px;\n letter-spacing: .15em;\n align-self: center;\n}\n.dtm-topbar.is-loading .dtm-topbar-loading {\n display: inline-block;\n}\n.dtm-topbar.is-loading {\n pointer-events: none;\n opacity: 0.7;\n}\n\n@keyframes dtm-blink { 0%,100%{opacity:1} 50%{opacity:0} }\n.dtm-blink { animation: dtm-blink 1s steps(1) infinite; }\n\n/* \u2500\u2500\u2500 \u97F3\u5272\u308C\u691C\u77E5\u30D0\u30C3\u30B8 \u2500\u2500\u2500 \u30AF\u30EA\u30C3\u30D7\u767A\u751F\u4E2D\u3060\u3051\u8868\u793A\u3055\u308C\u308B\u8B66\u544A\u30DC\u30BF\u30F3\u3002\u30AF\u30EA\u30C3\u30AF\u3067\u6D88\u305B\u308B\u3002 */\n.dtm-clip-badge {\n flex: 0 0 auto;\n min-height: 24px;\n padding: 0 8px;\n border: 2px solid var(--c-black);\n background: var(--dtm-danger);\n color: var(--c-white);\n font-family: var(--dtm-font);\n font-size: 10px;\n letter-spacing: .1em;\n cursor: pointer;\n box-shadow: 0 0 0 2px var(--dtm-danger), 2px 2px 0 var(--c-black);\n animation: dtm-blink .5s steps(1) infinite;\n}\n.dtm-clip-badge:active { transform: translate(2px,2px); box-shadow: none; }\n\n/* \u2500\u2500\u2500 \u30A4\u30F3\u30D5\u30A9\u30DC\u30BF\u30F3 \u2500\u2500\u2500 */\n.dtm-infobtn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n flex: 0 0 auto;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-surface);\n color: var(--dtm-muted);\n cursor: pointer;\n box-shadow: 1px 1px 0 var(--c-black);\n padding: 0;\n margin: 0;\n}\n.dtm-infobtn:hover {\n color: var(--dtm-primary);\n border-color: var(--dtm-primary);\n}\n.dtm-infobtn:active {\n transform: translate(1px, 1px);\n box-shadow: none;\n}\n\n/* \u2500\u2500\u2500 \u89E3\u8AAC\u30E2\u30FC\u30C0\u30EB \u2500\u2500\u2500 */\n.dtm-modal-overlay {\n position: fixed;\n inset: 0;\n z-index: 10000;\n background: rgba(0, 0, 0, 0.7);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n backdrop-filter: blur(2px);\n /* body\u76F4\u4E0B\u306B\u91CD\u306D\u305F\u5834\u5408\uFF08\u518D\u751F\u5C02\u7528\u30D3\u30E5\u30FC\uFF09\u3067\u3082\u6587\u5B57\u8272\u30FB\u30D5\u30A9\u30F3\u30C8\u304C\n .dtm-daw \u304B\u3089\u7D99\u627F\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u3053\u3053\u3067\u660E\u793A\u3059\u308B\u3002 */\n color: var(--dtm-text);\n font-family: var(--dtm-font);\n}\n.dtm-modal-overlay[hidden] {\n display: none !important;\n}\n\n/* \u2500\u2500\u2500 \u5229\u7528\u898F\u7D04\u540C\u610F\u30AB\u30D0\u30FC \u2500\u2500\u2500 */\n.dtm-consent-overlay {\n position: fixed;\n inset: 0;\n z-index: 10100;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n backdrop-filter: blur(2px);\n /* body\u76F4\u4E0B\u306B\u91CD\u306D\u308B\u305F\u3081 .dtm-daw \u304B\u3089\u7D99\u627F\u3067\u304D\u306A\u3044\u6587\u5B57\u8272\u30FB\u30D5\u30A9\u30F3\u30C8\u3092\u660E\u793A\u3002 */\n color: var(--dtm-text);\n font-family: var(--dtm-font);\n}\n.dtm-consent-overlay[hidden] {\n display: none !important;\n}\n.dtm-consent-modal {\n max-width: 450px;\n width: 100%;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n background: var(--dtm-surface);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-primary),\n 4px 4px 0 var(--c-black);\n overflow-y: auto;\n}\n.dtm-consent-header {\n background: var(--dtm-deep);\n color: var(--dtm-text);\n padding: 8px 12px;\n border-bottom: 2px solid var(--c-black);\n font-weight: bold;\n text-align: center;\n font-size: 14px;\n}\n.dtm-consent-body {\n padding: 12px 16px;\n font-size: 13px;\n line-height: 1.6;\n}\n.dtm-consent-body a {\n color: var(--dtm-primary);\n text-decoration: underline;\n}\n.dtm-consent-body a:hover {\n color: var(--dtm-accent);\n}\n.dtm-consent-footer {\n padding: 8px;\n border-top: 2px solid var(--c-black);\n background: var(--dtm-deep);\n display: flex;\n justify-content: center;\n}\n\n.dtm-confirm-footer {\n padding: 8px 12px;\n border-top: 2px solid var(--c-black);\n background: var(--dtm-deep);\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n}\n\n.dtm-modal {\n max-width: 500px;\n width: 100%;\n max-height: 80vh;\n display: flex;\n flex-direction: column;\n background: var(--dtm-surface);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 2px var(--c-black),\n 0 0 0 2px var(--dtm-primary),\n 4px 4px 0 var(--c-black);\n overflow: hidden;\n}\n.dtm-modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: var(--dtm-deep);\n padding: 8px 12px;\n border-bottom: 2px solid var(--c-black);\n}\n.dtm-modal-title {\n font-family: var(--dtm-font);\n font-size: 14px;\n color: var(--dtm-gold);\n font-weight: bold;\n}\n.dtm-modal-close {\n background: transparent;\n border: none;\n color: var(--dtm-text);\n font-size: 20px;\n cursor: pointer;\n padding: 0;\n line-height: 1;\n}\n.dtm-modal-close:hover {\n color: var(--dtm-danger);\n}\n.dtm-modal-body {\n padding: 12px;\n overflow-y: auto;\n overflow-x: hidden;\n font-size: 13px;\n line-height: 1.6;\n}\n.dtm-modal-body * {\n max-width: 100%;\n box-sizing: border-box;\n}\n.dtm-modal-body a {\n color: var(--dtm-primary);\n text-decoration: underline;\n}\n.dtm-modal-body a:hover {\n color: var(--dtm-accent);\n}\n.dtm-modal-body h4 {\n margin: 12px 0 6px 0;\n color: var(--dtm-primary);\n font-size: 13px;\n}\n.dtm-modal-body h4:first-child {\n margin-top: 0;\n}\n.dtm-modal-body p {\n margin: 0 0 8px 0;\n}\n.dtm-modal-body ul {\n margin: 0 0 8px 0;\n padding-left: 16px;\n}\n.dtm-modal-body li {\n margin-bottom: 4px;\n}\n.dtm-modal-body code {\n background: var(--dtm-deep);\n color: var(--dtm-accent);\n padding: 1px 4px;\n font-family: var(--dtm-font);\n font-size: 12px;\n}\n.dtm-modal-body pre {\n background: var(--dtm-deep);\n color: var(--dtm-success);\n padding: 8px;\n border: 1px solid var(--dtm-border2);\n margin: 6px 0;\n overflow-x: auto;\n font-family: var(--dtm-font);\n font-size: 12px;\n}\n\n.dtm-modal-sample-box {\n background: var(--dtm-deep);\n border: 1px solid var(--dtm-border2);\n border-radius: 4px;\n padding: 8px 10px;\n margin-bottom: 12px;\n}\n.dtm-modal-sample-box:last-child {\n margin-bottom: 0;\n}\n.dtm-modal-sample-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 6px;\n}\n.dtm-modal-sample-tag {\n font-family: var(--dtm-font);\n font-size: 11px;\n font-weight: bold;\n color: var(--dtm-accent);\n}\n.dtm-modal-sample-desc {\n margin: 6px 0 0 0;\n font-size: 11px;\n color: var(--dtm-muted);\n}\n.dtm-modal-sample-player-container {\n margin-top: 8px;\n}\n.dtm-modal-sample-player-container:empty {\n margin-top: 0;\n}\n.dtm-modal-sample-player-container .dtm-player {\n border: 1px solid var(--dtm-border2);\n box-shadow: none;\n background: rgba(0, 0, 0, 0.3);\n}\n.dtm-modal-sample-player-container .dtm-player-body {\n max-height: 100px;\n overflow-y: auto;\n}\n\n.dtm-hidden { display: none !important; }\n/* \u8AAD\u8FBC\u6642\u306E\u8B66\u544A\u304A\u77E5\u3089\u305B\uFF08\u4F8B: \u30B7\u30F3\u30D7\u30EB\u30E2\u30FC\u30C9\u3067\u306E\u30C8\u30E9\u30C3\u30AF\u5408\u7B97\uFF09\u3002 */\n.dtm-load-note {\n margin: 6px 0 0;\n padding: 0 2px;\n font-family: var(--dtm-font);\n font-size: 11px;\n line-height: 1.5;\n letter-spacing: .04em;\n color: var(--dtm-warn); /* \u8B66\u544A\u8272\uFF08\u30AA\u30EC\u30F3\u30B8\uFF09 */\n font-weight: bold;\n opacity: 1.0;\n}\n.dtm-load-note::before { content: \"\u26A0 \"; }\n.dtm-grow { flex: 1 1 auto; }\n.dtm-lyric-icon {\n flex: 0 0 auto;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n object-fit: cover;\n image-rendering: pixelated;\n}\n/* \u6B4C\u8A5E\u30C8\u30E9\u30C3\u30AF\u306E\u300C\u8A73\u7D30\u8A2D\u5B9A\u300D\u2014 \u5E38\u7528\u3057\u306A\u3044\u30D1\u30E9\u30E1\u30FC\u30BF\uFF08\u30AA\u30AF\u30BF\u30FC\u30D6/\u5B9A\u4F4D/\u30EA\u30D0\u30FC\u30D6\u9001\u308A/\n \u30B8\u30A7\u30F3\u30C0\u30FC/\u30D6\u30EC\u30B7\u30CD\u30B9/\u30D3\u30D6\u30E9\u30FC\u30C8\uFF09\u3092\u7573\u3093\u3067\u304A\u304F\u8EFD\u91CF\u306A details\u3002dtm-panel \u307B\u3069\n \u4EF0\u3005\u3057\u304F\u305B\u305A\u3001\u5C0F\u3055\u3044\u25B6\u30DE\u30FC\u30AB\u30FC\u3060\u3051\u4ED8\u3051\u308B\uFF08\u4ED5\u5207\u308A\u7DDA\u306F\u9AD8\u3055\u3092\u98DF\u3046\u306E\u3067\u7F6E\u304B\u306A\u3044\uFF09\u3002 */\n.dtm-advanced {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.dtm-advanced > summary {\n list-style: none;\n cursor: pointer;\n font-size: 11px;\n letter-spacing: .08em;\n color: var(--dtm-text);\n display: flex;\n align-items: center;\n gap: 6px;\n min-height: 28px;\n padding: 0 8px;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-deep);\n box-shadow: 2px 2px 0 var(--c-black);\n width: fit-content;\n}\n.dtm-advanced > summary:active { transform: translate(2px,2px); box-shadow: none; }\n.dtm-advanced > summary::-webkit-details-marker { display: none; }\n.dtm-advanced > summary::before { content: \"\u25B6\"; font-size: 9px; color: var(--dtm-accent); }\n.dtm-advanced[open] > summary::before { content: \"\u25BC\"; }\n.dtm-advanced[open] > summary { border-color: var(--dtm-primary); color: var(--dtm-primary); }\n\n/* \u2500\u2500\u2500 \u5E83\u5E45\u62E1\u5F35 \u2500\u2500\u2500 */\n@media (min-width: 768px) {\n .dtm-daw { gap: 8px; padding: 10px; }\n .dtm-roll { height: 420px; }\n}\n\n/* ====================================================\n MML PLAYER \u2014 \u518D\u751F\u5C02\u7528\u30D3\u30E5\u30FC\uFF08mountMmlPlayer\uFF09\n ==================================================== */\n.dtm-player {\n display: flex;\n flex-direction: column;\n gap: var(--dtm-gap);\n padding: var(--dtm-gap);\n background: var(--dtm-deep);\n border: 2px solid var(--dtm-border2);\n box-shadow: 4px 4px 0 var(--c-black);\n}\n.dtm-player-message {\n padding: 4px 8px;\n background: var(--c-purple);\n color: var(--c-yellow);\n font-size: 11px;\n border: 2px solid var(--c-black);\n box-shadow: inset 0 -2px 0 rgba(0,0,0,0.2);\n font-family: var(--dtm-font);\n text-align: center;\n width: 100%;\n box-sizing: border-box;\n}\n.dtm-player-head {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n.dtm-player-play {\n flex: 0 0 auto;\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--dtm-primary);\n color: var(--dtm-pfg);\n border: 2px solid var(--c-black);\n box-shadow: 2px 2px 0 var(--c-black);\n cursor: pointer;\n padding: 0;\n}\n.dtm-player-play:active { transform: translate(2px, 2px); box-shadow: none; }\n.dtm-player-play--stop { background: var(--dtm-danger); }\n.dtm-player-play:disabled { opacity: 0.4; cursor: default; }\n.dtm-player-beat-row {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n.dtm-player-beat-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--dtm-border2);\n transition: background 0.06s;\n}\n.dtm-player-beat-dot--on { background: var(--dtm-primary); }\n.dtm-player-bar {\n font-family: 'k8x12', monospace;\n font-size: 11px;\n color: var(--dtm-text);\n min-width: 2em;\n margin-left: 4px;\n}\n.dtm-player-chord {\n font-family: 'k8x12', monospace;\n font-size: 11px;\n color: var(--dtm-accent);\n min-width: 4em;\n margin-left: 8px;\n font-weight: bold;\n}\n.dtm-player-seek-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.dtm-player-seek {\n --fill: 0%;\n flex: 1 1 auto;\n min-width: 0;\n height: 12px;\n margin: 0;\n background: transparent;\n cursor: pointer;\n -webkit-appearance: none;\n appearance: none;\n}\n.dtm-player-seek:disabled { cursor: default; opacity: 0.4; }\n/* \u518D\u751F\u6E08\u307F\u533A\u9593\u3092YouTube\u98A8\u306B\u8272\u5857\u308A\u3059\u308B\uFF08--fill \u306F\u518D\u751F\u4F4D\u7F6E\u306B\u5FDC\u3058\u3066JS\u304B\u3089\u8A2D\u5B9A\uFF09 */\n.dtm-player-seek::-webkit-slider-runnable-track {\n height: 6px;\n background: linear-gradient(\n to right,\n var(--dtm-primary) var(--fill),\n var(--dtm-border2) var(--fill)\n );\n border: 2px solid var(--c-black);\n}\n.dtm-player-seek::-moz-range-track {\n height: 6px;\n background: var(--dtm-border2);\n border: 2px solid var(--c-black);\n}\n.dtm-player-seek::-moz-range-progress {\n height: 6px;\n background: var(--dtm-primary);\n border: 2px solid var(--c-black);\n border-right: none;\n}\n.dtm-player-seek::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 16px;\n margin-top: -7px;\n background: var(--dtm-primary);\n border: 2px solid var(--c-black);\n box-shadow: 2px 2px 0 var(--c-black);\n cursor: pointer;\n}\n.dtm-player-seek::-moz-range-thumb {\n width: 12px;\n height: 16px;\n background: var(--dtm-primary);\n border: 2px solid var(--c-black);\n box-shadow: 2px 2px 0 var(--c-black);\n cursor: pointer;\n border-radius: 0;\n}\n.dtm-player-time {\n flex: 0 0 auto;\n font-family: 'k8x12', monospace;\n font-size: 11px;\n color: var(--dtm-text);\n white-space: nowrap;\n}\n.dtm-player-dots {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.dtm-player-dot { width: 8px; height: 8px; display: inline-block; }\n.dtm-player-mml-header {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n.dtm-player-mml-link {\n font-family: 'k8x12', monospace;\n font-size: 10px;\n color: var(--dtm-muted);\n text-decoration: none;\n white-space: nowrap;\n}\n.dtm-player-mml-link:hover { color: var(--dtm-primary); }\n.dtm-player-more-container {\n position: relative;\n display: inline-flex;\n align-items: center;\n}\n.dtm-player-more-btn {\n background: transparent;\n border: none;\n color: var(--dtm-muted);\n cursor: pointer;\n padding: 2px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n width: 24px;\n height: 24px;\n transition: color 0.15s, background-color 0.15s;\n}\n.dtm-player-more-btn:hover,\n.dtm-player-more-btn.is-active {\n color: var(--dtm-text);\n background: var(--dtm-border2);\n}\n.dtm-player-menu {\n position: absolute;\n top: calc(100% + 4px);\n right: 0;\n background: var(--dtm-deep);\n border: 2px solid var(--dtm-border2);\n box-shadow: 4px 4px 0 var(--c-black);\n z-index: 200;\n display: flex;\n flex-direction: column;\n padding: 4px 0;\n min-width: 130px;\n font-family: var(--dtm-font);\n}\n.dtm-player-menu-item {\n background: transparent;\n border: none;\n color: var(--dtm-text);\n padding: 6px 12px;\n text-align: left;\n cursor: pointer;\n font-size: 11px;\n font-family: inherit;\n white-space: nowrap;\n width: 100%;\n box-sizing: border-box;\n transition: background-color 0.1s, color 0.1s;\n}\n.dtm-player-menu-item:hover {\n background: var(--dtm-primary);\n color: var(--dtm-pfg);\n}\n.dtm-player-emoji {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n font-size: 18px;\n line-height: 1;\n user-select: none;\n}\n.dtm-player-balloon {\n /* body \u76F4\u4E0B\u306B\u751F\u6210\u3055\u308C\u3001JS(positionBalloon)\u304C\u6BCE\u56DE left/top \u3092\u30A2\u30F3\u30AB\u30FC\u306E\u753B\u9762\u5EA7\u6A19\u3067\n \u8A2D\u5B9A\u3059\u308B\u3002overflow:hidden \u306A\u7956\u5148\uFF08\u6295\u7A3F\u30AB\u30FC\u30C9\u7B49\uFF09\u306B\u30AF\u30EA\u30C3\u30D7\u3055\u308C\u306A\u3044\u3088\u3046 fixed \u3067\u6D6E\u304B\u305B\u308B\u3002\n top \u306F\u30A2\u30F3\u30AB\u30FC\u4E0A\u7AEF-6px\u3001transform \u3067\u81EA\u8EAB\u306E\u5DE6\u4E0B\u89D2\u3092\u305D\u306E\u70B9\u306B\u5408\u308F\u305B\u308B\u3002 */\n position: fixed;\n left: 0;\n top: 0;\n transform: translate(-50%, -100%);\n z-index: 1000;\n display: none;\n pointer-events: none;\n font-family: var(--dtm-font);\n font-size: 9px;\n color: var(--c-black);\n background: var(--c-white);\n border: 2px solid var(--c-black);\n padding: 2px 4px;\n white-space: nowrap;\n box-shadow: 2px 2px 0 var(--c-black);\n}\n.dtm-player-balloon::after {\n content: \"\";\n position: absolute;\n bottom: -6px;\n left: 50%;\n transform: translateX(-50%) rotate(45deg);\n width: 8px;\n height: 8px;\n background: var(--c-white);\n border-right: 2px solid var(--c-black);\n border-bottom: 2px solid var(--c-black);\n}\n/* \u30A2\u30A4\u30B3\u30F3\u304C\u753B\u9762\u4E0A\u7AEF\u306B\u8FD1\u304F\u4E0A\u306B\u51FA\u3059\u4F59\u767D\u304C\u7121\u3044\u5834\u5408\u306F\u4E0B\u306B\u53CD\u8EE2\u8868\u793A\u3059\u308B\uFF08positionBalloon \u304C\u4ED8\u4E0E\uFF09 */\n.dtm-player-balloon--below {\n transform: translate(-50%, 0);\n}\n.dtm-player-balloon--below::after {\n bottom: auto;\n top: -6px;\n border-right: none;\n border-bottom: none;\n border-left: 2px solid var(--c-black);\n border-top: 2px solid var(--c-black);\n}\n.dtm-player-balloon--visible {\n display: block;\n animation: dtm-balloon-fade-in 0.1s steps(2);\n}\n.dtm-player-balloon--below.dtm-player-balloon--visible {\n animation-name: dtm-balloon-fade-in-below;\n}\n@keyframes dtm-balloon-fade-in {\n from { opacity: 0; transform: translate(-50%, -100%) translateY(4px); }\n to { opacity: 1; transform: translate(-50%, -100%) translateY(0); }\n}\n@keyframes dtm-balloon-fade-in-below {\n from { opacity: 0; transform: translate(-50%, 0) translateY(-4px); }\n to { opacity: 1; transform: translate(-50%, 0) translateY(0); }\n}\n@keyframes dtm-emoji-jump {\n 0% { transform: translateY(0); }\n 35% { transform: translateY(-5px); }\n 65% { transform: translateY(-5px); }\n 100% { transform: translateY(0); }\n}\n.dtm-player-emoji--jump {\n animation: dtm-emoji-jump 0.18s ease-out forwards;\n}\n.dtm-player-chip {\n font-family: 'k8x12', monospace;\n font-size: 9px;\n color: var(--dtm-text);\n background: var(--dtm-border2);\n padding: 2px 6px;\n white-space: nowrap;\n}\n.dtm-player-body {\n position: relative; /* \u30ED\u30FC\u30C7\u30A3\u30F3\u30B0\u30AA\u30FC\u30D0\u30FC\u30EC\u30A4\u306E\u57FA\u6E96\u3002\u30EC\u30FC\u30F3\u7FA4\u3060\u3051\u3092\u8986\u3046 */\n display: flex;\n flex-direction: column;\n gap: var(--dtm-gap);\n}\n.dtm-player-lane-row {\n position: relative;\n display: flex;\n align-items: stretch;\n gap: 6px;\n}\n.dtm-player-lane-label {\n position: relative;\n flex: 0 0 auto;\n width: 16px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n}\n.dtm-player-lane-label--btn {\n cursor: pointer;\n user-select: none;\n}\n.dtm-player-lane-label--btn:hover { opacity: 0.7; }\n.dtm-player-lane-label--muted { opacity: 0.3; }\n\n/* \u2500\u2500\u2500 \u30DF\u30E5\u30FC\u30C8\u8868\u793A\uFF08\u6392\u4ED6\u540C\u671F\uFF09 \u2500\u2500\u2500 */\n.dtm-player-emoji.is-muted,\n.dtm-player-lane-label.is-muted {\n position: relative;\n}\n\n/* \u30DF\u30E5\u30FC\u30C8\u6642\u306E\u300C\u00D7\u300D\u30DE\u30FC\u30AF\u91CD\u306D\u63CF\u304D */\n.dtm-player-emoji.is-muted::before,\n.dtm-player-lane-label.is-muted::before {\n content: \"\u00D7\";\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--dtm-danger);\n font-family: var(--dtm-font);\n font-size: 16px;\n font-weight: bold;\n z-index: 10;\n pointer-events: none;\n text-shadow: 1px 1px 0 var(--c-black);\n}\n\n.dtm-player-lane-label.is-muted::before {\n font-size: 14px;\n}\n\n/* \u30DF\u30E5\u30FC\u30C8\u6642\u306E\u30A2\u30A4\u30B3\u30F3\u3084\u8981\u7D20\u306E\u8584\u6697\u5316\uFF08\u5439\u304D\u51FA\u3057\u306F\u9664\u5916\uFF09 */\n.dtm-player-emoji.is-muted > img,\n.dtm-player-emoji.is-muted > span:not(.dtm-player-balloon) {\n opacity: 0.25;\n filter: grayscale(80%);\n}\n\n.dtm-player-lane-label.is-muted {\n opacity: 0.25;\n}\n\n/* \u30DF\u30E5\u30FC\u30C8\u6642\u306E\u30C8\u30E9\u30C3\u30AF\u30EC\u30FC\u30F3\uFF08\u30B9\u30AF\u30ED\u30FC\u30EB\u90E8\uFF09\u306E\u8584\u6697\u5316\u3068\u30C7\u30AB\u00D7\u30DE\u30FC\u30AF\uFF08\u8272\u5F31\u5BFE\u5FDC\uFF09 */\n.dtm-player-lane-row.is-muted::after {\n content: \"\u00D7\";\n position: absolute;\n top: 0;\n bottom: 0;\n left: 22px; /* label width (16px) + gap (6px) */\n right: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--dtm-danger);\n font-family: var(--dtm-font);\n font-size: 24px;\n font-weight: bold;\n background: rgba(0, 0, 0, 0.45);\n z-index: 10;\n pointer-events: none;\n text-shadow: 1px 1px 0 var(--c-black);\n}\n.dtm-player-lane-no {\n font-family: 'k8x12', monospace;\n font-size: 9px;\n color: var(--dtm-muted);\n}\n.dtm-player-lane {\n position: relative; /* \u30C8\u30FC\u30AF\u30F3\u306E offsetParent \u3092\u30EC\u30FC\u30F3\u306B\u56FA\u5B9A\u3057\u3001\u4E2D\u592E\u5BC4\u305B\u8A08\u7B97\u3092\u6B63\u3059 */\n flex: 1 1 auto;\n overflow-x: auto;\n overflow-y: hidden;\n white-space: nowrap;\n background: var(--c-black);\n border: none;\n padding: 0 6px;\n scrollbar-width: none;\n display: flex;\n align-items: center;\n}\n.dtm-player-lane::-webkit-scrollbar { display: none; }\n.dtm-tk {\n font-family: 'k8x12', monospace;\n font-size: 12px;\n color: var(--dtm-text);\n flex: 0 0 auto;\n}\n.dtm-tk--rest { color: var(--dtm-muted); }\n.dtm-tk--octave,\n.dtm-tk--shift,\n.dtm-tk--length,\n.dtm-tk--ctrl { color: var(--dtm-border2); }\n.dtm-tk--lyric { color: var(--dtm-text); letter-spacing: 1px; }\n/* \u7D99\u7D9A\uFF08\u30FC / \u301C\uFF09\u306F\u300C\u8A00\u3044\u76F4\u3055\u306A\u3044\u300D\u97F3\u3002\u304B\u306A\u3088\u308A\u6DE1\u304F\u3057\u3066\u97F3\u7BC0\u306E\u5207\u308C\u76EE\u3092\u76EE\u3067\u8FFD\u3048\u308B\u3088\u3046\u306B\u3059\u308B */\n.dtm-tk--lyric-tie { color: var(--dtm-muted); }\n/* \u4FC3\u97F3\uFF08\u3063\uFF09\u30FB\u4F11\u7B26\uFF08_\uFF09\u306F\u6B4C\u308F\u306A\u3044\u30CE\u30FC\u30C8\u3002\u3055\u3089\u306B\u843D\u3068\u3057\u3066\u7121\u97F3\u3067\u3042\u308B\u3053\u3068\u3092\u793A\u3059 */\n.dtm-tk--lyric-stop,\n.dtm-tk--lyric-rest { color: var(--dtm-border2); }\n.dtm-tk--break { color: var(--dtm-muted); opacity: 0.7; margin: 0 2px; }\n.dtm-tk--meta { color: var(--dtm-border2); margin-right: 4px; }\n.dtm-tk.is-active {\n background: var(--tk, var(--dtm-primary));\n color: var(--c-black);\n font-weight: bold;\n}\n\n/* \u2500\u2500\u2500 \u30B3\u30FC\u30C9\u9032\u884C\u30D7\u30EC\u30A4\u30E4\u30FC \u2500\u2500\u2500 */\n/* MML\u518D\u751F\u5C02\u7528\u30D7\u30EC\u30A4\u30E4\u30FC\uFF08.dtm-player\uFF09\u3068\u67A0\u30C7\u30B6\u30A4\u30F3\u3092\u63C3\u3048\u308B */\n.dtm-chord-player {\n display: flex;\n flex-direction: column;\n gap: var(--dtm-gap);\n padding: var(--dtm-gap);\n font-family: var(--dtm-font);\n font-size: 13px;\n letter-spacing: .06em;\n color: var(--dtm-text);\n background: var(--dtm-deep);\n border: 2px solid var(--dtm-border2);\n box-shadow: 4px 4px 0 var(--c-black);\n image-rendering: pixelated;\n -webkit-font-smoothing: none;\n}\n.dtm-chord-player *,\n.dtm-chord-player *::before,\n.dtm-chord-player *::after { box-sizing: border-box; }\n\n/* \u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u30D0\u30FC \u2014 MML\u518D\u751F\u5C02\u7528\u30D7\u30EC\u30A4\u30E4\u30FC\u306E .dtm-player-head \u3068\u63C3\u3048\u308B */\n.dtm-cp-ctrl {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n}\n.dtm-cp-ctrl .dtm-player-more-container {\n margin-left: auto;\n}\n\n/* \u30EB\u30FC\u30D7\u5207\u66FF\u30DC\u30BF\u30F3 */\n.dtm-cp-loop {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 2px solid var(--dtm-border2);\n background: var(--dtm-bg);\n color: var(--dtm-muted);\n cursor: pointer;\n flex: 0 0 auto;\n}\n.dtm-cp-loop:active { transform: translate(1px,1px); }\n.dtm-cp-loop--on {\n border-color: var(--c-black);\n background: var(--dtm-gold);\n color: var(--c-black);\n box-shadow: 2px 2px 0 var(--c-black);\n}\n\n/* \u30E1\u30BF\u60C5\u5831 (BPM) */\n.dtm-cp-meta {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .12em;\n color: var(--dtm-muted);\n}\n\n/* \u30B9\u30AF\u30ED\u30FC\u30EB\u30A8\u30EA\u30A2 */\n.dtm-cp-scroll {\n position: relative;\n max-height: 180px;\n overflow-y: auto;\n padding: 8px 10px 10px;\n scrollbar-width: none;\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n.dtm-cp-scroll::-webkit-scrollbar { display: none; }\n\n/* \u30BB\u30AF\u30B7\u30E7\u30F3\u898B\u51FA\u3057 \u2014 \u30BB\u30AF\u30B7\u30E7\u30F3\u8272\u306E\u5E2F\u3068\u30D4\u30AF\u30BB\u30EB\u30DE\u30FC\u30AB\u30FC */\n.dtm-cp-section {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: .14em;\n margin-top: 8px;\n padding: 1px 8px 1px 5px;\n color: var(--cp-fg, var(--dtm-text));\n border-left: 4px solid var(--cp-fg, var(--dtm-text));\n background: linear-gradient(90deg, var(--cp-bg, transparent), transparent);\n align-self: flex-start;\n}\n.dtm-cp-section::before {\n content: \"\u25A0\";\n margin-right: 5px;\n}\n\n/* \u5C0F\u7BC0\u884C */\n.dtm-cp-bar {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 2px;\n margin-top: 2px;\n}\n\n/* \u5C0F\u7BC0\u533A\u5207\u308A\u30D1\u30A4\u30D7 */\n.dtm-cp-pipe {\n font-size: 13px;\n color: rgba(255, 255, 255, 0.35);\n font-family: var(--dtm-font);\n font-weight: bold;\n margin: 0 4px;\n user-select: none;\n}\n\n/* \u30B3\u30FC\u30C9\u30BB\u30EB \u2014 \u30B2\u30FC\u30E0\u306E\u30DC\u30BF\u30F3\u98A8\u30C1\u30C3\u30D7\uFF08\u30BF\u30C3\u30D7\u3067\u305D\u3053\u304B\u3089\u518D\u751F\uFF09 */\n.dtm-cp-chord {\n display: inline-block;\n font-family: var(--dtm-font);\n font-size: 12px;\n padding: 2px 5px;\n border: 2px solid transparent;\n color: var(--cp-fg, var(--dtm-text));\n background: var(--cp-bg, transparent);\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n}\n.dtm-cp-chord:not(.dtm-cp-chord--dead):hover {\n border-color: var(--cp-fg, var(--dtm-text));\n}\n.dtm-cp-chord:not(.dtm-cp-chord--dead):active {\n transform: translate(1px,1px);\n}\n.dtm-cp-chord:focus-visible {\n outline: 2px solid var(--c-white);\n outline-offset: 0;\n}\n.dtm-cp-chord--dead { cursor: default; }\n.dtm-cp-chord--played { opacity: .45; }\n.dtm-cp-chord--active {\n border-color: var(--c-black);\n color: var(--c-black);\n background: var(--cp-fg, var(--dtm-primary));\n box-shadow: 2px 2px 0 var(--c-black);\n opacity: 1;\n}\n";
2462
3389
  /**
2463
3390
  * スタイルを document.head に一度だけ注入する。
2464
3391
  */
@@ -2493,7 +3420,7 @@ declare const showLoadingOverlay: (container: HTMLElement, options?: {
2493
3420
  * ピッチ(units) → 周波数(Hz)。A4 = 2139 units = 440Hz 基準。
2494
3421
  * 単位は 1/372オクターブの整数(`tuning.ts` 参照)。12平均律・31平均律とも同じ式で鳴る。
2495
3422
  */
2496
- declare const freqFromPitch: (pitchUnits: number) => number;
3423
+ declare const freqFromPitch: (pitchUnits: Units) => number;
2497
3424
  type Synth = {
2498
3425
  /** メロディックノートを発音する(PlayNoteEvent.when は ctx.currentTime からの相対秒) */
2499
3426
  playNote: (e: PlayNoteEvent) => void;
@@ -2519,122 +3446,6 @@ type SynthTone = {
2519
3446
  */
2520
3447
  declare const createSynth: (ctx: AudioContext, destination?: AudioNode, tone?: SynthTone) => Synth;
2521
3448
 
2522
- /**
2523
- * 音律(12平均律 / 31平均律)と、ピッチの内部表現。
2524
- *
2525
- * ## 単位
2526
- *
2527
- * ノートのピッチは **1/372オクターブの整数** で持つ。372 = 12 × 31 で、12と31は
2528
- * 互いに素なので最小公倍数がこれになり、**12平均律と31平均律の両方が誤差ゼロで
2529
- * 同じ数直線に乗る**。
2530
- *
2531
- * 12平均律 1半音 = 31 units
2532
- * 31平均律 1度 = 12 units
2533
- * 1 unit = 1200/372 ≒ 3.2258 セント
2534
- *
2535
- * 整数のままなので `a.pitchUnits === b.pitchUnits` の同値判定が全部生き残る
2536
- * (重複判定・当たり判定・協調編集の (startStep, pitchUnits) キー)。小数にすると
2537
- * ここが軒並み壊れるため、細かい整数単位を選んでいる。
2538
- *
2539
- * ## 音律は音声経路に入らない
2540
- *
2541
- * ピッチが絶対値なので、シーケンサも発音器も音律を知らなくてよい。音律は
2542
- * 「格子・記譜・表示」だけに効く編集上の概念で、`RenderConfig.edo` に置く。
2543
- */
2544
- /** 1オクターブあたりの units。12 × 31。 */
2545
- declare const UNITS_PER_OCTAVE = 372;
2546
- /** 12平均律の1半音あたりの units。 */
2547
- declare const UNITS_PER_SEMITONE: number;
2548
- /** 31平均律の1度あたりの units。 */
2549
- declare const UNITS_PER_EDO31_DEGREE: number;
2550
- /** 1 unit のセント値。 */
2551
- declare const CENTS_PER_UNIT: number;
2552
- /** A4 (MIDI 69) の units。周波数計算の基準。 */
2553
- declare const A4_UNITS: number;
2554
- /** A4 の周波数(Hz)。 */
2555
- declare const A4_HZ = 440;
2556
- /** 対応する音律(1オクターブの分割数)。 */
2557
- type Edo = 12 | 31;
2558
- /** 既定の音律。宣言のない曲はすべてこれ(=既存データは素通り)。 */
2559
- declare const DEFAULT_EDO: Edo;
2560
- /** その音律の1格子ステップが何 units か。 */
2561
- declare const unitsPerStep: (edo: Edo) => number;
2562
- /** MIDIノート番号 → units。 */
2563
- declare const midiToUnits: (midi: number) => number;
2564
- /**
2565
- * units → MIDIノート番号(小数)。
2566
- * 歌声合成(koe)は Hz を受けるためこの小数値をそのまま渡してよい。
2567
- * SoundFont のように整数ゾーンしか持たない発音器は {@link unitsToMidiDetune} を使う。
2568
- */
2569
- declare const unitsToMidi: (units: number) => number;
2570
- /** units → 周波数(Hz)。 */
2571
- declare const unitsToHz: (units: number) => number;
2572
- /**
2573
- * units → 「最寄りの整数MIDIノート番号 + セント補正」。
2574
- *
2575
- * SoundFont は音高ごとに整数キーでゾーンを持つため小数ピッチを直接鳴らせない。
2576
- * 最寄りのゾーンを鳴らして残差を `AudioBufferSourceNode.detune` で補正する。
2577
- * 31平均律での残差は最大 ±48.4 セントで、detune の可動域に十分収まる。
2578
- */
2579
- declare const unitsToMidiDetune: (units: number) => {
2580
- midi: number;
2581
- detuneCents: number;
2582
- };
2583
- /**
2584
- * 臨時記号1つあたりの格子ステップ数。
2585
- *
2586
- * - `#` / `+`(12平均律) … クロマチック半音上げ
2587
- * - `-` … クロマチック半音下げ
2588
- * - `+`(31平均律) … 格子1ステップ上げ(微分音)
2589
- * - `_` … 格子1ステップ下げ(微分音)
2590
- *
2591
- * 12平均律ではクロマチック半音=1格子ステップなので4記号すべてが従来の意味に潰れ、
2592
- * 既存MMLの解釈は1文字も変わらない。31平均律でのみ `#`/`-`(±2度)と
2593
- * `+`/`_`(±1度)が分岐する。
2594
- */
2595
- declare const chromaticStep: (edo: Edo) => number;
2596
- /** 微分音記号 `+` / `_` 1つあたりの格子ステップ数。常に1。 */
2597
- declare const MICRO_STEP = 1;
2598
- /** 幹音の文字か。 */
2599
- declare const isNaturalLetter: (ch: string) => boolean;
2600
- /** 幹音の文字 → オクターブ内の格子ステップ。未知の文字は null。 */
2601
- declare const naturalStep: (ch: string, edo: Edo) => number | null;
2602
- /**
2603
- * 音名・オクターブ・臨時記号から units を組み立てる。
2604
- *
2605
- * `octave` はMMLの `o` 指定(MIDI慣習で o4 の c が中央ド = MIDI 60)。
2606
- * 臨時記号でオクターブを跨ぐ綴り(`b##` が次オクターブへ、`c--` が前オクターブへ)も
2607
- * そのまま加算されるので、度数を 0〜edo-1 にクランプしてはいけない。
2608
- */
2609
- declare const spellingToUnits: (letter: string, octave: number, chromatic: number, micro: number, edo: Edo) => number | null;
2610
- /**
2611
- * 五度圏インデックス(C=0, G=1, F=-1, C#=7, Db=-5)→ オクターブ内の格子ステップ。
2612
- *
2613
- * ミーントーン系の音律はすべて「五度を何ステップとするか」だけで決まるため、
2614
- * 綴りを持つ音(chord-parser の `rootFifth` / `noteFifths` など)はこの一本で
2615
- * どちらの音律へも正確に写せる。12平均律では C# と Db が同じ値に潰れ、
2616
- * 31平均律では 2 と 3 に分かれる。
2617
- */
2618
- declare const fifthToStep: (fifthIndex: number, edo: Edo) => number;
2619
- /** 五度圏インデックス → オクターブ内の units。 */
2620
- declare const fifthToUnits: (fifthIndex: number, edo: Edo) => number;
2621
- /**
2622
- * ピッチ表現のバージョン。
2623
- *
2624
- * - v1 … `pitch` が半音(MIDIノート番号)。dtm 1.x
2625
- * - v2 … `pitchUnits` が 1/372オクターブ単位。dtm 2.x
2626
- *
2627
- * dtm 自身は通信路を持たず `onNotesPatch` / `applyPatch` のフックを出すだけなので、
2628
- * バージョンの突き合わせは利用側アプリのハンドシェイクの責務になる。変換の知識だけを
2629
- * ここに置く。v1→v2 は無損失だが、**v2→v1 は12平均律の曲でしか成立しない**
2630
- * (31平均律を半音へ丸めると最大48.4セント動く)。
2631
- */
2632
- declare const PITCH_ENCODING_VERSION = 2;
2633
- /** v1(半音)→ v2(units)。無損失。 */
2634
- declare const pitchV1ToUnits: (pitch: number) => number;
2635
- /** v2(units)→ v1(半音)。12平均律の曲でのみ無損失。 */
2636
- declare const unitsToPitchV1: (units: number) => number;
2637
-
2638
3449
  declare const VOICE_IMAGES: Record<string, string>;
2639
3450
 
2640
3451
  /**
@@ -2650,4 +3461,4 @@ declare const encodeWavPCM16: (channels: Float32Array[], sampleRate: number) =>
2650
3461
  /** Float32Array のチャンクを1本へ連結する。 */
2651
3462
  declare const concatFloat32: (chunks: Float32Array[]) => Float32Array;
2652
3463
 
2653
- export { A4_HZ, A4_UNITS, type AddNoteOptions, type AnyDrumPattern, type ApplyChordOptions, CENTS_PER_UNIT, type ChordPatternType, type ChordPlacement, type ChordPlayerInstance, type ConsumedSyllable, type CoreEventHandlers, type CustomVocalDef, DAW_CSS, DEFAULT_BPM, DEFAULT_EDO, DEFAULT_GATE, DEFAULT_PAN, DEFAULT_PLAYBACK_VELOCITY, DEFAULT_STEPS_PER_BAR, DEFAULT_VELOCITY, DEFAULT_VOCAL_VOLUME, DRUM_FONT, DRUM_KEYS, DRUM_PATTERNS, type DawInstance, type DawMode, type DawOptions, type DawViewState, type DrumPattern, type DrumPatternDef, type DtmStudio, type DtmStudioEngines, type DtmStudioOptions, EDO31_NAMES, type Edo, type ExportMidiOptions, type FadeScheduleParams, GM_INSTRUMENT_NAMES, INSTRUMENT_PRESETS, type InstrumentPreset, KEY_COUNT, KOE_BASE_URL, KOE_VOICEBANKS, KOE_VOICEBANK_LABELS, KOE_VOICEBANK_TERMS, type KoeVoiceOptions, LinkedList, type LoopConfig, type LoopPoint, type LyricSyllable, type LyricSyncData, type LyricTrack, type LyricsConductor, MAX_VOCAL_VOLUME, MICRO_STEP, MMLCore, type MMLDisplayToken, type MMLNotePlacement, MML_END_MARKER, type MidiExtraction, type MidiNotePlacement, MidiSearchClient, type MidiSearchConfig, type MidiTrackAnalysis, type MmlMeta, type MmlPlayback, type MmlPlayerInstance, type MmlPlayerOptions, type ModeSwitchInstance, type ModeSwitchOptions, type MountChordPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, type NoteData, type NoteRemove, type OctaveUnisonMode, PITCH_ENCODING_VERSION, PITCH_MAP, PITCH_RANGE_END, PITCH_RANGE_START, PREWARM_NOTES, type ParseMMLOptions, type ParseMidiFn, type ParsedMML, type PianoRollInstance, type PianoRollOptions, type PicotuneSearchParams, type PicotuneSong, type PitchToken, type PlayChordsOptions, type PlayDrumEvent, type PlayMmlOptions, type PlayNoteEvent, type PlayNoteOptions, type PlayPlacementsOptions, type PlaySingingMmlOptions, type PlaybackCue, type PlaybackState, type PresetSelectInstance, type PresetSelectOptions, type PreviewSoundCallback, type RenderConfig, type Renderer, type Sequencer, type SequencerOptions, type SequencerTrack, type SingingVoices, type SingingVoicesOptions, type StreamPlaybackOptions, type StreamVoiceNote, type StreamVoiceTrack, type Synth, TRACKS_ADVANCED, TRACKS_SIMPLE, type ToolMode, type TrackConfig, UNITS_PER_EDO31_DEGREE, UNITS_PER_OCTAVE, UNITS_PER_SEMITONE, VIBRATO_MIN_SEC, VOICE_IMAGES, VOICE_IMAGE_KEY, type VoiceExpression, type VoiceModel, type VoiceRegistry, analyzeMidiTracks, applyHarmonicFilter, applyMonophonic, buildChordPlacements, buildDrumPatternJson, buildNameToKeyMapping, chromaticStep, collectPitchTokens, concatFloat32, createAudioContext, createDtmStudio, createKlattVoice, createKoeVoice, createLyricsConductor, createPianoRoll, createRenderer, createSequencer, createSingingVoices, createSynth, createVoiceRegistry, decodeMml, decomposeToMonophonic, encodeMml, encodeWavPCM16, exportMIDI, extractDrumPatternFromNotes, extractMidiDrumPattern, extractMidiPlacements, extractMidiPlacementsByTrack, fifthToStep, fifthToUnits, formatMmlMeta, freqFromPitch, generateRandomPattern, getDrumPatternKeys, getMidiBPM, icon, injectStyles, isChordHeavyTrack, isNaturalLetter, isPlausibleMidiTranscription, isValidHttpUrl, keyCountFor, koeUrl, midiToUnits, mountChordPlayer, mountDAW, mountMmlPlayer, naturalStep, normalizeDrumPatterns, normalizeLyrics, panToStereo, parseCustomVocals, parseLyrics, parseMML, parseMmlMeta, pitchV1ToUnits, playChords, playMML, playNote, playPlacements, playSingingMML, resolveDrumPattern, resolveLoopPoint, shiftNotes, showLoadingOverlay, spellingToUnits, stripCustomVocals, stripLyrics, stripMmlMeta, transposeNotes, unitsPerRow, unitsPerStep, unitsToHz, unitsToMidi, unitsToMidiDetune, unitsToPitchV1, vocalVolumeToGain };
3464
+ export { A4_HZ, A4_UNITS, type AddNoteOptions, type AnyDrumPattern, type ApplyChordOptions, BREATH_MARK, type Band, CENTS_PER_UNIT, CORPUS_BANDS, CORPUS_SIZE, type ChordPatternType, type ChordPlacement, type ChordPlayerInstance, type ComposeOptions, type ComposeResult, type ComposeStats, type ComposedNote, type ConsumedSyllable, type CoreEventHandlers, type CustomVocalDef, DAW_CSS, DEFAULT_BPM, DEFAULT_EDO, DEFAULT_GATE, DEFAULT_PAN, DEFAULT_PLAYBACK_VELOCITY, DEFAULT_SECTIONS, DEFAULT_STEPS_PER_BAR, DEFAULT_VELOCITY, DEFAULT_VOCAL_VOLUME, DRUM_FONT, DRUM_KEYS, DRUM_PATTERNS, type DawInstance, type DawMode, type DawOptions, type DawViewState, type DrumPattern, type DrumPatternDef, type DtmStudio, type DtmStudioEngines, type DtmStudioOptions, EDO31_NAMES, type Edo, type ExportMidiOptions, FADE_IN_MARK, FADE_OUT_MARK, type FadeScheduleParams, GM_INSTRUMENT_NAMES, INSTRUMENT_PRESETS, type InstrumentPreset, KEY_COUNT, KOE_BASE_URL, KOE_VOICEBANKS, KOE_VOICEBANK_LABELS, KOE_VOICEBANK_TERMS, type KoeVoiceOptions, LinkedList, type LoopConfig, type LoopPoint, type LyricSyllable, type LyricSyllableKind, type LyricSyncData, type LyricTrack, type LyricsConductor, MAX_VOCAL_VOLUME, MICRO_STEP, MMLCore, type MMLDisplayToken, type MMLNotePlacement, MML_END_MARKER, type MetricNote, type MetricOptions, type MidiExtraction, type MidiNote, type MidiNotePlacement, MidiSearchClient, type MidiSearchConfig, type MidiTrackAnalysis, type MmlMeta, type MmlPlayback, type MmlPlayerInstance, type MmlPlayerOptions, type ModeSwitchInstance, type ModeSwitchOptions, type MountChordPlayerOptions, type MountEditorOptions, type MountPlayerOptions, type Note, type NoteData, type NoteRemove, type OctaveUnisonMode, PITCH_ENCODING_VERSION, PITCH_MAP, PITCH_RANGE_END, PITCH_RANGE_START, PORTAMENTO_MARK, PREWARM_NOTES, type ParseMMLOptions, type ParseMidiFn, type ParsedMML, type PianoRollInstance, type PianoRollOptions, type PicotuneSearchParams, type PicotuneSong, type PitchSegment, type PitchToken, type PlacedSection, type PlayChordsOptions, type PlayDrumEvent, type PlayMmlOptions, type PlayNoteEvent, type PlayNoteOptions, type PlayPlacementsOptions, type PlaySingingMmlOptions, type PlaybackCue, type PlaybackState, type PresetSelectInstance, type PresetSelectOptions, type PreviewSoundCallback, REST_MARK, type RenderConfig, type Renderer, SECTION_LABELS, SECTION_ORDER, SECTION_SPECS, STOP_MARK, type SectionKind, type SectionSpec, type Sequencer, type SequencerOptions, type SequencerTrack, type SingingVoices, type SingingVoicesOptions, type StreamPlaybackOptions, type StreamVoiceNote, type StreamVoiceNoteOptions, type StreamVoiceTrack, type StructureFeatures, type Synth, TIE_MARK, TIE_MERGE_MAX_SEC, TRACKS_ADVANCED, TRACKS_SIMPLE, type TensionFeatures, type TieSourceNote, type ToolMode, type TrackConfig, UNITS_PER_EDO31_DEGREE, UNITS_PER_OCTAVE, UNITS_PER_SEMITONE, type Units, VIBRATO_MIN_SEC, VOICE_IMAGES, VOICE_IMAGE_KEY, type VoiceExpression, type VoiceModel, type VoiceRegistry, addUnits, analyzeMidiTracks, applyHarmonicFilter, applyMonophonic, band, buildChordPlacements, buildDrumPatternJson, buildNameToKeyMapping, buildSectionPlan, buildStreamVoiceNotes, chromaticStep, collectPitchTokens, complementarity, composeSong, concatFloat32, createAudioContext, createDtmStudio, createKlattVoice, createKoeVoice, createLyricsConductor, createPianoRoll, createRenderer, createSequencer, createSingingVoices, createSynth, createVoiceRegistry, decodeMml, decomposeToMonophonic, displayKana, durationEntropy, encodeMml, encodeWavPCM16, exportMIDI, extractDrumPatternFromNotes, extractMidiDrumPattern, extractMidiPlacements, extractMidiPlacementsByTrack, featureDistance, featureVector, fifthToStep, fifthToUnits, formatMmlMeta, freqFromPitch, generateRandomPattern, getDrumPatternKeys, getMidiBPM, icon, injectStyles, isChordHeavyTrack, isNaturalLetter, isPlausibleMidiTranscription, isValidHttpUrl, keyCountFor, koeUrl, midiNote, midiToUnits, mountChordPlayer, mountDAW, mountMmlPlayer, naturalStep, normalizeDrumPatterns, normalizeLyrics, panToStereo, parseCustomVocals, parseLyrics, parseMML, parseMmlMeta, pitchV1ToUnits, playChords, playMML, playNote, playPlacements, playSingingMML, resolveDrumPattern, resolveLoopPoint, semitonesToUnits, shiftNotes, showLoadingOverlay, spelledToUnits, spellingToUnits, stripCustomVocals, stripLyrics, stripMmlMeta, structureFeatures, syllablesToText, tensionFeatures, transposeNotes, units, unitsPerRow, unitsPerStep, unitsToHz, unitsToMidi, unitsToMidiDetune, unitsToPitchV1, vocalVolumeToGain };