@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.mjs CHANGED
@@ -7,34 +7,6 @@ function createAudioContext() {
7
7
  drumGainNode.connect(audioCtx.destination);
8
8
  return { audioCtx, gainNode, drumGainNode };
9
9
  }
10
- function setupRecorder(audioCtx, gainNode, drumGainNode) {
11
- let isRecording = false;
12
- let recordedData = [[], []];
13
- const recorderProcessor = audioCtx.createScriptProcessor(4096, 2, 2);
14
- recorderProcessor.onaudioprocess = (e) => {
15
- if (!isRecording) return;
16
- const left = e.inputBuffer.getChannelData(0);
17
- const right = e.inputBuffer.getChannelData(1);
18
- recordedData[0].push(left.slice());
19
- recordedData[1].push(right.slice());
20
- };
21
- gainNode.connect(recorderProcessor);
22
- drumGainNode.connect(recorderProcessor);
23
- recorderProcessor.connect(audioCtx.destination);
24
- return {
25
- startRecording: () => {
26
- isRecording = true;
27
- },
28
- stopRecording: () => {
29
- isRecording = false;
30
- },
31
- getRecordedData: () => recordedData,
32
- isRecording: () => isRecording,
33
- clearRecordedData: () => {
34
- recordedData = [[], []];
35
- }
36
- };
37
- }
38
10
  async function fetchSoundFontList(ttl) {
39
11
  const res = await fetch(`https://rpgen3.github.io/soundfont/list/${ttl}.txt`);
40
12
  const str = await res.text();
@@ -1131,8 +1103,9 @@ var buildUI = (target, options) => {
1131
1103
  target.innerHTML = `
1132
1104
  <div class="dtm-daw" data-dtm="root">
1133
1105
  <div class="dtm-topbar" data-dtm="transport">
1106
+ <button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
1134
1107
  <button class="dtm-play" data-dtm="play" disabled>${icon("play")}<span>\u8A66\u8074</span></button>
1135
- <button class="dtm-iconbtn dtm-rec" data-dtm="rec" title="\u9332\u97F3">${icon("record")}</button>
1108
+ <button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
1136
1109
  <label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
1137
1110
  <span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
1138
1111
  <span class="dtm-grow"></span>
@@ -1297,10 +1270,12 @@ var buildUI = (target, options) => {
1297
1270
  </div>
1298
1271
  <div class="dtm-output dtm-hidden" data-dtm="output-container">
1299
1272
  <p class="dtm-label" data-dtm="output-status"></p>
1273
+ <div class="dtm-output-label">\u6539\u884C\u3042\u308A\u7248</div>
1300
1274
  <div class="dtm-output-row">
1301
1275
  <pre><code data-dtm="output-full"></code></pre>
1302
1276
  <button class="dtm-btn dtm-btn--primary dtm-btn--icon" data-dtm="copy-full" title="\u30B3\u30D4\u30FC">${icon("copy")}</button>
1303
1277
  </div>
1278
+ <div class="dtm-output-label">\uFF11\u884C\u7248</div>
1304
1279
  <div class="dtm-output-row">
1305
1280
  <pre><code data-dtm="output-mini"></code></pre>
1306
1281
  <button class="dtm-btn dtm-btn--primary dtm-btn--icon" data-dtm="copy-mini" title="\u30B3\u30D4\u30FC">${icon("copy")}</button>
@@ -1319,6 +1294,7 @@ var buildUI = (target, options) => {
1319
1294
  <div class="dtm-modal-body" data-dtm="modal-body"></div>
1320
1295
  </div>
1321
1296
  </div>
1297
+
1322
1298
  </div>`;
1323
1299
  const root = q(target, '[data-dtm="root"]');
1324
1300
  const sel = (name) => q(root, `[data-dtm="${name}"]`);
@@ -1327,7 +1303,8 @@ var buildUI = (target, options) => {
1327
1303
  topbar: sel("transport"),
1328
1304
  topbarLoading: sel("topbar-loading"),
1329
1305
  playBtn: sel("play"),
1330
- recBtn: sel("rec"),
1306
+ prevBarBtn: sel("prev-bar"),
1307
+ nextBarBtn: sel("next-bar"),
1331
1308
  soloCheckbox: sel("solo"),
1332
1309
  toolPen: sel("tool-pen"),
1333
1310
  toolSelect: sel("tool-select"),
@@ -2502,7 +2479,20 @@ var createWorkerBackend = async (workerUrl, options) => {
2502
2479
  };
2503
2480
  };
2504
2481
  var createKoeVoice = async (ctx, destination, options) => {
2505
- const backend = options.voiceWorkerUrl ? await createWorkerBackend(options.voiceWorkerUrl, options) : await createLocalBackend(options);
2482
+ let backend;
2483
+ if (options.voiceWorkerUrl) {
2484
+ try {
2485
+ backend = await createWorkerBackend(options.voiceWorkerUrl, options);
2486
+ } catch (err2) {
2487
+ console.warn(
2488
+ "[dtm] Failed to spawn voice worker. Falling back to local backend.",
2489
+ err2
2490
+ );
2491
+ backend = await createLocalBackend(options);
2492
+ }
2493
+ } else {
2494
+ backend = await createLocalBackend(options);
2495
+ }
2506
2496
  const renderCache = /* @__PURE__ */ new Map();
2507
2497
  const inflight = /* @__PURE__ */ new Map();
2508
2498
  const active = /* @__PURE__ */ new Set();
@@ -2683,7 +2673,7 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2683
2673
  if (!m?.renderToCache) continue;
2684
2674
  let n = 0;
2685
2675
  forEachSungNote(track, (note, prevVowel) => {
2686
- if (n >= count) return;
2676
+ if (n >= count && note.startSec >= 3) return;
2687
2677
  n++;
2688
2678
  promises.push(
2689
2679
  m.renderToCache?.(
@@ -2716,14 +2706,18 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2716
2706
  if (opts?.isAudible && !opts.isAudible(track)) continue;
2717
2707
  const t0 = anchorTime + note.startSec;
2718
2708
  if (model.renderToCache && model.scheduleCached) {
2719
- const key = await model.renderToCache(
2720
- note.syllable,
2721
- prevVowel,
2722
- note.pitch,
2723
- note.durationSec * 1e3
2724
- );
2725
- if (session !== streamSession) return;
2726
- if (key) model.scheduleCached(key, t0, peak, track.pan);
2709
+ const renderToCache = model.renderToCache;
2710
+ const scheduleCached = model.scheduleCached;
2711
+ void (async () => {
2712
+ const key = await renderToCache(
2713
+ note.syllable,
2714
+ prevVowel,
2715
+ note.pitch,
2716
+ note.durationSec * 1e3
2717
+ );
2718
+ if (session !== streamSession) return;
2719
+ if (key) scheduleCached(key, t0, peak, track.pan);
2720
+ })();
2727
2721
  } else {
2728
2722
  const when = t0 - ctx.currentTime;
2729
2723
  model(note.syllable, {
@@ -4017,6 +4011,110 @@ var isChordHeavyTrack = (notes, threshold = 0.6) => {
4017
4011
  return chordNotes / notes.length >= threshold;
4018
4012
  };
4019
4013
 
4014
+ // src/mml-info.ts
4015
+ var MML_INFO_HTML = `
4016
+ <div class="dtm-modal-body-content">
4017
+ <h4>1. \u97F3\u7B26\u3068\u4F11\u7B26</h4>
4018
+ <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>
4019
+ <ul>
4020
+ <li>\u534A\u97F3\u4E0A\u3052\u308B: <code>c#</code> \u307E\u305F\u306F <code>c+</code></li>
4021
+ <li>\u534A\u97F3\u4E0B\u3052\u308B: <code>d-</code></li>
4022
+ <li>\u4F11\u7B26: <code>r</code></li>
4023
+ </ul>
4024
+
4025
+ <h4>2. \u97F3\u306E\u9577\u3055</h4>
4026
+ <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>
4027
+ <ul>
4028
+ <li><code>c4</code> : 4\u5206\u97F3\u7B26\u306E\u30C9</li>
4029
+ <li><code>r8</code> : 8\u5206\u4F11\u7B26</li>
4030
+ <li><code>c4.</code> : \u4ED8\u70B94\u5206\u97F3\u7B26\u306E\u30C9\uFF08\u9577\u3055\u30921.5\u500D\u306B\uFF09</li>
4031
+ <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>
4032
+ </ul>
4033
+
4034
+ <h4>3. \u30AA\u30AF\u30BF\u30FC\u30D6\uFF08\u97F3\u306E\u9AD8\u3055\uFF09</h4>
4035
+ <ul>
4036
+ <li><code>o4</code>, <code>o5</code> : \u9AD8\u3055\u3092\u76F4\u63A5\u6307\u5B9A\uFF08\u3075\u3064\u3046\u306F o4 \u304B o5\uFF09</li>
4037
+ <li><code>&gt;</code> : 1\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0A\u3052\u308B</li>
4038
+ <li><code>&lt;</code> : 1\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0B\u3052\u308B</li>
4039
+ </ul>
4040
+
4041
+ <h4>4. \u30C6\u30F3\u30DD</h4>
4042
+ <ul>
4043
+ <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>
4044
+ </ul>
4045
+
4046
+ <h4>5. \u548C\u97F3</h4>
4047
+ <p>\u97F3\u7B26\u3092 <code>[</code> \u3068 <code>]</code> \u3067\u56F2\u3080\u3068\u540C\u6642\u306B\u767A\u97F3\u3057\u307E\u3059\u3002</p>
4048
+ <pre>\u4F8B: [ceg]4 \uFF08\u30C9\u30FB\u30DF\u30FB\u30BD\u30924\u5206\u97F3\u7B26\u3067\u540C\u6642\u306B\u767A\u97F3\uFF09</pre>
4049
+
4050
+ <h4>6. \u30C8\u30E9\u30C3\u30AF\u306E\u533A\u5207\u308A</h4>
4051
+ <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>
4052
+ <ul>
4053
+ <li><code>@0</code>: \u30E1\u30ED\u30C7\u30A3</li>
4054
+ <li><code>@1</code>: \u30B5\u30D6\u30E1\u30ED</li>
4055
+ <li><code>@2</code>: \u30D9\u30FC\u30B9</li>
4056
+ <li><code>@3</code>: \u4F34\u594F</li>
4057
+ </ul>
4058
+
4059
+ <h4>7. \u6B4C\u58F0\u30FB\u6B4C\u8A5E\u5165\u529B</h4>
4060
+ <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>
4061
+ <pre>\u4F8B: @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053</pre>
4062
+ <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>
4063
+
4064
+ <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>
4065
+
4066
+ <!-- \u30B5\u30F3\u30D7\u30EB1 -->
4067
+ <div class="dtm-modal-sample-box">
4068
+ <div class="dtm-modal-sample-header">
4069
+ <span class="dtm-modal-sample-tag">1. \u57FA\u672C\u306E\u30E1\u30ED\u30C7\u30A3</span>
4070
+ <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>
4071
+ </div>
4072
+ <pre style="margin: 0; padding: 6px;">@0 t120 l8 o5 c d e f g a b &gt; c</pre>
4073
+ <div class="dtm-modal-sample-desc">
4074
+ \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
4075
+ </div>
4076
+ <div style="margin-top: 8px;">
4077
+ <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>
4078
+ </div>
4079
+ <div class="dtm-modal-sample-player-container"></div>
4080
+ </div>
4081
+
4082
+ <!-- \u30B5\u30F3\u30D7\u30EB2 -->
4083
+ <div class="dtm-modal-sample-box">
4084
+ <div class="dtm-modal-sample-header">
4085
+ <span class="dtm-modal-sample-tag">2. \u8907\u6570\u30C8\u30E9\u30C3\u30AF\u3068\u548C\u97F3</span>
4086
+ <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>
4087
+ </div>
4088
+ <pre style="margin: 0; padding: 6px;">@0 t120 o5 c e g2 ;
4089
+ @3 o4 [ceg]2 [ceg]2</pre>
4090
+ <div class="dtm-modal-sample-desc">
4091
+ ; \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
4092
+ </div>
4093
+ <div style="margin-top: 8px;">
4094
+ <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>
4095
+ </div>
4096
+ <div class="dtm-modal-sample-player-container"></div>
4097
+ </div>
4098
+
4099
+ <!-- \u30B5\u30F3\u30D7\u30EB3 -->
4100
+ <div class="dtm-modal-sample-box">
4101
+ <div class="dtm-modal-sample-header">
4102
+ <span class="dtm-modal-sample-tag">3. \u6B4C\u5531\u4ED8\u304D (\u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D)</span>
4103
+ <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>
4104
+ </div>
4105
+ <pre style="margin: 0; padding: 6px;">@0 t120 v100 o4g8 g8 e8 e8 f8 e8 d8 c8 g8 g8 e8 e8 d4.;
4106
+ @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053;</pre>
4107
+ <div class="dtm-modal-sample-desc">
4108
+ @@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
4109
+ </div>
4110
+ <div style="margin-top: 8px;">
4111
+ <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>
4112
+ </div>
4113
+ <div class="dtm-modal-sample-player-container"></div>
4114
+ </div>
4115
+ </div>
4116
+ `;
4117
+
4020
4118
  // src/mml-parser.ts
4021
4119
  var PITCH_MAP2 = {
4022
4120
  c: 0,
@@ -4028,7 +4126,7 @@ var PITCH_MAP2 = {
4028
4126
  b: 11
4029
4127
  };
4030
4128
  var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
4031
- var META_DIRECTIVE = /#(inst|drum|volume|mode)=([\w-]+)/gi;
4129
+ var META_DIRECTIVE = /#(inst|drum|volume|drumvolume|mode)=([\w-]+)/gi;
4032
4130
  var parseMmlMeta = (mml) => {
4033
4131
  const meta = {};
4034
4132
  for (const m of mml.matchAll(META_DIRECTIVE)) {
@@ -4038,6 +4136,9 @@ var parseMmlMeta = (mml) => {
4038
4136
  else if (key === "volume") {
4039
4137
  const v = Number.parseInt(m[2], 10);
4040
4138
  if (!Number.isNaN(v)) meta.volume = v;
4139
+ } else if (key === "drumvolume") {
4140
+ const dv = Number.parseInt(m[2], 10);
4141
+ if (!Number.isNaN(dv)) meta.drumVolume = dv;
4041
4142
  } else if (key === "mode") {
4042
4143
  if (m[2] === "simple" || m[2] === "advanced") {
4043
4144
  meta.mode = m[2];
@@ -4052,6 +4153,8 @@ var formatMmlMeta = (meta) => {
4052
4153
  if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
4053
4154
  if (meta.drum) parts.push(`#drum=${meta.drum}`);
4054
4155
  if (meta.volume !== void 0) parts.push(`#volume=${meta.volume}`);
4156
+ if (meta.drumVolume !== void 0)
4157
+ parts.push(`#drumvolume=${meta.drumVolume}`);
4055
4158
  if (meta.mode) parts.push(`#mode=${meta.mode}`);
4056
4159
  return parts.join(" ");
4057
4160
  };
@@ -4639,9 +4742,13 @@ var DAW_CSS = `
4639
4742
  /* \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
4640
4743
  \u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u30D0\u30FC\uFF08.dtm-controlbar\uFF09\u306B\u3082\u4F9B\u7D66\u3059\u308B\u3002mountPresetSelect /
4641
4744
  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
4642
- 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 */
4745
+ 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
4746
+ \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
4747
+ .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 */
4643
4748
  .dtm-daw,
4644
- .dtm-controlbar {
4749
+ .dtm-controlbar,
4750
+ .dtm-modal-overlay,
4751
+ .dtm-consent-overlay {
4645
4752
  /* PICO-8 16\u8272\u30D1\u30EC\u30C3\u30C8\u3088\u308A */
4646
4753
  --c-black: #000000;
4647
4754
  --c-navy: #1d2b53;
@@ -4769,7 +4876,7 @@ var DAW_CSS = `
4769
4876
  top: 0;
4770
4877
  z-index: 20;
4771
4878
  display: flex;
4772
- flex-wrap: wrap;
4879
+ flex-wrap: nowrap;
4773
4880
  align-items: center;
4774
4881
  gap: var(--dtm-gap);
4775
4882
  padding: 6px;
@@ -4779,6 +4886,17 @@ var DAW_CSS = `
4779
4886
  inset 0 0 0 2px var(--c-black),
4780
4887
  0 0 0 2px var(--dtm-success),
4781
4888
  4px 4px 0 var(--c-black);
4889
+ overflow-x: auto;
4890
+ scrollbar-width: none;
4891
+ }
4892
+ .dtm-topbar::-webkit-scrollbar {
4893
+ display: none;
4894
+ }
4895
+ .dtm-topbar > * {
4896
+ flex-shrink: 0;
4897
+ }
4898
+ .dtm-topbar > .dtm-grow {
4899
+ flex-shrink: 1;
4782
4900
  }
4783
4901
 
4784
4902
  /* PLAY\u30DC\u30BF\u30F3 \u2014 \u30B2\u30FC\u30E0\u306E\u300C\u6C7A\u5B9A\u30DC\u30BF\u30F3\u300D\u7684\u5B58\u5728\u611F */
@@ -5129,6 +5247,15 @@ var DAW_CSS = `
5129
5247
  line-height: 1.8;
5130
5248
  color: var(--dtm-success);
5131
5249
  }
5250
+ .dtm-output-label {
5251
+ font-size: 11px;
5252
+ color: var(--dtm-muted);
5253
+ font-family: var(--dtm-font);
5254
+ margin-top: 10px;
5255
+ }
5256
+ .dtm-output-label:first-of-type {
5257
+ margin-top: 0;
5258
+ }
5132
5259
  .dtm-output-row { display: flex; gap: 8px; align-items: flex-start; margin-top: 6px; }
5133
5260
  .dtm-output-row pre { flex: 1; }
5134
5261
 
@@ -5239,10 +5366,76 @@ var DAW_CSS = `
5239
5366
  justify-content: center;
5240
5367
  padding: 16px;
5241
5368
  backdrop-filter: blur(2px);
5369
+ /* 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
5370
+ .dtm-daw \u304B\u3089\u7D99\u627F\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u3053\u3053\u3067\u660E\u793A\u3059\u308B\u3002 */
5371
+ color: var(--dtm-text);
5372
+ font-family: var(--dtm-font);
5242
5373
  }
5243
5374
  .dtm-modal-overlay[hidden] {
5244
5375
  display: none !important;
5245
5376
  }
5377
+
5378
+ /* \u2500\u2500\u2500 \u5229\u7528\u898F\u7D04\u540C\u610F\u30AB\u30D0\u30FC \u2500\u2500\u2500 */
5379
+ .dtm-consent-overlay {
5380
+ position: fixed;
5381
+ inset: 0;
5382
+ z-index: 10100;
5383
+ background: rgba(0, 0, 0, 0.6);
5384
+ display: flex;
5385
+ align-items: center;
5386
+ justify-content: center;
5387
+ padding: 16px;
5388
+ backdrop-filter: blur(2px);
5389
+ /* 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 */
5390
+ color: var(--dtm-text);
5391
+ font-family: var(--dtm-font);
5392
+ }
5393
+ .dtm-consent-overlay[hidden] {
5394
+ display: none !important;
5395
+ }
5396
+ .dtm-consent-modal {
5397
+ max-width: 450px;
5398
+ width: 100%;
5399
+ max-height: 85vh;
5400
+ display: flex;
5401
+ flex-direction: column;
5402
+ background: var(--dtm-surface);
5403
+ border: 2px solid var(--c-black);
5404
+ box-shadow:
5405
+ inset 0 0 0 2px var(--c-black),
5406
+ 0 0 0 2px var(--dtm-primary),
5407
+ 4px 4px 0 var(--c-black);
5408
+ overflow-y: auto;
5409
+ }
5410
+ .dtm-consent-header {
5411
+ background: var(--dtm-deep);
5412
+ color: var(--dtm-text);
5413
+ padding: 8px 12px;
5414
+ border-bottom: 2px solid var(--c-black);
5415
+ font-weight: bold;
5416
+ text-align: center;
5417
+ font-size: 14px;
5418
+ }
5419
+ .dtm-consent-body {
5420
+ padding: 12px 16px;
5421
+ font-size: 13px;
5422
+ line-height: 1.6;
5423
+ }
5424
+ .dtm-consent-body a {
5425
+ color: var(--dtm-primary);
5426
+ text-decoration: underline;
5427
+ }
5428
+ .dtm-consent-body a:hover {
5429
+ color: var(--dtm-accent);
5430
+ }
5431
+ .dtm-consent-footer {
5432
+ padding: 8px;
5433
+ border-top: 2px solid var(--c-black);
5434
+ background: var(--dtm-deep);
5435
+ display: flex;
5436
+ justify-content: center;
5437
+ }
5438
+
5246
5439
  .dtm-modal {
5247
5440
  max-width: 500px;
5248
5441
  width: 100%;
@@ -5376,18 +5569,19 @@ var DAW_CSS = `
5376
5569
  }
5377
5570
 
5378
5571
  .dtm-hidden { display: none !important; }
5379
- /* \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 */
5572
+ /* \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 */
5380
5573
  .dtm-load-note {
5381
5574
  margin: 6px 0 0;
5382
5575
  padding: 0 2px;
5383
5576
  font-family: var(--dtm-font);
5384
- font-size: 10px;
5577
+ font-size: 11px;
5385
5578
  line-height: 1.5;
5386
5579
  letter-spacing: .04em;
5387
- color: var(--dtm-muted);
5388
- opacity: .85;
5580
+ color: var(--dtm-warn); /* \u8B66\u544A\u8272\uFF08\u30AA\u30EC\u30F3\u30B8\uFF09 */
5581
+ font-weight: bold;
5582
+ opacity: 1.0;
5389
5583
  }
5390
- .dtm-load-note::before { content: "\u24D8 "; }
5584
+ .dtm-load-note::before { content: "\u26A0 "; }
5391
5585
  .dtm-grow { flex: 1 1 auto; }
5392
5586
  .dtm-lyric-icon {
5393
5587
  flex: 0 0 auto;
@@ -5788,6 +5982,7 @@ var STEPS_PER_BEAT3 = 48;
5788
5982
  var STEPS_PER_BAR = 192;
5789
5983
  var DEFAULT_TRACK_COLORS = ["#00e436", "#29adff", "#ff77a8", "#ffec27"];
5790
5984
  var activePlayer = null;
5985
+ var agreedModelsInSession = /* @__PURE__ */ new Set();
5791
5986
  var LYRIC_MODEL_LABELS = {
5792
5987
  klatt: "\u8EFD\u91CF\u30ED\u30DC\u58F0",
5793
5988
  ...KOE_VOICEBANK_LABELS
@@ -5904,6 +6099,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
5904
6099
  const drumPatternDict = options.drumPatterns ?? DRUM_PATTERNS;
5905
6100
  const drumPattern = meta.drum ? drumPatternDict[meta.drum] ?? null : null;
5906
6101
  const trackVolume = meta.volume ?? options.volume ?? 100;
6102
+ const drumVolume = meta.drumVolume ?? 80;
5907
6103
  const colors = options.trackColors ?? DEFAULT_TRACK_COLORS;
5908
6104
  const useSynth = options.synth ?? !options.onPlayNote;
5909
6105
  const secondsPerStep = 60 / bpm / STEPS_PER_BEAT3;
@@ -6043,16 +6239,21 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6043
6239
  const menuDropdown = doc.createElement("div");
6044
6240
  menuDropdown.className = "dtm-player-menu";
6045
6241
  menuDropdown.style.display = "none";
6046
- const copyMmlItem = doc.createElement("button");
6047
- copyMmlItem.type = "button";
6048
- copyMmlItem.className = "dtm-player-menu-item";
6049
- copyMmlItem.textContent = "MML\u30B3\u30D4\u30FC";
6050
- const embedItem = doc.createElement("button");
6051
- embedItem.type = "button";
6052
- embedItem.className = "dtm-player-menu-item";
6053
- embedItem.textContent = "\u57CB\u3081\u8FBC\u3080";
6054
- menuDropdown.appendChild(copyMmlItem);
6242
+ const makeMenuItem = (label) => {
6243
+ const item = doc.createElement("button");
6244
+ item.type = "button";
6245
+ item.className = "dtm-player-menu-item";
6246
+ item.textContent = label;
6247
+ return item;
6248
+ };
6249
+ const showMmlItem = makeMenuItem("MML\u3092\u8868\u793A");
6250
+ const mmlInfoItem = makeMenuItem("MML\u66F8\u5F0F\u3068\u306F");
6251
+ const embedItem = makeMenuItem("\u57CB\u3081\u8FBC\u3080");
6252
+ const copyMmlItem = makeMenuItem("MML\u30B3\u30D4\u30FC");
6253
+ menuDropdown.appendChild(showMmlItem);
6254
+ menuDropdown.appendChild(mmlInfoItem);
6055
6255
  menuDropdown.appendChild(embedItem);
6256
+ menuDropdown.appendChild(copyMmlItem);
6056
6257
  menuContainer.appendChild(menuDropdown);
6057
6258
  mmlHeader.appendChild(menuContainer);
6058
6259
  const toggleMenu = (show) => {
@@ -6075,38 +6276,200 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6075
6276
  e.stopPropagation();
6076
6277
  toggleMenu();
6077
6278
  });
6078
- copyMmlItem.addEventListener("click", async (e) => {
6079
- e.stopPropagation();
6080
- const success = await copyToClipboard(doc, mml);
6081
- if (success) {
6082
- copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F\uFF01";
6083
- } else {
6084
- copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u5931\u6557";
6279
+ let infoModalEl = null;
6280
+ let modalSamplePlayer = null;
6281
+ const closeInfoModal = () => {
6282
+ if (modalSamplePlayer) {
6283
+ modalSamplePlayer.stop();
6284
+ modalSamplePlayer.destroy();
6285
+ modalSamplePlayer = null;
6286
+ }
6287
+ infoModalEl?.remove();
6288
+ infoModalEl = null;
6289
+ };
6290
+ const openInfoModal = (title) => {
6291
+ closeInfoModal();
6292
+ const overlay = doc.createElement("div");
6293
+ overlay.className = "dtm-modal-overlay";
6294
+ const modal = doc.createElement("div");
6295
+ modal.className = "dtm-win dtm-modal";
6296
+ const header = doc.createElement("div");
6297
+ header.className = "dtm-modal-header";
6298
+ const titleEl = doc.createElement("span");
6299
+ titleEl.className = "dtm-modal-title";
6300
+ titleEl.textContent = title;
6301
+ const closeBtn = doc.createElement("button");
6302
+ closeBtn.type = "button";
6303
+ closeBtn.className = "dtm-modal-close";
6304
+ closeBtn.innerHTML = "&times;";
6305
+ closeBtn.title = "\u9589\u3058\u308B";
6306
+ header.append(titleEl, closeBtn);
6307
+ const modalBody = doc.createElement("div");
6308
+ modalBody.className = "dtm-modal-body";
6309
+ modal.append(header, modalBody);
6310
+ overlay.appendChild(modal);
6311
+ closeBtn.addEventListener("click", (e) => {
6312
+ e.stopPropagation();
6313
+ closeInfoModal();
6314
+ });
6315
+ overlay.addEventListener("click", (e) => {
6316
+ if (e.target === overlay) closeInfoModal();
6317
+ });
6318
+ doc.body.appendChild(overlay);
6319
+ infoModalEl = overlay;
6320
+ return modalBody;
6321
+ };
6322
+ const appendCopyButton = (parent, text) => {
6323
+ const actions = doc.createElement("div");
6324
+ actions.style.marginTop = "8px";
6325
+ const copyBtn = doc.createElement("button");
6326
+ copyBtn.type = "button";
6327
+ copyBtn.className = "dtm-btn dtm-btn--primary dtm-btn--xs";
6328
+ copyBtn.textContent = "\u{1F4CB} \u30B3\u30D4\u30FC";
6329
+ copyBtn.addEventListener("click", async (e) => {
6330
+ e.stopPropagation();
6331
+ const ok = await copyToClipboard(doc, text);
6332
+ copyBtn.textContent = ok ? "\u2713 \u30B3\u30D4\u30FC\u5B8C\u4E86" : "\u30B3\u30D4\u30FC\u5931\u6557";
6333
+ if (ok) copyBtn.classList.add("dtm-btn--success");
6334
+ setTimeout(() => {
6335
+ copyBtn.textContent = "\u{1F4CB} \u30B3\u30D4\u30FC";
6336
+ copyBtn.classList.remove("dtm-btn--success");
6337
+ }, 1200);
6338
+ });
6339
+ actions.appendChild(copyBtn);
6340
+ parent.appendChild(actions);
6341
+ };
6342
+ const wireSampleButtons = (modalBody) => {
6343
+ const copyBtns = modalBody.querySelectorAll(".dtm-modal-sample-copy-btn");
6344
+ for (const btn of copyBtns) {
6345
+ const el = btn;
6346
+ el.addEventListener("click", async (e) => {
6347
+ e.stopPropagation();
6348
+ const sampleMml = el.getAttribute("data-mml") ?? "";
6349
+ const original = el.textContent;
6350
+ const ok = await copyToClipboard(doc, sampleMml);
6351
+ el.textContent = ok ? "\u2713 \u30B3\u30D4\u30FC\u5B8C\u4E86" : "\u30B3\u30D4\u30FC\u5931\u6557";
6352
+ if (ok) el.classList.add("dtm-btn--success");
6353
+ setTimeout(() => {
6354
+ el.textContent = original;
6355
+ el.classList.remove("dtm-btn--success");
6356
+ }, 1200);
6357
+ });
6085
6358
  }
6086
- setTimeout(() => {
6087
- copyMmlItem.textContent = "MML\u30B3\u30D4\u30FC";
6088
- }, 2e3);
6359
+ let activeSampleBtn = null;
6360
+ const resetSampleBtn = (b) => {
6361
+ if (!b) return;
6362
+ b.textContent = "\u25B6 \u8A66\u8074";
6363
+ b.classList.remove("dtm-btn--danger");
6364
+ b.classList.add("dtm-btn--primary");
6365
+ };
6366
+ const markPlaying = (b) => {
6367
+ b.textContent = "\u25A0 \u505C\u6B62";
6368
+ b.classList.remove("dtm-btn--primary");
6369
+ b.classList.add("dtm-btn--danger");
6370
+ };
6371
+ const playBtns = modalBody.querySelectorAll(".dtm-modal-sample-play-btn");
6372
+ for (const btn of playBtns) {
6373
+ const el = btn;
6374
+ el.addEventListener("click", (e) => {
6375
+ e.stopPropagation();
6376
+ const sampleMml = el.getAttribute("data-mml") ?? "";
6377
+ if (activeSampleBtn === el && modalSamplePlayer) {
6378
+ if (modalSamplePlayer.isPlaying()) {
6379
+ modalSamplePlayer.stop();
6380
+ } else {
6381
+ modalSamplePlayer.play();
6382
+ markPlaying(el);
6383
+ }
6384
+ return;
6385
+ }
6386
+ if (modalSamplePlayer) {
6387
+ modalSamplePlayer.stop();
6388
+ modalSamplePlayer.destroy();
6389
+ modalSamplePlayer = null;
6390
+ }
6391
+ resetSampleBtn(activeSampleBtn);
6392
+ activeSampleBtn = el;
6393
+ const sampleBox = el.closest(".dtm-modal-sample-box");
6394
+ const container = sampleBox?.querySelector(
6395
+ ".dtm-modal-sample-player-container"
6396
+ );
6397
+ if (!container) return;
6398
+ container.innerHTML = "";
6399
+ modalSamplePlayer = mountMmlPlayer(container, sampleMml, {
6400
+ onPlayNote: options.onPlayNote,
6401
+ onPlayDrum: options.onPlayDrum,
6402
+ onResumeAudio: options.onResumeAudio,
6403
+ getAudioTime: options.getAudioTime,
6404
+ singingVoices: options.singingVoices,
6405
+ drumPatterns: options.drumPatterns,
6406
+ volume: trackVolume,
6407
+ // 解説モーダル内の試聴サンプルは規約同意を要求しない。
6408
+ skipConsent: true,
6409
+ onStop: () => {
6410
+ if (activeSampleBtn === el) resetSampleBtn(el);
6411
+ }
6412
+ });
6413
+ markPlaying(el);
6414
+ modalSamplePlayer.play();
6415
+ });
6416
+ }
6417
+ };
6418
+ showMmlItem.addEventListener("click", (e) => {
6419
+ e.stopPropagation();
6420
+ toggleMenu(false);
6421
+ const modalBody = openInfoModal("MML");
6422
+ const pre = doc.createElement("pre");
6423
+ pre.textContent = mml;
6424
+ pre.style.whiteSpace = "pre-wrap";
6425
+ pre.style.wordBreak = "break-all";
6426
+ modalBody.appendChild(pre);
6427
+ appendCopyButton(modalBody, mml);
6428
+ });
6429
+ mmlInfoItem.addEventListener("click", (e) => {
6430
+ e.stopPropagation();
6431
+ toggleMenu(false);
6432
+ const modalBody = openInfoModal("MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC");
6433
+ modalBody.innerHTML = MML_INFO_HTML;
6434
+ wireSampleButtons(modalBody);
6089
6435
  });
6090
6436
  embedItem.addEventListener("click", async (e) => {
6091
6437
  e.stopPropagation();
6092
- embedItem.textContent = "\u751F\u6210\u4E2D...";
6438
+ toggleMenu(false);
6439
+ const modalBody = openInfoModal("\u57CB\u3081\u8FBC\u307F");
6440
+ const loading = doc.createElement("p");
6441
+ loading.textContent = "\u751F\u6210\u4E2D...";
6442
+ modalBody.appendChild(loading);
6093
6443
  try {
6094
6444
  const embedBase = options.embedUrl ?? "https://onjmin.github.io/dtm/demo/embed.html";
6095
6445
  const payload = await encodeMml(mml);
6096
6446
  const url2 = `${embedBase}#${payload}`;
6097
6447
  const snippet = `<iframe src="${url2}" width="100%" height="260" frameborder="0" loading="lazy" title="@onjmin/dtm player"></iframe>`;
6098
- const success = await copyToClipboard(doc, snippet);
6099
- if (success) {
6100
- embedItem.textContent = "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F\uFF01";
6101
- } else {
6102
- embedItem.textContent = "\u30B3\u30D4\u30FC\u5931\u6557";
6103
- }
6448
+ if (!modalBody.isConnected) return;
6449
+ loading.remove();
6450
+ const desc = doc.createElement("p");
6451
+ 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";
6452
+ const pre = doc.createElement("pre");
6453
+ pre.textContent = snippet;
6454
+ pre.style.whiteSpace = "pre-wrap";
6455
+ pre.style.wordBreak = "break-all";
6456
+ modalBody.append(desc, pre);
6457
+ appendCopyButton(modalBody, snippet);
6104
6458
  } catch (err2) {
6105
6459
  console.error("[dtm] failed to generate embed snippet", err2);
6106
- embedItem.textContent = "\u751F\u6210\u5931\u6557";
6460
+ if (modalBody.isConnected) loading.textContent = "\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
6461
+ }
6462
+ });
6463
+ copyMmlItem.addEventListener("click", async (e) => {
6464
+ e.stopPropagation();
6465
+ const success = await copyToClipboard(doc, mml);
6466
+ if (success) {
6467
+ copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F\uFF01";
6468
+ } else {
6469
+ copyMmlItem.textContent = "\u30B3\u30D4\u30FC\u5931\u6557";
6107
6470
  }
6108
6471
  setTimeout(() => {
6109
- embedItem.textContent = "\u57CB\u3081\u8FBC\u3080";
6472
+ copyMmlItem.textContent = "MML\u30B3\u30D4\u30FC";
6110
6473
  }, 2e3);
6111
6474
  });
6112
6475
  const promotedToImage = /* @__PURE__ */ new Set();
@@ -6352,6 +6715,77 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6352
6715
  root.appendChild(termsDiv);
6353
6716
  }
6354
6717
  target.appendChild(root);
6718
+ let consentOverlayEl = null;
6719
+ const checkConsentAndShow = () => {
6720
+ try {
6721
+ if (options.skipConsent) return;
6722
+ const unagreed = termsModels.filter((model) => {
6723
+ if (agreedModelsInSession.has(model)) return false;
6724
+ try {
6725
+ if (typeof localStorage === "undefined" || !localStorage) return true;
6726
+ return localStorage.getItem(`dtm_agreed_terms_${model}`) !== "true";
6727
+ } catch (e) {
6728
+ console.warn(
6729
+ "[dtm-player] localStorage access denied in consent check",
6730
+ e
6731
+ );
6732
+ return true;
6733
+ }
6734
+ });
6735
+ if (unagreed.length === 0) return;
6736
+ const consentOverlay = doc.createElement("div");
6737
+ consentOverlay.className = "dtm-consent-overlay";
6738
+ const modal = doc.createElement("div");
6739
+ modal.className = "dtm-win dtm-consent-modal";
6740
+ const header = doc.createElement("div");
6741
+ header.className = "dtm-consent-header";
6742
+ header.textContent = "\u5229\u7528\u898F\u7D04\u306E\u78BA\u8A8D";
6743
+ const body2 = doc.createElement("div");
6744
+ body2.className = "dtm-consent-body";
6745
+ 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>`;
6746
+ for (const model of unagreed) {
6747
+ const label = KOE_VOICEBANK_LABELS[model] || model;
6748
+ const url2 = KOE_VOICEBANK_TERMS[model];
6749
+ contentHTML += `
6750
+ <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);">
6751
+ <div style="display: flex; align-items: center; gap: 4px; flex-wrap: wrap; font-size: 11px; font-weight: bold; color: var(--dtm-gold);">
6752
+ <span>\u4F7F\u7528\u6642\u306B\u306F</span>
6753
+ <a href="${url2}" target="_blank" rel="noopener noreferrer" style="color: var(--dtm-primary); text-decoration: underline;">${label}UTAU\u97F3\u6E90</a>
6754
+ <span>\u306E\u5229\u7528\u898F\u7D04\u306B\u5F93\u3063\u3066\u304F\u3060\u3055\u3044</span>
6755
+ </div>
6756
+ </div>
6757
+ `;
6758
+ }
6759
+ body2.innerHTML = contentHTML;
6760
+ const footer = doc.createElement("div");
6761
+ footer.className = "dtm-consent-footer";
6762
+ const btn = doc.createElement("button");
6763
+ btn.type = "button";
6764
+ btn.className = "dtm-btn dtm-btn--success";
6765
+ btn.textContent = "\u540C\u610F\u3057\u3066\u5229\u7528\u3059\u308B";
6766
+ btn.onclick = () => {
6767
+ for (const model of unagreed) {
6768
+ try {
6769
+ if (typeof localStorage !== "undefined" && localStorage) {
6770
+ localStorage.setItem(`dtm_agreed_terms_${model}`, "true");
6771
+ }
6772
+ } catch (e) {
6773
+ }
6774
+ agreedModelsInSession.add(model);
6775
+ }
6776
+ consentOverlay.remove();
6777
+ consentOverlayEl = null;
6778
+ };
6779
+ footer.appendChild(btn);
6780
+ modal.append(header, body2, footer);
6781
+ consentOverlay.appendChild(modal);
6782
+ doc.body.appendChild(consentOverlay);
6783
+ consentOverlayEl = consentOverlay;
6784
+ } catch (err2) {
6785
+ console.error("[dtm-player] Error in checkConsentAndShow:", err2);
6786
+ }
6787
+ };
6788
+ checkConsentAndShow();
6355
6789
  const autoScroll = (lane, el) => {
6356
6790
  if (el.offsetWidth === 0 || lane.clientWidth === 0) return;
6357
6791
  const elementCenter = el.offsetLeft + el.offsetWidth / 2;
@@ -6410,7 +6844,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6410
6844
  if (useSynth) ensureSynth().playNote(e);
6411
6845
  },
6412
6846
  onPlayDrum: (e) => {
6413
- const velocity = e.velocity * (trackVolume / 100);
6847
+ const velocity = e.velocity * (drumVolume / 100) * (trackVolume / 100);
6414
6848
  options.onPlayDrum?.({ ...e, velocity });
6415
6849
  if (useSynth) ensureSynth().playDrum({ ...e, velocity });
6416
6850
  },
@@ -6526,6 +6960,8 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6526
6960
  hideActiveBalloon();
6527
6961
  }
6528
6962
  root.remove();
6963
+ consentOverlayEl?.remove();
6964
+ closeInfoModal();
6529
6965
  };
6530
6966
  const instance = {
6531
6967
  play,
@@ -6567,108 +7003,6 @@ var CHORD_INFO_HTML = `
6567
7003
  </ul>
6568
7004
  </div>
6569
7005
  `;
6570
- var MML_INFO_HTML = `
6571
- <div class="dtm-modal-body-content">
6572
- <h4>1. \u97F3\u7B26\u3068\u4F11\u7B26</h4>
6573
- <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>
6574
- <ul>
6575
- <li>\u534A\u97F3\u4E0A\u3052\u308B: <code>c#</code> \u307E\u305F\u306F <code>c+</code></li>
6576
- <li>\u534A\u97F3\u4E0B\u3052\u308B: <code>d-</code></li>
6577
- <li>\u4F11\u7B26: <code>r</code></li>
6578
- </ul>
6579
-
6580
- <h4>2. \u97F3\u306E\u9577\u3055</h4>
6581
- <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>
6582
- <ul>
6583
- <li><code>c4</code> : 4\u5206\u97F3\u7B26\u306E\u30C9</li>
6584
- <li><code>r8</code> : 8\u5206\u4F11\u7B26</li>
6585
- <li><code>c4.</code> : \u4ED8\u70B94\u5206\u97F3\u7B26\u306E\u30C9\uFF08\u9577\u3055\u30921.5\u500D\u306B\uFF09</li>
6586
- <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>
6587
- </ul>
6588
-
6589
- <h4>3. \u30AA\u30AF\u30BF\u30FC\u30D6\uFF08\u97F3\u306E\u9AD8\u3055\uFF09</h4>
6590
- <ul>
6591
- <li><code>o4</code>, <code>o5</code> : \u9AD8\u3055\u3092\u76F4\u63A5\u6307\u5B9A\uFF08\u3075\u3064\u3046\u306F o4 \u304B o5\uFF09</li>
6592
- <li><code>&gt;</code> : 1\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0A\u3052\u308B</li>
6593
- <li><code>&lt;</code> : 1\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0B\u3052\u308B</li>
6594
- </ul>
6595
-
6596
- <h4>4. \u30C6\u30F3\u30DD</h4>
6597
- <ul>
6598
- <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>
6599
- </ul>
6600
-
6601
- <h4>5. \u548C\u97F3</h4>
6602
- <p>\u97F3\u7B26\u3092 <code>[</code> \u3068 <code>]</code> \u3067\u56F2\u3080\u3068\u540C\u6642\u306B\u767A\u97F3\u3057\u307E\u3059\u3002</p>
6603
- <pre>\u4F8B: [ceg]4 \uFF08\u30C9\u30FB\u30DF\u30FB\u30BD\u30924\u5206\u97F3\u7B26\u3067\u540C\u6642\u306B\u767A\u97F3\uFF09</pre>
6604
-
6605
- <h4>6. \u30C8\u30E9\u30C3\u30AF\u306E\u533A\u5207\u308A</h4>
6606
- <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>
6607
- <ul>
6608
- <li><code>@0</code>: \u30E1\u30ED\u30C7\u30A3</li>
6609
- <li><code>@1</code>: \u30B5\u30D6\u30E1\u30ED</li>
6610
- <li><code>@2</code>: \u30D9\u30FC\u30B9</li>
6611
- <li><code>@3</code>: \u4F34\u594F</li>
6612
- </ul>
6613
-
6614
- <h4>7. \u6B4C\u58F0\u30FB\u6B4C\u8A5E\u5165\u529B</h4>
6615
- <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>
6616
- <pre>\u4F8B: @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053</pre>
6617
- <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>
6618
-
6619
- <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>
6620
-
6621
- <!-- \u30B5\u30F3\u30D7\u30EB1 -->
6622
- <div class="dtm-modal-sample-box">
6623
- <div class="dtm-modal-sample-header">
6624
- <span class="dtm-modal-sample-tag">1. \u57FA\u672C\u306E\u30E1\u30ED\u30C7\u30A3</span>
6625
- <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>
6626
- </div>
6627
- <pre style="margin: 0; padding: 6px;">@0 t120 l8 o5 c d e f g a b &gt; c</pre>
6628
- <div class="dtm-modal-sample-desc">
6629
- \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
6630
- </div>
6631
- <div style="margin-top: 8px;">
6632
- <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>
6633
- </div>
6634
- <div class="dtm-modal-sample-player-container"></div>
6635
- </div>
6636
-
6637
- <!-- \u30B5\u30F3\u30D7\u30EB2 -->
6638
- <div class="dtm-modal-sample-box">
6639
- <div class="dtm-modal-sample-header">
6640
- <span class="dtm-modal-sample-tag">2. \u8907\u6570\u30C8\u30E9\u30C3\u30AF\u3068\u548C\u97F3</span>
6641
- <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>
6642
- </div>
6643
- <pre style="margin: 0; padding: 6px;">@0 t120 o5 c e g2 ;
6644
- @3 o4 [ceg]2 [ceg]2</pre>
6645
- <div class="dtm-modal-sample-desc">
6646
- ; \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
6647
- </div>
6648
- <div style="margin-top: 8px;">
6649
- <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>
6650
- </div>
6651
- <div class="dtm-modal-sample-player-container"></div>
6652
- </div>
6653
-
6654
- <!-- \u30B5\u30F3\u30D7\u30EB3 -->
6655
- <div class="dtm-modal-sample-box">
6656
- <div class="dtm-modal-sample-header">
6657
- <span class="dtm-modal-sample-tag">3. \u6B4C\u5531\u4ED8\u304D (\u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D)</span>
6658
- <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>
6659
- </div>
6660
- <pre style="margin: 0; padding: 6px;">@0 t120 v100 o4g8 g8 e8 e8 f8 e8 d8 c8 g8 g8 e8 e8 d4.;
6661
- @@0 tsukuyomi \u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u3069\u3093\u3050\u308A\u3053;</pre>
6662
- <div class="dtm-modal-sample-desc">
6663
- @@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
6664
- </div>
6665
- <div style="margin-top: 8px;">
6666
- <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>
6667
- </div>
6668
- <div class="dtm-modal-sample-player-container"></div>
6669
- </div>
6670
- </div>
6671
- `;
6672
7006
  var MIDI_INFO_HTML = `
6673
7007
  <div class="dtm-modal-body-content">
6674
7008
  <h4>1. MIDI\u30D5\u30A1\u30A4\u30EB\u3068\u306F</h4>
@@ -7612,7 +7946,7 @@ var mountDAW = (target, options = {}) => {
7612
7946
  };
7613
7947
  const updateTransport = () => {
7614
7948
  const playing = playbackState === "playing";
7615
- const label = playing ? "\u4E00\u6642\u505C\u6B62" : playbackState === "paused" ? "\u518D\u958B" : "\u8A66\u8074";
7949
+ const label = playing ? "\u505C\u6B62" : playbackState === "paused" ? "\u518D\u958B" : "\u8A66\u8074";
7616
7950
  refs.playBtn.innerHTML = `${icon(playing ? "pause" : "play")}<span>${label}</span>`;
7617
7951
  refs.playBtn.classList.toggle("dtm-play--stop", playing);
7618
7952
  };
@@ -7880,6 +8214,7 @@ var mountDAW = (target, options = {}) => {
7880
8214
  instrument: currentInstrument || void 0,
7881
8215
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
7882
8216
  volume: masterVolume,
8217
+ drumVolume,
7883
8218
  mode
7884
8219
  });
7885
8220
  if (refs.decomposeChordToggle.checked) {
@@ -8031,6 +8366,11 @@ var mountDAW = (target, options = {}) => {
8031
8366
  refs.masterVolume.value = String(meta.volume);
8032
8367
  refs.masterVolumeLabel.textContent = `${meta.volume}%`;
8033
8368
  }
8369
+ if (meta.drumVolume !== void 0) {
8370
+ drumVolume = meta.drumVolume;
8371
+ refs.drumVolume.value = String(meta.drumVolume);
8372
+ refs.drumVolumeLabel.textContent = `${meta.drumVolume}%`;
8373
+ }
8034
8374
  for (const t of trackStates) {
8035
8375
  t.lyrics = "";
8036
8376
  t.lyricModel = "";
@@ -8039,8 +8379,8 @@ var mountDAW = (target, options = {}) => {
8039
8379
  t.vocalPan = 64;
8040
8380
  t.vocalOctave = 0;
8041
8381
  }
8042
- lyrics?.forEach((lt, idx) => {
8043
- const t = trackStates[idx];
8382
+ lyrics?.forEach((lt) => {
8383
+ const t = trackStates[lt.trackId];
8044
8384
  if (!t) return;
8045
8385
  t.lyrics = lt.syllables.map((s) => s.kana).join("");
8046
8386
  t.lyricModel = lt.model;
@@ -8184,8 +8524,17 @@ var mountDAW = (target, options = {}) => {
8184
8524
  const wireEvents = () => {
8185
8525
  refs.playBtn.addEventListener("click", togglePlay);
8186
8526
  refs.playBtn.disabled = false;
8187
- refs.recBtn.addEventListener("click", () => options.onToggleRecord?.());
8188
- refs.recBtn.style.display = options.onToggleRecord ? "" : "none";
8527
+ refs.prevBarBtn.addEventListener("click", () => {
8528
+ const targetStep = Math.max(
8529
+ 0,
8530
+ Math.floor((getCurrentPlayStep() - 1) / renderConfig.stepsPerBar) * renderConfig.stepsPerBar
8531
+ );
8532
+ jumpTo(targetStep);
8533
+ });
8534
+ refs.nextBarBtn.addEventListener("click", () => {
8535
+ const targetStep = Math.floor(getCurrentPlayStep() / renderConfig.stepsPerBar + 1) * renderConfig.stepsPerBar;
8536
+ jumpTo(targetStep);
8537
+ });
8189
8538
  refs.soloCheckbox.addEventListener("change", () => {
8190
8539
  isSolo = refs.soloCheckbox.checked;
8191
8540
  });
@@ -8548,6 +8897,20 @@ var mountDAW = (target, options = {}) => {
8548
8897
  if (playbackState === "paused") return pausedPlayStep;
8549
8898
  return playStartStep;
8550
8899
  };
8900
+ const jumpTo = async (step) => {
8901
+ const wasPlaying = playbackState === "playing";
8902
+ if (wasPlaying) {
8903
+ sequencer.stop();
8904
+ options.singingVoices?.stopStream();
8905
+ playStartStep = step;
8906
+ pausedPlayStep = step;
8907
+ currentPlayStep = step;
8908
+ playbackState = "paused";
8909
+ await play();
8910
+ } else {
8911
+ forcePauseAt(step);
8912
+ }
8913
+ };
8551
8914
  const forcePauseAt = (step) => {
8552
8915
  playStartStep = step;
8553
8916
  pausedPlayStep = step;
@@ -8622,6 +8985,7 @@ var playMML = (mml, options = {}) => {
8622
8985
  const drumPatternDict = options.drumPatterns ?? DRUM_PATTERNS;
8623
8986
  const drumPattern = meta.drum ? drumPatternDict[meta.drum] ?? null : null;
8624
8987
  let masterVolume = meta.volume ?? options.volume ?? 100;
8988
+ let drumVolume = meta.drumVolume ?? 80;
8625
8989
  const trackIndices = [...new Set(placements.map((p) => p.trackIndex))].sort(
8626
8990
  (a, b) => a - b
8627
8991
  );
@@ -8658,7 +9022,7 @@ var playMML = (mml, options = {}) => {
8658
9022
  synth?.playNote(e);
8659
9023
  },
8660
9024
  onPlayDrum: (e) => {
8661
- const velocity = e.velocity * (masterVolume / 100);
9025
+ const velocity = e.velocity * (drumVolume / 100) * (masterVolume / 100);
8662
9026
  options.onPlayDrum?.({ ...e, velocity });
8663
9027
  synth?.playDrum({ ...e, velocity });
8664
9028
  },
@@ -9216,7 +9580,6 @@ var importFrom = async (url2, name) => {
9216
9580
  var createDtmStudio = async (options = {}) => {
9217
9581
  const cdn = { ...DEFAULT_CDN, ...options.cdn };
9218
9582
  const features = {
9219
- recorder: true,
9220
9583
  midi: true,
9221
9584
  chord: true,
9222
9585
  presetUI: true,
@@ -9255,68 +9618,6 @@ var createDtmStudio = async (options = {}) => {
9255
9618
  const singingVoices = createSingingVoices(audioCtx, masterGain, {
9256
9619
  voiceWorkerUrl
9257
9620
  });
9258
- const recorder = features.recorder ? setupRecorder(audioCtx, masterGain, drumGain) : null;
9259
- const downloadWav = () => {
9260
- if (!recorder) return;
9261
- const recordedData = recorder.getRecordedData();
9262
- const ch = recordedData.length;
9263
- const len = recordedData[0].length;
9264
- if (len === 0) return;
9265
- const bufSize = recordedData[0][0].length;
9266
- const wave = new Float32Array(ch * len * bufSize);
9267
- let idx = 0;
9268
- for (let i = 0; i < len; i++)
9269
- for (let j = 0; j < bufSize; j++)
9270
- for (let k = 0; k < ch; k++) wave[idx++] = recordedData[k][i][j];
9271
- const sampleRate = audioCtx.sampleRate;
9272
- const channels = 2;
9273
- const bitRate = 16;
9274
- const step = bitRate / 8;
9275
- const blockSize = channels * step;
9276
- const byteLen = wave.length * step;
9277
- const view = new DataView(new ArrayBuffer(44 + byteLen));
9278
- const ws = (off2, s) => {
9279
- for (let i = 0; i < s.length; i++)
9280
- view.setUint8(off2 + i, s.charCodeAt(i));
9281
- };
9282
- ws(0, "RIFF");
9283
- view.setUint32(4, 32 + byteLen, true);
9284
- ws(8, "WAVE");
9285
- ws(12, "fmt ");
9286
- view.setUint32(16, 16, true);
9287
- view.setUint16(20, 1, true);
9288
- view.setUint16(22, channels, true);
9289
- view.setUint32(24, sampleRate, true);
9290
- view.setUint32(28, sampleRate * blockSize, true);
9291
- view.setUint16(32, blockSize, true);
9292
- view.setUint16(34, bitRate, true);
9293
- ws(36, "data");
9294
- view.setUint32(40, byteLen, true);
9295
- const clamp4 = (n, a2, b) => Math.max(a2, Math.min(b, n));
9296
- let off = 44;
9297
- for (let i = 0; i < wave.length; i++, off += step)
9298
- view.setInt16(
9299
- off,
9300
- clamp4(Math.round(wave[i] * 32768), -32768, 32767),
9301
- true
9302
- );
9303
- const blob2 = new Blob([view], { type: "audio/wav" });
9304
- const url2 = URL.createObjectURL(blob2);
9305
- const a = document.createElement("a");
9306
- a.href = url2;
9307
- a.download = "record.wav";
9308
- a.click();
9309
- URL.revokeObjectURL(url2);
9310
- };
9311
- const onToggleRecord = recorder ? () => {
9312
- if (recorder.isRecording()) {
9313
- recorder.stopRecording();
9314
- downloadWav();
9315
- } else {
9316
- recorder.clearRecordedData();
9317
- recorder.startRecording();
9318
- }
9319
- } : void 0;
9320
9621
  const listReady = new Promise((resolve) => {
9321
9622
  SoundFont_list.init();
9322
9623
  SoundFont_list.onload(() => resolve());
@@ -9523,7 +9824,6 @@ var createDtmStudio = async (options = {}) => {
9523
9824
  onPlayDrum: playDrum,
9524
9825
  singingVoices,
9525
9826
  parseMidi,
9526
- onToggleRecord,
9527
9827
  onInstrumentChange: handleInstrumentChange,
9528
9828
  ...dawOverrides
9529
9829
  };
@@ -9814,7 +10114,6 @@ export {
9814
10114
  playMML,
9815
10115
  playSingingMML,
9816
10116
  setDrawOffset,
9817
- setupRecorder,
9818
10117
  shiftNotes,
9819
10118
  stripLyrics,
9820
10119
  stripMmlMeta,