@onjmin/dtm 2.1.11 → 2.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +243 -0
- package/dist/index.d.mts +847 -45
- package/dist/index.d.ts +847 -45
- package/dist/index.js +9681 -612
- package/dist/index.mjs +9652 -612
- package/dist/voice-worker.js +40 -26
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,175 @@ declare const GM_INSTRUMENT_NAMES: string[];
|
|
|
22
22
|
*/
|
|
23
23
|
declare const programOfInstrumentName: (name: string) => number | null;
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* 伴奏音源(mp3 / wav / YouTube)の同時再生。
|
|
27
|
+
*
|
|
28
|
+
* 打ち込みと**同じ時計(AudioContext)**で頭を揃えるのがこのモジュールの仕事で、
|
|
29
|
+
* 音源の種類ごとに揃え方が違う:
|
|
30
|
+
*
|
|
31
|
+
* - `buffer` … デコード済みの {@link AudioBuffer} を `start(when, offset)` で予約する。
|
|
32
|
+
* サンプル単位で正確。オーディオグラフを通るのでWAV書き出し・録音にも入る。
|
|
33
|
+
* - `element` … `<audio>` 直再生。CORSが無いURLはデコードできないのでこちらへ落ちる。
|
|
34
|
+
* グラフの外で鳴るため録音には入らない。再生開始の遅延とドリフトを実測して詰める。
|
|
35
|
+
* - `youtube` … IFrame Player API。`element` と同じくグラフの外+ドリフト補正。
|
|
36
|
+
* ただし再生速度の微調整が効かない(離散値しか受け付けない)ので、ズレたら seek で直す。
|
|
37
|
+
*
|
|
38
|
+
* 「曲のどこで」「音源のどこから」鳴らすかは呼び出し側が {@link backingMediaSec} で
|
|
39
|
+
* 1つの数(再生開始時点における音源内の位置)へ畳んでから渡す。イントロを飛ばす、
|
|
40
|
+
* 曲の途中から重ねる、といった指定はすべてこの数の符号と大きさに吸収される。
|
|
41
|
+
*/
|
|
42
|
+
/** 音源の鳴らし方。URL/ファイルの種類と、デコードできたかで決まる。 */
|
|
43
|
+
type BackingMode = "buffer" | "element" | "youtube";
|
|
44
|
+
/** 読み込み結果。UIの状態表示に使う。 */
|
|
45
|
+
type BackingLoaded = {
|
|
46
|
+
mode: BackingMode;
|
|
47
|
+
/** 音源の長さ(秒)。取得できないときは0。 */
|
|
48
|
+
durationSec: number;
|
|
49
|
+
/**
|
|
50
|
+
* オーディオグラフを通るか(=マスタ音量・WAV書き出し・録音に乗るか)。
|
|
51
|
+
* `buffer` のみ true。
|
|
52
|
+
*/
|
|
53
|
+
routed: boolean;
|
|
54
|
+
/** 表示名(ファイル名・動画ID等)。 */
|
|
55
|
+
label: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* {@link BackingAudio.startRolling} の結果。
|
|
59
|
+
*
|
|
60
|
+
* `measured` が false でも**音源は鳴らし始めている**(時間内に進み始めたのを
|
|
61
|
+
* 確認できなかっただけ)。呼び出し側は改めて鳴らし直してはいけない——
|
|
62
|
+
* 止めて鳴らし直すと、立ち上がりをもう一度やり直すことになって余計に遅れる。
|
|
63
|
+
*/
|
|
64
|
+
type BackingRoll = {
|
|
65
|
+
/** 実測(または推定)した時刻。 */
|
|
66
|
+
atTime: number;
|
|
67
|
+
/** その時刻に音源がいた位置(秒)。 */
|
|
68
|
+
mediaSec: number;
|
|
69
|
+
/** 本当に進み始めたのを確認できたか。 */
|
|
70
|
+
measured: boolean;
|
|
71
|
+
};
|
|
72
|
+
/** 再生開始の指定。 */
|
|
73
|
+
type BackingStartOptions = {
|
|
74
|
+
/** 打ち込み側の再生開始時刻(`getAudioTime()` と同じ時計の絶対秒)。 */
|
|
75
|
+
atTime: number;
|
|
76
|
+
/**
|
|
77
|
+
* `atTime` の瞬間に鳴っているべき音源内の位置(秒)。
|
|
78
|
+
* {@link rangeStartSec} より手前なら「まだ鳴らさない」区間で、そこへ達した時刻から鳴り始める。
|
|
79
|
+
*/
|
|
80
|
+
mediaSec: number;
|
|
81
|
+
/** 音源を鳴らし始める位置(秒)。未指定なら頭から。ここより手前は鳴らさない。 */
|
|
82
|
+
rangeStartSec?: number;
|
|
83
|
+
/** 音源を止める位置(秒)。0/未指定なら最後まで。 */
|
|
84
|
+
endSec?: number;
|
|
85
|
+
};
|
|
86
|
+
type BackingAudio = {
|
|
87
|
+
/** 音源を読み込む。URL文字列(mp3/wav/YouTube)またはアップロードされたファイル。 */
|
|
88
|
+
load: (src: string | File) => Promise<BackingLoaded>;
|
|
89
|
+
/** 読み込み済みの音源を破棄する(再生中なら止める)。 */
|
|
90
|
+
clear: () => void;
|
|
91
|
+
/**
|
|
92
|
+
* 再生開始の直前に呼ぶ準備。YouTubeのように「鳴り始めるまで待ちが要る」音源が
|
|
93
|
+
* 目的位置をバッファするための猶予で、他の音源では何もしない。
|
|
94
|
+
*/
|
|
95
|
+
arm: (mediaSec: number) => Promise<void>;
|
|
96
|
+
start: (options: BackingStartOptions) => void;
|
|
97
|
+
/**
|
|
98
|
+
* 音源を**今すぐ**鳴らし始め、本当に音が進み出すまで待ってから、
|
|
99
|
+
* 「その瞬間の時計(`getAudioTime` と同じ絶対秒)と、そのとき音源がいた位置」を返す。
|
|
100
|
+
*
|
|
101
|
+
* 再生要求から実際に鳴り出すまでの遅れは環境依存で事前に読めない
|
|
102
|
+
* (YouTubeのバッファ、`<audio>` のデコード、初回再生のウォームアップ等)。
|
|
103
|
+
* 呼び出し側はこの実測値を見て、**打ち込み側の開始時刻を決められる**。
|
|
104
|
+
*
|
|
105
|
+
* 待ち合わせるのは**待たないと直しようがない音源だけ**(YouTube)。
|
|
106
|
+
* - デコード済み(`buffer`)… 予約がサンプル単位で正確。`null`。
|
|
107
|
+
* - `<audio>` 直再生 … 予約してから実測で詰めるほうが精度が出る(実測で数ms)。
|
|
108
|
+
* 待ち合わせると、待った時点で音源が先行してしまい、戻す手段が
|
|
109
|
+
* seek(=毎回100ms前後の立ち上がりを伴う)しか無くなる。`null`。
|
|
110
|
+
* - 時間内に鳴り始めなかった … `null`(呼び出し側は従来どおり予約で始める)。
|
|
111
|
+
*/
|
|
112
|
+
startRolling: (options: {
|
|
113
|
+
mediaSec: number;
|
|
114
|
+
rangeStartSec?: number;
|
|
115
|
+
endSec?: number;
|
|
116
|
+
/** 鳴り始めを待つ上限(秒)。既定5。 */
|
|
117
|
+
timeoutSec?: number;
|
|
118
|
+
}) => Promise<BackingRoll | null>;
|
|
119
|
+
/**
|
|
120
|
+
* 鳴らしたまま、合わせる先(時刻と音源位置の対応)だけ差し替える。
|
|
121
|
+
* {@link startRolling} の実測で打ち込み側の開始時刻がずれたときに、
|
|
122
|
+
* 音源をそこへ合わせ直すために使う。
|
|
123
|
+
*/
|
|
124
|
+
rebase: (line: {
|
|
125
|
+
atTime: number;
|
|
126
|
+
mediaSec: number;
|
|
127
|
+
}, options?: {
|
|
128
|
+
snap?: boolean;
|
|
129
|
+
}) => void;
|
|
130
|
+
stop: () => void;
|
|
131
|
+
/** 0-100。 */
|
|
132
|
+
setVolume: (volume: number) => void;
|
|
133
|
+
setMuted: (muted: boolean) => void;
|
|
134
|
+
/** 読み込み済みなら true。 */
|
|
135
|
+
isLoaded: () => boolean;
|
|
136
|
+
getLoaded: () => BackingLoaded | null;
|
|
137
|
+
destroy: () => void;
|
|
138
|
+
};
|
|
139
|
+
type BackingAudioOptions = {
|
|
140
|
+
audioContext: AudioContext;
|
|
141
|
+
/** グラフを通る音源(buffer)の接続先。通常はマスタゲイン。 */
|
|
142
|
+
destination: AudioNode;
|
|
143
|
+
/** YouTubeプレイヤーを描く枠。未指定/nullのときYouTubeは読み込めない。 */
|
|
144
|
+
getYoutubeContainer?: () => HTMLElement | null;
|
|
145
|
+
/** 読み込み・再生の失敗を利用側へ伝える(UIの状態表示用)。 */
|
|
146
|
+
onError?: (message: string) => void;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* 再生開始時点における音源内の位置(秒)を求める。
|
|
150
|
+
*
|
|
151
|
+
* 音源と打ち込みのずれは「**どちらが何秒先に始まるか**」の1つの符号付きの数
|
|
152
|
+
* ({@link BackingOffset.offsetSec})だけで決まる。
|
|
153
|
+
*
|
|
154
|
+
* - `offsetSec > 0` … 音源が先。その秒数だけ音源を鳴らしてから打ち込みが始まる。
|
|
155
|
+
* - `offsetSec < 0` … 打ち込みが先。その秒数だけ経ってから音源が鳴り出す。
|
|
156
|
+
* - `offsetSec = 0` … 同時。
|
|
157
|
+
*
|
|
158
|
+
* 戻り値が再生範囲の開始より手前なら、そこへ届くまで音源は鳴らない(負なら待ち時間)。
|
|
159
|
+
*/
|
|
160
|
+
declare const backingMediaSec: (o: BackingOffset) => number;
|
|
161
|
+
/** {@link backingMediaSec} の引数。 */
|
|
162
|
+
type BackingOffset = {
|
|
163
|
+
/** 打ち込みの再生開始ステップ。 */
|
|
164
|
+
fromStep: number;
|
|
165
|
+
/** 開始のずれ(秒)。正=音源が先、負=打ち込みが先。 */
|
|
166
|
+
offsetSec: number;
|
|
167
|
+
/** 音源の再生範囲の開始(秒)。頭のいらない部分を飛ばす。 */
|
|
168
|
+
rangeStartSec: number;
|
|
169
|
+
secondsPerStep: number;
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* `1:23.456` / `83.456` / `1:02:03` のような時間表記を秒へ直す。
|
|
173
|
+
* 読めない文字列は null(呼び出し側で入力を弾く)。
|
|
174
|
+
*/
|
|
175
|
+
declare const parseTimeSec: (text: string) => number | null;
|
|
176
|
+
/** 秒を `1:23.456` 表記へ直す(1時間以上は `1:02:03.000`)。 */
|
|
177
|
+
declare const formatTimeSec: (sec: number) => string;
|
|
178
|
+
/** YouTubeのURLから動画IDを取り出す(`youtu.be` 短縮・`/embed/`・`/shorts/` も見る)。 */
|
|
179
|
+
declare const parseYoutubeId: (url: string) => string | null;
|
|
180
|
+
/** URLがYouTubeか。 */
|
|
181
|
+
declare const isYoutubeUrl: (url: string) => boolean;
|
|
182
|
+
/**
|
|
183
|
+
* 動画IDからサムネイル画像のURLを決める(APIキーも通信の事前確認も要らない)。
|
|
184
|
+
*
|
|
185
|
+
* `maxresdefault` は**無い動画がある**(元が低解像度・古い投稿)ので、実際に
|
|
186
|
+
* 読み込んで確かめてから返す。存在しないサムネに対してYouTubeは404だけでなく
|
|
187
|
+
* 120×90のグレー画像を返すことがあるため、大きさでも弾く。
|
|
188
|
+
*
|
|
189
|
+
* 見つからなければ null。呼び出し側は「背景を出さない」を選べる。
|
|
190
|
+
*/
|
|
191
|
+
declare const resolveYoutubeThumbnail: (videoId: string) => Promise<string | null>;
|
|
192
|
+
declare const createBackingAudio: (options: BackingAudioOptions) => BackingAudio;
|
|
193
|
+
|
|
25
194
|
type MountChordPlayerOptions = {
|
|
26
195
|
/** 使用する AudioContext(省略時は内部生成) */
|
|
27
196
|
audioContext?: AudioContext;
|
|
@@ -523,11 +692,6 @@ type ComposeScale = {
|
|
|
523
692
|
* ここに無い2音は「音階の外」として、和音構成音か経過音のときだけ通す。
|
|
524
693
|
*/
|
|
525
694
|
core: number[];
|
|
526
|
-
/**
|
|
527
|
-
* 主和音が短三和音か。調性格(`COMPOSE_KEYS`)をどちら側から引くかに使う。
|
|
528
|
-
* 主音が短三和音を持つ音階に長調の調性格を当てると、説明と響きが食い違う。
|
|
529
|
-
*/
|
|
530
|
-
minorish: boolean;
|
|
531
695
|
/**
|
|
532
696
|
* **中核音の外を厳しく締めるか。**
|
|
533
697
|
*
|
|
@@ -626,20 +790,23 @@ declare const resolveCenter: (scale: ComposeScale) => TonicCenter | null;
|
|
|
626
790
|
*
|
|
627
791
|
* ## セクションごとに何を変えるか
|
|
628
792
|
*
|
|
629
|
-
* | | メロディ | 音域 | 密度 |
|
|
630
|
-
*
|
|
631
|
-
* | イントロ | 無し | — | — |
|
|
632
|
-
* | Aメロ | 有り | 低め | 控えめ |
|
|
633
|
-
* | Bメロ | 有り | 中 | 上げる |
|
|
634
|
-
* | サビ | 有り | 高い | 最大 |
|
|
635
|
-
* | Cメロ | 有り | 中高 | やや控えめ |
|
|
636
|
-
* | 落ちサビ | 有り | 高い | 薄い |
|
|
637
|
-
* | 間奏 | 無し | — | — |
|
|
638
|
-
* | アウトロ | 有り | 低め | 薄い |
|
|
793
|
+
* | | メロディ | 音域 | 密度 | 終わり方 |
|
|
794
|
+
* |---|---|---|---|---|
|
|
795
|
+
* | イントロ | 無し | — | — | — |
|
|
796
|
+
* | Aメロ | 有り | 低め | 控えめ | 半終止 |
|
|
797
|
+
* | Bメロ | 有り | 中 | 上げる | ドミナントで宙吊り |
|
|
798
|
+
* | サビ | 有り | 高い | 最大 | 主音へ全終止 |
|
|
799
|
+
* | Cメロ | 有り | 中高 | やや控えめ | 解決しない音 |
|
|
800
|
+
* | 落ちサビ | 有り | 高い | 薄い | 主音へ全終止 |
|
|
801
|
+
* | 間奏 | 無し | — | — | — |
|
|
802
|
+
* | アウトロ | 有り | 低め | 薄い | 主音へ全終止 |
|
|
639
803
|
*
|
|
640
804
|
* メロディを書かないセクション(イントロ・間奏)でも、伴奏・ベース・ドラムは鳴る。
|
|
641
805
|
* ここを「メロディが無いだけの同じ小節」にすると、結局のっぺりしたままになるので、
|
|
642
|
-
*
|
|
806
|
+
* 伴奏の奏法で差を付ける。
|
|
807
|
+
*
|
|
808
|
+
* **ドラムはセクションで変えない。** 自動作曲のドラムは曲を通して固定パターンを
|
|
809
|
+
* 選ぶ仕様なので(`compose.ts` の `pickBuiltinDrum`)、セクションごとの強度は持たない。
|
|
643
810
|
*
|
|
644
811
|
* ## テンプレートによる曲構成
|
|
645
812
|
*
|
|
@@ -666,8 +833,35 @@ declare const SECTION_ORDER: SectionKind[];
|
|
|
666
833
|
*/
|
|
667
834
|
declare const DEFAULT_SECTIONS: SectionKind[];
|
|
668
835
|
type SectionSpec = {
|
|
669
|
-
/**
|
|
836
|
+
/**
|
|
837
|
+
* 小節数の代表値。`rnd` を渡さずに {@link buildSectionPlan} を呼んだときの長さで、
|
|
838
|
+
* 「押す前に曲の長さを見せる」UI 表示などが使う。実際の作曲は
|
|
839
|
+
* {@link SectionSpec.barChoices} から seed ごとに引く。
|
|
840
|
+
*/
|
|
670
841
|
bars: number;
|
|
842
|
+
/**
|
|
843
|
+
* seed ごとに引くセクション長の候補(要素の重複が重み)。
|
|
844
|
+
*
|
|
845
|
+
* **なぜ定数をやめたか。** 長さが定数だと、BPM も調もメロディ型も引き直している
|
|
846
|
+
* のに**曲の骨格だけが全 seed で同一**になる。曲の頭からの小節割りが毎回同じなのは
|
|
847
|
+
* 生成器の指紋そのもので、参考曲と一致しているかどうか以前の問題。加えて
|
|
848
|
+
* `scripts/compare-reach.ts` の教訓(`scripts/README.md` の4番)どおり、
|
|
849
|
+
* **候補に一度も現れない長さは、採点の重みをどう変えても出てこない**。
|
|
850
|
+
*
|
|
851
|
+
* **なぜ4の倍数か。** 参考コーパス91本の主旋律を1小節以上の休みで切って
|
|
852
|
+
* ブロック長を測ると、71%が4の倍数・82%が偶数で、8/16/24/32小節に山が立つ。
|
|
853
|
+
* 加えてコード進行を4小節単位のまとまりで組んでいる(`compose.ts` の `progression`)
|
|
854
|
+
* ので、4の倍数から外れた長さは締めの4小節が途中で切れる。
|
|
855
|
+
*
|
|
856
|
+
* イントロだけは実測がはっきりしている。曲頭から主旋律が入るまでの小節数は
|
|
857
|
+
* 0小節が33%・8小節が30%・4小節が15%で、**8小節が4小節の約2倍**。曲の長さとの
|
|
858
|
+
* 相関は r=0.01 で、長い曲ほどイントロが長いという関係は無い(=曲長の関数に
|
|
859
|
+
* してはいけない)。0小節はここでは引かない。「イントロを作るか」は UI の
|
|
860
|
+
* チェックで表明されているので、チェックが付いているのに消すのは筋が違う。
|
|
861
|
+
*
|
|
862
|
+
* 省略時は {@link SectionSpec.bars} 固定(外から独自の spec を渡す場合のため)。
|
|
863
|
+
*/
|
|
864
|
+
barChoices?: number[];
|
|
671
865
|
/** メロディを書くか。イントロと間奏は伴奏だけ。 */
|
|
672
866
|
melody: boolean;
|
|
673
867
|
/**
|
|
@@ -680,8 +874,6 @@ type SectionSpec = {
|
|
|
680
874
|
* リズム型を選ぶときの「休符を含む型」の引きやすさに効く。
|
|
681
875
|
*/
|
|
682
876
|
density: number;
|
|
683
|
-
/** ドラムの強度。0=抑えめ 1=標準 2=最大。 */
|
|
684
|
-
drumLevel: 0 | 1 | 2;
|
|
685
877
|
/**
|
|
686
878
|
* セクションの終わりの着地音(主音からの音階度数)。
|
|
687
879
|
* `null` はメロディが無いセクション。
|
|
@@ -720,8 +912,28 @@ type PlacedSection = {
|
|
|
720
912
|
*
|
|
721
913
|
* 同じ種別が2回以上現れた場合、2回目以降は `restatement: true` が付く。
|
|
722
914
|
* compose側はこのフラグを見て、1回目のメロディ・リズムを再現する。
|
|
915
|
+
*
|
|
916
|
+
* `rnd` を渡すと、セクション長を {@link SectionSpec.barChoices} から引く
|
|
917
|
+
* (渡さなければ {@link SectionSpec.bars} の代表値。UI の長さ表示など、
|
|
918
|
+
* 引くたびに答えが変わっては困る場所のため)。
|
|
919
|
+
*
|
|
920
|
+
* **長さは種別ごとに1回だけ引く。** 1番のAメロが8小節で2番が16小節、という
|
|
921
|
+
* 曲は書けなくはないが、`restatement` は1番の対応小節をそのまま歌い直す仕組み
|
|
922
|
+
* なので、長さが違うと後半だけ別のフレーズになる。同じ名前のセクションは同じ
|
|
923
|
+
* 長さで揃えるほうが「同じフレーズが返ってきた」という手応えを壊さない。
|
|
924
|
+
*/
|
|
925
|
+
declare const buildSectionPlan: (kinds: SectionKind[], templateName?: string, rnd?: () => number) => PlacedSection[];
|
|
926
|
+
/**
|
|
927
|
+
* その構成で曲が何小節になりうるか(最短・最長・代表値)。
|
|
928
|
+
*
|
|
929
|
+
* セクション長を seed ごとに引くようにしたので、「押す前に何小節か」を
|
|
930
|
+
* 1つの数で見せることはできない。UI はここが返す幅を出す。
|
|
723
931
|
*/
|
|
724
|
-
declare const
|
|
932
|
+
declare const sectionPlanBarRange: (kinds: SectionKind[], templateName?: string) => {
|
|
933
|
+
min: number;
|
|
934
|
+
max: number;
|
|
935
|
+
typical: number;
|
|
936
|
+
};
|
|
725
937
|
|
|
726
938
|
/**
|
|
727
939
|
* 自動作曲マクロ。コード進行・メロディ・サブメロ・ハモリ・ベース・伴奏・パッドを
|
|
@@ -751,20 +963,22 @@ declare const buildSectionPlan: (kinds: SectionKind[], templateName?: string) =>
|
|
|
751
963
|
*
|
|
752
964
|
* ## 「中央値へ寄せる」のをやめた理由
|
|
753
965
|
*
|
|
754
|
-
* 以前は帯の内側にも中央値へ向かう傾斜を付けていた(`centeredBand
|
|
966
|
+
* 以前は帯の内側にも中央値へ向かう傾斜を付けていた(`centeredBand`。**現在は削除済み**)。全項目が帯の端へ
|
|
755
967
|
* 同時に寄った曲を弾くためだったが、**目標帯は21項目を独立に採った周辺分布**なので、
|
|
756
968
|
* その中央値は「どの曲でもない平均的な一点」でしかない。コーパス91本を自分の帯で
|
|
757
969
|
* 測り直すと、人間の曲は中央値で**10項目が帯の外**にあり、素点の中央値は 0.673——
|
|
758
970
|
* **生成物(0.851)のほうが較正元より高い点を取っていた**。選抜を完全に切っても
|
|
759
971
|
* 生成物 0.970 対 人間 0.921 で、選抜ではなく採点式そのものが中央を報酬にしていた。
|
|
760
972
|
*
|
|
761
|
-
*
|
|
762
|
-
* 「人間が書いた曲の形か」の判断は**項目の組み合わせ**——コーパス91本のうち
|
|
763
|
-
* **いちばん近い1曲との距離**(`nearestProfileDistance`、重み `typicality`)——へ渡した。
|
|
764
|
-
* 中央から離れることではなく、**人が1本も居ない場所に居ること**を罰する形にしてある。
|
|
973
|
+
* そこで周辺分布の項目は「人間の範囲に居るか」の確認だけに格下げした(`plausibleBand`)。
|
|
765
974
|
* さらに、人間の曲が普通いくつの軸を外すかを実測して {@link DEVIATION_BUDGET} とし、
|
|
766
975
|
* その本数だけは減点を見逃す。検算は `scripts/check-evaluator.ts`。
|
|
767
976
|
*
|
|
977
|
+
* **コーパスへの「近さ」は採点していない。** 一時期は「いちばん近い1曲との距離」
|
|
978
|
+
* (`nearestProfileDistance`、重み `typicality`)を入れていたが、罰の半径を広げた
|
|
979
|
+
* だけで同じ誤りだった——理由は {@link WEIGHTS} の直後に書いてある。
|
|
980
|
+
* `nearestProfileDistance` は今も `scripts/calibrate-corpus.ts` が**測定のために**使う。
|
|
981
|
+
*
|
|
768
982
|
* 指標に最適化した結果として全曲が同じ統計値へ寄るのを防ぐため、直近に作った曲との
|
|
769
983
|
* 距離も加点する({@link ComposeOptions.recent})。
|
|
770
984
|
*
|
|
@@ -1184,6 +1398,274 @@ declare const resolveComposeKey: (choice?: string, rnd?: () => number) => Resolv
|
|
|
1184
1398
|
*/
|
|
1185
1399
|
declare const getComposeKeyDescription: (choice: string) => string;
|
|
1186
1400
|
|
|
1401
|
+
/**
|
|
1402
|
+
* UST(UTAU Sequence Text)入出力ユーティリティ。
|
|
1403
|
+
*
|
|
1404
|
+
* - 入力: `.ust` のテキストを解析し、ノート配置と歌詞(かな)を返す。
|
|
1405
|
+
* USTは1ファイル=1パートなので、ハモリ等で分かれた複数ファイルを
|
|
1406
|
+
* それぞれ別トラックへ流し込む使い方を想定している。
|
|
1407
|
+
* - 出力: 1トラックぶんのノートと歌詞から `.ust` テキストを生成する。
|
|
1408
|
+
*
|
|
1409
|
+
* MIDIと違い、USTは**歌詞を持っている**のが取り込む価値の中心にある。
|
|
1410
|
+
* ノートだけならMIDIで足りるので、歌詞をこのアプリの歌詞表現
|
|
1411
|
+
* (かな+制御記号。ノート1つにつき1音節)へ落とすことに注力する。
|
|
1412
|
+
*
|
|
1413
|
+
* 中国語音源のUST(原音名がピンイン)も、かなへ転写して取り込む。
|
|
1414
|
+
* 詳細は {@link looksLikePinyin} と `pinyin.ts`。
|
|
1415
|
+
*/
|
|
1416
|
+
|
|
1417
|
+
/** USTの音価の解像度(4分音符あたり)。UTAUでは480固定。 */
|
|
1418
|
+
declare const UST_TICKS_PER_BEAT = 480;
|
|
1419
|
+
/** {@link lyricToSyllable} の結果。 */
|
|
1420
|
+
type ParsedLyric = {
|
|
1421
|
+
/**
|
|
1422
|
+
* このアプリの歌詞へ写したモーラ列(=必要なノート数)。休符・ブレスなら空。
|
|
1423
|
+
* 日本語の原音名なら必ず1つだが、ピンインは1音節が2〜3モーラになる
|
|
1424
|
+
* (`xing` → `["し", "ん"]`)。呼び出し側はノートを割って割り当てる。
|
|
1425
|
+
*/
|
|
1426
|
+
moras: string[];
|
|
1427
|
+
/** `Lyric=R` 等の休符か。 */
|
|
1428
|
+
rest: boolean;
|
|
1429
|
+
/** `息R` `R吸` `@br1` 等のブレスか。休符と同じくノートを作らない。 */
|
|
1430
|
+
breath: boolean;
|
|
1431
|
+
/** かなへ落とせなかったか(読み込み結果の注意書き用)。 */
|
|
1432
|
+
unknown: boolean;
|
|
1433
|
+
};
|
|
1434
|
+
/**
|
|
1435
|
+
* USTの `Lyric=` 1つを、このアプリの歌詞へ変換する。
|
|
1436
|
+
*
|
|
1437
|
+
* UTAUの原音名は音源ごとに流儀が違うので、実体のかなだけを掬う:
|
|
1438
|
+
* - 連続音 `a か` / `- か` … 空白区切りの**最後の語**が実体。
|
|
1439
|
+
* - サフィックス付き `かC4` `か強` … 先頭のかな列だけを採る。
|
|
1440
|
+
* - ローマ字命名 `ka` `kya` … {@link ROMAJI_KANA} でかなへ寄せる。
|
|
1441
|
+
* - ブレス `息R` `R吸` `@br1` … 歌詞ではなく息継ぎ({@link UST_BREATH_KANJI_RE})。
|
|
1442
|
+
* - 休符 `R` `RE` `a R2` … 歌わない({@link isUstRest})。接尾辞付きも休符。
|
|
1443
|
+
* - ピンイン `xing` `tou` … 中国語音源のとき({@link looksLikePinyin})だけ
|
|
1444
|
+
* {@link pinyinToMoras} でかなへ寄せる。日本語ローマ字と綴りが衝突する
|
|
1445
|
+
* (`wo` = を / ウオ)ため、ファイル単位で決めた結果を渡してもらう。
|
|
1446
|
+
*
|
|
1447
|
+
* 変換できない綴り(CVVCの `a k` のような子音だけの断片など)は継続記号にする。
|
|
1448
|
+
* 言い直さずに繋ぐのが元の意図に一番近く、音節とノートの1:1もずれない。
|
|
1449
|
+
*/
|
|
1450
|
+
declare const lyricToSyllable: (raw: string, pinyin?: boolean) => ParsedLyric;
|
|
1451
|
+
/**
|
|
1452
|
+
* このUSTが中国語ピンインで書かれているか、歌詞全体から決める。
|
|
1453
|
+
*
|
|
1454
|
+
* `ni` `ta` `ka` は日本語ローマ字とピンインの両方で成立し、`wo` に至っては
|
|
1455
|
+
* 読みが違う(を / ウオ)。トークン単位では決められないので、**ピンインでしか
|
|
1456
|
+
* 成立しない綴り**(`xing` `tou` `she` …)がローマ字歌詞の過半を占めるか、で見る。
|
|
1457
|
+
*
|
|
1458
|
+
* 日本語音源が持つ拡張かなの原音名(`she` `fa` など)はピンインとしても読めて
|
|
1459
|
+
* しまうため、数個の一致では倒れないように割合で判定する。
|
|
1460
|
+
*/
|
|
1461
|
+
declare const looksLikePinyin: (lyrics: Iterable<string>) => boolean;
|
|
1462
|
+
/** USTから取り出したノート1つ(ピッチはMIDIノート番号=`NoteNum` そのまま)。 */
|
|
1463
|
+
type UstNotePlacement = {
|
|
1464
|
+
startStep: number;
|
|
1465
|
+
pitch: number;
|
|
1466
|
+
durationSteps: number;
|
|
1467
|
+
velocity: number;
|
|
1468
|
+
};
|
|
1469
|
+
/** UST1ファイル=1パートぶんの取り込み結果。 */
|
|
1470
|
+
type UstTrackData = {
|
|
1471
|
+
/** 読み込んだファイル名(トラック割り当ての表示用)。 */
|
|
1472
|
+
name: string;
|
|
1473
|
+
/** `Tempo` の値。書かれていなければ null。 */
|
|
1474
|
+
bpm: number | null;
|
|
1475
|
+
notes: UstNotePlacement[];
|
|
1476
|
+
/** ノート1つにつき1音節の歌詞テキスト(かな+制御記号)。 */
|
|
1477
|
+
lyrics: string;
|
|
1478
|
+
/** かなへ落とせなかった歌詞の数(読み込み結果の注意書き用)。 */
|
|
1479
|
+
unknownLyricCount: number;
|
|
1480
|
+
/** 中国語ピンインとして読んだか({@link looksLikePinyin} の判定結果)。 */
|
|
1481
|
+
pinyin: boolean;
|
|
1482
|
+
};
|
|
1483
|
+
/**
|
|
1484
|
+
* USTのバイト列を文字列へ復号する。
|
|
1485
|
+
*
|
|
1486
|
+
* UTAUの標準はShift_JIS、OpenUtau等の新しい環境はUTF-8で書く。拡張子では
|
|
1487
|
+
* 見分けられないので、**UTF-8として妥当かどうか**で判定する(日本語の
|
|
1488
|
+
* Shift_JIS列がUTF-8として妥当になることはほぼ無い)。
|
|
1489
|
+
*/
|
|
1490
|
+
declare const decodeUstText: (bytes: Uint8Array) => string;
|
|
1491
|
+
declare const parseUst: (source: Uint8Array | string, name?: string) => UstTrackData;
|
|
1492
|
+
type BuildUstOptions = {
|
|
1493
|
+
/** 出力するトラックのノート(`startStep` 昇順)。 */
|
|
1494
|
+
notes: Note[];
|
|
1495
|
+
/**
|
|
1496
|
+
* ノートと index で1:1に対応する歌詞かな(`normalizeLyrics` の結果を
|
|
1497
|
+
* 1音節ずつ文字列にしたもの)。足りないぶんは {@link DEFAULT_UST_LYRIC} を使う。
|
|
1498
|
+
*/
|
|
1499
|
+
syllables?: string[];
|
|
1500
|
+
bpm: number;
|
|
1501
|
+
/** `[#SETTING]` の ProjectName / OutFile の元にする名前。 */
|
|
1502
|
+
projectName?: string;
|
|
1503
|
+
};
|
|
1504
|
+
/** 歌詞が無いノートに当てる歌詞。UTAU側で必ず何か鳴らせるように「あ」にする。 */
|
|
1505
|
+
declare const DEFAULT_UST_LYRIC = "\u3042";
|
|
1506
|
+
/**
|
|
1507
|
+
* 1トラックぶんのノートと歌詞から `.ust` テキストを生成する。
|
|
1508
|
+
*
|
|
1509
|
+
* USTは**単旋律しか表現できない**フォーマットなので、和音・重なりは先勝ちで
|
|
1510
|
+
* 1本へ潰す(同時刻なら先に置かれたノート、跨ぎは前のノートを優先)。捨てた
|
|
1511
|
+
* ノートに割り当たっていた音節も一緒に捨て、音節とノートの1:1を保つ。
|
|
1512
|
+
*
|
|
1513
|
+
* ノートを消費しない記号のうち、クレッシェンド `↑` とデクレッシェンド `↓` は
|
|
1514
|
+
* `Envelope=`(音量エンベロープ)へ写す({@link assignFadeEnvelopes})。
|
|
1515
|
+
* ブレス `、` に当たる表現はUSTに無いので落とす。
|
|
1516
|
+
*
|
|
1517
|
+
* 文字コードはUTF-8で書き、`Charset=UTF-8` を添える(UTAU本体はShift_JISが
|
|
1518
|
+
* 既定だが、`Charset` 行があればUTF-8のUSTも読める)。改行はUTAUに合わせてCRLF。
|
|
1519
|
+
*/
|
|
1520
|
+
declare const buildUst: (options: BuildUstOptions) => string;
|
|
1521
|
+
|
|
1522
|
+
/**
|
|
1523
|
+
* ガイドツアー(スポットライト型ウォークスルー)。
|
|
1524
|
+
*
|
|
1525
|
+
* 画面を暗幕で覆い、対象要素だけをくり抜いて吹き出しで説明する。
|
|
1526
|
+
* 「解説はあるのに、どこを触ればいいのか分からない」を解消するための導線で、
|
|
1527
|
+
* このライブラリが持つ大量の `dtm-infobtn`(個別の解説モーダル)への入口も兼ねる。
|
|
1528
|
+
*
|
|
1529
|
+
* 単体で使える純粋なDOMユーティリティにしてある。`mountDAW` に依存しないので、
|
|
1530
|
+
* 埋め込み側が自分のUIを指すステップを書いて呼ぶこともできる。
|
|
1531
|
+
*
|
|
1532
|
+
* startTour({ steps: DAW_TOUR_STEPS, root: dawRoot });
|
|
1533
|
+
*
|
|
1534
|
+
* 暗幕は `document.body` 直下に置く。`.dtm-daw` の外に出るため、スタイル側で
|
|
1535
|
+
* `.dtm-tour` にもデザイントークンを供給している(styles.ts の共有セレクタ参照)。
|
|
1536
|
+
*/
|
|
1537
|
+
/** ツアーの1ステップ。 */
|
|
1538
|
+
type TourStep = {
|
|
1539
|
+
/**
|
|
1540
|
+
* ハイライトする要素。CSSセレクタ(`root` の中を検索)か、要素を返す関数。
|
|
1541
|
+
* 省略すると画面中央のカードだけを出す(導入・締めのステップ向け)。
|
|
1542
|
+
*
|
|
1543
|
+
* 解決できない・非表示(`display:none` や 0 サイズ)の場合、そのステップは
|
|
1544
|
+
* 自動的に飛ばされる。機能を切って使う利用者(`features.midi: false` 等)でも
|
|
1545
|
+
* 存在しないUIを指して固まらないようにするため。
|
|
1546
|
+
*/
|
|
1547
|
+
target?: string | (() => Element | null | undefined);
|
|
1548
|
+
/** 吹き出しの見出し。 */
|
|
1549
|
+
title: string;
|
|
1550
|
+
/** 吹き出しの本文。HTMLを書ける。 */
|
|
1551
|
+
body: string;
|
|
1552
|
+
/**
|
|
1553
|
+
* 吹き出しを対象の上下どちらに出すか。既定 `"auto"`(入る方を選ぶ)。
|
|
1554
|
+
* 対象が画面に対して大きすぎる場合は `"auto"` でも画面下部に固定される。
|
|
1555
|
+
*/
|
|
1556
|
+
placement?: "auto" | "top" | "bottom";
|
|
1557
|
+
/**
|
|
1558
|
+
* 表示直前に呼ばれる。対象要素(解決できていれば)が渡る。
|
|
1559
|
+
* `false` を返すとこのステップを飛ばす。
|
|
1560
|
+
*/
|
|
1561
|
+
before?: (target: Element | null) => boolean | undefined;
|
|
1562
|
+
/**
|
|
1563
|
+
* 目的別の分岐。指定すると「次へ」の代わりに選択肢ボタンを並べ、
|
|
1564
|
+
* 選ばれた枝のステップ列で以降を差し替える。
|
|
1565
|
+
*
|
|
1566
|
+
* 「カバーを作りたい」「自動作曲したい」「自分で打ち込みたい」のように
|
|
1567
|
+
* 入口の目的がはっきり分かれる場合、全員に同じ順路を歩かせるより短く終わる。
|
|
1568
|
+
*/
|
|
1569
|
+
branches?: TourBranch[];
|
|
1570
|
+
};
|
|
1571
|
+
/** {@link TourStep.branches} の選択肢。 */
|
|
1572
|
+
type TourBranch = {
|
|
1573
|
+
/** ボタンの見出し。 */
|
|
1574
|
+
label: string;
|
|
1575
|
+
/** 見出しの下に添える補足(省略可)。 */
|
|
1576
|
+
hint?: string;
|
|
1577
|
+
/** この枝を選んだときに続けて表示するステップ列。 */
|
|
1578
|
+
steps: TourStep[];
|
|
1579
|
+
/**
|
|
1580
|
+
* この枝を出してよいか(省略時は常に出す)。表示の直前に評価する。
|
|
1581
|
+
*
|
|
1582
|
+
* 個々のステップは対象が無ければ勝手に飛ぶが、枝そのものは飛ばせない。
|
|
1583
|
+
* 機能を切っている利用者に「カバー曲を作りたい」を見せて、選んだ先が
|
|
1584
|
+
* 全部飛んで空になる——という行き止まりを防ぐために要る。
|
|
1585
|
+
*/
|
|
1586
|
+
when?: (root: ParentNode) => boolean;
|
|
1587
|
+
};
|
|
1588
|
+
/**
|
|
1589
|
+
* `selector` に一致する要素が実際に表示されているか。
|
|
1590
|
+
* {@link TourBranch.when} に書く「この機能、そもそもこの画面にある?」の判定用。
|
|
1591
|
+
*/
|
|
1592
|
+
declare const isTourTargetVisible: (root: ParentNode, selector: string) => boolean;
|
|
1593
|
+
/** 吹き出しの文言。日本語以外へ差し替えるためのフック。 */
|
|
1594
|
+
type TourLabels = {
|
|
1595
|
+
next: string;
|
|
1596
|
+
prev: string;
|
|
1597
|
+
skip: string;
|
|
1598
|
+
/** 最終ステップの「次へ」に使う文言。 */
|
|
1599
|
+
done: string;
|
|
1600
|
+
/** 進捗表示(1始まりの現在位置と総数)。 */
|
|
1601
|
+
progress: (current: number, total: number) => string;
|
|
1602
|
+
};
|
|
1603
|
+
/** {@link startTour} のオプション。 */
|
|
1604
|
+
type TourOptions = {
|
|
1605
|
+
/** 表示するステップ列。空なら何もしない。 */
|
|
1606
|
+
steps: TourStep[];
|
|
1607
|
+
/**
|
|
1608
|
+
* `target` にセレクタを書いたときの検索基点(既定 `document`)。
|
|
1609
|
+
* DAWのルート要素を渡すと、同一ページに複数マウントしていても取り違えない。
|
|
1610
|
+
*/
|
|
1611
|
+
root?: ParentNode;
|
|
1612
|
+
/**
|
|
1613
|
+
* 「もう見た」フラグの保存先キー(既定 `"dtm-tour-seen"`)。
|
|
1614
|
+
* 最後まで進むか、スキップ/中断したときに記録する。`null` で記録しない。
|
|
1615
|
+
*/
|
|
1616
|
+
storageKey?: string | null;
|
|
1617
|
+
/** 文言の差し替え。 */
|
|
1618
|
+
labels?: Partial<TourLabels>;
|
|
1619
|
+
/** 終了時に呼ばれる。最後まで見たかどうかが渡る。 */
|
|
1620
|
+
onEnd?: (completed: boolean) => void;
|
|
1621
|
+
};
|
|
1622
|
+
/** {@link startTour} の戻り値。 */
|
|
1623
|
+
type TourInstance = {
|
|
1624
|
+
/** 次のステップへ。最終ステップでは終了する。 */
|
|
1625
|
+
next: () => void;
|
|
1626
|
+
/** 前のステップへ。 */
|
|
1627
|
+
prev: () => void;
|
|
1628
|
+
/** 中断して閉じる。 */
|
|
1629
|
+
stop: () => void;
|
|
1630
|
+
/** 表示中かどうか。 */
|
|
1631
|
+
isActive: () => boolean;
|
|
1632
|
+
};
|
|
1633
|
+
/** 「もう見た」フラグの既定の保存キー。 */
|
|
1634
|
+
declare const TOUR_STORAGE_KEY = "dtm-tour-seen";
|
|
1635
|
+
/** ツアーを既に見たかどうか。初回だけ自動再生したいときの判定に使う。 */
|
|
1636
|
+
declare const hasSeenTour: (storageKey?: string) => boolean;
|
|
1637
|
+
/** 「もう見た」フラグを立てる。 */
|
|
1638
|
+
declare const markTourSeen: (storageKey?: string) => void;
|
|
1639
|
+
/** 「もう見た」フラグを消す(ツアーをもう一度自動再生させたいとき用)。 */
|
|
1640
|
+
declare const clearTourSeen: (storageKey?: string) => void;
|
|
1641
|
+
/**
|
|
1642
|
+
* ガイドツアーを開始する。既に別のツアーが動いていても、呼ぶ側で止める必要はない
|
|
1643
|
+
* (新しい方が古い方を閉じる)。
|
|
1644
|
+
*/
|
|
1645
|
+
declare const startTour: (options: TourOptions) => TourInstance;
|
|
1646
|
+
/**
|
|
1647
|
+
* `mountDAW` が組み立てるUIに対する既定のツアー。
|
|
1648
|
+
*
|
|
1649
|
+
* 入口で<b>目的</b>を尋ね、その枝だけを歩かせる。想定している層が
|
|
1650
|
+
*
|
|
1651
|
+
* 1. 音源に合わせてカバーを作りたい
|
|
1652
|
+
* 2. とりあえず自動で曲が欲しい
|
|
1653
|
+
* 3. 自分で打ち込みたい
|
|
1654
|
+
*
|
|
1655
|
+
* と目的からして分かれており、全員に同じ順路を歩かせると
|
|
1656
|
+
* 「自分に関係のない説明を我慢して読む時間」が大半になってしまうため。
|
|
1657
|
+
*
|
|
1658
|
+
* セレクタはすべて `data-dtm` 属性で書いてある。存在しないUI(機能を切っている、
|
|
1659
|
+
* 伴奏音源を注入していない等)を指すステップは自動的に飛ばされるので、
|
|
1660
|
+
* 利用側の構成に合わせて間引く必要はない。
|
|
1661
|
+
*/
|
|
1662
|
+
declare const DAW_TOUR_STEPS: TourStep[];
|
|
1663
|
+
/** ヘルプから目的別ツアーを直接始めるための枝の一覧(`label` は表示用)。 */
|
|
1664
|
+
declare const DAW_TOUR_BRANCHES: Record<"cover" | "compose" | "sequence", {
|
|
1665
|
+
label: string;
|
|
1666
|
+
steps: TourStep[];
|
|
1667
|
+
}>;
|
|
1668
|
+
|
|
1187
1669
|
type SongDrumInstruction = {
|
|
1188
1670
|
ranges: [number, number][];
|
|
1189
1671
|
patternBars?: number;
|
|
@@ -1270,7 +1752,10 @@ type PitchSegment = {
|
|
|
1270
1752
|
pitch: number;
|
|
1271
1753
|
/** ノートの母音オンセットからの相対秒(区間の開始位置)。 */
|
|
1272
1754
|
atSec: number;
|
|
1273
|
-
/**
|
|
1755
|
+
/**
|
|
1756
|
+
* 直前区間から時間を掛けて滑る(`〜`)。false(`ー`)は素早く移る。
|
|
1757
|
+
* どちらもピッチは連続に動く——違いは掛ける時間だけ({@link glideMsForSegments})。
|
|
1758
|
+
*/
|
|
1274
1759
|
portamento: boolean;
|
|
1275
1760
|
};
|
|
1276
1761
|
|
|
@@ -1310,9 +1795,16 @@ declare const VIBRATO_MIN_SEC = 0.35;
|
|
|
1310
1795
|
* オプトインのフォルマント合成ヘルパ(createKlattVoice / createVoiceRegistry)を分離して提供する。
|
|
1311
1796
|
*/
|
|
1312
1797
|
|
|
1313
|
-
/**
|
|
1798
|
+
/**
|
|
1799
|
+
* 継続記号。直前の音を言い直さずに保ち、ピッチだけを次の音へ移す。
|
|
1800
|
+
* 声が繋がっている以上ピッチも繋がっているべきなので、移動は音価に比例した
|
|
1801
|
+
* 短いグライドで行う(瞬間移動させると耳が「言い直した」と受け取る)。
|
|
1802
|
+
*/
|
|
1314
1803
|
declare const TIE_MARK = "\u30FC";
|
|
1315
|
-
/**
|
|
1804
|
+
/**
|
|
1805
|
+
* 継続記号(ポルタメント)。{@link TIE_MARK} と同じだが、区間の大半を掛けて
|
|
1806
|
+
* ゆっくり滑る=しゃくり・スラーになる。違いは掛ける時間だけ。
|
|
1807
|
+
*/
|
|
1316
1808
|
declare const PORTAMENTO_MARK = "\u301C";
|
|
1317
1809
|
/** 促音。ノートを消費し、無音の閉鎖として間を作る。 */
|
|
1318
1810
|
declare const STOP_MARK = "\u3063";
|
|
@@ -1323,6 +1815,9 @@ declare const BREATH_MARK = "\u3001";
|
|
|
1323
1815
|
/**
|
|
1324
1816
|
* デクレッシェンド。ノートは消費せず、直前の音(継続で結合されていればその全体)を
|
|
1325
1817
|
* 歌いながら声量0へ落とす。`あーーーーー↓` で「あ」を伸ばしたまま消えていく。
|
|
1818
|
+
*
|
|
1819
|
+
* 1音へ複数書くと、書いた位置が減り方の中継点になる(`ぎ↓ー↓` = 50%→0%)。
|
|
1820
|
+
* 詳細は {@link buildFadeCurve}。
|
|
1326
1821
|
*/
|
|
1327
1822
|
declare const FADE_OUT_MARK = "\u2193";
|
|
1328
1823
|
/**
|
|
@@ -1499,7 +1994,16 @@ type VoiceModel = {
|
|
|
1499
1994
|
/** 歌いながら声量0へ落とす(`↓`)。{@link LyricSyllable.fadeOut} を引き回したもの。 */
|
|
1500
1995
|
fadeOut?: boolean,
|
|
1501
1996
|
/** 歌いながら声量を上げる(`↑`)。両方立てるとスウェルになる。 */
|
|
1502
|
-
fadeIn?: boolean
|
|
1997
|
+
fadeIn?: boolean,
|
|
1998
|
+
/** 記号を複数書いたときの声量の中継点列({@link buildFadeCurve})。 */
|
|
1999
|
+
fadeCurve?: FadeStop[],
|
|
2000
|
+
/**
|
|
2001
|
+
* この音が占める長さ(秒。`t0` から数える)。
|
|
2002
|
+
* 合成した素片は音価より長いことがある(短すぎるノートは合成の都合で
|
|
2003
|
+
* 引き伸ばして作られる)ので、渡すと**その長さで切って**鳴らす。
|
|
2004
|
+
* 省略すると素片の長さのまま鳴らす(従来どおり)。
|
|
2005
|
+
*/
|
|
2006
|
+
durationSec?: number) => void;
|
|
1503
2007
|
/** スケジュール済みの発音をすべて即停止する(停止・一時停止・シーク時)。 */
|
|
1504
2008
|
stopAll?: () => void;
|
|
1505
2009
|
};
|
|
@@ -1627,6 +2131,11 @@ type StreamVoiceNote = {
|
|
|
1627
2131
|
fadeOut?: boolean;
|
|
1628
2132
|
/** このノートを歌いながら声量を上げていく(`↑`)。{@link StreamVoiceNote.fadeOut} の対。 */
|
|
1629
2133
|
fadeIn?: boolean;
|
|
2134
|
+
/**
|
|
2135
|
+
* 声量の中継点列(`↓` / `↑` を1音へ複数書いたとき)。{@link buildFadeCurve} 参照。
|
|
2136
|
+
* 記号が1つだけ/スウェルのときは付かない(=従来どおり1音まるごと)。
|
|
2137
|
+
*/
|
|
2138
|
+
fadeCurve?: FadeStop[];
|
|
1630
2139
|
};
|
|
1631
2140
|
/**
|
|
1632
2141
|
* 継続記号でノートを結合できる上限(秒)。これを超える長さは1回の合成に載せず、
|
|
@@ -2028,6 +2537,19 @@ type TrackConfig = {
|
|
|
2028
2537
|
/** 0-127。velocity兼ボリューム(既存挙動を踏襲) */
|
|
2029
2538
|
volume: number;
|
|
2030
2539
|
};
|
|
2540
|
+
/**
|
|
2541
|
+
* 声量エンベロープの中継点。`↓` / `↑` を1音へ複数書いたときの
|
|
2542
|
+
* 「途中でここまで落とす(上げる)」を表す。
|
|
2543
|
+
*
|
|
2544
|
+
* 位置を秒ではなく比率で持つのは、音源ごとに実時間の決め方(合成長・リリースの
|
|
2545
|
+
* 取り方)が違うため。受け取った側が自分のサステイン区間へ写して使う。
|
|
2546
|
+
*/
|
|
2547
|
+
type FadeStop = {
|
|
2548
|
+
/** サステイン区間内の位置 0〜1(0=立ち上がり直後、1=終端)。 */
|
|
2549
|
+
at: number;
|
|
2550
|
+
/** その位置で到達する声量(ピークに対する比 0〜1)。 */
|
|
2551
|
+
level: number;
|
|
2552
|
+
};
|
|
2031
2553
|
type PlayNoteEvent = {
|
|
2032
2554
|
trackId: string;
|
|
2033
2555
|
/**
|
|
@@ -2051,6 +2573,12 @@ type PlayNoteEvent = {
|
|
|
2051
2573
|
* 未指定なら従来どおり単一ピッチの1音として鳴らせばよい。
|
|
2052
2574
|
*/
|
|
2053
2575
|
pitchSegments?: PitchSegment[];
|
|
2576
|
+
/**
|
|
2577
|
+
* 声量エンベロープの中継点列(`↓` / `↑` を1音へ複数書いたとき)。
|
|
2578
|
+
* 未指定なら {@link LyricSyllable.fadeOut} / {@link LyricSyllable.fadeIn} の
|
|
2579
|
+
* とおり「1音まるごと」の増減になる。
|
|
2580
|
+
*/
|
|
2581
|
+
fadeCurve?: FadeStop[];
|
|
2054
2582
|
/**
|
|
2055
2583
|
* ステレオ定位 -1(完全左)〜+1(完全右)、0が中央。未指定は中央扱い。
|
|
2056
2584
|
* 歌詞トラックに p<n>(0-127, 既定64=中央) があれば正規化した値が載る。
|
|
@@ -2108,8 +2636,8 @@ type LyricSyllable = {
|
|
|
2108
2636
|
*/
|
|
2109
2637
|
kind?: LyricSyllableKind;
|
|
2110
2638
|
/**
|
|
2111
|
-
* `kind === "tie"`
|
|
2112
|
-
* false
|
|
2639
|
+
* `kind === "tie"` のとき、直前ピッチから時間を掛けて滑る(`〜`)。
|
|
2640
|
+
* false(`ー`)なら素早く移る。どちらもピッチは連続に動き、違いは掛ける時間だけ。
|
|
2113
2641
|
*/
|
|
2114
2642
|
portamento?: boolean;
|
|
2115
2643
|
/**
|
|
@@ -2124,6 +2652,11 @@ type LyricSyllable = {
|
|
|
2124
2652
|
* 記号自体はノートを消費しない({@link LyricSyllable.breathAfter} と同じ扱い)。
|
|
2125
2653
|
* 継続記号で1音へ結合されたグループに付いた場合は、**結合後の音全体**に掛かる——
|
|
2126
2654
|
* `あーーーーー↓` は「あ」を伸ばしたまま最後まで滑らかに消えていく、という意味になる。
|
|
2655
|
+
*
|
|
2656
|
+
* 1つの音へ**複数書く**と、書いた位置が減り方の中継点になる({@link FadeStop})。
|
|
2657
|
+
* k個書いたとき、i番目が付いた区間の終わりで声量が (k-i)/k 倍になる:
|
|
2658
|
+
* `ぎ↓ー↓` なら「ぎ」の終わりで50%→音の終わりで0%、`ぎ↓ー↓ー↓` なら
|
|
2659
|
+
* 66%→33%→0%。最後の1つだけは書いた位置に関わらず音の終端へ置かれる。
|
|
2127
2660
|
*/
|
|
2128
2661
|
fadeOut?: boolean;
|
|
2129
2662
|
/**
|
|
@@ -2132,6 +2665,10 @@ type LyricSyllable = {
|
|
|
2132
2665
|
* 両方立っている場合(`あーーー↑↓`)は**スウェル**——小さく入って中央で最大、
|
|
2133
2666
|
* そこから消えていく。声楽の messa di voce にあたる形で、`↑↓` `↓↑` どちらの
|
|
2134
2667
|
* 順で書いても同じ意味になる。
|
|
2668
|
+
*
|
|
2669
|
+
* {@link LyricSyllable.fadeOut} と同じく複数書けば中継点になる(`ぎ↑ー↑` なら
|
|
2670
|
+
* 「ぎ」の終わりで50%→音の終わりで最大)。ただし `↑` と `↓` が同じ音に
|
|
2671
|
+
* 混在するときはスウェル優先で、中継点は付かない。
|
|
2135
2672
|
*/
|
|
2136
2673
|
fadeIn?: boolean;
|
|
2137
2674
|
};
|
|
@@ -2436,6 +2973,13 @@ type DawOptions = {
|
|
|
2436
2973
|
drumFont?: string;
|
|
2437
2974
|
/** 歌唱合成の先読みや制御を行うヘルパ(.koe音源の再生前プリロードに使用) */
|
|
2438
2975
|
singingVoices?: SingingVoices;
|
|
2976
|
+
/**
|
|
2977
|
+
* 伴奏音源(mp3 / wav / YouTube)の同時再生器。
|
|
2978
|
+
*
|
|
2979
|
+
* ライブラリ自身は音を出さないので、AudioContextを持つ利用側(`createDtmStudio` 等)が
|
|
2980
|
+
* `createBackingAudio` で作って渡す。渡さないとオーディオ同時再生のUIごと出ない。
|
|
2981
|
+
*/
|
|
2982
|
+
backingAudio?: BackingAudio;
|
|
2439
2983
|
/**
|
|
2440
2984
|
* 編集中の音割れ検知メーター(マスタの安全リミッター手前を監視)。
|
|
2441
2985
|
* 渡すとDAW UIにクリップ警告バッジを表示できる。studio.mountEditor が自動的に渡す。
|
|
@@ -2458,6 +3002,30 @@ type DawOptions = {
|
|
|
2458
3002
|
onLoopChange?: (loop: boolean) => void;
|
|
2459
3003
|
/** 利用規約への同意画面の表示をスキップするかどうか */
|
|
2460
3004
|
skipConsent?: boolean;
|
|
3005
|
+
/**
|
|
3006
|
+
* ツールバーにヘルプ(「?」)ボタンを出すか(既定 true)。
|
|
3007
|
+
* 使い方モーダルとガイドツアーの入口で、画面内に散らばる ⓘ 解説への案内も兼ねる。
|
|
3008
|
+
*/
|
|
3009
|
+
showHelp?: boolean;
|
|
3010
|
+
/**
|
|
3011
|
+
* ガイドツアー(スポットライト型ウォークスルー)の設定。
|
|
3012
|
+
*
|
|
3013
|
+
* **自動再生は既定でオフ**。埋め込み先の第一印象を勝手に上書きしないための既定値で、
|
|
3014
|
+
* 初回に流したいアプリ(デモページ等)だけが `autoStart: true` を渡す。
|
|
3015
|
+
* 自動再生を切っていても、ヘルプボタンからはいつでも開始できる。
|
|
3016
|
+
*/
|
|
3017
|
+
tour?: {
|
|
3018
|
+
/** 初回訪問時(`storageKey` が未記録のとき)に自動で開始する。既定 false。 */
|
|
3019
|
+
autoStart?: boolean;
|
|
3020
|
+
/** 既定ステップ({@link import("./tour").DAW_TOUR_STEPS})を丸ごと差し替える。 */
|
|
3021
|
+
steps?: TourStep[];
|
|
3022
|
+
/** 既定ステップの後ろに足すステップ(埋め込み側のUIを案内したいとき)。 */
|
|
3023
|
+
extraSteps?: TourStep[];
|
|
3024
|
+
/** 「もう見た」フラグの保存キー(既定 `"dtm-tour-seen"`)。`null` で記録しない。 */
|
|
3025
|
+
storageKey?: string | null;
|
|
3026
|
+
/** 吹き出しの文言の差し替え(日本語以外へ差し替えるとき)。 */
|
|
3027
|
+
labels?: Partial<TourLabels>;
|
|
3028
|
+
};
|
|
2461
3029
|
/**
|
|
2462
3030
|
* シンプルモードでトラック数超過コンテンツを読み込もうとしたとき、上級者モードへの切替を要求するコールバック。
|
|
2463
3031
|
* `mountModeSwitch` が自動的に接続する。未接続なら確認モーダルは表示しない。
|
|
@@ -2501,6 +3069,8 @@ type DawInstance = {
|
|
|
2501
3069
|
loadMML: (mml: string) => void;
|
|
2502
3070
|
loadMIDI: (bytes: Uint8Array) => void | Promise<void>;
|
|
2503
3071
|
exportMIDI: () => Blob;
|
|
3072
|
+
/** 選択中のトラック1本をUST(UTAU)テキストのBlobで返す。 */
|
|
3073
|
+
exportUST: () => Blob;
|
|
2504
3074
|
setBpm: (bpm: number) => void;
|
|
2505
3075
|
/** 現在のループ再生状態(ON/OFF)を返す */
|
|
2506
3076
|
getLoop: () => boolean;
|
|
@@ -2564,6 +3134,16 @@ type DawInstance = {
|
|
|
2564
3134
|
};
|
|
2565
3135
|
/** パース済みMIDIオブジェクトと選択トラックインデックスを直接適用する(上級者モード切替後の再ロード用)。 */
|
|
2566
3136
|
applyMidiParsed?: (midi: unknown, selectedIndices: number[]) => void;
|
|
3137
|
+
/**
|
|
3138
|
+
* 解析済みUSTを `startIndex` のトラックから順に適用する
|
|
3139
|
+
* (上級者モード切替後の再ロード用。省略時は選択中のトラックから)。
|
|
3140
|
+
*/
|
|
3141
|
+
applyUstParsed?: (ustTracks: UstTrackData[], startIndex?: number) => void;
|
|
3142
|
+
/**
|
|
3143
|
+
* ガイドツアーを開始する(自動再生の設定に関わらず、いつでも呼べる)。
|
|
3144
|
+
* ヘルプボタンの「ガイドツアーを見る」と同じ入口。
|
|
3145
|
+
*/
|
|
3146
|
+
startTour: () => void;
|
|
2567
3147
|
destroy: () => void;
|
|
2568
3148
|
};
|
|
2569
3149
|
type LoopPoint = {
|
|
@@ -2975,6 +3555,7 @@ declare const MACRO_STORAGE_KEYS: {
|
|
|
2975
3555
|
readonly key: "dtm-macro:key";
|
|
2976
3556
|
readonly scale: "dtm-macro:scale";
|
|
2977
3557
|
readonly shift: "dtm-macro:shift";
|
|
3558
|
+
readonly shiftActiveOnly: "dtm-macro:shift-active-only";
|
|
2978
3559
|
readonly transpose: "dtm-macro:transpose";
|
|
2979
3560
|
};
|
|
2980
3561
|
type MacroStorageKey = keyof typeof MACRO_STORAGE_KEYS;
|
|
@@ -3054,6 +3635,15 @@ declare class MMLCore {
|
|
|
3054
3635
|
moveNoteEnd(_: number): void;
|
|
3055
3636
|
resizeNote(noteId: number, durationSteps: number): void;
|
|
3056
3637
|
resizeNoteEnd(_: number): void;
|
|
3638
|
+
/**
|
|
3639
|
+
* 全ノートをまとめて時間方向へずらす(マクロの「シフト」用)。
|
|
3640
|
+
*
|
|
3641
|
+
* {@link moveNote} は「いまの曲の長さ+1小節」でクランプするが、こちらはしない。
|
|
3642
|
+
* 曲の頭に無音を作る操作=曲を伸ばす操作なので、いまの長さで頭打ちにすると
|
|
3643
|
+
* 大きくずらしたときにノートが末尾へ団子になってしまう。
|
|
3644
|
+
* 0より前へはみ出したノートは捨てる(従来のシフトと同じ)。
|
|
3645
|
+
*/
|
|
3646
|
+
shiftAllNotes(steps: number): void;
|
|
3057
3647
|
getNotes(): Note[];
|
|
3058
3648
|
getMML(volumeOverride?: number): string;
|
|
3059
3649
|
setVolume(volume: number): void;
|
|
@@ -3257,6 +3847,16 @@ declare const extractMidiDrumPattern: (midi: unknown, drumFont?: string) => {
|
|
|
3257
3847
|
* 他のMMLプレイヤーには無害(解析時に除去される)。
|
|
3258
3848
|
*/
|
|
3259
3849
|
type MmlMeta = {
|
|
3850
|
+
/**
|
|
3851
|
+
* 書き出したライブラリのバージョン(`#ver=`)。
|
|
3852
|
+
*
|
|
3853
|
+
* 自動作曲の素材は外部コーパスから作るので、**どのバージョンまでが
|
|
3854
|
+
* どの素材で作られたか**を後から辿れる必要がある(権利の申告が誤っていた
|
|
3855
|
+
* 場合の是正範囲を言うため)。台帳は `docs/dataset-provenance.md`。
|
|
3856
|
+
*
|
|
3857
|
+
* 読み込み側は使わない——**記録のためだけ**に持つ。
|
|
3858
|
+
*/
|
|
3859
|
+
version?: string;
|
|
3260
3860
|
/** 楽器プリセット名(INSTRUMENT_PRESETS のキー等。利用側が音源解決に使う) */
|
|
3261
3861
|
instrument?: string;
|
|
3262
3862
|
/** ドラムパターン名(DRUM_PATTERNS のキー) */
|
|
@@ -3343,6 +3943,32 @@ type MmlMeta = {
|
|
|
3343
3943
|
* 省略時はオフ。
|
|
3344
3944
|
*/
|
|
3345
3945
|
loop?: boolean;
|
|
3946
|
+
/**
|
|
3947
|
+
* 同時再生する伴奏音源のURL(mp3 / wav / YouTube)。`#audio=` で埋め込む。
|
|
3948
|
+
*
|
|
3949
|
+
* URLだけを持ち、**アップロードされたファイルは持たない**(受け取った相手が
|
|
3950
|
+
* 再生できないMMLになるため、ファイル読み込み時は音源関連の宣言ごと出力しない)。
|
|
3951
|
+
*/
|
|
3952
|
+
audio?: string;
|
|
3953
|
+
/** 伴奏音源のどこから鳴らすか(秒)。`#audiostart=` で埋め込む。省略時は0(頭から)。 */
|
|
3954
|
+
audioStart?: number;
|
|
3955
|
+
/** 伴奏音源のどこで止めるか(秒)。`#audioend=` で埋め込む。省略時は最後まで。 */
|
|
3956
|
+
audioEnd?: number;
|
|
3957
|
+
/**
|
|
3958
|
+
* 伴奏音源と打ち込みの**開始のずれ**(秒)。`#audiooffset=` で埋め込む。省略時は0(同時)。
|
|
3959
|
+
*
|
|
3960
|
+
* 正なら音源が先に始まり、その秒数だけ経ってから打ち込みが始まる。
|
|
3961
|
+
* 負ならその逆(打ち込みが先で、音源が後から入る)。
|
|
3962
|
+
*/
|
|
3963
|
+
audioOffset?: number;
|
|
3964
|
+
/**
|
|
3965
|
+
* 伴奏音源を貼り付ける曲側の位置(ステップ。1小節=192)。`#audioat=` で埋め込む**旧形式**。
|
|
3966
|
+
* {@link audioOffset} に置き換わったが、既存のMMLを読めるよう解析だけ残してある
|
|
3967
|
+
* (利用側でテンポを使って `audioOffset` へ読み替える)。
|
|
3968
|
+
*/
|
|
3969
|
+
audioAt?: number;
|
|
3970
|
+
/** 伴奏音源の音量 0-100。`#audiovol=` で埋め込む。省略時は80。 */
|
|
3971
|
+
audioVolume?: number;
|
|
3346
3972
|
};
|
|
3347
3973
|
/** MMLからトップレベル宣言を抽出する */
|
|
3348
3974
|
declare const parseMmlMeta: (mml: string) => MmlMeta;
|
|
@@ -3418,17 +4044,6 @@ type ParseMMLOptions = {
|
|
|
3418
4044
|
*/
|
|
3419
4045
|
declare const parseMML: (mml: string, options?: ParseMMLOptions) => ParsedMML;
|
|
3420
4046
|
|
|
3421
|
-
/**
|
|
3422
|
-
* mountMmlPlayer — MML文字列を「読むだけ」で再生できる軽量ビュー(Layer 2)。
|
|
3423
|
-
*
|
|
3424
|
-
* 編集UIの mountDAW と対になる存在で、ノート列を横スクロールのトークン帯として表示し、
|
|
3425
|
-
* 再生位置をハイライト&オートスクロールする。パースは DTM 正準の parseMML を、
|
|
3426
|
-
* 再生スケジューリングは createSequencer を用いるため、コアとモデルが一本化される。
|
|
3427
|
-
*
|
|
3428
|
-
* 発音は mountDAW と同じく onPlayNote へ注入する設計だが、手軽に鳴らせるよう
|
|
3429
|
-
* オプトインの簡易square-wave synth(`synth`)も同梱する。onPlayNote 未指定なら既定で有効。
|
|
3430
|
-
*/
|
|
3431
|
-
|
|
3432
4047
|
type MmlPlayerOptions = {
|
|
3433
4048
|
/** ループ再生(既定オフ)。#loop=on メタ行でも指定可 */
|
|
3434
4049
|
loop?: boolean;
|
|
@@ -3457,6 +4072,12 @@ type MmlPlayerOptions = {
|
|
|
3457
4072
|
trackColors?: string[];
|
|
3458
4073
|
/** 歌唱合成の先読みや制御を行うヘルパ(.koe音源の再生前プリロードに使用) */
|
|
3459
4074
|
singingVoices?: SingingVoices;
|
|
4075
|
+
/**
|
|
4076
|
+
* 伴奏音源(mp3 / wav / YouTube)の同時再生器。
|
|
4077
|
+
* MMLの `#audio=<URL>` を鳴らすのに使う(URLを持たないファイル読み込みはMMLに載らないので、
|
|
4078
|
+
* ここで鳴るのは常にURLの音源)。渡さないと `#audio=` は無視される。
|
|
4079
|
+
*/
|
|
4080
|
+
backingAudio?: BackingAudio;
|
|
3460
4081
|
/** 再生終了または手動停止時に呼び出されるコールバック */
|
|
3461
4082
|
onStop?: () => void;
|
|
3462
4083
|
/** 埋め込みプレイヤーのベースURL(例: "https://onjmin.github.io/dtm/demo/embed.html") */
|
|
@@ -3490,6 +4111,106 @@ declare const decodeMml: (payload: string) => Promise<string>;
|
|
|
3490
4111
|
*/
|
|
3491
4112
|
declare const mountMmlPlayer: (target: HTMLElement, mml: string, options?: MmlPlayerOptions) => MmlPlayerInstance;
|
|
3492
4113
|
|
|
4114
|
+
/**
|
|
4115
|
+
* MusicXML の入出力。
|
|
4116
|
+
*
|
|
4117
|
+
* ## MIDI と何が違うか
|
|
4118
|
+
*
|
|
4119
|
+
* MIDI は「いつ・どの高さの音を鳴らすか」しか持たない。MusicXML は**楽譜**なので、
|
|
4120
|
+
* パートの名前・声部・歌詞・調号・拍子・音符の綴り(ファ♯かソ♭か)まで持つ。
|
|
4121
|
+
* このライブラリにとって効くのは主に2つ。
|
|
4122
|
+
*
|
|
4123
|
+
* - **主旋律がどれか分かる。** MIDI からの取り込みはチャンネルを見て推定するしか
|
|
4124
|
+
* なく、ハモリや対旋律を主旋律と取り違える(`scripts/calibrate-corpus.ts` の
|
|
4125
|
+
* 主旋律選択が長い注釈を必要としているのはそのため)。MusicXML はパートが
|
|
4126
|
+
* 明示されている。
|
|
4127
|
+
* - **歌詞が音符に紐づいている。** 歌声合成へそのまま渡せる。MIDI では歌詞と
|
|
4128
|
+
* 音符の対応が失われる。
|
|
4129
|
+
*
|
|
4130
|
+
* ## 音律について
|
|
4131
|
+
*
|
|
4132
|
+
* 31平均律({@link file://./tuning.ts})の音は MusicXML の `<alter>` に小数を書けば
|
|
4133
|
+
* 表現できる(仕様上は許されている)。ただし**読める相手は限られる**ので、
|
|
4134
|
+
* 書き出しでは12平均律へ丸めた整数を使い、微分音は捨てる。31平均律の曲を
|
|
4135
|
+
* 往復させたいときは MML を使うこと。
|
|
4136
|
+
*
|
|
4137
|
+
* ## 対応範囲
|
|
4138
|
+
*
|
|
4139
|
+
* 読み込みは `score-partwise`(一般的なほう)のみ。`score-timewise` は実物が
|
|
4140
|
+
* ほぼ流通していないので見ない。和音(`<chord>`)・タイ・声部・複数パートは扱う。
|
|
4141
|
+
* 繰り返し記号(`<repeat>`)は**展開しない**——展開すると小節番号がずれて、
|
|
4142
|
+
* 取り込んだ後の編集で位置を見失う。
|
|
4143
|
+
*/
|
|
4144
|
+
|
|
4145
|
+
/** 取り込んだ1音。 */
|
|
4146
|
+
type MusicXmlNotePlacement = {
|
|
4147
|
+
/** 何番目のパートから来たか(0始まり)。 */
|
|
4148
|
+
partIndex: number;
|
|
4149
|
+
startStep: number;
|
|
4150
|
+
/** MIDIノート番号。 */
|
|
4151
|
+
pitch: number;
|
|
4152
|
+
durationSteps: number;
|
|
4153
|
+
/** 音符に紐づいた歌詞(`<lyric><text>`)。無ければ空。 */
|
|
4154
|
+
lyric: string;
|
|
4155
|
+
};
|
|
4156
|
+
type MusicXmlPart = {
|
|
4157
|
+
index: number;
|
|
4158
|
+
/** `<part-name>`。無ければ `Part 1` のような既定名。 */
|
|
4159
|
+
name: string;
|
|
4160
|
+
noteCount: number;
|
|
4161
|
+
/** このパートに歌詞が付いているか。歌トラックの候補を見分けるのに使う。 */
|
|
4162
|
+
hasLyrics: boolean;
|
|
4163
|
+
/** 平均音高(MIDIノート番号)。主旋律の目安。 */
|
|
4164
|
+
avgPitch: number;
|
|
4165
|
+
};
|
|
4166
|
+
type MusicXmlExtraction = {
|
|
4167
|
+
parts: MusicXmlPart[];
|
|
4168
|
+
placements: MusicXmlNotePlacement[];
|
|
4169
|
+
bpm: number;
|
|
4170
|
+
};
|
|
4171
|
+
/**
|
|
4172
|
+
* MusicXML を読んで、音符・パート情報・テンポを返す。
|
|
4173
|
+
*
|
|
4174
|
+
* @param xml `.musicxml` / `.xml` のテキスト。圧縮形式(`.mxl`)は呼び出し側で
|
|
4175
|
+
* 展開してから渡すこと(ZIP の展開をこのライブラリへ持ち込まない)。
|
|
4176
|
+
*/
|
|
4177
|
+
declare const parseMusicXML: (xml: string) => MusicXmlExtraction;
|
|
4178
|
+
type ExportMusicXmlPart = {
|
|
4179
|
+
/** パート名。楽譜ソフトの左端に出る。 */
|
|
4180
|
+
name: string;
|
|
4181
|
+
notes: Note[];
|
|
4182
|
+
/** 音符に付ける歌詞。`notes` と同じ並び順で対応させる。短ければ足りないぶんは付かない。 */
|
|
4183
|
+
lyrics?: string[];
|
|
4184
|
+
};
|
|
4185
|
+
type ExportMusicXmlOptions = {
|
|
4186
|
+
parts: ExportMusicXmlPart[];
|
|
4187
|
+
bpm: number;
|
|
4188
|
+
stepsPerBar: number;
|
|
4189
|
+
/** 曲名。`<work-title>` に入る。 */
|
|
4190
|
+
title?: string;
|
|
4191
|
+
};
|
|
4192
|
+
/**
|
|
4193
|
+
* トラック群から MusicXML(`score-partwise`)を作る。
|
|
4194
|
+
*
|
|
4195
|
+
* **同時刻に始まる音は `<chord>` でまとめる。** ここを素直に並べて書くと、重なって
|
|
4196
|
+
* いた音が後ろへずれて**曲が変わる**(実測で 384/67 の音が 432 へ動いた)。
|
|
4197
|
+
* `<chord>` は声部の概念ではなく「直前の音と同時に鳴る」という意味なので、
|
|
4198
|
+
* 声部を持たないピアノロールでもそのまま使える。
|
|
4199
|
+
*
|
|
4200
|
+
* 声部は分けない。開始位置が違うまま重なる音(`<backup>` が要る形)は、
|
|
4201
|
+
* **次の音までで切る**——位置は保たれ、音価だけが短くなる。小節をまたぐ音を
|
|
4202
|
+
* 小節の終わりで切るのと同じ扱いで、鳴りの開始と高さは変えない。
|
|
4203
|
+
*/
|
|
4204
|
+
declare const exportMusicXML: (options: ExportMusicXmlOptions) => string;
|
|
4205
|
+
/**
|
|
4206
|
+
* 取り込んだ音を、このライブラリのノート表現へ写す。
|
|
4207
|
+
*
|
|
4208
|
+
* `id` は付けない——採番はピアノロール側(`addNote`)の仕事で、ここで振ると
|
|
4209
|
+
* 既存のノートと衝突する。MIDI 取り込みが `MidiNotePlacement` を返して
|
|
4210
|
+
* 呼び出し側に置かせているのと同じ形。
|
|
4211
|
+
*/
|
|
4212
|
+
declare const musicXmlToNotes: (placements: MusicXmlNotePlacement[], partIndex: number) => Omit<Note, "id">[];
|
|
4213
|
+
|
|
3493
4214
|
type PianoRollInstance = {
|
|
3494
4215
|
core: MMLCore;
|
|
3495
4216
|
getNotes: () => Note[];
|
|
@@ -3517,6 +4238,30 @@ type PianoRollInstance = {
|
|
|
3517
4238
|
};
|
|
3518
4239
|
declare const createPianoRoll: (options: PianoRollOptions, handlers: CoreEventHandlers) => PianoRollInstance;
|
|
3519
4240
|
|
|
4241
|
+
/**
|
|
4242
|
+
* 中国語ピンイン → かな の転写。
|
|
4243
|
+
*
|
|
4244
|
+
* UTAUの中国語音源は原音名をピンインのまま並べる(`xing` `tou` `she`)。
|
|
4245
|
+
* このアプリの歌詞は**かな=日本語のモーラ**しか持てないので、
|
|
4246
|
+
* 「日本語話者がカタカナで書き取る」やり方で寄せる。歌わせた音は
|
|
4247
|
+
* 日本語訛りの中国語になるが、読めない綴りとして落とすよりはるかに近い。
|
|
4248
|
+
*
|
|
4249
|
+
* 割り切っていること:
|
|
4250
|
+
* - **声調は落とす。** 歌では旋律が声調を上書きするので、実害が小さい。
|
|
4251
|
+
* - **日本語に無い音は慣用のカタカナ表記へ倒す。** そり舌音(`zh` `ch` `sh` `r`)は
|
|
4252
|
+
* ジャ/チャ/シャ行、`e`[ɤ] はア段(`le` → ラ、`she` → シャ)、舌尖母音の
|
|
4253
|
+
* `i`(`zi` `ci` `si` `zhi` …)はズ/ツ/ス/ジ…、`ü` はユ段。
|
|
4254
|
+
* - **1音節が複数モーラになる。** `xing` → シ+ン、`tou` → ト+オ、`guo` → グ+オ。
|
|
4255
|
+
* このアプリは1ノート1モーラなので、呼び出し側はノートを割って割り当てる
|
|
4256
|
+
* (撥音を独立ノートに置くのは日本語の歌でも同じ)。
|
|
4257
|
+
*/
|
|
4258
|
+
/**
|
|
4259
|
+
* ピンイン1音節をかなのモーラ列へ写す。ピンインとして読めなければ null。
|
|
4260
|
+
*
|
|
4261
|
+
* 声調番号(`xing1`)と `v` / `u:`(ü の代替綴り)は受ける。
|
|
4262
|
+
*/
|
|
4263
|
+
declare const pinyinToMoras: (raw: string) => string[] | null;
|
|
4264
|
+
|
|
3520
4265
|
/**
|
|
3521
4266
|
* 1つのピアノロール描画面(ヘッダー/鍵盤/グリッドの3枚のCanvas)を表す描画器。
|
|
3522
4267
|
*
|
|
@@ -3575,6 +4320,11 @@ declare const createRenderer: (mountTarget: HTMLElement, width: number | undefin
|
|
|
3575
4320
|
* 旧 demo/index.html の startPlayback / updatePlayback / stopPlayback を移植・整理。
|
|
3576
4321
|
*/
|
|
3577
4322
|
|
|
4323
|
+
/**
|
|
4324
|
+
* `start()` してから実際に曲が始まるまでの安全先読みバッファ(秒)。
|
|
4325
|
+
* 開始時刻を外から逆算したい呼び出し側(伴奏音源との待ち合わせ等)が使う。
|
|
4326
|
+
*/
|
|
4327
|
+
declare const SEQUENCER_START_DELAY = 0.1;
|
|
3578
4328
|
type SequencerTrack = {
|
|
3579
4329
|
id: string;
|
|
3580
4330
|
volume: number;
|
|
@@ -3601,11 +4351,23 @@ type SequencerOptions = {
|
|
|
3601
4351
|
onPlayDrum: (e: PlayDrumEvent) => void;
|
|
3602
4352
|
/** 毎フレーム currentPlayStep を通知(プレイヘッド/オートスクロール用) */
|
|
3603
4353
|
onTick: (currentPlayStep: number) => void;
|
|
4354
|
+
/**
|
|
4355
|
+
* ノートが尽きても、ここで返す秒数(再生開始からの相対秒)までは曲を終わらせない。
|
|
4356
|
+
*
|
|
4357
|
+
* 伴奏音源(mp3等)を一緒に鳴らしているとき、打ち込みが先に終わっても曲は
|
|
4358
|
+
* まだ続いている。これが無いと音源の途中で「終了」してしまい、プレイヘッドも
|
|
4359
|
+
* 止まる。ノートが1つも無くても、これが正なら再生自体は走る。
|
|
4360
|
+
*/
|
|
4361
|
+
getMinEndSec?: () => number;
|
|
3604
4362
|
onEnd: (interrupted?: boolean) => void;
|
|
3605
4363
|
stepsPerBar: number;
|
|
3606
4364
|
};
|
|
3607
4365
|
type Sequencer = {
|
|
3608
|
-
|
|
4366
|
+
/**
|
|
4367
|
+
* 再生を始める。`preRollSec` を渡すと、その秒数だけ**待ってから**曲を走らせる
|
|
4368
|
+
* (伴奏音源の前奏を先に鳴らす用途。曲データには手を触れない)。
|
|
4369
|
+
*/
|
|
4370
|
+
start: (fromStep?: number, preRollSec?: number) => void;
|
|
3609
4371
|
stop: () => void;
|
|
3610
4372
|
isActive: () => boolean;
|
|
3611
4373
|
/**
|
|
@@ -3750,7 +4512,20 @@ type DtmStudioOptions = {
|
|
|
3750
4512
|
chord?: boolean;
|
|
3751
4513
|
/** 編集UIに楽器プリセット選択UIを差し込む。 */
|
|
3752
4514
|
presetUI?: boolean;
|
|
4515
|
+
/**
|
|
4516
|
+
* 編集UIのツールバーにヘルプ(「?」)ボタンを出す。
|
|
4517
|
+
* 使い方モーダルと目的別ガイドツアーの入口になる。
|
|
4518
|
+
*/
|
|
4519
|
+
help?: boolean;
|
|
3753
4520
|
};
|
|
4521
|
+
/**
|
|
4522
|
+
* ガイドツアーの設定。この studio から生やす編集UI全体の既定値になる
|
|
4523
|
+
* (`mountEditor` の `tour` で個別に上書きできる)。
|
|
4524
|
+
*
|
|
4525
|
+
* **自動再生は既定でオフ**。埋め込み先の第一印象を勝手に上書きしないための既定値で、
|
|
4526
|
+
* 初回に流したいアプリだけが `{ autoStart: true }` を渡す。
|
|
4527
|
+
*/
|
|
4528
|
+
tour?: DawOptions["tour"];
|
|
3754
4529
|
/** MIDI検索クライアントの設定(未指定なら検索UI非表示)。 */
|
|
3755
4530
|
midiSearch?: MidiSearchConfig;
|
|
3756
4531
|
/** ドラムパターン辞書。 */
|
|
@@ -3943,6 +4718,12 @@ type DtmStudio = {
|
|
|
3943
4718
|
setDelayAmount: (amount: number) => void;
|
|
3944
4719
|
/** マスタバスのグルーコンプレッサー量を 0-100 で変更する。 */
|
|
3945
4720
|
setMasterCompression: (amount: number) => void;
|
|
4721
|
+
/**
|
|
4722
|
+
* 直近にマウントした編集UIでガイドツアーを開始する。
|
|
4723
|
+
* 自動再生(`tour.autoStart`)を切っていても、独自の「使い方」ボタンから呼べる。
|
|
4724
|
+
* 編集UIが1つも無いときは何もしない。
|
|
4725
|
+
*/
|
|
4726
|
+
startTour: () => void;
|
|
3946
4727
|
/** AudioContext を閉じ、生成物を破棄する。 */
|
|
3947
4728
|
dispose: () => void;
|
|
3948
4729
|
};
|
|
@@ -3958,7 +4739,7 @@ declare const createDtmStudio: (options?: DtmStudioOptions) => Promise<DtmStudio
|
|
|
3958
4739
|
* ドット絵・レトロゲーム風ピクセルアートデザイン(美咲フォント・角なし・ハードシャドウ)。
|
|
3959
4740
|
* モバイルファースト(狭幅基準)で設計し、min-width メディアクエリで広幅に拡張する。
|
|
3960
4741
|
*/
|
|
3961
|
-
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/* \u30DC\u30FC\u30AB\u30EB\u304C\u9078\u629E\u3055\u308C\u3066\u3044\u308B\u30C8\u30E9\u30C3\u30AF\u306E\u30BF\u30D6\u306B\u3001\u3046\u3063\u3059\u3089\u58F0\u306E\u30A2\u30A4\u30B3\u30F3\u3092\u91CD\u306D\u308B\uFF08\u80CC\u666F\u8272\u30FB\u30B5\u30A4\u30BA\u306F\u5909\u3048\u306A\u3044\uFF09 */\n.dtm-pill--vocal::after {\n content: '';\n position: absolute;\n inset: 0;\n background-image: var(--dtm-pill-icon);\n background-size: contain;\n background-repeat: no-repeat;\n background-position: right center;\n opacity: 0.4;\n pointer-events: none;\n}\n.dtm-pill__label {\n position: relative;\n z-index: 1;\n}\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 \u81EA\u52D5\u4F5C\u66F2\u30B3\u30F3\u30C6\u30CA\uFF08\u30DE\u30AF\u30ED\u30D1\u30CD\u30EB\u5185\u306E\u5C02\u7528\u30AB\u30FC\u30C9\u98A8\u30B0\u30EB\u30FC\u30D7\u67A0\uFF09 \u2500\u2500\u2500 */\n.dtm-compose-container {\n background: var(--dtm-deep);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 1px var(--dtm-border2),\n 0 0 0 1px var(--dtm-success),\n 3px 3px 0 var(--c-black);\n padding: 10px 12px 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n margin-bottom: 6px;\n}\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-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-player-loop:active { transform: translate(1px, 1px); }\n.dtm-player-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.dtm-player-loop: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";
|
|
4742
|
+
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 \u30AC\u30A4\u30C9\u30C4\u30A2\u30FC\u306E\u6697\u5E55(.dtm-tour)\u306F document.body \u76F4\u4E0B\u3078\u91CD\u306D\u308B\u305F\u3081\u3001.dtm-daw \u306E\u5916\u306B\u51FA\u308B\u3002\n \u3053\u308C\u3089\u3082\u540C\u69D8\u306B\u30C8\u30FC\u30AF\u30F3\u3092\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.dtm-tour {\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/* \u30DC\u30FC\u30AB\u30EB\u304C\u9078\u629E\u3055\u308C\u3066\u3044\u308B\u30C8\u30E9\u30C3\u30AF\u306E\u30BF\u30D6\u306B\u3001\u3046\u3063\u3059\u3089\u58F0\u306E\u30A2\u30A4\u30B3\u30F3\u3092\u91CD\u306D\u308B\uFF08\u80CC\u666F\u8272\u30FB\u30B5\u30A4\u30BA\u306F\u5909\u3048\u306A\u3044\uFF09 */\n.dtm-pill--vocal::after {\n content: '';\n position: absolute;\n inset: 0;\n background-image: var(--dtm-pill-icon);\n background-size: contain;\n background-repeat: no-repeat;\n background-position: right center;\n opacity: 0.4;\n pointer-events: none;\n}\n.dtm-pill__label {\n position: relative;\n z-index: 1;\n}\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 \u81EA\u52D5\u4F5C\u66F2\u30B3\u30F3\u30C6\u30CA\uFF08\u30DE\u30AF\u30ED\u30D1\u30CD\u30EB\u5185\u306E\u5C02\u7528\u30AB\u30FC\u30C9\u98A8\u30B0\u30EB\u30FC\u30D7\u67A0\uFF09 \u2500\u2500\u2500 */\n.dtm-compose-container {\n background: var(--dtm-deep);\n border: 2px solid var(--c-black);\n box-shadow:\n inset 0 0 0 1px var(--dtm-border2),\n 0 0 0 1px var(--dtm-success),\n 3px 3px 0 var(--c-black);\n padding: 10px 12px 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n margin-bottom: 6px;\n}\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/* \u2500\u2500\u2500 \u30AC\u30A4\u30C9\u30C4\u30A2\u30FC\uFF08\u30B9\u30DD\u30C3\u30C8\u30E9\u30A4\u30C8\u578B\u30A6\u30A9\u30FC\u30AF\u30B9\u30EB\u30FC\uFF09 \u2500\u2500\u2500 */\n/* \u6697\u5E55\u306F .dtm-tour-spot \u306E\u5DE8\u5927\u306A box-shadow \u3067\u63CF\u304F\u3002box-shadow \u306F\u5F53\u305F\u308A\u5224\u5B9A\u3092\n \u6301\u305F\u306A\u3044\u306E\u3067\u3001\u7D20\u901A\u308A\u3092\u9632\u3050\u906E\u853D\u306F .dtm-tour\uFF08\u5168\u753B\u9762\uFF09\u5074\u304C\u62C5\u5F53\u3059\u308B\u3002 */\n.dtm-tour {\n position: fixed;\n inset: 0;\n z-index: 10050;\n color: var(--dtm-text);\n font-family: var(--dtm-font);\n font-size: 13px;\n line-height: 1.6;\n letter-spacing: .04em;\n -webkit-font-smoothing: none;\n font-smooth: never;\n -webkit-tap-highlight-color: transparent;\n}\n.dtm-tour * { box-sizing: border-box; max-width: 100%; }\n.dtm-tour-spot {\n position: fixed;\n left: 0;\n top: 0;\n width: 0;\n height: 0;\n pointer-events: none;\n border: 2px solid var(--dtm-gold);\n box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.74);\n transition: left .18s ease, top .18s ease, width .18s ease, height .18s ease;\n}\n.dtm-tour-bubble {\n position: fixed;\n left: 0;\n top: 0;\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 transition: left .18s ease, top .18s ease;\n}\n.dtm-tour-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: var(--dtm-deep);\n padding: 4px 8px;\n border-bottom: 2px solid var(--c-black);\n}\n.dtm-tour-progress {\n font-size: 11px;\n color: var(--dtm-muted);\n}\n.dtm-tour-close {\n background: transparent;\n border: none;\n color: var(--dtm-text);\n font-size: 18px;\n line-height: 1;\n padding: 0 2px;\n cursor: pointer;\n}\n.dtm-tour-close:hover { color: var(--dtm-danger); }\n.dtm-tour-title {\n padding: 8px 10px 0;\n color: var(--dtm-gold);\n font-size: 14px;\n font-weight: bold;\n}\n.dtm-tour-body {\n padding: 6px 10px 8px;\n max-height: 40vh;\n overflow-y: auto;\n}\n.dtm-tour-body p { margin: 0 0 6px; }\n.dtm-tour-body p:last-child { margin-bottom: 0; }\n.dtm-tour-body ul { margin: 0 0 6px; padding-left: 16px; }\n.dtm-tour-body li { margin-bottom: 3px; }\n.dtm-tour-body b { color: var(--dtm-primary); }\n.dtm-tour-foot {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n border-top: 2px solid var(--c-black);\n background: var(--dtm-deep);\n}\n.dtm-tour-spacer { flex: 1; }\n.dtm-tour-btn {\n min-height: 32px;\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: 12px;\n letter-spacing: .04em;\n cursor: pointer;\n box-shadow: 2px 2px 0 var(--c-black);\n}\n.dtm-tour-btn:active { transform: translate(2px, 2px); box-shadow: none; }\n.dtm-tour-btn--ghost { background: transparent; }\n.dtm-tour-btn--primary {\n border-color: var(--dtm-primary);\n background: var(--dtm-primary);\n color: var(--dtm-pfg);\n}\n\n/* \u76EE\u7684\u5225\u306E\u5206\u5C90\uFF08\u30C4\u30A2\u30FC\u5192\u982D\u306E\u300C\u4F55\u3092\u3057\u3066\u307F\u305F\u3044\uFF1F\u300D\uFF09\u3002 */\n.dtm-tour-branches {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: 8px;\n}\n.dtm-tour-branch {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n width: 100%;\n min-height: var(--dtm-tap);\n padding: 6px 10px;\n border: 2px solid var(--dtm-primary);\n background: var(--dtm-surface);\n color: var(--dtm-text);\n font-family: var(--dtm-font);\n font-size: 13px;\n letter-spacing: .04em;\n text-align: left;\n cursor: pointer;\n box-shadow: 2px 2px 0 var(--c-black);\n}\n.dtm-tour-branch:active { transform: translate(2px, 2px); box-shadow: none; }\n.dtm-tour-branch-label { color: var(--dtm-primary); }\n.dtm-tour-branch-hint { font-size: 11px; color: var(--dtm-muted); }\n\n/* \u2500\u2500\u2500 \u30D8\u30EB\u30D7\u306E\u30CF\u30D6\uFF08\u300C?\u300D\u30DC\u30BF\u30F3\u306E\u30E2\u30FC\u30C0\u30EB\uFF09 \u2500\u2500\u2500 */\n.dtm-help-goals {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin: 6px 0 4px;\n}\n/* \u76EE\u7684\u30DC\u30BF\u30F3\u306F\u898B\u51FA\u3057\u3068\u88DC\u8DB3\u306E2\u6BB5\u7D44\u307F\u306B\u3059\u308B\u3002.dtm-btn \u306F1\u884C\u524D\u63D0\u306E\n inline-flex \u306A\u306E\u3067\u3001\u3053\u3053\u3060\u3051\u7E26\u4E26\u3073\u30FB\u5DE6\u5BC4\u305B\u30FB\u9AD8\u3055\u81EA\u52D5\u306B\u4E0A\u66F8\u304D\u3059\u308B\u3002 */\n.dtm-help-goals .dtm-btn {\n flex-direction: column;\n align-items: flex-start;\n gap: 1px;\n width: 100%;\n height: auto;\n padding: 6px 10px;\n text-align: left;\n white-space: normal;\n}\n.dtm-help-goals .dtm-btn small {\n font-size: 11px;\n opacity: .8;\n font-weight: normal;\n}\n.dtm-help-topics {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 6px;\n}\n.dtm-help-topics .dtm-btn {\n min-height: 32px;\n padding: 0 8px;\n font-size: 12px;\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/* \u4F34\u594F\u97F3\u6E90\uFF08mp3/wav/YouTube\uFF09\u306E\u72B6\u614B\u8868\u793A\u3002\u8AAD\u307F\u8FBC\u307F\u7D50\u679C\u306E\u5831\u544A\u306A\u306E\u3067\u8B66\u544A\u8272\u306B\u306F\u3057\u306A\u3044\u3002 */\n.dtm-audio-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-muted);\n}\n.dtm-audio-note--warn { color: var(--dtm-warn); font-weight: bold; }\n/* YouTube\u30D7\u30EC\u30A4\u30E4\u30FC\u306E\u67A0\u3002API\u304C\u4E2D\u306E\u8981\u7D20\u3092 iframe \u3078\u5DEE\u3057\u66FF\u3048\u308B\u306E\u3067\u3001\u67A0\u5074\u3067\u5BF8\u6CD5\u3092\u6C7A\u3081\u308B\u3002 */\n.dtm-audio-yt { width: 100%; max-width: 320px; aspect-ratio: 16 / 9; }\n.dtm-audio-yt iframe { display: block; width: 100%; height: 100%; border: 0; }\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-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-player-loop:active { transform: translate(1px, 1px); }\n.dtm-player-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.dtm-player-loop: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";
|
|
3962
4743
|
/**
|
|
3963
4744
|
* スタイルを document.head に一度だけ注入する。
|
|
3964
4745
|
*/
|
|
@@ -4019,6 +4800,27 @@ type SynthTone = {
|
|
|
4019
4800
|
*/
|
|
4020
4801
|
declare const createSynth: (ctx: AudioContext, destination?: AudioNode, tone?: SynthTone) => Synth;
|
|
4021
4802
|
|
|
4803
|
+
/**
|
|
4804
|
+
* ライブラリのバージョン。**書き出したファイルに埋め込むための唯一の出どころ。**
|
|
4805
|
+
*
|
|
4806
|
+
* ## 何のためにあるか
|
|
4807
|
+
*
|
|
4808
|
+
* 自動作曲が使う素材(`compose-phrases.ts` など)は外部のコーパスから作る。
|
|
4809
|
+
* そのコーパスに「パブリックドメインとして投稿されたが実はそうでなかった」ものが
|
|
4810
|
+
* 混じっていた場合、**どのバージョンまでが汚染されているか**を後から言えなければ、
|
|
4811
|
+
* 是正のしようがない(全部を疑うか、何もしないかの二択になる)。
|
|
4812
|
+
*
|
|
4813
|
+
* だから書き出す MML と MIDI にバージョンを埋める。手元のファイルを見れば、
|
|
4814
|
+
* それがどの素材から作られたかを {@link file://../docs/dataset-provenance.md} で
|
|
4815
|
+
* 引ける。素材を差し替えたらバージョンを上げ、台帳に追記する。
|
|
4816
|
+
*
|
|
4817
|
+
* ## package.json と二重管理になる理由
|
|
4818
|
+
*
|
|
4819
|
+
* ブラウザ向けのバンドルに `package.json` を読ませたくないので定数で持つ。
|
|
4820
|
+
* ずれると埋め込む値が嘘になるので、`scripts/check-version.ts` が一致を検算する。
|
|
4821
|
+
*/
|
|
4822
|
+
declare const DTM_VERSION = "2.1.12";
|
|
4823
|
+
|
|
4022
4824
|
declare const VOICE_IMAGES: Record<string, string>;
|
|
4023
4825
|
|
|
4024
4826
|
/**
|
|
@@ -4034,4 +4836,4 @@ declare const encodeWavPCM16: (channels: Float32Array[], sampleRate: number) =>
|
|
|
4034
4836
|
/** Float32Array のチャンクを1本へ連結する。 */
|
|
4035
4837
|
declare const concatFloat32: (chunks: Float32Array[]) => Float32Array;
|
|
4036
4838
|
|
|
4037
|
-
export { A4_HZ, A4_UNITS, type AddNoteOptions, type AnyDrumPattern, type ApplyChordOptions, BLUES_SCALE, BREATH_MARK, type Band, CENTS_PER_UNIT, COMPOSE_KEYS, COMPOSE_MOOD_GROUPS, COMPOSE_SCALES, COMPOSE_SCALE_IDS, CORPUS_BANDS, CORPUS_SIZE, type ChordPatternType, type ChordPlacement, type ChordPlayerInstance, type ComposeKeyTarget, type ComposeMoodGroup, type ComposeMoodId, type ComposeOptions, type ComposeResult, type ComposeScale, type ComposeScaleId, 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, type ExportMidiTrack, FADE_IN_MARK, FADE_OUT_MARK, type FadeScheduleParams, GLOBAL_STORAGE_KEYS, GM_BRIGHT_CEILING, GM_INSTRUMENT_NAMES, GM_INSTRUMENT_RANGE, type GlobalStorageKey, HARMONIC_MINOR_SCALE, HUNGARIAN_SCALE, INSTRUMENT_PRESETS, type InstrumentPreset, KEY_COUNT, KOE_BASE_URL, KOE_VOICEBANKS, KOE_VOICEBANK_LABELS, KOE_VOICEBANK_TERMS, type KeyMode, type KoeVoiceOptions, LinkedList, type LoopConfig, type LoopPoint, type LyricSyllable, type LyricSyllableKind, type LyricSyncData, type LyricTrack, type LyricsConductor, MACRO_STORAGE_KEYS, MAJOR_KEY_IDS, MAJOR_SCALE, MAX_VOCAL_VOLUME, MICRO_STEP, MINOR_KEY_IDS, MMLCore, type MMLDisplayToken, type MMLNotePlacement, MML_END_MARKER, type MacroStorageKey, type MelodyForm, 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, type ResolvedComposeKey, SECTION_LABELS, SECTION_ORDER, SECTION_SPECS, STOP_MARK, type ScaleDegree, 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 TonicCenter, type TonicDegree, 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, fitInstrumentOctave, formatMmlMeta, freqFromPitch, generateRandomPattern, getComposeKeyDescription, getComposeScaleDescription, 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, programOfInstrumentName, readGlobalBool, readGlobalNumber, readGlobalSetting, readMacroSections, readMacroSetting, resolveCenter, resolveComposeKey, resolveComposeScale, resolveDrumPattern, resolveLoopPoint, scaleDegrees, scaleSize, semitonesToUnits, shiftNotes, showLoadingOverlay, spelledToUnits, spellingToUnits, stripCustomVocals, stripLyrics, stripMmlMeta, structureFeatures, syllablesToText, tensionFeatures, transposeNotes, units, unitsPerRow, unitsPerStep, unitsToHz, unitsToMidi, unitsToMidiDetune, unitsToPitchV1, vocalVolumeToGain, writeGlobalSetting, writeMacroSections, writeMacroSetting };
|
|
4839
|
+
export { A4_HZ, A4_UNITS, type AddNoteOptions, type AnyDrumPattern, type ApplyChordOptions, BLUES_SCALE, BREATH_MARK, type BackingAudio, type BackingAudioOptions, type BackingLoaded, type BackingMode, type BackingStartOptions, type Band, type BuildUstOptions, CENTS_PER_UNIT, COMPOSE_KEYS, COMPOSE_MOOD_GROUPS, COMPOSE_SCALES, COMPOSE_SCALE_IDS, CORPUS_BANDS, CORPUS_SIZE, type ChordPatternType, type ChordPlacement, type ChordPlayerInstance, type ComposeKeyTarget, type ComposeMoodGroup, type ComposeMoodId, type ComposeOptions, type ComposeResult, type ComposeScale, type ComposeScaleId, type ComposeStats, type ComposedNote, type ConsumedSyllable, type CoreEventHandlers, type CustomVocalDef, DAW_CSS, DAW_TOUR_BRANCHES, DAW_TOUR_STEPS, DEFAULT_BPM, DEFAULT_EDO, DEFAULT_GATE, DEFAULT_PAN, DEFAULT_PLAYBACK_VELOCITY, DEFAULT_SECTIONS, DEFAULT_STEPS_PER_BAR, DEFAULT_UST_LYRIC, DEFAULT_VELOCITY, DEFAULT_VOCAL_VOLUME, DRUM_FONT, DRUM_KEYS, DRUM_PATTERNS, DTM_VERSION, 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 ExportMidiTrack, type ExportMusicXmlOptions, type ExportMusicXmlPart, FADE_IN_MARK, FADE_OUT_MARK, type FadeScheduleParams, type FadeStop, GLOBAL_STORAGE_KEYS, GM_BRIGHT_CEILING, GM_INSTRUMENT_NAMES, GM_INSTRUMENT_RANGE, type GlobalStorageKey, HARMONIC_MINOR_SCALE, HUNGARIAN_SCALE, INSTRUMENT_PRESETS, type InstrumentPreset, KEY_COUNT, KOE_BASE_URL, KOE_VOICEBANKS, KOE_VOICEBANK_LABELS, KOE_VOICEBANK_TERMS, type KeyMode, type KoeVoiceOptions, LinkedList, type LoopConfig, type LoopPoint, type LyricSyllable, type LyricSyllableKind, type LyricSyncData, type LyricTrack, type LyricsConductor, MACRO_STORAGE_KEYS, MAJOR_KEY_IDS, MAJOR_SCALE, MAX_VOCAL_VOLUME, MICRO_STEP, MINOR_KEY_IDS, MMLCore, type MMLDisplayToken, type MMLNotePlacement, MML_END_MARKER, type MacroStorageKey, type MelodyForm, 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 MusicXmlExtraction, type MusicXmlNotePlacement, type MusicXmlPart, 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, type ResolvedComposeKey, SECTION_LABELS, SECTION_ORDER, SECTION_SPECS, SEQUENCER_START_DELAY, STOP_MARK, type ScaleDegree, 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, TOUR_STORAGE_KEY, TRACKS_ADVANCED, TRACKS_SIMPLE, type TensionFeatures, type TieSourceNote, type TonicCenter, type TonicDegree, type ToolMode, type TourBranch, type TourInstance, type TourLabels, type TourOptions, type TourStep, type TrackConfig, UNITS_PER_EDO31_DEGREE, UNITS_PER_OCTAVE, UNITS_PER_SEMITONE, UST_TICKS_PER_BEAT, type Units, type UstNotePlacement, type UstTrackData, VIBRATO_MIN_SEC, VOICE_IMAGES, VOICE_IMAGE_KEY, type VoiceExpression, type VoiceModel, type VoiceRegistry, addUnits, analyzeMidiTracks, applyHarmonicFilter, applyMonophonic, backingMediaSec, band, buildChordPlacements, buildDrumPatternJson, buildNameToKeyMapping, buildSectionPlan, buildStreamVoiceNotes, buildUst, chromaticStep, clearTourSeen, collectPitchTokens, complementarity, composeSong, concatFloat32, createAudioContext, createBackingAudio, createDtmStudio, createKlattVoice, createKoeVoice, createLyricsConductor, createPianoRoll, createRenderer, createSequencer, createSingingVoices, createSynth, createVoiceRegistry, decodeMml, decodeUstText, decomposeToMonophonic, displayKana, durationEntropy, encodeMml, encodeWavPCM16, exportMIDI, exportMusicXML, extractDrumPatternFromNotes, extractMidiDrumPattern, extractMidiPlacements, extractMidiPlacementsByTrack, featureDistance, featureVector, fifthToStep, fifthToUnits, fitInstrumentOctave, formatMmlMeta, formatTimeSec, freqFromPitch, generateRandomPattern, getComposeKeyDescription, getComposeScaleDescription, getDrumPatternKeys, getMidiBPM, hasSeenTour, icon, injectStyles, isChordHeavyTrack, isNaturalLetter, isPlausibleMidiTranscription, isTourTargetVisible, isValidHttpUrl, isYoutubeUrl, keyCountFor, koeUrl, looksLikePinyin, lyricToSyllable, markTourSeen, midiNote, midiToUnits, mountChordPlayer, mountDAW, mountMmlPlayer, musicXmlToNotes, naturalStep, normalizeDrumPatterns, normalizeLyrics, panToStereo, parseCustomVocals, parseLyrics, parseMML, parseMmlMeta, parseMusicXML, parseTimeSec, parseUst, parseYoutubeId, pinyinToMoras, pitchV1ToUnits, playChords, playMML, playNote, playPlacements, playSingingMML, programOfInstrumentName, readGlobalBool, readGlobalNumber, readGlobalSetting, readMacroSections, readMacroSetting, resolveCenter, resolveComposeKey, resolveComposeScale, resolveDrumPattern, resolveLoopPoint, resolveYoutubeThumbnail, scaleDegrees, scaleSize, sectionPlanBarRange, semitonesToUnits, shiftNotes, showLoadingOverlay, spelledToUnits, spellingToUnits, startTour, stripCustomVocals, stripLyrics, stripMmlMeta, structureFeatures, syllablesToText, tensionFeatures, transposeNotes, units, unitsPerRow, unitsPerStep, unitsToHz, unitsToMidi, unitsToMidiDetune, unitsToPitchV1, vocalVolumeToGain, writeGlobalSetting, writeMacroSections, writeMacroSetting };
|