@onjmin/dtm 0.1.24 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -99,7 +99,6 @@ __export(index_exports, {
99
99
  playMML: () => playMML,
100
100
  playSingingMML: () => playSingingMML,
101
101
  setDrawOffset: () => setDrawOffset,
102
- setupRecorder: () => setupRecorder,
103
102
  shiftNotes: () => shiftNotes,
104
103
  stripLyrics: () => stripLyrics,
105
104
  stripMmlMeta: () => stripMmlMeta,
@@ -116,34 +115,6 @@ function createAudioContext() {
116
115
  drumGainNode.connect(audioCtx.destination);
117
116
  return { audioCtx, gainNode, drumGainNode };
118
117
  }
119
- function setupRecorder(audioCtx, gainNode, drumGainNode) {
120
- let isRecording = false;
121
- let recordedData = [[], []];
122
- const recorderProcessor = audioCtx.createScriptProcessor(4096, 2, 2);
123
- recorderProcessor.onaudioprocess = (e) => {
124
- if (!isRecording) return;
125
- const left = e.inputBuffer.getChannelData(0);
126
- const right = e.inputBuffer.getChannelData(1);
127
- recordedData[0].push(left.slice());
128
- recordedData[1].push(right.slice());
129
- };
130
- gainNode.connect(recorderProcessor);
131
- drumGainNode.connect(recorderProcessor);
132
- recorderProcessor.connect(audioCtx.destination);
133
- return {
134
- startRecording: () => {
135
- isRecording = true;
136
- },
137
- stopRecording: () => {
138
- isRecording = false;
139
- },
140
- getRecordedData: () => recordedData,
141
- isRecording: () => isRecording,
142
- clearRecordedData: () => {
143
- recordedData = [[], []];
144
- }
145
- };
146
- }
147
118
  async function fetchSoundFontList(ttl) {
148
119
  const res = await fetch(`https://rpgen3.github.io/soundfont/list/${ttl}.txt`);
149
120
  const str = await res.text();
@@ -1240,8 +1211,9 @@ var buildUI = (target, options) => {
1240
1211
  target.innerHTML = `
1241
1212
  <div class="dtm-daw" data-dtm="root">
1242
1213
  <div class="dtm-topbar" data-dtm="transport">
1214
+ <button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
1243
1215
  <button class="dtm-play" data-dtm="play" disabled>${icon("play")}<span>\u8A66\u8074</span></button>
1244
- <button class="dtm-iconbtn dtm-rec" data-dtm="rec" title="\u9332\u97F3">${icon("record")}</button>
1216
+ <button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
1245
1217
  <label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
1246
1218
  <span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
1247
1219
  <span class="dtm-grow"></span>
@@ -1406,10 +1378,12 @@ var buildUI = (target, options) => {
1406
1378
  </div>
1407
1379
  <div class="dtm-output dtm-hidden" data-dtm="output-container">
1408
1380
  <p class="dtm-label" data-dtm="output-status"></p>
1381
+ <div class="dtm-output-label">\u6539\u884C\u3042\u308A\u7248</div>
1409
1382
  <div class="dtm-output-row">
1410
1383
  <pre><code data-dtm="output-full"></code></pre>
1411
1384
  <button class="dtm-btn dtm-btn--primary dtm-btn--icon" data-dtm="copy-full" title="\u30B3\u30D4\u30FC">${icon("copy")}</button>
1412
1385
  </div>
1386
+ <div class="dtm-output-label">\uFF11\u884C\u7248</div>
1413
1387
  <div class="dtm-output-row">
1414
1388
  <pre><code data-dtm="output-mini"></code></pre>
1415
1389
  <button class="dtm-btn dtm-btn--primary dtm-btn--icon" data-dtm="copy-mini" title="\u30B3\u30D4\u30FC">${icon("copy")}</button>
@@ -1428,6 +1402,7 @@ var buildUI = (target, options) => {
1428
1402
  <div class="dtm-modal-body" data-dtm="modal-body"></div>
1429
1403
  </div>
1430
1404
  </div>
1405
+
1431
1406
  </div>`;
1432
1407
  const root = q(target, '[data-dtm="root"]');
1433
1408
  const sel = (name) => q(root, `[data-dtm="${name}"]`);
@@ -1436,7 +1411,8 @@ var buildUI = (target, options) => {
1436
1411
  topbar: sel("transport"),
1437
1412
  topbarLoading: sel("topbar-loading"),
1438
1413
  playBtn: sel("play"),
1439
- recBtn: sel("rec"),
1414
+ prevBarBtn: sel("prev-bar"),
1415
+ nextBarBtn: sel("next-bar"),
1440
1416
  soloCheckbox: sel("solo"),
1441
1417
  toolPen: sel("tool-pen"),
1442
1418
  toolSelect: sel("tool-select"),
@@ -2611,7 +2587,20 @@ var createWorkerBackend = async (workerUrl, options) => {
2611
2587
  };
2612
2588
  };
2613
2589
  var createKoeVoice = async (ctx, destination, options) => {
2614
- const backend = options.voiceWorkerUrl ? await createWorkerBackend(options.voiceWorkerUrl, options) : await createLocalBackend(options);
2590
+ let backend;
2591
+ if (options.voiceWorkerUrl) {
2592
+ try {
2593
+ backend = await createWorkerBackend(options.voiceWorkerUrl, options);
2594
+ } catch (err2) {
2595
+ console.warn(
2596
+ "[dtm] Failed to spawn voice worker. Falling back to local backend.",
2597
+ err2
2598
+ );
2599
+ backend = await createLocalBackend(options);
2600
+ }
2601
+ } else {
2602
+ backend = await createLocalBackend(options);
2603
+ }
2615
2604
  const renderCache = /* @__PURE__ */ new Map();
2616
2605
  const inflight = /* @__PURE__ */ new Map();
2617
2606
  const active = /* @__PURE__ */ new Set();
@@ -2792,7 +2781,7 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2792
2781
  if (!m?.renderToCache) continue;
2793
2782
  let n = 0;
2794
2783
  forEachSungNote(track, (note, prevVowel) => {
2795
- if (n >= count) return;
2784
+ if (n >= count && note.startSec >= 3) return;
2796
2785
  n++;
2797
2786
  promises.push(
2798
2787
  m.renderToCache?.(
@@ -2825,14 +2814,18 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2825
2814
  if (opts?.isAudible && !opts.isAudible(track)) continue;
2826
2815
  const t0 = anchorTime + note.startSec;
2827
2816
  if (model.renderToCache && model.scheduleCached) {
2828
- const key = await model.renderToCache(
2829
- note.syllable,
2830
- prevVowel,
2831
- note.pitch,
2832
- note.durationSec * 1e3
2833
- );
2834
- if (session !== streamSession) return;
2835
- if (key) model.scheduleCached(key, t0, peak, track.pan);
2817
+ const renderToCache = model.renderToCache;
2818
+ const scheduleCached = model.scheduleCached;
2819
+ void (async () => {
2820
+ const key = await renderToCache(
2821
+ note.syllable,
2822
+ prevVowel,
2823
+ note.pitch,
2824
+ note.durationSec * 1e3
2825
+ );
2826
+ if (session !== streamSession) return;
2827
+ if (key) scheduleCached(key, t0, peak, track.pan);
2828
+ })();
2836
2829
  } else {
2837
2830
  const when = t0 - ctx.currentTime;
2838
2831
  model(note.syllable, {
@@ -4126,6 +4119,110 @@ var isChordHeavyTrack = (notes, threshold = 0.6) => {
4126
4119
  return chordNotes / notes.length >= threshold;
4127
4120
  };
4128
4121
 
4122
+ // src/mml-info.ts
4123
+ var MML_INFO_HTML = `
4124
+ <div class="dtm-modal-body-content">
4125
+ <h4>1. \u97F3\u7B26\u3068\u4F11\u7B26</h4>
4126
+ <p><code>c</code>(\u30C9) <code>d</code>(\u30EC) <code>e</code>(\u30DF) <code>f</code>(\u30D5\u30A1) <code>g</code>(\u30BD) <code>a</code>(\u30E9) <code>b</code>(\u30B7) \u306E\u30A2\u30EB\u30D5\u30A1\u30D9\u30C3\u30C8\u3067\u8868\u3057\u307E\u3059\u3002</p>
4127
+ <ul>
4128
+ <li>\u534A\u97F3\u4E0A\u3052\u308B: <code>c#</code> \u307E\u305F\u306F <code>c+</code></li>
4129
+ <li>\u534A\u97F3\u4E0B\u3052\u308B: <code>d-</code></li>
4130
+ <li>\u4F11\u7B26: <code>r</code></li>
4131
+ </ul>
4132
+
4133
+ <h4>2. \u97F3\u306E\u9577\u3055</h4>
4134
+ <p>\u97F3\u540D\u3084\u4F11\u7B26\u306E\u5F8C\u306B\u6570\u5024\u3067\u6307\u5B9A\u3057\u307E\u3059\uFF08\u4F8B: <code>4</code> = 4\u5206\u97F3\u7B26, <code>8</code> = 8\u5206\u97F3\u7B26, <code>16</code> = 16\u5206\u97F3\u7B26\uFF09\u3002</p>
4135
+ <ul>
4136
+ <li><code>c4</code> : 4\u5206\u97F3\u7B26\u306E\u30C9</li>
4137
+ <li><code>r8</code> : 8\u5206\u4F11\u7B26</li>
4138
+ <li><code>c4.</code> : \u4ED8\u70B94\u5206\u97F3\u7B26\u306E\u30C9\uFF08\u9577\u3055\u30921.5\u500D\u306B\uFF09</li>
4139
+ <li>\u6570\u5024\u3092\u7701\u7565\u3059\u308B\u3068\u3001<code>l</code> \u30B3\u30DE\u30F3\u30C9\u3067\u8A2D\u5B9A\u3055\u308C\u305F\u30C7\u30D5\u30A9\u30EB\u30C8\u9577\uFF08\u901A\u5E3816\u5206\uFF09\u306B\u306A\u308A\u307E\u3059\u3002</li>
4140
+ </ul>
4141
+
4142
+ <h4>3. \u30AA\u30AF\u30BF\u30FC\u30D6\uFF08\u97F3\u306E\u9AD8\u3055\uFF09</h4>
4143
+ <ul>
4144
+ <li><code>o4</code>, <code>o5</code> : \u9AD8\u3055\u3092\u76F4\u63A5\u6307\u5B9A\uFF08\u3075\u3064\u3046\u306F o4 \u304B o5\uFF09</li>
4145
+ <li><code>&gt;</code> : 1\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0A\u3052\u308B</li>
4146
+ <li><code>&lt;</code> : 1\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0B\u3052\u308B</li>
4147
+ </ul>
4148
+
4149
+ <h4>4. \u30C6\u30F3\u30DD</h4>
4150
+ <ul>
4151
+ <li><code>t120</code> : \u66F2\u306E\u901F\u3055\u3092BPM120\u306B\u6307\u5B9A\u3002\u203B\u30E1\u30ED\u30C7\u30A3\uFF08@0\uFF09\u306E\u30C6\u30F3\u30DD\u6307\u5B9A\u304C\u66F2\u5168\u4F53\u306B\u53CD\u6620\u3055\u308C\u307E\u3059\u3002</li>
4152
+ </ul>
4153
+
4154
+ <h4>5. \u548C\u97F3</h4>
4155
+ <p>\u97F3\u7B26\u3092 <code>[</code> \u3068 <code>]</code> \u3067\u56F2\u3080\u3068\u540C\u6642\u306B\u767A\u97F3\u3057\u307E\u3059\u3002</p>
4156
+ <pre>\u4F8B: [ceg]4 \uFF08\u30C9\u30FB\u30DF\u30FB\u30BD\u30924\u5206\u97F3\u7B26\u3067\u540C\u6642\u306B\u767A\u97F3\uFF09</pre>
4157
+
4158
+ <h4>6. \u30C8\u30E9\u30C3\u30AF\u306E\u533A\u5207\u308A</h4>
4159
+ <p><code>;</code> \u307E\u305F\u306F <code>@0</code>\u301C<code>@3</code> \u3067\u30C8\u30E9\u30C3\u30AF\u3092\u5207\u308A\u66FF\u3048\u307E\u3059\u3002</p>
4160
+ <ul>
4161
+ <li><code>@0</code>: \u30E1\u30ED\u30C7\u30A3</li>
4162
+ <li><code>@1</code>: \u30B5\u30D6\u30E1\u30ED</li>
4163
+ <li><code>@2</code>: \u30D9\u30FC\u30B9</li>
4164
+ <li><code>@3</code>: \u4F34\u594F</li>
4165
+ </ul>
4166
+
4167
+ <h4>7. \u6B4C\u58F0\u30FB\u6B4C\u8A5E\u5165\u529B</h4>
4168
+ <p><code>@@&lt;\u30C8\u30E9\u30C3\u30AF\u756A\u53F7&gt; &lt;\u97F3\u6E90\u540D&gt; &lt;\u6B4C\u8A5E&gt;</code> \u306E\u5F62\u5F0F\u3067\u3001\u97F3\u7B26\u3068\u540C\u671F\u3059\u308B\u6B4C\u8A5E\u3092\u5165\u529B\u3067\u304D\u307E\u3059\u3002</p>
4169
+ <pre>\u4F8B: @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053</pre>
4170
+ <p style="margin-top:4px; margin-bottom:16px;"><small>\uFF08\u97F3\u6E90\u540D\u306F <code>tsukuyomi</code> \u3084 <code>klatt</code>, <code>roze</code> \u306A\u3069\u306E\u97F3\u58F0\u30E2\u30C7\u30EB\u3092\u6307\u5B9A\u3067\u304D\u307E\u3059\uFF09</small></p>
4171
+
4172
+ <h4 style="margin-top: 18px; border-top: 1px solid var(--dtm-border2); padding-top: 8px;">\u30B5\u30F3\u30D7\u30EB\u66F2\uFF08\u8A66\u8074\u30FB\u30B3\u30D4\u30FC\uFF09</h4>
4173
+
4174
+ <!-- \u30B5\u30F3\u30D7\u30EB1 -->
4175
+ <div class="dtm-modal-sample-box">
4176
+ <div class="dtm-modal-sample-header">
4177
+ <span class="dtm-modal-sample-tag">1. \u57FA\u672C\u306E\u30E1\u30ED\u30C7\u30A3</span>
4178
+ <button class="dtm-btn dtm-btn--ghost dtm-btn--xs dtm-modal-sample-copy-btn" data-mml="@0 t120 l8 o5 c d e f g a b > c">\u{1F4CB} \u30B3\u30D4\u30FC</button>
4179
+ </div>
4180
+ <pre style="margin: 0; padding: 6px;">@0 t120 l8 o5 c d e f g a b &gt; c</pre>
4181
+ <div class="dtm-modal-sample-desc">
4182
+ \u57FA\u672C\u7684\u306A\u30E1\u30ED\u30C7\u30A3\u306E\u66F8\u304D\u65B9\uFF08\u97F3\u540D\u30FB\u9577\u3055\u30FB\u30AA\u30AF\u30BF\u30FC\u30D6\u3068\u30C6\u30F3\u30DD\uFF09\u3002
4183
+ </div>
4184
+ <div style="margin-top: 8px;">
4185
+ <button class="dtm-btn dtm-btn--primary dtm-btn--xs dtm-modal-sample-play-btn" data-mml="@0 t120 l8 o5 c d e f g a b > c">\u25B6 \u8A66\u8074</button>
4186
+ </div>
4187
+ <div class="dtm-modal-sample-player-container"></div>
4188
+ </div>
4189
+
4190
+ <!-- \u30B5\u30F3\u30D7\u30EB2 -->
4191
+ <div class="dtm-modal-sample-box">
4192
+ <div class="dtm-modal-sample-header">
4193
+ <span class="dtm-modal-sample-tag">2. \u8907\u6570\u30C8\u30E9\u30C3\u30AF\u3068\u548C\u97F3</span>
4194
+ <button class="dtm-btn dtm-btn--ghost dtm-btn--xs dtm-modal-sample-copy-btn" data-mml="@0 t120 o5 c e g2 ; @3 o4 [ceg]2 [ceg]2">\u{1F4CB} \u30B3\u30D4\u30FC</button>
4195
+ </div>
4196
+ <pre style="margin: 0; padding: 6px;">@0 t120 o5 c e g2 ;
4197
+ @3 o4 [ceg]2 [ceg]2</pre>
4198
+ <div class="dtm-modal-sample-desc">
4199
+ ; \u3067\u30C8\u30E9\u30C3\u30AF\uFF08\u4E0A\uFF1D\u30E1\u30ED\u30C7\u30A3\uFF0F\u4E0B\uFF1D\u4F34\u594F\uFF09\u3092\u5206\u3051\u3001[ceg] \u3067\u548C\u97F3\u3092\u9CF4\u3089\u3057\u307E\u3059\u3002
4200
+ </div>
4201
+ <div style="margin-top: 8px;">
4202
+ <button class="dtm-btn dtm-btn--primary dtm-btn--xs dtm-modal-sample-play-btn" data-mml="@0 t120 o5 c e g2 ; @3 o4 [ceg]2 [ceg]2">\u25B6 \u8A66\u8074</button>
4203
+ </div>
4204
+ <div class="dtm-modal-sample-player-container"></div>
4205
+ </div>
4206
+
4207
+ <!-- \u30B5\u30F3\u30D7\u30EB3 -->
4208
+ <div class="dtm-modal-sample-box">
4209
+ <div class="dtm-modal-sample-header">
4210
+ <span class="dtm-modal-sample-tag">3. \u6B4C\u5531\u4ED8\u304D (\u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D)</span>
4211
+ <button class="dtm-btn dtm-btn--ghost dtm-btn--xs dtm-modal-sample-copy-btn" data-mml="@0 t120 v100 o4g8 g8 e8 e8 f8 e8 d8 c8 g8 g8 e8 e8 d4.; @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053;">\u{1F4CB} \u30B3\u30D4\u30FC</button>
4212
+ </div>
4213
+ <pre style="margin: 0; padding: 6px;">@0 t120 v100 o4g8 g8 e8 e8 f8 e8 d8 c8 g8 g8 e8 e8 d4.;
4214
+ @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053;</pre>
4215
+ <div class="dtm-modal-sample-desc">
4216
+ @@0 tsukuyomi \u6B4C\u8A5E... \u3067\u30E1\u30ED\u30C7\u30A3\u30C8\u30E9\u30C3\u30AF\u306B\u6B4C\u8A5E\u3092\u540C\u671F\u3055\u305B\u3066\u6B4C\u308F\u305B\u307E\u3059\u3002\u203B\u72EC\u81EA\u62E1\u5F35
4217
+ </div>
4218
+ <div style="margin-top: 8px;">
4219
+ <button class="dtm-btn dtm-btn--primary dtm-btn--xs dtm-modal-sample-play-btn" data-mml="@0 t120 v100 o4g8 g8 e8 e8 f8 e8 d8 c8 g8 g8 e8 e8 d4.; @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053;">\u25B6 \u8A66\u8074</button>
4220
+ </div>
4221
+ <div class="dtm-modal-sample-player-container"></div>
4222
+ </div>
4223
+ </div>
4224
+ `;
4225
+
4129
4226
  // src/mml-parser.ts
4130
4227
  var PITCH_MAP2 = {
4131
4228
  c: 0,
@@ -4137,7 +4234,7 @@ var PITCH_MAP2 = {
4137
4234
  b: 11
4138
4235
  };
4139
4236
  var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
4140
- var META_DIRECTIVE = /#(inst|drum|volume|mode)=([\w-]+)/gi;
4237
+ var META_DIRECTIVE = /#(inst|drum|volume|drumvolume|mode)=([\w-]+)/gi;
4141
4238
  var parseMmlMeta = (mml) => {
4142
4239
  const meta = {};
4143
4240
  for (const m of mml.matchAll(META_DIRECTIVE)) {
@@ -4147,6 +4244,9 @@ var parseMmlMeta = (mml) => {
4147
4244
  else if (key === "volume") {
4148
4245
  const v = Number.parseInt(m[2], 10);
4149
4246
  if (!Number.isNaN(v)) meta.volume = v;
4247
+ } else if (key === "drumvolume") {
4248
+ const dv = Number.parseInt(m[2], 10);
4249
+ if (!Number.isNaN(dv)) meta.drumVolume = dv;
4150
4250
  } else if (key === "mode") {
4151
4251
  if (m[2] === "simple" || m[2] === "advanced") {
4152
4252
  meta.mode = m[2];
@@ -4161,6 +4261,8 @@ var formatMmlMeta = (meta) => {
4161
4261
  if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
4162
4262
  if (meta.drum) parts.push(`#drum=${meta.drum}`);
4163
4263
  if (meta.volume !== void 0) parts.push(`#volume=${meta.volume}`);
4264
+ if (meta.drumVolume !== void 0)
4265
+ parts.push(`#drumvolume=${meta.drumVolume}`);
4164
4266
  if (meta.mode) parts.push(`#mode=${meta.mode}`);
4165
4267
  return parts.join(" ");
4166
4268
  };
@@ -4748,9 +4850,13 @@ var DAW_CSS = `
4748
4850
  /* \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
4749
4851
  \u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u30D0\u30FC\uFF08.dtm-controlbar\uFF09\u306B\u3082\u4F9B\u7D66\u3059\u308B\u3002mountPresetSelect /
4750
4852
  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
4751
- 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 */
4853
+ 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
4854
+ \u518D\u751F\u5C02\u7528\u30D3\u30E5\u30FC\u306E\u30E2\u30FC\u30C0\u30EB\uFF0F\u5229\u7528\u898F\u7D04\u30AB\u30D0\u30FC\u306F document.body \u76F4\u4E0B\u3078\u91CD\u306D\u308B\u305F\u3081\u3001
4855
+ .dtm-daw \u306E\u5916\u306B\u51FA\u308B\u3002\u3053\u308C\u3089\u3082\u540C\u69D8\u306B\u30C8\u30FC\u30AF\u30F3\u3092\u4F9B\u7D66\u3057\u306A\u3044\u3068\u9ED2\u5730\u30FB\u767D\u6587\u5B57\u306B\u306A\u308B\u3002 */
4752
4856
  .dtm-daw,
4753
- .dtm-controlbar {
4857
+ .dtm-controlbar,
4858
+ .dtm-modal-overlay,
4859
+ .dtm-consent-overlay {
4754
4860
  /* PICO-8 16\u8272\u30D1\u30EC\u30C3\u30C8\u3088\u308A */
4755
4861
  --c-black: #000000;
4756
4862
  --c-navy: #1d2b53;
@@ -4878,7 +4984,7 @@ var DAW_CSS = `
4878
4984
  top: 0;
4879
4985
  z-index: 20;
4880
4986
  display: flex;
4881
- flex-wrap: wrap;
4987
+ flex-wrap: nowrap;
4882
4988
  align-items: center;
4883
4989
  gap: var(--dtm-gap);
4884
4990
  padding: 6px;
@@ -4888,6 +4994,17 @@ var DAW_CSS = `
4888
4994
  inset 0 0 0 2px var(--c-black),
4889
4995
  0 0 0 2px var(--dtm-success),
4890
4996
  4px 4px 0 var(--c-black);
4997
+ overflow-x: auto;
4998
+ scrollbar-width: none;
4999
+ }
5000
+ .dtm-topbar::-webkit-scrollbar {
5001
+ display: none;
5002
+ }
5003
+ .dtm-topbar > * {
5004
+ flex-shrink: 0;
5005
+ }
5006
+ .dtm-topbar > .dtm-grow {
5007
+ flex-shrink: 1;
4891
5008
  }
4892
5009
 
4893
5010
  /* PLAY\u30DC\u30BF\u30F3 \u2014 \u30B2\u30FC\u30E0\u306E\u300C\u6C7A\u5B9A\u30DC\u30BF\u30F3\u300D\u7684\u5B58\u5728\u611F */
@@ -5238,6 +5355,15 @@ var DAW_CSS = `
5238
5355
  line-height: 1.8;
5239
5356
  color: var(--dtm-success);
5240
5357
  }
5358
+ .dtm-output-label {
5359
+ font-size: 11px;
5360
+ color: var(--dtm-muted);
5361
+ font-family: var(--dtm-font);
5362
+ margin-top: 10px;
5363
+ }
5364
+ .dtm-output-label:first-of-type {
5365
+ margin-top: 0;
5366
+ }
5241
5367
  .dtm-output-row { display: flex; gap: 8px; align-items: flex-start; margin-top: 6px; }
5242
5368
  .dtm-output-row pre { flex: 1; }
5243
5369
 
@@ -5348,10 +5474,76 @@ var DAW_CSS = `
5348
5474
  justify-content: center;
5349
5475
  padding: 16px;
5350
5476
  backdrop-filter: blur(2px);
5477
+ /* 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
5478
+ .dtm-daw \u304B\u3089\u7D99\u627F\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u3053\u3053\u3067\u660E\u793A\u3059\u308B\u3002 */
5479
+ color: var(--dtm-text);
5480
+ font-family: var(--dtm-font);
5351
5481
  }
5352
5482
  .dtm-modal-overlay[hidden] {
5353
5483
  display: none !important;
5354
5484
  }
5485
+
5486
+ /* \u2500\u2500\u2500 \u5229\u7528\u898F\u7D04\u540C\u610F\u30AB\u30D0\u30FC \u2500\u2500\u2500 */
5487
+ .dtm-consent-overlay {
5488
+ position: fixed;
5489
+ inset: 0;
5490
+ z-index: 10100;
5491
+ background: rgba(0, 0, 0, 0.6);
5492
+ display: flex;
5493
+ align-items: center;
5494
+ justify-content: center;
5495
+ padding: 16px;
5496
+ backdrop-filter: blur(2px);
5497
+ /* 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 */
5498
+ color: var(--dtm-text);
5499
+ font-family: var(--dtm-font);
5500
+ }
5501
+ .dtm-consent-overlay[hidden] {
5502
+ display: none !important;
5503
+ }
5504
+ .dtm-consent-modal {
5505
+ max-width: 450px;
5506
+ width: 100%;
5507
+ max-height: 85vh;
5508
+ display: flex;
5509
+ flex-direction: column;
5510
+ background: var(--dtm-surface);
5511
+ border: 2px solid var(--c-black);
5512
+ box-shadow:
5513
+ inset 0 0 0 2px var(--c-black),
5514
+ 0 0 0 2px var(--dtm-primary),
5515
+ 4px 4px 0 var(--c-black);
5516
+ overflow-y: auto;
5517
+ }
5518
+ .dtm-consent-header {
5519
+ background: var(--dtm-deep);
5520
+ color: var(--dtm-text);
5521
+ padding: 8px 12px;
5522
+ border-bottom: 2px solid var(--c-black);
5523
+ font-weight: bold;
5524
+ text-align: center;
5525
+ font-size: 14px;
5526
+ }
5527
+ .dtm-consent-body {
5528
+ padding: 12px 16px;
5529
+ font-size: 13px;
5530
+ line-height: 1.6;
5531
+ }
5532
+ .dtm-consent-body a {
5533
+ color: var(--dtm-primary);
5534
+ text-decoration: underline;
5535
+ }
5536
+ .dtm-consent-body a:hover {
5537
+ color: var(--dtm-accent);
5538
+ }
5539
+ .dtm-consent-footer {
5540
+ padding: 8px;
5541
+ border-top: 2px solid var(--c-black);
5542
+ background: var(--dtm-deep);
5543
+ display: flex;
5544
+ justify-content: center;
5545
+ }
5546
+
5355
5547
  .dtm-modal {
5356
5548
  max-width: 500px;
5357
5549
  width: 100%;
@@ -5485,18 +5677,19 @@ var DAW_CSS = `
5485
5677
  }
5486
5678
 
5487
5679
  .dtm-hidden { display: none !important; }
5488
- /* \u8AAD\u8FBC\u6642\u306E\u63A7\u3048\u3081\u306A\u304A\u77E5\u3089\u305B\uFF08\u4F8B: \u30B7\u30F3\u30D7\u30EB\u30E2\u30FC\u30C9\u3067\u306E\u30C8\u30E9\u30C3\u30AF\u5408\u7B97\uFF09\u3002\u4E3B\u5F35\u3057\u3059\u304E\u306A\u3044 muted \u8868\u793A\u3002 */
5680
+ /* \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 */
5489
5681
  .dtm-load-note {
5490
5682
  margin: 6px 0 0;
5491
5683
  padding: 0 2px;
5492
5684
  font-family: var(--dtm-font);
5493
- font-size: 10px;
5685
+ font-size: 11px;
5494
5686
  line-height: 1.5;
5495
5687
  letter-spacing: .04em;
5496
- color: var(--dtm-muted);
5497
- opacity: .85;
5688
+ color: var(--dtm-warn); /* \u8B66\u544A\u8272\uFF08\u30AA\u30EC\u30F3\u30B8\uFF09 */
5689
+ font-weight: bold;
5690
+ opacity: 1.0;
5498
5691
  }
5499
- .dtm-load-note::before { content: "\u24D8 "; }
5692
+ .dtm-load-note::before { content: "\u26A0 "; }
5500
5693
  .dtm-grow { flex: 1 1 auto; }
5501
5694
  .dtm-lyric-icon {
5502
5695
  flex: 0 0 auto;
@@ -5897,6 +6090,7 @@ var STEPS_PER_BEAT3 = 48;
5897
6090
  var STEPS_PER_BAR = 192;
5898
6091
  var DEFAULT_TRACK_COLORS = ["#00e436", "#29adff", "#ff77a8", "#ffec27"];
5899
6092
  var activePlayer = null;
6093
+ var agreedModelsInSession = /* @__PURE__ */ new Set();
5900
6094
  var LYRIC_MODEL_LABELS = {
5901
6095
  klatt: "\u8EFD\u91CF\u30ED\u30DC\u58F0",
5902
6096
  ...KOE_VOICEBANK_LABELS
@@ -6013,6 +6207,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6013
6207
  const drumPatternDict = options.drumPatterns ?? DRUM_PATTERNS;
6014
6208
  const drumPattern = meta.drum ? drumPatternDict[meta.drum] ?? null : null;
6015
6209
  const trackVolume = meta.volume ?? options.volume ?? 100;
6210
+ const drumVolume = meta.drumVolume ?? 80;
6016
6211
  const colors = options.trackColors ?? DEFAULT_TRACK_COLORS;
6017
6212
  const useSynth = options.synth ?? !options.onPlayNote;
6018
6213
  const secondsPerStep = 60 / bpm / STEPS_PER_BEAT3;
@@ -6152,16 +6347,21 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6152
6347
  const menuDropdown = doc.createElement("div");
6153
6348
  menuDropdown.className = "dtm-player-menu";
6154
6349
  menuDropdown.style.display = "none";
6155
- const copyMmlItem = doc.createElement("button");
6156
- copyMmlItem.type = "button";
6157
- copyMmlItem.className = "dtm-player-menu-item";
6158
- copyMmlItem.textContent = "MML\u30B3\u30D4\u30FC";
6159
- const embedItem = doc.createElement("button");
6160
- embedItem.type = "button";
6161
- embedItem.className = "dtm-player-menu-item";
6162
- embedItem.textContent = "\u57CB\u3081\u8FBC\u3080";
6163
- menuDropdown.appendChild(copyMmlItem);
6350
+ const makeMenuItem = (label) => {
6351
+ const item = doc.createElement("button");
6352
+ item.type = "button";
6353
+ item.className = "dtm-player-menu-item";
6354
+ item.textContent = label;
6355
+ return item;
6356
+ };
6357
+ const showMmlItem = makeMenuItem("MML\u3092\u8868\u793A");
6358
+ const mmlInfoItem = makeMenuItem("MML\u66F8\u5F0F\u3068\u306F");
6359
+ const embedItem = makeMenuItem("\u57CB\u3081\u8FBC\u3080");
6360
+ const copyMmlItem = makeMenuItem("MML\u30B3\u30D4\u30FC");
6361
+ menuDropdown.appendChild(showMmlItem);
6362
+ menuDropdown.appendChild(mmlInfoItem);
6164
6363
  menuDropdown.appendChild(embedItem);
6364
+ menuDropdown.appendChild(copyMmlItem);
6165
6365
  menuContainer.appendChild(menuDropdown);
6166
6366
  mmlHeader.appendChild(menuContainer);
6167
6367
  const toggleMenu = (show) => {
@@ -6184,38 +6384,200 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6184
6384
  e.stopPropagation();
6185
6385
  toggleMenu();
6186
6386
  });
6187
- copyMmlItem.addEventListener("click", async (e) => {
6188
- e.stopPropagation();
6189
- const success = await copyToClipboard(doc, mml);
6190
- if (success) {
6191
- copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F\uFF01";
6192
- } else {
6193
- copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u5931\u6557";
6387
+ let infoModalEl = null;
6388
+ let modalSamplePlayer = null;
6389
+ const closeInfoModal = () => {
6390
+ if (modalSamplePlayer) {
6391
+ modalSamplePlayer.stop();
6392
+ modalSamplePlayer.destroy();
6393
+ modalSamplePlayer = null;
6394
+ }
6395
+ infoModalEl?.remove();
6396
+ infoModalEl = null;
6397
+ };
6398
+ const openInfoModal = (title) => {
6399
+ closeInfoModal();
6400
+ const overlay = doc.createElement("div");
6401
+ overlay.className = "dtm-modal-overlay";
6402
+ const modal = doc.createElement("div");
6403
+ modal.className = "dtm-win dtm-modal";
6404
+ const header = doc.createElement("div");
6405
+ header.className = "dtm-modal-header";
6406
+ const titleEl = doc.createElement("span");
6407
+ titleEl.className = "dtm-modal-title";
6408
+ titleEl.textContent = title;
6409
+ const closeBtn = doc.createElement("button");
6410
+ closeBtn.type = "button";
6411
+ closeBtn.className = "dtm-modal-close";
6412
+ closeBtn.innerHTML = "&times;";
6413
+ closeBtn.title = "\u9589\u3058\u308B";
6414
+ header.append(titleEl, closeBtn);
6415
+ const modalBody = doc.createElement("div");
6416
+ modalBody.className = "dtm-modal-body";
6417
+ modal.append(header, modalBody);
6418
+ overlay.appendChild(modal);
6419
+ closeBtn.addEventListener("click", (e) => {
6420
+ e.stopPropagation();
6421
+ closeInfoModal();
6422
+ });
6423
+ overlay.addEventListener("click", (e) => {
6424
+ if (e.target === overlay) closeInfoModal();
6425
+ });
6426
+ doc.body.appendChild(overlay);
6427
+ infoModalEl = overlay;
6428
+ return modalBody;
6429
+ };
6430
+ const appendCopyButton = (parent, text) => {
6431
+ const actions = doc.createElement("div");
6432
+ actions.style.marginTop = "8px";
6433
+ const copyBtn = doc.createElement("button");
6434
+ copyBtn.type = "button";
6435
+ copyBtn.className = "dtm-btn dtm-btn--primary dtm-btn--xs";
6436
+ copyBtn.textContent = "\u{1F4CB} \u30B3\u30D4\u30FC";
6437
+ copyBtn.addEventListener("click", async (e) => {
6438
+ e.stopPropagation();
6439
+ const ok = await copyToClipboard(doc, text);
6440
+ copyBtn.textContent = ok ? "\u2713 \u30B3\u30D4\u30FC\u5B8C\u4E86" : "\u30B3\u30D4\u30FC\u5931\u6557";
6441
+ if (ok) copyBtn.classList.add("dtm-btn--success");
6442
+ setTimeout(() => {
6443
+ copyBtn.textContent = "\u{1F4CB} \u30B3\u30D4\u30FC";
6444
+ copyBtn.classList.remove("dtm-btn--success");
6445
+ }, 1200);
6446
+ });
6447
+ actions.appendChild(copyBtn);
6448
+ parent.appendChild(actions);
6449
+ };
6450
+ const wireSampleButtons = (modalBody) => {
6451
+ const copyBtns = modalBody.querySelectorAll(".dtm-modal-sample-copy-btn");
6452
+ for (const btn of copyBtns) {
6453
+ const el = btn;
6454
+ el.addEventListener("click", async (e) => {
6455
+ e.stopPropagation();
6456
+ const sampleMml = el.getAttribute("data-mml") ?? "";
6457
+ const original = el.textContent;
6458
+ const ok = await copyToClipboard(doc, sampleMml);
6459
+ el.textContent = ok ? "\u2713 \u30B3\u30D4\u30FC\u5B8C\u4E86" : "\u30B3\u30D4\u30FC\u5931\u6557";
6460
+ if (ok) el.classList.add("dtm-btn--success");
6461
+ setTimeout(() => {
6462
+ el.textContent = original;
6463
+ el.classList.remove("dtm-btn--success");
6464
+ }, 1200);
6465
+ });
6194
6466
  }
6195
- setTimeout(() => {
6196
- copyMmlItem.textContent = "MML\u30B3\u30D4\u30FC";
6197
- }, 2e3);
6467
+ let activeSampleBtn = null;
6468
+ const resetSampleBtn = (b) => {
6469
+ if (!b) return;
6470
+ b.textContent = "\u25B6 \u8A66\u8074";
6471
+ b.classList.remove("dtm-btn--danger");
6472
+ b.classList.add("dtm-btn--primary");
6473
+ };
6474
+ const markPlaying = (b) => {
6475
+ b.textContent = "\u25A0 \u505C\u6B62";
6476
+ b.classList.remove("dtm-btn--primary");
6477
+ b.classList.add("dtm-btn--danger");
6478
+ };
6479
+ const playBtns = modalBody.querySelectorAll(".dtm-modal-sample-play-btn");
6480
+ for (const btn of playBtns) {
6481
+ const el = btn;
6482
+ el.addEventListener("click", (e) => {
6483
+ e.stopPropagation();
6484
+ const sampleMml = el.getAttribute("data-mml") ?? "";
6485
+ if (activeSampleBtn === el && modalSamplePlayer) {
6486
+ if (modalSamplePlayer.isPlaying()) {
6487
+ modalSamplePlayer.stop();
6488
+ } else {
6489
+ modalSamplePlayer.play();
6490
+ markPlaying(el);
6491
+ }
6492
+ return;
6493
+ }
6494
+ if (modalSamplePlayer) {
6495
+ modalSamplePlayer.stop();
6496
+ modalSamplePlayer.destroy();
6497
+ modalSamplePlayer = null;
6498
+ }
6499
+ resetSampleBtn(activeSampleBtn);
6500
+ activeSampleBtn = el;
6501
+ const sampleBox = el.closest(".dtm-modal-sample-box");
6502
+ const container = sampleBox?.querySelector(
6503
+ ".dtm-modal-sample-player-container"
6504
+ );
6505
+ if (!container) return;
6506
+ container.innerHTML = "";
6507
+ modalSamplePlayer = mountMmlPlayer(container, sampleMml, {
6508
+ onPlayNote: options.onPlayNote,
6509
+ onPlayDrum: options.onPlayDrum,
6510
+ onResumeAudio: options.onResumeAudio,
6511
+ getAudioTime: options.getAudioTime,
6512
+ singingVoices: options.singingVoices,
6513
+ drumPatterns: options.drumPatterns,
6514
+ volume: trackVolume,
6515
+ // 解説モーダル内の試聴サンプルは規約同意を要求しない。
6516
+ skipConsent: true,
6517
+ onStop: () => {
6518
+ if (activeSampleBtn === el) resetSampleBtn(el);
6519
+ }
6520
+ });
6521
+ markPlaying(el);
6522
+ modalSamplePlayer.play();
6523
+ });
6524
+ }
6525
+ };
6526
+ showMmlItem.addEventListener("click", (e) => {
6527
+ e.stopPropagation();
6528
+ toggleMenu(false);
6529
+ const modalBody = openInfoModal("MML");
6530
+ const pre = doc.createElement("pre");
6531
+ pre.textContent = mml;
6532
+ pre.style.whiteSpace = "pre-wrap";
6533
+ pre.style.wordBreak = "break-all";
6534
+ modalBody.appendChild(pre);
6535
+ appendCopyButton(modalBody, mml);
6536
+ });
6537
+ mmlInfoItem.addEventListener("click", (e) => {
6538
+ e.stopPropagation();
6539
+ toggleMenu(false);
6540
+ const modalBody = openInfoModal("MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC");
6541
+ modalBody.innerHTML = MML_INFO_HTML;
6542
+ wireSampleButtons(modalBody);
6198
6543
  });
6199
6544
  embedItem.addEventListener("click", async (e) => {
6200
6545
  e.stopPropagation();
6201
- embedItem.textContent = "\u751F\u6210\u4E2D...";
6546
+ toggleMenu(false);
6547
+ const modalBody = openInfoModal("\u57CB\u3081\u8FBC\u307F");
6548
+ const loading = doc.createElement("p");
6549
+ loading.textContent = "\u751F\u6210\u4E2D...";
6550
+ modalBody.appendChild(loading);
6202
6551
  try {
6203
6552
  const embedBase = options.embedUrl ?? "https://onjmin.github.io/dtm/demo/embed.html";
6204
6553
  const payload = await encodeMml(mml);
6205
6554
  const url2 = `${embedBase}#${payload}`;
6206
6555
  const snippet = `<iframe src="${url2}" width="100%" height="260" frameborder="0" loading="lazy" title="@onjmin/dtm player"></iframe>`;
6207
- const success = await copyToClipboard(doc, snippet);
6208
- if (success) {
6209
- embedItem.textContent = "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F\uFF01";
6210
- } else {
6211
- embedItem.textContent = "\u30B3\u30D4\u30FC\u5931\u6557";
6212
- }
6556
+ if (!modalBody.isConnected) return;
6557
+ loading.remove();
6558
+ const desc = doc.createElement("p");
6559
+ desc.textContent = "\u3053\u306EHTML\u3092\u30D6\u30ED\u30B0\u3084\u30B5\u30A4\u30C8\u306B\u8CBC\u308A\u4ED8\u3051\u308B\u3068\u3001\u30D7\u30EC\u30A4\u30E4\u30FC\u3092\u305D\u306E\u307E\u307E\u57CB\u3081\u8FBC\u3081\u307E\u3059\u3002";
6560
+ const pre = doc.createElement("pre");
6561
+ pre.textContent = snippet;
6562
+ pre.style.whiteSpace = "pre-wrap";
6563
+ pre.style.wordBreak = "break-all";
6564
+ modalBody.append(desc, pre);
6565
+ appendCopyButton(modalBody, snippet);
6213
6566
  } catch (err2) {
6214
6567
  console.error("[dtm] failed to generate embed snippet", err2);
6215
- embedItem.textContent = "\u751F\u6210\u5931\u6557";
6568
+ if (modalBody.isConnected) loading.textContent = "\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
6569
+ }
6570
+ });
6571
+ copyMmlItem.addEventListener("click", async (e) => {
6572
+ e.stopPropagation();
6573
+ const success = await copyToClipboard(doc, mml);
6574
+ if (success) {
6575
+ copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F\uFF01";
6576
+ } else {
6577
+ copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u5931\u6557";
6216
6578
  }
6217
6579
  setTimeout(() => {
6218
- embedItem.textContent = "\u57CB\u3081\u8FBC\u3080";
6580
+ copyMmlItem.textContent = "MML\u30B3\u30D4\u30FC";
6219
6581
  }, 2e3);
6220
6582
  });
6221
6583
  const promotedToImage = /* @__PURE__ */ new Set();
@@ -6461,6 +6823,77 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6461
6823
  root.appendChild(termsDiv);
6462
6824
  }
6463
6825
  target.appendChild(root);
6826
+ let consentOverlayEl = null;
6827
+ const checkConsentAndShow = () => {
6828
+ try {
6829
+ if (options.skipConsent) return;
6830
+ const unagreed = termsModels.filter((model) => {
6831
+ if (agreedModelsInSession.has(model)) return false;
6832
+ try {
6833
+ if (typeof localStorage === "undefined" || !localStorage) return true;
6834
+ return localStorage.getItem(`dtm_agreed_terms_${model}`) !== "true";
6835
+ } catch (e) {
6836
+ console.warn(
6837
+ "[dtm-player] localStorage access denied in consent check",
6838
+ e
6839
+ );
6840
+ return true;
6841
+ }
6842
+ });
6843
+ if (unagreed.length === 0) return;
6844
+ const consentOverlay = doc.createElement("div");
6845
+ consentOverlay.className = "dtm-consent-overlay";
6846
+ const modal = doc.createElement("div");
6847
+ modal.className = "dtm-win dtm-consent-modal";
6848
+ const header = doc.createElement("div");
6849
+ header.className = "dtm-consent-header";
6850
+ header.textContent = "\u5229\u7528\u898F\u7D04\u306E\u78BA\u8A8D";
6851
+ const body2 = doc.createElement("div");
6852
+ body2.className = "dtm-consent-body";
6853
+ let contentHTML = `<p style="margin: 0 0 8px 0; line-height: 1.4; font-weight: bold; color: var(--dtm-danger);">\u672C\u30C7\u30FC\u30BF\u306B\u306F UTAU \u6B4C\u58F0\u97F3\u6E90\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002<br>\u3054\u5229\u7528\u306B\u3042\u305F\u3063\u3066\u306F\u3001\u4EE5\u4E0B\u306E\u97F3\u6E90\u5229\u7528\u898F\u7D04\u3078\u306E\u540C\u610F\u304C\u5FC5\u8981\u3067\u3059\u3002</p>`;
6854
+ for (const model of unagreed) {
6855
+ const label = KOE_VOICEBANK_LABELS[model] || model;
6856
+ const url2 = KOE_VOICEBANK_TERMS[model];
6857
+ contentHTML += `
6858
+ <div style="margin-bottom: 8px; padding: 6px 10px; background: var(--dtm-deep); border: 2px solid var(--c-black); box-shadow: 2px 2px 0 var(--c-black);">
6859
+ <div style="display: flex; align-items: center; gap: 4px; flex-wrap: wrap; font-size: 11px; font-weight: bold; color: var(--dtm-gold);">
6860
+ <span>\u4F7F\u7528\u6642\u306B\u306F</span>
6861
+ <a href="${url2}" target="_blank" rel="noopener noreferrer" style="color: var(--dtm-primary); text-decoration: underline;">${label}UTAU\u97F3\u6E90</a>
6862
+ <span>\u306E\u5229\u7528\u898F\u7D04\u306B\u5F93\u3063\u3066\u304F\u3060\u3055\u3044</span>
6863
+ </div>
6864
+ </div>
6865
+ `;
6866
+ }
6867
+ body2.innerHTML = contentHTML;
6868
+ const footer = doc.createElement("div");
6869
+ footer.className = "dtm-consent-footer";
6870
+ const btn = doc.createElement("button");
6871
+ btn.type = "button";
6872
+ btn.className = "dtm-btn dtm-btn--success";
6873
+ btn.textContent = "\u540C\u610F\u3057\u3066\u5229\u7528\u3059\u308B";
6874
+ btn.onclick = () => {
6875
+ for (const model of unagreed) {
6876
+ try {
6877
+ if (typeof localStorage !== "undefined" && localStorage) {
6878
+ localStorage.setItem(`dtm_agreed_terms_${model}`, "true");
6879
+ }
6880
+ } catch (e) {
6881
+ }
6882
+ agreedModelsInSession.add(model);
6883
+ }
6884
+ consentOverlay.remove();
6885
+ consentOverlayEl = null;
6886
+ };
6887
+ footer.appendChild(btn);
6888
+ modal.append(header, body2, footer);
6889
+ consentOverlay.appendChild(modal);
6890
+ doc.body.appendChild(consentOverlay);
6891
+ consentOverlayEl = consentOverlay;
6892
+ } catch (err2) {
6893
+ console.error("[dtm-player] Error in checkConsentAndShow:", err2);
6894
+ }
6895
+ };
6896
+ checkConsentAndShow();
6464
6897
  const autoScroll = (lane, el) => {
6465
6898
  if (el.offsetWidth === 0 || lane.clientWidth === 0) return;
6466
6899
  const elementCenter = el.offsetLeft + el.offsetWidth / 2;
@@ -6519,7 +6952,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6519
6952
  if (useSynth) ensureSynth().playNote(e);
6520
6953
  },
6521
6954
  onPlayDrum: (e) => {
6522
- const velocity = e.velocity * (trackVolume / 100);
6955
+ const velocity = e.velocity * (drumVolume / 100) * (trackVolume / 100);
6523
6956
  options.onPlayDrum?.({ ...e, velocity });
6524
6957
  if (useSynth) ensureSynth().playDrum({ ...e, velocity });
6525
6958
  },
@@ -6635,6 +7068,8 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6635
7068
  hideActiveBalloon();
6636
7069
  }
6637
7070
  root.remove();
7071
+ consentOverlayEl?.remove();
7072
+ closeInfoModal();
6638
7073
  };
6639
7074
  const instance = {
6640
7075
  play,
@@ -6676,108 +7111,6 @@ var CHORD_INFO_HTML = `
6676
7111
  </ul>
6677
7112
  </div>
6678
7113
  `;
6679
- var MML_INFO_HTML = `
6680
- <div class="dtm-modal-body-content">
6681
- <h4>1. \u97F3\u7B26\u3068\u4F11\u7B26</h4>
6682
- <p><code>c</code>(\u30C9) <code>d</code>(\u30EC) <code>e</code>(\u30DF) <code>f</code>(\u30D5\u30A1) <code>g</code>(\u30BD) <code>a</code>(\u30E9) <code>b</code>(\u30B7) \u306E\u30A2\u30EB\u30D5\u30A1\u30D9\u30C3\u30C8\u3067\u8868\u3057\u307E\u3059\u3002</p>
6683
- <ul>
6684
- <li>\u534A\u97F3\u4E0A\u3052\u308B: <code>c#</code> \u307E\u305F\u306F <code>c+</code></li>
6685
- <li>\u534A\u97F3\u4E0B\u3052\u308B: <code>d-</code></li>
6686
- <li>\u4F11\u7B26: <code>r</code></li>
6687
- </ul>
6688
-
6689
- <h4>2. \u97F3\u306E\u9577\u3055</h4>
6690
- <p>\u97F3\u540D\u3084\u4F11\u7B26\u306E\u5F8C\u306B\u6570\u5024\u3067\u6307\u5B9A\u3057\u307E\u3059\uFF08\u4F8B: <code>4</code> = 4\u5206\u97F3\u7B26, <code>8</code> = 8\u5206\u97F3\u7B26, <code>16</code> = 16\u5206\u97F3\u7B26\uFF09\u3002</p>
6691
- <ul>
6692
- <li><code>c4</code> : 4\u5206\u97F3\u7B26\u306E\u30C9</li>
6693
- <li><code>r8</code> : 8\u5206\u4F11\u7B26</li>
6694
- <li><code>c4.</code> : \u4ED8\u70B94\u5206\u97F3\u7B26\u306E\u30C9\uFF08\u9577\u3055\u30921.5\u500D\u306B\uFF09</li>
6695
- <li>\u6570\u5024\u3092\u7701\u7565\u3059\u308B\u3068\u3001<code>l</code> \u30B3\u30DE\u30F3\u30C9\u3067\u8A2D\u5B9A\u3055\u308C\u305F\u30C7\u30D5\u30A9\u30EB\u30C8\u9577\uFF08\u901A\u5E3816\u5206\uFF09\u306B\u306A\u308A\u307E\u3059\u3002</li>
6696
- </ul>
6697
-
6698
- <h4>3. \u30AA\u30AF\u30BF\u30FC\u30D6\uFF08\u97F3\u306E\u9AD8\u3055\uFF09</h4>
6699
- <ul>
6700
- <li><code>o4</code>, <code>o5</code> : \u9AD8\u3055\u3092\u76F4\u63A5\u6307\u5B9A\uFF08\u3075\u3064\u3046\u306F o4 \u304B o5\uFF09</li>
6701
- <li><code>&gt;</code> : 1\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0A\u3052\u308B</li>
6702
- <li><code>&lt;</code> : 1\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0B\u3052\u308B</li>
6703
- </ul>
6704
-
6705
- <h4>4. \u30C6\u30F3\u30DD</h4>
6706
- <ul>
6707
- <li><code>t120</code> : \u66F2\u306E\u901F\u3055\u3092BPM120\u306B\u6307\u5B9A\u3002\u203B\u30E1\u30ED\u30C7\u30A3\uFF08@0\uFF09\u306E\u30C6\u30F3\u30DD\u6307\u5B9A\u304C\u66F2\u5168\u4F53\u306B\u53CD\u6620\u3055\u308C\u307E\u3059\u3002</li>
6708
- </ul>
6709
-
6710
- <h4>5. \u548C\u97F3</h4>
6711
- <p>\u97F3\u7B26\u3092 <code>[</code> \u3068 <code>]</code> \u3067\u56F2\u3080\u3068\u540C\u6642\u306B\u767A\u97F3\u3057\u307E\u3059\u3002</p>
6712
- <pre>\u4F8B: [ceg]4 \uFF08\u30C9\u30FB\u30DF\u30FB\u30BD\u30924\u5206\u97F3\u7B26\u3067\u540C\u6642\u306B\u767A\u97F3\uFF09</pre>
6713
-
6714
- <h4>6. \u30C8\u30E9\u30C3\u30AF\u306E\u533A\u5207\u308A</h4>
6715
- <p><code>;</code> \u307E\u305F\u306F <code>@0</code>\u301C<code>@3</code> \u3067\u30C8\u30E9\u30C3\u30AF\u3092\u5207\u308A\u66FF\u3048\u307E\u3059\u3002</p>
6716
- <ul>
6717
- <li><code>@0</code>: \u30E1\u30ED\u30C7\u30A3</li>
6718
- <li><code>@1</code>: \u30B5\u30D6\u30E1\u30ED</li>
6719
- <li><code>@2</code>: \u30D9\u30FC\u30B9</li>
6720
- <li><code>@3</code>: \u4F34\u594F</li>
6721
- </ul>
6722
-
6723
- <h4>7. \u6B4C\u58F0\u30FB\u6B4C\u8A5E\u5165\u529B</h4>
6724
- <p><code>@@&lt;\u30C8\u30E9\u30C3\u30AF\u756A\u53F7&gt; &lt;\u97F3\u6E90\u540D&gt; &lt;\u6B4C\u8A5E&gt;</code> \u306E\u5F62\u5F0F\u3067\u3001\u97F3\u7B26\u3068\u540C\u671F\u3059\u308B\u6B4C\u8A5E\u3092\u5165\u529B\u3067\u304D\u307E\u3059\u3002</p>
6725
- <pre>\u4F8B: @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053</pre>
6726
- <p style="margin-top:4px; margin-bottom:16px;"><small>\uFF08\u97F3\u6E90\u540D\u306F <code>tsukuyomi</code> \u3084 <code>klatt</code>, <code>roze</code> \u306A\u3069\u306E\u97F3\u58F0\u30E2\u30C7\u30EB\u3092\u6307\u5B9A\u3067\u304D\u307E\u3059\uFF09</small></p>
6727
-
6728
- <h4 style="margin-top: 18px; border-top: 1px solid var(--dtm-border2); padding-top: 8px;">\u30B5\u30F3\u30D7\u30EB\u66F2\uFF08\u8A66\u8074\u30FB\u30B3\u30D4\u30FC\uFF09</h4>
6729
-
6730
- <!-- \u30B5\u30F3\u30D7\u30EB1 -->
6731
- <div class="dtm-modal-sample-box">
6732
- <div class="dtm-modal-sample-header">
6733
- <span class="dtm-modal-sample-tag">1. \u57FA\u672C\u306E\u30E1\u30ED\u30C7\u30A3</span>
6734
- <button class="dtm-btn dtm-btn--ghost dtm-btn--xs dtm-modal-sample-copy-btn" data-mml="@0 t120 l8 o5 c d e f g a b > c">\u{1F4CB} \u30B3\u30D4\u30FC</button>
6735
- </div>
6736
- <pre style="margin: 0; padding: 6px;">@0 t120 l8 o5 c d e f g a b &gt; c</pre>
6737
- <div class="dtm-modal-sample-desc">
6738
- \u57FA\u672C\u7684\u306A\u30E1\u30ED\u30C7\u30A3\u306E\u66F8\u304D\u65B9\uFF08\u97F3\u540D\u30FB\u9577\u3055\u30FB\u30AA\u30AF\u30BF\u30FC\u30D6\u3068\u30C6\u30F3\u30DD\uFF09\u3002
6739
- </div>
6740
- <div style="margin-top: 8px;">
6741
- <button class="dtm-btn dtm-btn--primary dtm-btn--xs dtm-modal-sample-play-btn" data-mml="@0 t120 l8 o5 c d e f g a b > c">\u25B6 \u8A66\u8074</button>
6742
- </div>
6743
- <div class="dtm-modal-sample-player-container"></div>
6744
- </div>
6745
-
6746
- <!-- \u30B5\u30F3\u30D7\u30EB2 -->
6747
- <div class="dtm-modal-sample-box">
6748
- <div class="dtm-modal-sample-header">
6749
- <span class="dtm-modal-sample-tag">2. \u8907\u6570\u30C8\u30E9\u30C3\u30AF\u3068\u548C\u97F3</span>
6750
- <button class="dtm-btn dtm-btn--ghost dtm-btn--xs dtm-modal-sample-copy-btn" data-mml="@0 t120 o5 c e g2 ; @3 o4 [ceg]2 [ceg]2">\u{1F4CB} \u30B3\u30D4\u30FC</button>
6751
- </div>
6752
- <pre style="margin: 0; padding: 6px;">@0 t120 o5 c e g2 ;
6753
- @3 o4 [ceg]2 [ceg]2</pre>
6754
- <div class="dtm-modal-sample-desc">
6755
- ; \u3067\u30C8\u30E9\u30C3\u30AF\uFF08\u4E0A\uFF1D\u30E1\u30ED\u30C7\u30A3\uFF0F\u4E0B\uFF1D\u4F34\u594F\uFF09\u3092\u5206\u3051\u3001[ceg] \u3067\u548C\u97F3\u3092\u9CF4\u3089\u3057\u307E\u3059\u3002
6756
- </div>
6757
- <div style="margin-top: 8px;">
6758
- <button class="dtm-btn dtm-btn--primary dtm-btn--xs dtm-modal-sample-play-btn" data-mml="@0 t120 o5 c e g2 ; @3 o4 [ceg]2 [ceg]2">\u25B6 \u8A66\u8074</button>
6759
- </div>
6760
- <div class="dtm-modal-sample-player-container"></div>
6761
- </div>
6762
-
6763
- <!-- \u30B5\u30F3\u30D7\u30EB3 -->
6764
- <div class="dtm-modal-sample-box">
6765
- <div class="dtm-modal-sample-header">
6766
- <span class="dtm-modal-sample-tag">3. \u6B4C\u5531\u4ED8\u304D (\u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D)</span>
6767
- <button class="dtm-btn dtm-btn--ghost dtm-btn--xs dtm-modal-sample-copy-btn" data-mml="@0 t120 v100 o4g8 g8 e8 e8 f8 e8 d8 c8 g8 g8 e8 e8 d4.; @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053;">\u{1F4CB} \u30B3\u30D4\u30FC</button>
6768
- </div>
6769
- <pre style="margin: 0; padding: 6px;">@0 t120 v100 o4g8 g8 e8 e8 f8 e8 d8 c8 g8 g8 e8 e8 d4.;
6770
- @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053;</pre>
6771
- <div class="dtm-modal-sample-desc">
6772
- @@0 tsukuyomi \u6B4C\u8A5E... \u3067\u30E1\u30ED\u30C7\u30A3\u30C8\u30E9\u30C3\u30AF\u306B\u6B4C\u8A5E\u3092\u540C\u671F\u3055\u305B\u3066\u6B4C\u308F\u305B\u307E\u3059\u3002\u203B\u72EC\u81EA\u62E1\u5F35
6773
- </div>
6774
- <div style="margin-top: 8px;">
6775
- <button class="dtm-btn dtm-btn--primary dtm-btn--xs dtm-modal-sample-play-btn" data-mml="@0 t120 v100 o4g8 g8 e8 e8 f8 e8 d8 c8 g8 g8 e8 e8 d4.; @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053;">\u25B6 \u8A66\u8074</button>
6776
- </div>
6777
- <div class="dtm-modal-sample-player-container"></div>
6778
- </div>
6779
- </div>
6780
- `;
6781
7114
  var MIDI_INFO_HTML = `
6782
7115
  <div class="dtm-modal-body-content">
6783
7116
  <h4>1. MIDI\u30D5\u30A1\u30A4\u30EB\u3068\u306F</h4>
@@ -7721,7 +8054,7 @@ var mountDAW = (target, options = {}) => {
7721
8054
  };
7722
8055
  const updateTransport = () => {
7723
8056
  const playing = playbackState === "playing";
7724
- const label = playing ? "\u4E00\u6642\u505C\u6B62" : playbackState === "paused" ? "\u518D\u958B" : "\u8A66\u8074";
8057
+ const label = playing ? "\u505C\u6B62" : playbackState === "paused" ? "\u518D\u958B" : "\u8A66\u8074";
7725
8058
  refs.playBtn.innerHTML = `${icon(playing ? "pause" : "play")}<span>${label}</span>`;
7726
8059
  refs.playBtn.classList.toggle("dtm-play--stop", playing);
7727
8060
  };
@@ -7989,6 +8322,7 @@ var mountDAW = (target, options = {}) => {
7989
8322
  instrument: currentInstrument || void 0,
7990
8323
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
7991
8324
  volume: masterVolume,
8325
+ drumVolume,
7992
8326
  mode
7993
8327
  });
7994
8328
  if (refs.decomposeChordToggle.checked) {
@@ -8140,6 +8474,11 @@ var mountDAW = (target, options = {}) => {
8140
8474
  refs.masterVolume.value = String(meta.volume);
8141
8475
  refs.masterVolumeLabel.textContent = `${meta.volume}%`;
8142
8476
  }
8477
+ if (meta.drumVolume !== void 0) {
8478
+ drumVolume = meta.drumVolume;
8479
+ refs.drumVolume.value = String(meta.drumVolume);
8480
+ refs.drumVolumeLabel.textContent = `${meta.drumVolume}%`;
8481
+ }
8143
8482
  for (const t of trackStates) {
8144
8483
  t.lyrics = "";
8145
8484
  t.lyricModel = "";
@@ -8148,8 +8487,8 @@ var mountDAW = (target, options = {}) => {
8148
8487
  t.vocalPan = 64;
8149
8488
  t.vocalOctave = 0;
8150
8489
  }
8151
- lyrics?.forEach((lt, idx) => {
8152
- const t = trackStates[idx];
8490
+ lyrics?.forEach((lt) => {
8491
+ const t = trackStates[lt.trackId];
8153
8492
  if (!t) return;
8154
8493
  t.lyrics = lt.syllables.map((s) => s.kana).join("");
8155
8494
  t.lyricModel = lt.model;
@@ -8293,8 +8632,17 @@ var mountDAW = (target, options = {}) => {
8293
8632
  const wireEvents = () => {
8294
8633
  refs.playBtn.addEventListener("click", togglePlay);
8295
8634
  refs.playBtn.disabled = false;
8296
- refs.recBtn.addEventListener("click", () => options.onToggleRecord?.());
8297
- refs.recBtn.style.display = options.onToggleRecord ? "" : "none";
8635
+ refs.prevBarBtn.addEventListener("click", () => {
8636
+ const targetStep = Math.max(
8637
+ 0,
8638
+ Math.floor((getCurrentPlayStep() - 1) / renderConfig.stepsPerBar) * renderConfig.stepsPerBar
8639
+ );
8640
+ jumpTo(targetStep);
8641
+ });
8642
+ refs.nextBarBtn.addEventListener("click", () => {
8643
+ const targetStep = Math.floor(getCurrentPlayStep() / renderConfig.stepsPerBar + 1) * renderConfig.stepsPerBar;
8644
+ jumpTo(targetStep);
8645
+ });
8298
8646
  refs.soloCheckbox.addEventListener("change", () => {
8299
8647
  isSolo = refs.soloCheckbox.checked;
8300
8648
  });
@@ -8657,6 +9005,20 @@ var mountDAW = (target, options = {}) => {
8657
9005
  if (playbackState === "paused") return pausedPlayStep;
8658
9006
  return playStartStep;
8659
9007
  };
9008
+ const jumpTo = async (step) => {
9009
+ const wasPlaying = playbackState === "playing";
9010
+ if (wasPlaying) {
9011
+ sequencer.stop();
9012
+ options.singingVoices?.stopStream();
9013
+ playStartStep = step;
9014
+ pausedPlayStep = step;
9015
+ currentPlayStep = step;
9016
+ playbackState = "paused";
9017
+ await play();
9018
+ } else {
9019
+ forcePauseAt(step);
9020
+ }
9021
+ };
8660
9022
  const forcePauseAt = (step) => {
8661
9023
  playStartStep = step;
8662
9024
  pausedPlayStep = step;
@@ -8731,6 +9093,7 @@ var playMML = (mml, options = {}) => {
8731
9093
  const drumPatternDict = options.drumPatterns ?? DRUM_PATTERNS;
8732
9094
  const drumPattern = meta.drum ? drumPatternDict[meta.drum] ?? null : null;
8733
9095
  let masterVolume = meta.volume ?? options.volume ?? 100;
9096
+ let drumVolume = meta.drumVolume ?? 80;
8734
9097
  const trackIndices = [...new Set(placements.map((p) => p.trackIndex))].sort(
8735
9098
  (a, b) => a - b
8736
9099
  );
@@ -8767,7 +9130,7 @@ var playMML = (mml, options = {}) => {
8767
9130
  synth?.playNote(e);
8768
9131
  },
8769
9132
  onPlayDrum: (e) => {
8770
- const velocity = e.velocity * (masterVolume / 100);
9133
+ const velocity = e.velocity * (drumVolume / 100) * (masterVolume / 100);
8771
9134
  options.onPlayDrum?.({ ...e, velocity });
8772
9135
  synth?.playDrum({ ...e, velocity });
8773
9136
  },
@@ -9326,7 +9689,6 @@ var importFrom = async (url2, name) => {
9326
9689
  var createDtmStudio = async (options = {}) => {
9327
9690
  const cdn = { ...DEFAULT_CDN, ...options.cdn };
9328
9691
  const features = {
9329
- recorder: true,
9330
9692
  midi: true,
9331
9693
  chord: true,
9332
9694
  presetUI: true,
@@ -9365,68 +9727,6 @@ var createDtmStudio = async (options = {}) => {
9365
9727
  const singingVoices = createSingingVoices(audioCtx, masterGain, {
9366
9728
  voiceWorkerUrl
9367
9729
  });
9368
- const recorder = features.recorder ? setupRecorder(audioCtx, masterGain, drumGain) : null;
9369
- const downloadWav = () => {
9370
- if (!recorder) return;
9371
- const recordedData = recorder.getRecordedData();
9372
- const ch = recordedData.length;
9373
- const len = recordedData[0].length;
9374
- if (len === 0) return;
9375
- const bufSize = recordedData[0][0].length;
9376
- const wave = new Float32Array(ch * len * bufSize);
9377
- let idx = 0;
9378
- for (let i = 0; i < len; i++)
9379
- for (let j = 0; j < bufSize; j++)
9380
- for (let k = 0; k < ch; k++) wave[idx++] = recordedData[k][i][j];
9381
- const sampleRate = audioCtx.sampleRate;
9382
- const channels = 2;
9383
- const bitRate = 16;
9384
- const step = bitRate / 8;
9385
- const blockSize = channels * step;
9386
- const byteLen = wave.length * step;
9387
- const view = new DataView(new ArrayBuffer(44 + byteLen));
9388
- const ws = (off2, s) => {
9389
- for (let i = 0; i < s.length; i++)
9390
- view.setUint8(off2 + i, s.charCodeAt(i));
9391
- };
9392
- ws(0, "RIFF");
9393
- view.setUint32(4, 32 + byteLen, true);
9394
- ws(8, "WAVE");
9395
- ws(12, "fmt ");
9396
- view.setUint32(16, 16, true);
9397
- view.setUint16(20, 1, true);
9398
- view.setUint16(22, channels, true);
9399
- view.setUint32(24, sampleRate, true);
9400
- view.setUint32(28, sampleRate * blockSize, true);
9401
- view.setUint16(32, blockSize, true);
9402
- view.setUint16(34, bitRate, true);
9403
- ws(36, "data");
9404
- view.setUint32(40, byteLen, true);
9405
- const clamp4 = (n, a2, b) => Math.max(a2, Math.min(b, n));
9406
- let off = 44;
9407
- for (let i = 0; i < wave.length; i++, off += step)
9408
- view.setInt16(
9409
- off,
9410
- clamp4(Math.round(wave[i] * 32768), -32768, 32767),
9411
- true
9412
- );
9413
- const blob2 = new Blob([view], { type: "audio/wav" });
9414
- const url2 = URL.createObjectURL(blob2);
9415
- const a = document.createElement("a");
9416
- a.href = url2;
9417
- a.download = "record.wav";
9418
- a.click();
9419
- URL.revokeObjectURL(url2);
9420
- };
9421
- const onToggleRecord = recorder ? () => {
9422
- if (recorder.isRecording()) {
9423
- recorder.stopRecording();
9424
- downloadWav();
9425
- } else {
9426
- recorder.clearRecordedData();
9427
- recorder.startRecording();
9428
- }
9429
- } : void 0;
9430
9730
  const listReady = new Promise((resolve) => {
9431
9731
  SoundFont_list.init();
9432
9732
  SoundFont_list.onload(() => resolve());
@@ -9633,7 +9933,6 @@ var createDtmStudio = async (options = {}) => {
9633
9933
  onPlayDrum: playDrum,
9634
9934
  singingVoices,
9635
9935
  parseMidi,
9636
- onToggleRecord,
9637
9936
  onInstrumentChange: handleInstrumentChange,
9638
9937
  ...dawOverrides
9639
9938
  };
@@ -9925,7 +10224,6 @@ var createDtmStudio = async (options = {}) => {
9925
10224
  playMML,
9926
10225
  playSingingMML,
9927
10226
  setDrawOffset,
9928
- setupRecorder,
9929
10227
  shiftNotes,
9930
10228
  stripLyrics,
9931
10229
  stripMmlMeta,