@onjmin/dtm 0.1.36 → 0.1.38

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
@@ -146,6 +146,7 @@ async function buildNameToKeyMapping() {
146
146
  }
147
147
  return nameToKey;
148
148
  }
149
+ var GM_INSTRUMENT_NAMES = FONT_NAME_SURIKOV.trim().split("\n").map((line) => line.slice(line.indexOf(" ") + 1));
149
150
 
150
151
  // node_modules/.pnpm/@onjmin+chord-parser@1.0.2/node_modules/@onjmin/chord-parser/dist/index.mjs
151
152
  var SHARP_NAMES = [
@@ -1223,14 +1224,17 @@ var buildUI = (target, options) => {
1223
1224
  target.innerHTML = `
1224
1225
  <div class="dtm-daw" data-dtm="root">
1225
1226
  <div class="dtm-topbar" data-dtm="transport">
1226
- <button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
1227
- <button class="dtm-play" data-dtm="play" disabled>${icon("play")}<span>\u8A66\u8074</span></button>
1228
- <button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
1229
- <label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
1230
- <span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
1231
- <span class="dtm-grow"></span>
1232
- <span class="dtm-label">BPM</span>
1233
- <input type="number" class="dtm-input dtm-input--num" data-dtm="bpm" value="${defaultBpm}" min="20" max="300">
1227
+ <div class="dtm-topbar-row1">
1228
+ <button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
1229
+ <button class="dtm-play" data-dtm="play" disabled>${icon("play")}<span>\u8A66\u8074</span></button>
1230
+ <button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
1231
+ <label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
1232
+ <span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
1233
+ <span class="dtm-grow"></span>
1234
+ <span class="dtm-label">BPM</span>
1235
+ <input type="number" class="dtm-input dtm-input--num" data-dtm="bpm" value="${defaultBpm}" min="20" max="300">
1236
+ </div>
1237
+ <div class="dtm-tracks" data-dtm="track-tabs"></div>
1234
1238
  </div>
1235
1239
 
1236
1240
  <div class="dtm-tooldock">
@@ -1253,8 +1257,6 @@ var buildUI = (target, options) => {
1253
1257
  </select>
1254
1258
  </div>
1255
1259
 
1256
- <div class="dtm-tracks" data-dtm="track-tabs"></div>
1257
-
1258
1260
  <div class="dtm-roll-wrap">
1259
1261
  <div class="dtm-roll" data-dtm="roll">
1260
1262
  <div data-dtm="wrapper" style="position:absolute;inset:0;"></div>
@@ -3549,12 +3551,14 @@ var drawKeyboard = () => {
3549
3551
  const totalPitch = pitchIndex + pitchRangeStart;
3550
3552
  const pitchMod12 = totalPitch % 12;
3551
3553
  const isBlackKey = blackKeyPitches.has(pitchMod12);
3554
+ const octave = Math.floor(totalPitch / 12) - 1;
3555
+ const isC4Range = octave === 4;
3552
3556
  const screenY = y - g_draw_offset_y;
3553
3557
  const bkW = Math.floor(KEYBOARD_WIDTH * BK_RATIO);
3554
3558
  if (isBlackKey) {
3555
- g_key_ctx.fillStyle = WHITE_KEY;
3559
+ g_key_ctx.fillStyle = isC4Range ? "#d8d4be" : WHITE_KEY;
3556
3560
  g_key_ctx.fillRect(0, screenY, KEYBOARD_WIDTH, keyHeight);
3557
- g_key_ctx.fillStyle = BLACK_KEY;
3561
+ g_key_ctx.fillStyle = isC4Range ? "#1a1408" : BLACK_KEY;
3558
3562
  g_key_ctx.fillRect(0, screenY, bkW, keyHeight);
3559
3563
  g_key_ctx.strokeStyle = BK_EDGE;
3560
3564
  g_key_ctx.lineWidth = 1;
@@ -3563,7 +3567,7 @@ var drawKeyboard = () => {
3563
3567
  g_key_ctx.lineTo(bkW, screenY + keyHeight);
3564
3568
  g_key_ctx.stroke();
3565
3569
  } else {
3566
- g_key_ctx.fillStyle = WHITE_KEY;
3570
+ g_key_ctx.fillStyle = isC4Range ? "#dedad0" : WHITE_KEY;
3567
3571
  g_key_ctx.fillRect(0, screenY, KEYBOARD_WIDTH, keyHeight);
3568
3572
  if (pitchMod12 === 5 || pitchMod12 === 0) {
3569
3573
  g_key_ctx.strokeStyle = WW_SEP;
@@ -3575,13 +3579,13 @@ var drawKeyboard = () => {
3575
3579
  }
3576
3580
  }
3577
3581
  if (pitchMod12 === 0) {
3578
- const octave = Math.floor(totalPitch / 12) - 1;
3582
+ const octave2 = Math.floor(totalPitch / 12) - 1;
3579
3583
  g_key_ctx.fillStyle = "#555040";
3580
3584
  g_key_ctx.font = "10px 'k8x12',monospace";
3581
3585
  g_key_ctx.textAlign = "right";
3582
3586
  g_key_ctx.textBaseline = "bottom";
3583
3587
  g_key_ctx.fillText(
3584
- `${KEY_NAMES[pitchMod12]}${octave}`,
3588
+ `${KEY_NAMES[pitchMod12]}${octave2}`,
3585
3589
  KEYBOARD_WIDTH - 4,
3586
3590
  screenY + keyHeight - 2
3587
3591
  );
@@ -3633,17 +3637,22 @@ var drawGrid = (noteLengthSteps = 1) => {
3633
3637
  drawKeyboard();
3634
3638
  drawHeader();
3635
3639
  g_grid_ctx.clearRect(0, 0, g_grid_canvas.width, g_grid_canvas.height);
3636
- const { keyHeight, keyCount, stepWidth, stepsPerBar } = g_config;
3640
+ const { keyHeight, keyCount, stepWidth, stepsPerBar, pitchRangeStart } = g_config;
3637
3641
  const startY = Math.floor(g_draw_offset_y / keyHeight) * keyHeight;
3638
3642
  const endY = g_draw_offset_y + g_grid_canvas.height;
3639
3643
  for (let y = startY; y < endY; y += keyHeight) {
3640
3644
  const pitchIndex = keyCount - 1 - y / keyHeight;
3645
+ const totalPitch = pitchIndex + pitchRangeStart;
3641
3646
  const pitchMod12 = pitchIndex % 12;
3642
3647
  const isBlackKey = blackKeyPitches.has(pitchMod12);
3643
3648
  const isC = pitchMod12 === 0;
3649
+ const octave = Math.floor(totalPitch / 12) - 1;
3650
+ const isC4Range = octave === 4;
3644
3651
  const screenY = y - g_draw_offset_y;
3645
- if (isBlackKey) {
3646
- g_grid_ctx.fillStyle = "#0d1020";
3652
+ g_grid_ctx.fillStyle = isBlackKey ? "#080b16" : "#111628";
3653
+ g_grid_ctx.fillRect(0, screenY, g_grid_canvas.width, keyHeight);
3654
+ if (isC4Range) {
3655
+ g_grid_ctx.fillStyle = "rgba(41,173,255,0.05)";
3647
3656
  g_grid_ctx.fillRect(0, screenY, g_grid_canvas.width, keyHeight);
3648
3657
  }
3649
3658
  g_grid_ctx.beginPath();
@@ -4285,6 +4294,7 @@ var PITCH_MAP2 = {
4285
4294
  };
4286
4295
  var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
4287
4296
  var META_DIRECTIVE = /#(inst|drum|volume|drumvolume|mode)=([\w-]+)/gi;
4297
+ var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
4288
4298
  var parseMmlMeta = (mml) => {
4289
4299
  const meta = {};
4290
4300
  for (const m of mml.matchAll(META_DIRECTIVE)) {
@@ -4303,9 +4313,17 @@ var parseMmlMeta = (mml) => {
4303
4313
  }
4304
4314
  }
4305
4315
  }
4316
+ for (const m of mml.matchAll(TRACK_INST_DIRECTIVE)) {
4317
+ const idx = Number.parseInt(m[1], 10);
4318
+ const name = m[2].trim();
4319
+ if (!Number.isNaN(idx) && name) {
4320
+ meta.trackInstruments ??= {};
4321
+ meta.trackInstruments[idx] = name;
4322
+ }
4323
+ }
4306
4324
  return meta;
4307
4325
  };
4308
- var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "");
4326
+ var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "").replace(TRACK_INST_DIRECTIVE, "");
4309
4327
  var formatMmlMeta = (meta, space = "") => {
4310
4328
  const parts = [];
4311
4329
  if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
@@ -4314,6 +4332,11 @@ var formatMmlMeta = (meta, space = "") => {
4314
4332
  if (meta.drumVolume !== void 0)
4315
4333
  parts.push(`#drumvolume=${meta.drumVolume}`);
4316
4334
  if (meta.mode) parts.push(`#mode=${meta.mode}`);
4335
+ if (meta.trackInstruments) {
4336
+ for (const [idx, name] of Object.entries(meta.trackInstruments)) {
4337
+ if (name) parts.push(`#t${idx}inst=${name}`);
4338
+ }
4339
+ }
4317
4340
  return parts.join(space);
4318
4341
  };
4319
4342
  var parseMML = (mml, options = {}) => {
@@ -4935,7 +4958,7 @@ var DAW_CSS = `
4935
4958
  top: 0;
4936
4959
  z-index: 20;
4937
4960
  display: flex;
4938
- flex-wrap: nowrap;
4961
+ flex-wrap: wrap;
4939
4962
  align-items: center;
4940
4963
  gap: var(--dtm-gap);
4941
4964
  padding: 6px;
@@ -4945,18 +4968,19 @@ var DAW_CSS = `
4945
4968
  inset 0 0 0 2px var(--c-black),
4946
4969
  0 0 0 2px var(--dtm-success),
4947
4970
  4px 4px 0 var(--c-black);
4971
+ }
4972
+ .dtm-topbar-row1 {
4973
+ display: flex;
4974
+ align-items: center;
4975
+ gap: var(--dtm-gap);
4976
+ flex-basis: 100%;
4977
+ min-width: 0;
4948
4978
  overflow-x: auto;
4949
4979
  scrollbar-width: none;
4950
4980
  }
4951
- .dtm-topbar::-webkit-scrollbar {
4952
- display: none;
4953
- }
4954
- .dtm-topbar > * {
4955
- flex-shrink: 0;
4956
- }
4957
- .dtm-topbar > .dtm-grow {
4958
- flex-shrink: 1;
4959
- }
4981
+ .dtm-topbar-row1::-webkit-scrollbar { display: none; }
4982
+ .dtm-topbar-row1 > * { flex-shrink: 0; }
4983
+ .dtm-topbar-row1 > .dtm-grow { flex-shrink: 1; }
4960
4984
 
4961
4985
  /* PLAY\u30DC\u30BF\u30F3 \u2014 \u30B2\u30FC\u30E0\u306E\u300C\u6C7A\u5B9A\u30DC\u30BF\u30F3\u300D\u7684\u5B58\u5728\u611F */
4962
4986
  .dtm-play {
@@ -5146,46 +5170,39 @@ var DAW_CSS = `
5146
5170
  .dtm-modebtn--active { background: var(--dtm-primary); color: var(--dtm-pfg); }
5147
5171
  .dtm-modebtn:not(.dtm-modebtn--active):active { background: var(--dtm-border2); }
5148
5172
 
5149
- /* \u2500\u2500\u2500 \u30C8\u30E9\u30C3\u30AF\u30D4\u30EB\uFF08\u30AD\u30E3\u30E9\u30AF\u30BF\u30FC\u9078\u629E\u30DC\u30BF\u30F3\uFF09 \u2500\u2500\u2500 */
5173
+ /* \u2500\u2500\u2500 \u30C8\u30E9\u30C3\u30AF\u30D4\u30EB\uFF08\u756A\u53F7\u30DC\u30BF\u30F3\u3001\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u30D0\u30FC2\u884C\u76EE\uFF09 \u2500\u2500\u2500 */
5150
5174
  .dtm-tracks {
5175
+ flex-basis: 100%;
5151
5176
  display: flex;
5152
- flex-wrap: wrap;
5153
- gap: var(--dtm-gap);
5177
+ flex-wrap: nowrap;
5178
+ gap: 3px;
5154
5179
  }
5155
5180
  .dtm-pill {
5156
5181
  --dtm-pill-color: var(--dtm-primary);
5157
5182
  display: inline-flex;
5158
5183
  align-items: center;
5159
- gap: 8px;
5160
- flex: 1 1 auto;
5161
5184
  justify-content: center;
5162
- min-height: 42px;
5163
- padding: 0 12px;
5164
- border: 2px solid var(--dtm-border2);
5165
- background: var(--dtm-deep);
5166
- color: var(--dtm-muted);
5185
+ flex: 1 1 0;
5186
+ min-width: 0;
5187
+ height: 26px;
5188
+ padding: 0;
5189
+ border: 2px solid var(--c-black);
5190
+ background: color-mix(in srgb, var(--dtm-pill-color) 40%, black);
5191
+ color: var(--c-white);
5167
5192
  font-family: var(--dtm-font);
5168
- font-size: 13px;
5169
- text-transform: uppercase;
5170
- letter-spacing: .1em;
5193
+ font-size: 11px;
5194
+ font-weight: bold;
5171
5195
  cursor: pointer;
5172
- box-shadow: 3px 3px 0 var(--c-black);
5173
- }
5174
- .dtm-pill .dtm-dot {
5175
- width: 8px; height: 8px;
5176
- background: var(--dtm-pill-color);
5177
- flex: 0 0 auto;
5178
- box-shadow: 1px 1px 0 var(--c-black);
5196
+ box-shadow: 2px 2px 0 var(--c-black);
5197
+ opacity: 0.7;
5179
5198
  }
5180
- /* \u30A2\u30AF\u30C6\u30A3\u30D6\u9078\u629E = \u91D1\u8272\u30CF\u30A4\u30E9\u30A4\u30C8 + \u30AB\u30FC\u30BD\u30EB */
5199
+ /* \u30A2\u30AF\u30C6\u30A3\u30D6\u9078\u629E = \u4E0D\u900F\u660E + \u91D1\u67A0 */
5181
5200
  .dtm-pill--active {
5201
+ opacity: 1;
5182
5202
  border-color: var(--dtm-gold);
5183
- color: var(--dtm-gold);
5184
- background: var(--dtm-surface);
5185
- box-shadow: 0 0 0 2px var(--dtm-gold), 3px 3px 0 var(--c-black);
5203
+ box-shadow: 0 0 0 1px var(--dtm-gold), 2px 2px 0 var(--c-black);
5186
5204
  }
5187
- .dtm-pill--active::before { content: "\u25BA "; font-size: 10px; }
5188
- .dtm-pill:not(.dtm-pill--active):active { transform: translate(3px,3px); box-shadow: none; }
5205
+ .dtm-pill:not(.dtm-pill--active):active { transform: translate(2px,2px); box-shadow: none; }
5189
5206
 
5190
5207
  /* \u2500\u2500\u2500 \u30D4\u30A2\u30CE\u30ED\u30FC\u30EB\uFF08\u30C8\u30E9\u30C3\u30AB\u30FC\u98A8\uFF09 \u2500\u2500\u2500 */
5191
5208
  .dtm-roll-wrap { display: flex; gap: var(--dtm-gap); }
@@ -5329,7 +5346,7 @@ var DAW_CSS = `
5329
5346
  }
5330
5347
  .dtm-overlay[hidden] { display: none; }
5331
5348
  .dtm-overlay::before {
5332
- content: 'NOW LOADING';
5349
+ content: '\u30ED\u30FC\u30C9\u4E2D';
5333
5350
  font-family: var(--dtm-font);
5334
5351
  font-size: 13px;
5335
5352
  color: var(--dtm-primary);
@@ -5522,6 +5539,15 @@ var DAW_CSS = `
5522
5539
  justify-content: center;
5523
5540
  }
5524
5541
 
5542
+ .dtm-confirm-footer {
5543
+ padding: 8px 12px;
5544
+ border-top: 2px solid var(--c-black);
5545
+ background: var(--dtm-deep);
5546
+ display: flex;
5547
+ gap: 8px;
5548
+ justify-content: flex-end;
5549
+ }
5550
+
5525
5551
  .dtm-modal {
5526
5552
  max-width: 500px;
5527
5553
  width: 100%;
@@ -6544,9 +6570,11 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6544
6570
  const mmlInfoItem = makeMenuItem("MML\u66F8\u5F0F\u3068\u306F");
6545
6571
  const embedItem = makeMenuItem("\u57CB\u3081\u8FBC\u3080");
6546
6572
  const copyMmlItem = makeMenuItem("MML\u30B3\u30D4\u30FC");
6547
- menuDropdown.appendChild(showMmlItem);
6548
- menuDropdown.appendChild(mmlInfoItem);
6549
- menuDropdown.appendChild(embedItem);
6573
+ if (!options._skipInfoModals) {
6574
+ menuDropdown.appendChild(showMmlItem);
6575
+ menuDropdown.appendChild(mmlInfoItem);
6576
+ menuDropdown.appendChild(embedItem);
6577
+ }
6550
6578
  menuDropdown.appendChild(copyMmlItem);
6551
6579
  menuContainer.appendChild(menuDropdown);
6552
6580
  mmlHeader.appendChild(menuContainer);
@@ -6700,6 +6728,8 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6700
6728
  volume: trackVolume,
6701
6729
  // 解説モーダル内の試聴サンプルは規約同意を要求しない。
6702
6730
  skipConsent: true,
6731
+ // 再帰的なモーダル生成を防ぐ。
6732
+ _skipInfoModals: true,
6703
6733
  onStop: () => {
6704
6734
  if (activeSampleBtn === el) resetSampleBtn(el);
6705
6735
  }
@@ -6709,65 +6739,67 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6709
6739
  });
6710
6740
  }
6711
6741
  };
6712
- showMmlItem.addEventListener("click", (e) => {
6713
- e.stopPropagation();
6714
- toggleMenu(false);
6715
- const modalBody = openInfoModal("MML\u3092\u8868\u793A");
6716
- const desc = doc.createElement("p");
6717
- desc.textContent = "\u3053\u306EMML\u3092\u30B3\u30D4\u30FC\u3057\u3066\u3001\u4ED6\u306E\u30D7\u30EC\u30A4\u30E4\u30FC\u3084\u5171\u6709URL\u306B\u8CBC\u308A\u4ED8\u3051\u3066\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002";
6718
- desc.style.marginBottom = "8px";
6719
- modalBody.appendChild(desc);
6720
- const sourceMml = options.getMml?.() ?? mml;
6721
- const displayMml = sourceMml.split(";").map((s) => s.trim()).filter((s) => s.length > 0).join(";\n");
6722
- const pre = doc.createElement("pre");
6723
- pre.textContent = displayMml;
6724
- pre.style.whiteSpace = "pre-wrap";
6725
- pre.style.wordBreak = "break-all";
6726
- pre.style.cursor = "text";
6727
- pre.addEventListener("click", () => {
6728
- const range = doc.createRange();
6729
- range.selectNodeContents(pre);
6730
- const sel = doc.defaultView?.getSelection();
6731
- sel?.removeAllRanges();
6732
- sel?.addRange(range);
6733
- });
6734
- modalBody.appendChild(pre);
6735
- appendCopyButton(modalBody, sourceMml);
6736
- });
6737
- mmlInfoItem.addEventListener("click", (e) => {
6738
- e.stopPropagation();
6739
- toggleMenu(false);
6740
- const modalBody = openInfoModal("MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC");
6741
- modalBody.innerHTML = MML_INFO_HTML;
6742
- wireSampleButtons(modalBody);
6743
- });
6744
- embedItem.addEventListener("click", async (e) => {
6745
- e.stopPropagation();
6746
- toggleMenu(false);
6747
- const modalBody = openInfoModal("\u57CB\u3081\u8FBC\u307F");
6748
- const loading = doc.createElement("p");
6749
- loading.textContent = "\u751F\u6210\u4E2D...";
6750
- modalBody.appendChild(loading);
6751
- try {
6752
- const embedBase = options.embedUrl ?? "https://onjmin.github.io/dtm/demo/embed.html";
6753
- const payload = await encodeMml(mml);
6754
- const url2 = `${embedBase}#${payload}`;
6755
- const snippet = `<iframe src="${url2}" width="100%" height="260" frameborder="0" loading="lazy" title="@onjmin/dtm player"></iframe>`;
6756
- if (!modalBody.isConnected) return;
6757
- loading.remove();
6742
+ if (!options._skipInfoModals) {
6743
+ showMmlItem.addEventListener("click", (e) => {
6744
+ e.stopPropagation();
6745
+ toggleMenu(false);
6746
+ const modalBody = openInfoModal("MML\u3092\u8868\u793A");
6758
6747
  const desc = doc.createElement("p");
6759
- 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";
6748
+ desc.textContent = "\u3053\u306EMML\u3092\u30B3\u30D4\u30FC\u3057\u3066\u3001\u4ED6\u306E\u30D7\u30EC\u30A4\u30E4\u30FC\u3084\u5171\u6709URL\u306B\u8CBC\u308A\u4ED8\u3051\u3066\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002";
6749
+ desc.style.marginBottom = "8px";
6750
+ modalBody.appendChild(desc);
6751
+ const sourceMml = options.getMml?.() ?? mml;
6752
+ const displayMml = sourceMml.split(";").map((s) => s.trim()).filter((s) => s.length > 0).join(";\n");
6760
6753
  const pre = doc.createElement("pre");
6761
- pre.textContent = snippet;
6754
+ pre.textContent = displayMml;
6762
6755
  pre.style.whiteSpace = "pre-wrap";
6763
6756
  pre.style.wordBreak = "break-all";
6764
- modalBody.append(desc, pre);
6765
- appendCopyButton(modalBody, snippet);
6766
- } catch (err2) {
6767
- console.error("[dtm] failed to generate embed snippet", err2);
6768
- if (modalBody.isConnected) loading.textContent = "\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
6769
- }
6770
- });
6757
+ pre.style.cursor = "text";
6758
+ pre.addEventListener("click", () => {
6759
+ const range = doc.createRange();
6760
+ range.selectNodeContents(pre);
6761
+ const sel = doc.defaultView?.getSelection();
6762
+ sel?.removeAllRanges();
6763
+ sel?.addRange(range);
6764
+ });
6765
+ modalBody.appendChild(pre);
6766
+ appendCopyButton(modalBody, sourceMml);
6767
+ });
6768
+ mmlInfoItem.addEventListener("click", (e) => {
6769
+ e.stopPropagation();
6770
+ toggleMenu(false);
6771
+ const modalBody = openInfoModal("MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC");
6772
+ modalBody.innerHTML = MML_INFO_HTML;
6773
+ wireSampleButtons(modalBody);
6774
+ });
6775
+ embedItem.addEventListener("click", async (e) => {
6776
+ e.stopPropagation();
6777
+ toggleMenu(false);
6778
+ const modalBody = openInfoModal("\u57CB\u3081\u8FBC\u307F");
6779
+ const loading = doc.createElement("p");
6780
+ loading.textContent = "\u751F\u6210\u4E2D...";
6781
+ modalBody.appendChild(loading);
6782
+ try {
6783
+ const embedBase = options.embedUrl ?? "https://onjmin.github.io/dtm/demo/embed.html";
6784
+ const payload = await encodeMml(mml);
6785
+ const url2 = `${embedBase}#${payload}`;
6786
+ const snippet = `<iframe src="${url2}" width="100%" height="260" frameborder="0" loading="lazy" title="@onjmin/dtm player"></iframe>`;
6787
+ if (!modalBody.isConnected) return;
6788
+ loading.remove();
6789
+ const desc = doc.createElement("p");
6790
+ 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";
6791
+ const pre = doc.createElement("pre");
6792
+ pre.textContent = snippet;
6793
+ pre.style.whiteSpace = "pre-wrap";
6794
+ pre.style.wordBreak = "break-all";
6795
+ modalBody.append(desc, pre);
6796
+ appendCopyButton(modalBody, snippet);
6797
+ } catch (err2) {
6798
+ console.error("[dtm] failed to generate embed snippet", err2);
6799
+ if (modalBody.isConnected) loading.textContent = "\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
6800
+ }
6801
+ });
6802
+ }
6771
6803
  copyMmlItem.addEventListener("click", async (e) => {
6772
6804
  e.stopPropagation();
6773
6805
  const success = await copyToClipboard(doc, options.getMml?.() ?? mml);
@@ -7364,8 +7396,8 @@ var MIDI_INFO_HTML = `
7364
7396
 
7365
7397
  <h4>3. \u30E2\u30FC\u30C9\u306B\u3088\u308B\u53D6\u308A\u8FBC\u307F\u65B9\u306E\u9055\u3044</h4>
7366
7398
  <ul>
7367
- <li><strong>SIMPLE</strong>: \u5404\u30C8\u30E9\u30C3\u30AF\u306E\u7279\u5FB4\u304B\u3089\u3001\u30E1\u30ED\u30C7\u30A3\u30FC\u30FB\u30B5\u30D6\u30E1\u30ED\u30FB\u30D9\u30FC\u30B9\u30FB\u4F34\u594F\u306E4\u3064\u306E\u5F79\u5272\u306B\u81EA\u52D5\u3067\u632F\u308A\u5206\u3051\u3089\u308C\u307E\u3059\u3002</li>
7368
- <li><strong>ADVANCED</strong>: MIDI\u306E\u30C8\u30E9\u30C3\u30AF\u69CB\u6210\u304C\u305D\u306E\u307E\u307E\u53CD\u6620\u3055\u308C\u307E\u3059\uFF081\u5BFE1\uFF09\u3002</li>
7399
+ <li><strong>\u521D\u5FC3\u8005\u30E2\u30FC\u30C9</strong>: \u5404\u30C8\u30E9\u30C3\u30AF\u306E\u7279\u5FB4\u304B\u3089\u3001\u30E1\u30ED\u30C7\u30A3\u30FC\u30FB\u30B5\u30D6\u30E1\u30ED\u30FB\u30D9\u30FC\u30B9\u30FB\u4F34\u594F\u306E4\u3064\u306E\u5F79\u5272\u306B\u81EA\u52D5\u3067\u632F\u308A\u5206\u3051\u3089\u308C\u307E\u3059\u3002</li>
7400
+ <li><strong>\u4E0A\u7D1A\u8005\u30E2\u30FC\u30C9</strong>: MIDI\u306E\u30C8\u30E9\u30C3\u30AF\u69CB\u6210\u304C\u305D\u306E\u307E\u307E\u53CD\u6620\u3055\u308C\u307E\u3059\uFF081\u5BFE1\uFF09\u3002</li>
7369
7401
  </ul>
7370
7402
 
7371
7403
  <h4>4. MIDI\u30D5\u30A1\u30A4\u30EB\u3092\u624B\u306B\u5165\u308C\u308B</h4>
@@ -7417,105 +7449,105 @@ var TRACKS_SIMPLE = [
7417
7449
  var TRACKS_ADVANCED = [
7418
7450
  {
7419
7451
  id: "t0",
7420
- name: "TRACK 01",
7452
+ name: "\u30C8\u30E9\u30C3\u30AF1",
7421
7453
  color: [41, 173, 255],
7422
7454
  instrument: 0,
7423
7455
  volume: 100
7424
7456
  },
7425
7457
  {
7426
7458
  id: "t1",
7427
- name: "TRACK 02",
7459
+ name: "\u30C8\u30E9\u30C3\u30AF2",
7428
7460
  color: [0, 228, 54],
7429
7461
  instrument: 1,
7430
7462
  volume: 100
7431
7463
  },
7432
7464
  {
7433
7465
  id: "t2",
7434
- name: "TRACK 03",
7466
+ name: "\u30C8\u30E9\u30C3\u30AF3",
7435
7467
  color: [255, 119, 168],
7436
7468
  instrument: 2,
7437
7469
  volume: 100
7438
7470
  },
7439
7471
  {
7440
7472
  id: "t3",
7441
- name: "TRACK 04",
7473
+ name: "\u30C8\u30E9\u30C3\u30AF4",
7442
7474
  color: [255, 163, 0],
7443
7475
  instrument: 3,
7444
7476
  volume: 100
7445
7477
  },
7446
7478
  {
7447
7479
  id: "t4",
7448
- name: "TRACK 05",
7480
+ name: "\u30C8\u30E9\u30C3\u30AF5",
7449
7481
  color: [255, 236, 39],
7450
7482
  instrument: 4,
7451
7483
  volume: 100
7452
7484
  },
7453
7485
  {
7454
7486
  id: "t5",
7455
- name: "TRACK 06",
7487
+ name: "\u30C8\u30E9\u30C3\u30AF6",
7456
7488
  color: [131, 118, 156],
7457
7489
  instrument: 5,
7458
7490
  volume: 100
7459
7491
  },
7460
7492
  {
7461
7493
  id: "t6",
7462
- name: "TRACK 07",
7494
+ name: "\u30C8\u30E9\u30C3\u30AF7",
7463
7495
  color: [255, 0, 77],
7464
7496
  instrument: 6,
7465
7497
  volume: 100
7466
7498
  },
7467
7499
  {
7468
7500
  id: "t7",
7469
- name: "TRACK 08",
7501
+ name: "\u30C8\u30E9\u30C3\u30AF8",
7470
7502
  color: [255, 204, 170],
7471
7503
  instrument: 7,
7472
7504
  volume: 100
7473
7505
  },
7474
7506
  {
7475
7507
  id: "t8",
7476
- name: "TRACK 09",
7508
+ name: "\u30C8\u30E9\u30C3\u30AF9",
7477
7509
  color: [194, 195, 199],
7478
7510
  instrument: 8,
7479
7511
  volume: 100
7480
7512
  },
7481
7513
  {
7482
7514
  id: "t9",
7483
- name: "TRACK 10",
7515
+ name: "\u30C8\u30E9\u30C3\u30AF10",
7484
7516
  color: [0, 135, 81],
7485
7517
  instrument: 9,
7486
7518
  volume: 100
7487
7519
  },
7488
7520
  {
7489
7521
  id: "t10",
7490
- name: "TRACK 11",
7522
+ name: "\u30C8\u30E9\u30C3\u30AF11",
7491
7523
  color: [171, 82, 54],
7492
7524
  instrument: 10,
7493
7525
  volume: 100
7494
7526
  },
7495
7527
  {
7496
7528
  id: "t11",
7497
- name: "TRACK 12",
7529
+ name: "\u30C8\u30E9\u30C3\u30AF12",
7498
7530
  color: [126, 37, 83],
7499
7531
  instrument: 11,
7500
7532
  volume: 100
7501
7533
  },
7502
7534
  {
7503
7535
  id: "t12",
7504
- name: "TRACK 13",
7536
+ name: "\u30C8\u30E9\u30C3\u30AF13",
7505
7537
  color: [255, 241, 232],
7506
7538
  instrument: 12,
7507
7539
  volume: 100
7508
7540
  },
7509
7541
  {
7510
7542
  id: "t13",
7511
- name: "TRACK 14",
7543
+ name: "\u30C8\u30E9\u30C3\u30AF14",
7512
7544
  color: [120, 200, 255],
7513
7545
  instrument: 13,
7514
7546
  volume: 100
7515
7547
  },
7516
7548
  {
7517
7549
  id: "t14",
7518
- name: "TRACK 15",
7550
+ name: "\u30C8\u30E9\u30C3\u30AF15",
7519
7551
  color: [100, 255, 160],
7520
7552
  instrument: 14,
7521
7553
  volume: 100
@@ -7529,6 +7561,13 @@ var LYRIC_MODEL_LABELS2 = {
7529
7561
  };
7530
7562
  var lyricModelLabel = (model) => LYRIC_MODEL_LABELS2[model] ?? model;
7531
7563
  var clamp3 = (v, min, max) => Math.min(Math.max(v, min), max);
7564
+ var normalizeInstrumentName = (name) => {
7565
+ if (!name) return "";
7566
+ const stripped = name.replace(/\s+/g, "").toLowerCase();
7567
+ return GM_INSTRUMENT_NAMES.find(
7568
+ (n) => n.replace(/\s+/g, "").toLowerCase() === stripped
7569
+ ) ?? name;
7570
+ };
7532
7571
  var mountDAW = (target, options = {}) => {
7533
7572
  injectStyles();
7534
7573
  const getAudioTime = options.getAudioTime ?? (() => performance.now() / 1e3);
@@ -7568,7 +7607,8 @@ var mountDAW = (target, options = {}) => {
7568
7607
  let snapGridSteps = 12;
7569
7608
  const gridLineSteps = 48;
7570
7609
  let currentOffsetX = 0;
7571
- let currentOffsetY = (104 - 1 - 60) * renderConfig.keyHeight - 215;
7610
+ const _initPitch = options.initialScrollPitch;
7611
+ let currentOffsetY = _initPitch !== void 0 ? (renderConfig.keyCount - 1 - _initPitch) * renderConfig.keyHeight - 215 : (104 - 1 - 60) * renderConfig.keyHeight - 215;
7572
7612
  let playStartStep = 0;
7573
7613
  let isSolo = false;
7574
7614
  let lyricTrackIndices = /* @__PURE__ */ new Set();
@@ -7581,6 +7621,24 @@ var mountDAW = (target, options = {}) => {
7581
7621
  let copiedNotes = [];
7582
7622
  let trackStates = [];
7583
7623
  let suppressPatch = false;
7624
+ let lyricsDebounceTimer = null;
7625
+ const fireLyricsChange = (t) => {
7626
+ if (!options.onLyricsChange) return;
7627
+ const trackId = t.config.id;
7628
+ const data = {
7629
+ lyrics: t.lyrics,
7630
+ model: t.lyricModel,
7631
+ vocalVolume: t.vocalVolume,
7632
+ vocalGate: t.vocalGate,
7633
+ vocalPan: t.vocalPan,
7634
+ vocalOctave: t.vocalOctave
7635
+ };
7636
+ if (lyricsDebounceTimer) clearTimeout(lyricsDebounceTimer);
7637
+ lyricsDebounceTimer = setTimeout(() => {
7638
+ options.onLyricsChange(trackId, data);
7639
+ lyricsDebounceTimer = null;
7640
+ }, 300);
7641
+ };
7584
7642
  const hiddenTracks = /* @__PURE__ */ new Set();
7585
7643
  const audioMutedTracks = /* @__PURE__ */ new Set();
7586
7644
  const createTrackStates = () => {
@@ -7629,7 +7687,8 @@ var mountDAW = (target, options = {}) => {
7629
7687
  vocalVolume: DEFAULT_VOCAL_VOLUME,
7630
7688
  vocalGate: 100,
7631
7689
  vocalPan: 64,
7632
- vocalOctave: 0
7690
+ vocalOctave: 0,
7691
+ trackInstrument: ""
7633
7692
  };
7634
7693
  });
7635
7694
  };
@@ -8335,19 +8394,20 @@ var mountDAW = (target, options = {}) => {
8335
8394
  };
8336
8395
  const updateTrackPanel = () => {
8337
8396
  refs.trackTabs.innerHTML = "";
8338
- for (const t of trackStates) {
8397
+ for (const [i, t] of trackStates.entries()) {
8339
8398
  const [r, g, b] = t.config.color;
8340
8399
  const btn = document.createElement("button");
8341
8400
  btn.className = `dtm-pill ${t.config.id === activeTrackId ? "dtm-pill--active" : ""}`;
8342
8401
  btn.style.setProperty("--dtm-pill-color", `rgb(${r},${g},${b})`);
8343
- btn.innerHTML = `<span class="dtm-dot"></span><span>${t.config.name}</span>`;
8402
+ btn.title = t.config.name;
8403
+ btn.textContent = String(i + 1);
8344
8404
  btn.addEventListener("click", () => switchTrack(t.config.id));
8345
8405
  refs.trackTabs.appendChild(btn);
8346
8406
  }
8347
8407
  const active = getActive();
8348
8408
  refs.trackBody.innerHTML = `
8349
8409
  <div class="dtm-row">
8350
- <span class="dtm-label">velocity</span>
8410
+ <span class="dtm-label">\u30D9\u30ED\u30B7\u30C6\u30A3</span>
8351
8411
  <input type="range" class="dtm-range dtm-grow" data-dtm="track-vol" min="0" max="127" value="${active.volume}">
8352
8412
  <span class="dtm-label" data-dtm="track-vol-label">${active.volume}</span>
8353
8413
  </div>`;
@@ -8362,6 +8422,59 @@ var mountDAW = (target, options = {}) => {
8362
8422
  active.core.setVolume(active.volume);
8363
8423
  volLabel.textContent = String(active.volume);
8364
8424
  });
8425
+ const instRow = document.createElement("div");
8426
+ instRow.className = "dtm-row";
8427
+ instRow.innerHTML = `<span class="dtm-label">\u697D\u5668</span>`;
8428
+ const instSel = document.createElement("select");
8429
+ instSel.className = "dtm-select dtm-grow";
8430
+ const defaultOpt = document.createElement("option");
8431
+ defaultOpt.value = "";
8432
+ defaultOpt.textContent = "\u30C7\u30D5\u30A9\u30EB\u30C8\uFF08\u30D7\u30EA\u30BB\u30C3\u30C8\uFF09";
8433
+ instSel.appendChild(defaultOpt);
8434
+ const GM_GROUPS = [
8435
+ "\u30D4\u30A2\u30CE",
8436
+ "\u30AF\u30ED\u30DE\u30C6\u30A3\u30C3\u30AF\u30D1\u30FC\u30AB\u30C3\u30B7\u30E7\u30F3",
8437
+ "\u30AA\u30EB\u30AC\u30F3",
8438
+ "\u30AE\u30BF\u30FC",
8439
+ "\u30D9\u30FC\u30B9",
8440
+ "\u30B9\u30C8\u30EA\u30F3\u30B0\u30B9",
8441
+ "\u30A2\u30F3\u30B5\u30F3\u30D6\u30EB",
8442
+ "\u30D6\u30E9\u30B9",
8443
+ "\u30EA\u30FC\u30C9\uFF08\u6728\u7BA1\uFF09",
8444
+ "\u30D1\u30A4\u30D7",
8445
+ "\u30B7\u30F3\u30BB\u30EA\u30FC\u30C9",
8446
+ "\u30B7\u30F3\u30BB\u30D1\u30C3\u30C9",
8447
+ "\u30B7\u30F3\u30BB\u30A8\u30D5\u30A7\u30AF\u30C8",
8448
+ "\u30A8\u30B9\u30CB\u30C3\u30AF",
8449
+ "\u30D1\u30FC\u30AB\u30C3\u30B7\u30D6",
8450
+ "\u30B5\u30A6\u30F3\u30C9\u30A8\u30D5\u30A7\u30AF\u30C8"
8451
+ ];
8452
+ GM_GROUPS.forEach((groupName, gi) => {
8453
+ const grp = document.createElement("optgroup");
8454
+ grp.label = groupName;
8455
+ for (let j = 0; j < 8; j++) {
8456
+ const name = GM_INSTRUMENT_NAMES[gi * 8 + j];
8457
+ if (!name) break;
8458
+ const o = document.createElement("option");
8459
+ o.value = name;
8460
+ o.textContent = name;
8461
+ grp.appendChild(o);
8462
+ }
8463
+ instSel.appendChild(grp);
8464
+ });
8465
+ instSel.value = normalizeInstrumentName(active.trackInstrument);
8466
+ const syncInstDisabled = () => {
8467
+ instSel.disabled = !!active.lyricModel;
8468
+ instSel.title = active.lyricModel ? "\u6B4C\u8A5E\u30E2\u30FC\u30C9\u306E\u3068\u304D\u306F\u697D\u5668\u3092\u500B\u5225\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093" : "";
8469
+ };
8470
+ syncInstDisabled();
8471
+ instSel.addEventListener("change", () => {
8472
+ active.trackInstrument = instSel.value;
8473
+ const trackIndex = trackStates.indexOf(active);
8474
+ options.onTrackInstrumentChange?.(trackIndex, active.trackInstrument);
8475
+ });
8476
+ instRow.appendChild(instSel);
8477
+ refs.trackBody.appendChild(instRow);
8365
8478
  if (isAdvanced || active.config.id !== "chord") {
8366
8479
  const lyricDiv = document.createElement("div");
8367
8480
  lyricDiv.className = "dtm-row";
@@ -8369,7 +8482,7 @@ var mountDAW = (target, options = {}) => {
8369
8482
  lyricDiv.style.alignItems = "stretch";
8370
8483
  lyricDiv.innerHTML = `
8371
8484
  <div class="dtm-row">
8372
- <span class="dtm-label">\u266A \u6B4C\u8A5E</span>
8485
+ <span class="dtm-label">\u266A UTAU</span>
8373
8486
  <select class="dtm-select" data-dtm="lyric-model" aria-label="\u6B4C\u5531\u30E2\u30C7\u30EB"></select>
8374
8487
  <img class="dtm-lyric-icon dtm-hidden" data-dtm="lyric-icon" width="20" height="20" alt="" draggable="false">
8375
8488
  <select class="dtm-select" data-dtm="lyric-octave" aria-label="\u30AA\u30AF\u30BF\u30FC\u30D6\uFF08\u97F3\u6E90\u306E\u5F97\u610F\u97F3\u57DF\u306B\u5408\u308F\u305B\u308B\uFF09" title="\u30AA\u30AF\u30BF\u30FC\u30D6">
@@ -8443,7 +8556,7 @@ var mountDAW = (target, options = {}) => {
8443
8556
  o.textContent = label;
8444
8557
  lyricModelSel.appendChild(o);
8445
8558
  };
8446
- addOpt("", "\u306A\u3057");
8559
+ addOpt("", "\u30DC\u30FC\u30AB\u30EB\u306A\u3057");
8447
8560
  for (const m of LYRIC_MODELS) addOpt(m, lyricModelLabel(m));
8448
8561
  if (active.lyricModel && !LYRIC_MODELS.includes(active.lyricModel)) {
8449
8562
  addOpt(active.lyricModel, lyricModelLabel(active.lyricModel));
@@ -8492,21 +8605,27 @@ var mountDAW = (target, options = {}) => {
8492
8605
  lyricModelSel.addEventListener("change", () => {
8493
8606
  active.lyricModel = lyricModelSel.value;
8494
8607
  syncLyricVisibility();
8608
+ syncInstDisabled();
8609
+ fireLyricsChange(active);
8495
8610
  });
8496
8611
  lyricOctaveSel.addEventListener("change", () => {
8497
8612
  active.vocalOctave = Number.parseInt(lyricOctaveSel.value, 10);
8613
+ fireLyricsChange(active);
8498
8614
  });
8499
8615
  lyricInput.addEventListener("input", () => {
8500
8616
  active.lyrics = lyricInput.value;
8501
8617
  updateLyricCount();
8618
+ fireLyricsChange(active);
8502
8619
  });
8503
8620
  lyricVol.addEventListener("input", () => {
8504
8621
  active.vocalVolume = Number.parseInt(lyricVol.value, 10);
8505
8622
  lyricVolLabel.textContent = lyricVol.value;
8623
+ fireLyricsChange(active);
8506
8624
  });
8507
8625
  lyricPan.addEventListener("input", () => {
8508
8626
  active.vocalPan = Number.parseInt(lyricPan.value, 10);
8509
8627
  lyricPanLabel.textContent = fmtPan(active.vocalPan);
8628
+ fireLyricsChange(active);
8510
8629
  });
8511
8630
  lyricPanLabel.style.cursor = "pointer";
8512
8631
  lyricPanLabel.title = "\u30BF\u30C3\u30D7\u3067\u4E2D\u592E(C)\u3078";
@@ -8514,6 +8633,7 @@ var mountDAW = (target, options = {}) => {
8514
8633
  active.vocalPan = 64;
8515
8634
  lyricPan.value = "64";
8516
8635
  lyricPanLabel.textContent = fmtPan(64);
8636
+ fireLyricsChange(active);
8517
8637
  });
8518
8638
  }
8519
8639
  if (active.config.id === "chord" && showChord) {
@@ -8588,13 +8708,19 @@ var mountDAW = (target, options = {}) => {
8588
8708
  const barLimitBars = Number(refs.barLimitSelect.value);
8589
8709
  const limitSteps = barLimitBars > 0 ? barLimitBars * renderConfig.stepsPerBar : Infinity;
8590
8710
  const clipNotes = (notes) => limitSteps === Infinity ? notes : notes.filter((n) => n.startStep < limitSteps);
8711
+ const trackInstrumentsForMeta = {};
8712
+ trackStates.forEach((t, i) => {
8713
+ if (t.trackInstrument) trackInstrumentsForMeta[i] = t.trackInstrument;
8714
+ });
8715
+ const trackInstMeta = Object.keys(trackInstrumentsForMeta).length > 0 ? trackInstrumentsForMeta : void 0;
8591
8716
  const metaLineFull = formatMmlMeta(
8592
8717
  {
8593
8718
  instrument: currentInstrument || void 0,
8594
8719
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
8595
8720
  volume: masterVolume,
8596
8721
  drumVolume,
8597
- mode
8722
+ mode,
8723
+ trackInstruments: trackInstMeta
8598
8724
  },
8599
8725
  " "
8600
8726
  );
@@ -8604,7 +8730,8 @@ var mountDAW = (target, options = {}) => {
8604
8730
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
8605
8731
  volume: masterVolume,
8606
8732
  drumVolume,
8607
- mode
8733
+ mode,
8734
+ trackInstruments: trackInstMeta
8608
8735
  },
8609
8736
  ""
8610
8737
  );
@@ -8762,6 +8889,13 @@ var mountDAW = (target, options = {}) => {
8762
8889
  refs.drumVolume.value = String(meta.drumVolume);
8763
8890
  refs.drumVolumeLabel.textContent = `${meta.drumVolume}%`;
8764
8891
  }
8892
+ trackStates.forEach((t, i) => {
8893
+ const name = normalizeInstrumentName(meta.trackInstruments?.[i] ?? "");
8894
+ if (t.trackInstrument !== name) {
8895
+ t.trackInstrument = name;
8896
+ options.onTrackInstrumentChange?.(i, name);
8897
+ }
8898
+ });
8765
8899
  for (const t of trackStates) {
8766
8900
  t.lyrics = "";
8767
8901
  t.lyricModel = "";
@@ -8921,6 +9055,31 @@ var mountDAW = (target, options = {}) => {
8921
9055
  setLoading(false);
8922
9056
  }, 30);
8923
9057
  };
9058
+ const showConfirmModal = (message) => new Promise((resolve) => {
9059
+ const overlay = document.createElement("div");
9060
+ overlay.className = "dtm-modal-overlay";
9061
+ overlay.innerHTML = `
9062
+ <div class="dtm-modal">
9063
+ <div class="dtm-modal-header">
9064
+ <span class="dtm-modal-title">\u30E2\u30FC\u30C9\u306E\u78BA\u8A8D</span>
9065
+ </div>
9066
+ <div class="dtm-modal-body"><p>${message}</p></div>
9067
+ <div class="dtm-confirm-footer">
9068
+ <button class="dtm-btn dtm-btn--ghost dtm-confirm-no">\u3044\u3044\u3048\uFF08\u3053\u306E\u307E\u307E\u8AAD\u307F\u8FBC\u3080\uFF09</button>
9069
+ <button class="dtm-btn dtm-btn--primary dtm-confirm-yes">\u306F\u3044\uFF08\u4E0A\u7D1A\u8005\u30E2\u30FC\u30C9\u306B\u5207\u308A\u66FF\u3048\u308B\uFF09</button>
9070
+ </div>
9071
+ </div>`;
9072
+ const close = (result) => {
9073
+ overlay.remove();
9074
+ resolve(result);
9075
+ };
9076
+ overlay.querySelector(".dtm-confirm-yes").addEventListener("click", () => close(true));
9077
+ overlay.querySelector(".dtm-confirm-no").addEventListener("click", () => close(false));
9078
+ overlay.addEventListener("click", (e) => {
9079
+ if (e.target === overlay) close(false);
9080
+ });
9081
+ document.body.appendChild(overlay);
9082
+ });
8924
9083
  const wireEvents = () => {
8925
9084
  refs.playBtn.addEventListener("click", togglePlay);
8926
9085
  refs.playBtn.disabled = false;
@@ -9040,10 +9199,25 @@ var mountDAW = (target, options = {}) => {
9040
9199
  "click",
9041
9200
  () => copy(refs.outputMini.textContent ?? "", refs.copyMiniBtn)
9042
9201
  );
9043
- refs.mmlLoadBtn.addEventListener(
9044
- "click",
9045
- () => overlayDuring(() => loadMML(refs.mmlInput.value))
9046
- );
9202
+ refs.mmlLoadBtn.addEventListener("click", async () => {
9203
+ const mml = refs.mmlInput.value;
9204
+ if (!isAdvanced && options.onRequestAdvancedMode) {
9205
+ const { mergedTrackCount } = parseMML(mml, {
9206
+ stepsPerBar: renderConfig.stepsPerBar,
9207
+ clampTrackCount: trackStates.length
9208
+ });
9209
+ if (mergedTrackCount > 0) {
9210
+ const confirmed = await showConfirmModal(
9211
+ "\u521D\u5FC3\u8005\u30E2\u30FC\u30C9\u3067\u8AAD\u307F\u8FBC\u3080\u3068\u3001\u97F3\u304C\u5D29\u308C\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002<br>\u4E0A\u7D1A\u8005\u30E2\u30FC\u30C9\u306B\u5207\u308A\u66FF\u3048\u307E\u3059\u304B\uFF1F"
9212
+ );
9213
+ if (confirmed) {
9214
+ options.onRequestAdvancedMode(mml);
9215
+ return;
9216
+ }
9217
+ }
9218
+ }
9219
+ overlayDuring(() => loadMML(mml));
9220
+ });
9047
9221
  let activeSamplePlayer = null;
9048
9222
  let activeSampleButton = null;
9049
9223
  const collapseActiveSample = () => {
@@ -9136,6 +9310,7 @@ var mountDAW = (target, options = {}) => {
9136
9310
  singingVoices: options.singingVoices,
9137
9311
  drumPatterns: options.drumPatterns,
9138
9312
  volume: masterVolume,
9313
+ _skipInfoModals: true,
9139
9314
  onStop: () => {
9140
9315
  if (activeSampleButton === htmlBtn) {
9141
9316
  htmlBtn.textContent = "\u25B6 \u8A66\u8074";
@@ -9217,7 +9392,7 @@ var mountDAW = (target, options = {}) => {
9217
9392
  refs.overlay.hidden = true;
9218
9393
  setLoading(false);
9219
9394
  });
9220
- refs.midiLoadBtn.addEventListener("click", () => {
9395
+ refs.midiLoadBtn.addEventListener("click", async () => {
9221
9396
  if (!pendingMidi) return;
9222
9397
  const selected = [];
9223
9398
  const btns = refs.midiTrackSelection.querySelectorAll("button");
@@ -9226,6 +9401,19 @@ var mountDAW = (target, options = {}) => {
9226
9401
  selected.push(detectedTracks[i].index);
9227
9402
  });
9228
9403
  if (selected.length === 0) return;
9404
+ if (!isAdvanced && options.onRequestAdvancedMode && selected.length > trackStates.length) {
9405
+ const confirmed = await showConfirmModal(
9406
+ "\u521D\u5FC3\u8005\u30E2\u30FC\u30C9\u3067\u8AAD\u307F\u8FBC\u3080\u3068\u3001\u97F3\u304C\u5D29\u308C\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002<br>\u4E0A\u7D1A\u8005\u30E2\u30FC\u30C9\u306B\u5207\u308A\u66FF\u3048\u307E\u3059\u304B\uFF1F"
9407
+ );
9408
+ if (confirmed) {
9409
+ const midi = pendingMidi;
9410
+ const sel = selected.slice();
9411
+ options.onRequestAdvancedMode(void 0, (newDaw) => {
9412
+ newDaw.applyMidiParsed?.(midi, sel);
9413
+ });
9414
+ return;
9415
+ }
9416
+ }
9229
9417
  overlayDuring(() => applyMidiSelection(pendingMidi, selected));
9230
9418
  });
9231
9419
  };
@@ -9362,6 +9550,9 @@ var mountDAW = (target, options = {}) => {
9362
9550
  },
9363
9551
  loadMML,
9364
9552
  loadMIDI,
9553
+ applyMidiParsed: (midi, selectedIndices) => {
9554
+ overlayDuring(() => applyMidiSelection(midi, selectedIndices));
9555
+ },
9365
9556
  exportMIDI: exportMIDI2,
9366
9557
  setBpm,
9367
9558
  getPlaybackState: () => playbackState,
@@ -9396,6 +9587,37 @@ var mountDAW = (target, options = {}) => {
9396
9587
  if (audible) audioMutedTracks.delete(trackId);
9397
9588
  else audioMutedTracks.add(trackId);
9398
9589
  },
9590
+ applyLyrics: (trackId, data) => {
9591
+ const t = trackStates.find((s) => s.config.id === trackId);
9592
+ if (!t) return;
9593
+ t.lyrics = data.lyrics;
9594
+ t.lyricModel = data.model;
9595
+ t.vocalVolume = data.vocalVolume;
9596
+ t.vocalGate = data.vocalGate;
9597
+ t.vocalPan = data.vocalPan;
9598
+ t.vocalOctave = data.vocalOctave;
9599
+ },
9600
+ applyTrackInstrument: (trackIndex, instrumentName) => {
9601
+ const t = trackStates[trackIndex];
9602
+ if (!t) return;
9603
+ const name = normalizeInstrumentName(instrumentName);
9604
+ t.trackInstrument = name;
9605
+ if (t.config.id === activeTrackId) updateTrackPanel();
9606
+ },
9607
+ noteToCanvas: (step, pitch) => {
9608
+ const canvas = getGridCanvas();
9609
+ const x = step * renderConfig.stepWidth - currentOffsetX;
9610
+ const y = (renderConfig.keyCount - 1 - pitch) * renderConfig.keyHeight - currentOffsetY;
9611
+ const onScreen = x >= 0 && x <= canvas.width && y >= 0 && y <= canvas.height;
9612
+ let side = null;
9613
+ if (!onScreen) {
9614
+ if (x < 0) side = "left";
9615
+ else if (x > canvas.width) side = "right";
9616
+ else if (y < 0) side = "top";
9617
+ else side = "bottom";
9618
+ }
9619
+ return { x, y, onScreen, side };
9620
+ },
9399
9621
  destroy: () => {
9400
9622
  sequencer.stop();
9401
9623
  options.singingVoices?.stopStream();
@@ -10046,7 +10268,7 @@ var SoundFont = class _SoundFont {
10046
10268
  const { buffer, _param } = zone;
10047
10269
  if (!buffer || !_param) return;
10048
10270
  src.buffer = buffer;
10049
- g.gain.value = volume;
10271
+ g.gain.setValueAtTime(volume, _when);
10050
10272
  src.playbackRate.setValueAtTime(_param.playbackRate, 0);
10051
10273
  Object.assign(src, _param.src);
10052
10274
  const _duration = duration + _SoundFont.afterTime;
@@ -10098,6 +10320,12 @@ var adjustZone = async (ctx, zone) => {
10098
10320
  }
10099
10321
  } else if (zone.file) {
10100
10322
  const buf = Uint8Array.from(atob(zone.file), (c) => c.charCodeAt(0)).buffer;
10323
+ if (ctx.state !== "running") {
10324
+ try {
10325
+ await ctx.resume();
10326
+ } catch {
10327
+ }
10328
+ }
10101
10329
  zone.buffer = await ctx.decodeAudioData(buf);
10102
10330
  }
10103
10331
  for (const [k, v] of [
@@ -10314,6 +10542,14 @@ var createDtmStudio = async (options = {}) => {
10314
10542
  }
10315
10543
  })();
10316
10544
  let nameToKey = {};
10545
+ const resolveNameToKey = (name) => {
10546
+ if (nameToKey[name]) return nameToKey[name];
10547
+ const stripped = name.replace(/\s+/g, "").toLowerCase();
10548
+ const canonical = Object.keys(nameToKey).find(
10549
+ (k) => k.replace(/\s+/g, "").toLowerCase() === stripped
10550
+ );
10551
+ return canonical ? nameToKey[canonical] : void 0;
10552
+ };
10317
10553
  const soundFonts = /* @__PURE__ */ new Map();
10318
10554
  const loadingByKey = /* @__PURE__ */ new Map();
10319
10555
  const loadInstrument = (instrumentKey) => {
@@ -10399,7 +10635,7 @@ var createDtmStudio = async (options = {}) => {
10399
10635
  if (opts.label !== null) {
10400
10636
  const lab = doc.createElement("span");
10401
10637
  lab.className = "dtm-controlbar-label";
10402
- lab.textContent = opts.label ?? "INSTRUMENT";
10638
+ lab.textContent = opts.label ?? "\u697D\u5668\u30D7\u30EA\u30BB\u30C3\u30C8";
10403
10639
  wrapper.appendChild(lab);
10404
10640
  }
10405
10641
  const select = doc.createElement("select");
@@ -10464,7 +10700,13 @@ var createDtmStudio = async (options = {}) => {
10464
10700
  const mountedPlayers = [];
10465
10701
  const mountedModeSwitches = [];
10466
10702
  const mountEditor = (target, opts = {}) => {
10467
- const { preset, presetUI, onInstrumentChange, ...dawOverrides } = opts;
10703
+ const {
10704
+ preset,
10705
+ presetUI,
10706
+ onInstrumentChange,
10707
+ onTrackInstrumentChange: externalOnTrackInstrumentChange,
10708
+ ...dawOverrides
10709
+ } = opts;
10468
10710
  const tracks = dawOverrides.tracks ?? TRACKS_SIMPLE;
10469
10711
  const trackIds = tracks.map((t) => t.id);
10470
10712
  const presetKey = preset && INSTRUMENT_PRESETS[preset] ? preset : defaultPreset;
@@ -10472,14 +10714,29 @@ var createDtmStudio = async (options = {}) => {
10472
10714
  const initialPreset = meta.instrument && INSTRUMENT_PRESETS[meta.instrument] ? meta.instrument : presetKey;
10473
10715
  let editorPreset = initialPreset;
10474
10716
  const isAdvancedMode = dawOverrides.mode === "advanced";
10717
+ const trackInstOverrides = /* @__PURE__ */ new Map();
10718
+ if (meta.trackInstruments) {
10719
+ for (const [idxStr, name] of Object.entries(meta.trackInstruments)) {
10720
+ const idx = Number(idxStr);
10721
+ const key = resolveNameToKey(name);
10722
+ if (key) trackInstOverrides.set(idx, key);
10723
+ }
10724
+ }
10475
10725
  const playNote = (e) => {
10476
- const sf2 = resolveSoundFont(
10477
- editorPreset,
10478
- e.trackId,
10479
- isAdvancedMode ? "advanced" : "simple"
10480
- );
10481
- if (!sf2) return;
10482
- sf2.play({
10726
+ const trackIdx = tracks.findIndex((t) => t.id === e.trackId);
10727
+ const overrideKey = trackIdx >= 0 ? trackInstOverrides.get(trackIdx) : void 0;
10728
+ let sfInst;
10729
+ if (overrideKey) {
10730
+ sfInst = soundFonts.get(overrideKey);
10731
+ } else {
10732
+ sfInst = resolveSoundFont(
10733
+ editorPreset,
10734
+ e.trackId,
10735
+ isAdvancedMode ? "advanced" : "simple"
10736
+ );
10737
+ }
10738
+ if (!sfInst) return;
10739
+ sfInst.play({
10483
10740
  ctx: audioCtx,
10484
10741
  destination: masterGain,
10485
10742
  pitch: e.pitch,
@@ -10496,6 +10753,17 @@ var createDtmStudio = async (options = {}) => {
10496
10753
  }
10497
10754
  onInstrumentChange?.(key);
10498
10755
  };
10756
+ const handleTrackInstrumentChange = async (trackIndex, instrumentName) => {
10757
+ if (!instrumentName) {
10758
+ trackInstOverrides.delete(trackIndex);
10759
+ return;
10760
+ }
10761
+ await listReady;
10762
+ const key = resolveNameToKey(instrumentName);
10763
+ if (!key) return;
10764
+ trackInstOverrides.set(trackIndex, key);
10765
+ await loadInstrument(key);
10766
+ };
10499
10767
  const base = {
10500
10768
  getAudioTime: () => audioCtx.currentTime,
10501
10769
  onResumeAudio: resumeAudio,
@@ -10504,6 +10772,10 @@ var createDtmStudio = async (options = {}) => {
10504
10772
  singingVoices,
10505
10773
  parseMidi,
10506
10774
  onInstrumentChange: handleInstrumentChange,
10775
+ onTrackInstrumentChange: (idx, name) => {
10776
+ void handleTrackInstrumentChange(idx, name);
10777
+ externalOnTrackInstrumentChange?.(idx, name);
10778
+ },
10507
10779
  ...dawOverrides
10508
10780
  };
10509
10781
  const daw = mountDAW(target, base);
@@ -10551,6 +10823,10 @@ var createDtmStudio = async (options = {}) => {
10551
10823
  presetSelect.setValue(name);
10552
10824
  }
10553
10825
  },
10826
+ applyTrackInstrument: (trackIndex, instrumentName) => {
10827
+ daw.applyTrackInstrument(trackIndex, instrumentName);
10828
+ void handleTrackInstrumentChange(trackIndex, instrumentName);
10829
+ },
10554
10830
  destroy
10555
10831
  };
10556
10832
  };
@@ -10558,8 +10834,8 @@ var createDtmStudio = async (options = {}) => {
10558
10834
  const doc = target.ownerDocument;
10559
10835
  const tracksFor = opts.tracksFor ?? ((m) => m === "advanced" ? TRACKS_ADVANCED : TRACKS_SIMPLE);
10560
10836
  const labels = {
10561
- simple: opts.labels?.simple ?? "\u30B7\u30F3\u30D7\u30EB",
10562
- advanced: opts.labels?.advanced ?? "\u30A2\u30C9\u30D0\u30F3\u30B9"
10837
+ simple: opts.labels?.simple ?? "\u521D\u5FC3\u8005",
10838
+ advanced: opts.labels?.advanced ?? "\u4E0A\u7D1A\u8005"
10563
10839
  };
10564
10840
  const editorOptionsFor = (mode) => typeof opts.editorOptions === "function" ? opts.editorOptions(mode) : opts.editorOptions ?? {};
10565
10841
  let currentMode = opts.mode ?? "simple";
@@ -10569,7 +10845,7 @@ var createDtmStudio = async (options = {}) => {
10569
10845
  if (opts.label !== null) {
10570
10846
  const lab = doc.createElement("span");
10571
10847
  lab.className = "dtm-controlbar-label";
10572
- lab.textContent = opts.label ?? "MODE";
10848
+ lab.textContent = opts.label ?? "\u30E2\u30FC\u30C9";
10573
10849
  wrapper.appendChild(lab);
10574
10850
  }
10575
10851
  const seg = doc.createElement("div");
@@ -10600,7 +10876,15 @@ var createDtmStudio = async (options = {}) => {
10600
10876
  ...editorOpts,
10601
10877
  mode,
10602
10878
  tracks: tracksFor(mode),
10603
- initialMML: mml ?? editorOpts.initialMML
10879
+ initialMML: mml ?? editorOpts.initialMML,
10880
+ onRequestAdvancedMode: (pendingMml, applyMidi) => {
10881
+ doUnmount();
10882
+ currentMode = "advanced";
10883
+ updateButtons();
10884
+ opts.onChange?.("advanced");
10885
+ doMount("advanced", pendingMml);
10886
+ if (applyMidi && daw) applyMidi(daw);
10887
+ }
10604
10888
  });
10605
10889
  attachWrapper();
10606
10890
  opts.onMount?.(daw, mode);
@@ -10650,24 +10934,44 @@ var createDtmStudio = async (options = {}) => {
10650
10934
  (idx) => getRoleForTrackIndex(idx, isAdvancedMode ? "advanced" : "simple")
10651
10935
  );
10652
10936
  const loadTrackIds = trackIds.length > 0 ? trackIds : [...TRACK_ROLES];
10653
- void loadPreset(
10654
- playerPreset,
10655
- loadTrackIds,
10656
- isAdvancedMode ? "advanced" : "simple"
10657
- );
10658
- const playPlayerNote = (e) => {
10659
- const idx = Number(e.trackId);
10660
- const role = getRoleForTrackIndex(
10661
- idx,
10662
- isAdvancedMode ? "advanced" : "simple"
10663
- );
10664
- const sf2 = resolveSoundFont(
10937
+ const playerTrackInstKeys = /* @__PURE__ */ new Map();
10938
+ const loadPlayerTrackInstruments = async () => {
10939
+ if (!meta.trackInstruments) return;
10940
+ await listReady;
10941
+ for (const [idxStr, name] of Object.entries(meta.trackInstruments)) {
10942
+ const key = resolveNameToKey(name);
10943
+ if (!key) continue;
10944
+ playerTrackInstKeys.set(Number(idxStr), key);
10945
+ await loadInstrument(key);
10946
+ }
10947
+ };
10948
+ void Promise.all([
10949
+ loadPreset(
10665
10950
  playerPreset,
10666
- role,
10951
+ loadTrackIds,
10667
10952
  isAdvancedMode ? "advanced" : "simple"
10668
- );
10669
- if (!sf2) return;
10670
- sf2.play({
10953
+ ),
10954
+ loadPlayerTrackInstruments()
10955
+ ]);
10956
+ const playPlayerNote = (e) => {
10957
+ const idx = Number(e.trackId);
10958
+ const overrideKey = playerTrackInstKeys.get(idx);
10959
+ let sfInst;
10960
+ if (overrideKey) {
10961
+ sfInst = soundFonts.get(overrideKey);
10962
+ } else {
10963
+ const role = getRoleForTrackIndex(
10964
+ idx,
10965
+ isAdvancedMode ? "advanced" : "simple"
10966
+ );
10967
+ sfInst = resolveSoundFont(
10968
+ playerPreset,
10969
+ role,
10970
+ isAdvancedMode ? "advanced" : "simple"
10971
+ );
10972
+ }
10973
+ if (!sfInst) return;
10974
+ sfInst.play({
10671
10975
  ctx: audioCtx,
10672
10976
  destination: masterGain,
10673
10977
  pitch: e.pitch,
@@ -10725,6 +11029,7 @@ export {
10725
11029
  DRUM_FONT,
10726
11030
  DRUM_KEYS,
10727
11031
  DRUM_PATTERNS,
11032
+ GM_INSTRUMENT_NAMES,
10728
11033
  INSTRUMENT_PRESETS,
10729
11034
  KOE_BASE_URL,
10730
11035
  KOE_VOICEBANKS,