@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.js CHANGED
@@ -31,6 +31,7 @@ __export(index_exports, {
31
31
  DRUM_FONT: () => DRUM_FONT,
32
32
  DRUM_KEYS: () => DRUM_KEYS,
33
33
  DRUM_PATTERNS: () => DRUM_PATTERNS,
34
+ GM_INSTRUMENT_NAMES: () => GM_INSTRUMENT_NAMES,
34
35
  INSTRUMENT_PRESETS: () => INSTRUMENT_PRESETS,
35
36
  KOE_BASE_URL: () => KOE_BASE_URL,
36
37
  KOE_VOICEBANKS: () => KOE_VOICEBANKS,
@@ -256,6 +257,7 @@ async function buildNameToKeyMapping() {
256
257
  }
257
258
  return nameToKey;
258
259
  }
260
+ var GM_INSTRUMENT_NAMES = FONT_NAME_SURIKOV.trim().split("\n").map((line) => line.slice(line.indexOf(" ") + 1));
259
261
 
260
262
  // node_modules/.pnpm/@onjmin+chord-parser@1.0.2/node_modules/@onjmin/chord-parser/dist/index.mjs
261
263
  var SHARP_NAMES = [
@@ -1333,14 +1335,17 @@ var buildUI = (target, options) => {
1333
1335
  target.innerHTML = `
1334
1336
  <div class="dtm-daw" data-dtm="root">
1335
1337
  <div class="dtm-topbar" data-dtm="transport">
1336
- <button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
1337
- <button class="dtm-play" data-dtm="play" disabled>${icon("play")}<span>\u8A66\u8074</span></button>
1338
- <button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
1339
- <label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
1340
- <span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
1341
- <span class="dtm-grow"></span>
1342
- <span class="dtm-label">BPM</span>
1343
- <input type="number" class="dtm-input dtm-input--num" data-dtm="bpm" value="${defaultBpm}" min="20" max="300">
1338
+ <div class="dtm-topbar-row1">
1339
+ <button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
1340
+ <button class="dtm-play" data-dtm="play" disabled>${icon("play")}<span>\u8A66\u8074</span></button>
1341
+ <button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
1342
+ <label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
1343
+ <span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
1344
+ <span class="dtm-grow"></span>
1345
+ <span class="dtm-label">BPM</span>
1346
+ <input type="number" class="dtm-input dtm-input--num" data-dtm="bpm" value="${defaultBpm}" min="20" max="300">
1347
+ </div>
1348
+ <div class="dtm-tracks" data-dtm="track-tabs"></div>
1344
1349
  </div>
1345
1350
 
1346
1351
  <div class="dtm-tooldock">
@@ -1363,8 +1368,6 @@ var buildUI = (target, options) => {
1363
1368
  </select>
1364
1369
  </div>
1365
1370
 
1366
- <div class="dtm-tracks" data-dtm="track-tabs"></div>
1367
-
1368
1371
  <div class="dtm-roll-wrap">
1369
1372
  <div class="dtm-roll" data-dtm="roll">
1370
1373
  <div data-dtm="wrapper" style="position:absolute;inset:0;"></div>
@@ -3659,12 +3662,14 @@ var drawKeyboard = () => {
3659
3662
  const totalPitch = pitchIndex + pitchRangeStart;
3660
3663
  const pitchMod12 = totalPitch % 12;
3661
3664
  const isBlackKey = blackKeyPitches.has(pitchMod12);
3665
+ const octave = Math.floor(totalPitch / 12) - 1;
3666
+ const isC4Range = octave === 4;
3662
3667
  const screenY = y - g_draw_offset_y;
3663
3668
  const bkW = Math.floor(KEYBOARD_WIDTH * BK_RATIO);
3664
3669
  if (isBlackKey) {
3665
- g_key_ctx.fillStyle = WHITE_KEY;
3670
+ g_key_ctx.fillStyle = isC4Range ? "#d8d4be" : WHITE_KEY;
3666
3671
  g_key_ctx.fillRect(0, screenY, KEYBOARD_WIDTH, keyHeight);
3667
- g_key_ctx.fillStyle = BLACK_KEY;
3672
+ g_key_ctx.fillStyle = isC4Range ? "#1a1408" : BLACK_KEY;
3668
3673
  g_key_ctx.fillRect(0, screenY, bkW, keyHeight);
3669
3674
  g_key_ctx.strokeStyle = BK_EDGE;
3670
3675
  g_key_ctx.lineWidth = 1;
@@ -3673,7 +3678,7 @@ var drawKeyboard = () => {
3673
3678
  g_key_ctx.lineTo(bkW, screenY + keyHeight);
3674
3679
  g_key_ctx.stroke();
3675
3680
  } else {
3676
- g_key_ctx.fillStyle = WHITE_KEY;
3681
+ g_key_ctx.fillStyle = isC4Range ? "#dedad0" : WHITE_KEY;
3677
3682
  g_key_ctx.fillRect(0, screenY, KEYBOARD_WIDTH, keyHeight);
3678
3683
  if (pitchMod12 === 5 || pitchMod12 === 0) {
3679
3684
  g_key_ctx.strokeStyle = WW_SEP;
@@ -3685,13 +3690,13 @@ var drawKeyboard = () => {
3685
3690
  }
3686
3691
  }
3687
3692
  if (pitchMod12 === 0) {
3688
- const octave = Math.floor(totalPitch / 12) - 1;
3693
+ const octave2 = Math.floor(totalPitch / 12) - 1;
3689
3694
  g_key_ctx.fillStyle = "#555040";
3690
3695
  g_key_ctx.font = "10px 'k8x12',monospace";
3691
3696
  g_key_ctx.textAlign = "right";
3692
3697
  g_key_ctx.textBaseline = "bottom";
3693
3698
  g_key_ctx.fillText(
3694
- `${KEY_NAMES[pitchMod12]}${octave}`,
3699
+ `${KEY_NAMES[pitchMod12]}${octave2}`,
3695
3700
  KEYBOARD_WIDTH - 4,
3696
3701
  screenY + keyHeight - 2
3697
3702
  );
@@ -3743,17 +3748,22 @@ var drawGrid = (noteLengthSteps = 1) => {
3743
3748
  drawKeyboard();
3744
3749
  drawHeader();
3745
3750
  g_grid_ctx.clearRect(0, 0, g_grid_canvas.width, g_grid_canvas.height);
3746
- const { keyHeight, keyCount, stepWidth, stepsPerBar } = g_config;
3751
+ const { keyHeight, keyCount, stepWidth, stepsPerBar, pitchRangeStart } = g_config;
3747
3752
  const startY = Math.floor(g_draw_offset_y / keyHeight) * keyHeight;
3748
3753
  const endY = g_draw_offset_y + g_grid_canvas.height;
3749
3754
  for (let y = startY; y < endY; y += keyHeight) {
3750
3755
  const pitchIndex = keyCount - 1 - y / keyHeight;
3756
+ const totalPitch = pitchIndex + pitchRangeStart;
3751
3757
  const pitchMod12 = pitchIndex % 12;
3752
3758
  const isBlackKey = blackKeyPitches.has(pitchMod12);
3753
3759
  const isC = pitchMod12 === 0;
3760
+ const octave = Math.floor(totalPitch / 12) - 1;
3761
+ const isC4Range = octave === 4;
3754
3762
  const screenY = y - g_draw_offset_y;
3755
- if (isBlackKey) {
3756
- g_grid_ctx.fillStyle = "#0d1020";
3763
+ g_grid_ctx.fillStyle = isBlackKey ? "#080b16" : "#111628";
3764
+ g_grid_ctx.fillRect(0, screenY, g_grid_canvas.width, keyHeight);
3765
+ if (isC4Range) {
3766
+ g_grid_ctx.fillStyle = "rgba(41,173,255,0.05)";
3757
3767
  g_grid_ctx.fillRect(0, screenY, g_grid_canvas.width, keyHeight);
3758
3768
  }
3759
3769
  g_grid_ctx.beginPath();
@@ -4395,6 +4405,7 @@ var PITCH_MAP2 = {
4395
4405
  };
4396
4406
  var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
4397
4407
  var META_DIRECTIVE = /#(inst|drum|volume|drumvolume|mode)=([\w-]+)/gi;
4408
+ var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
4398
4409
  var parseMmlMeta = (mml) => {
4399
4410
  const meta = {};
4400
4411
  for (const m of mml.matchAll(META_DIRECTIVE)) {
@@ -4413,9 +4424,17 @@ var parseMmlMeta = (mml) => {
4413
4424
  }
4414
4425
  }
4415
4426
  }
4427
+ for (const m of mml.matchAll(TRACK_INST_DIRECTIVE)) {
4428
+ const idx = Number.parseInt(m[1], 10);
4429
+ const name = m[2].trim();
4430
+ if (!Number.isNaN(idx) && name) {
4431
+ meta.trackInstruments ??= {};
4432
+ meta.trackInstruments[idx] = name;
4433
+ }
4434
+ }
4416
4435
  return meta;
4417
4436
  };
4418
- var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "");
4437
+ var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "").replace(TRACK_INST_DIRECTIVE, "");
4419
4438
  var formatMmlMeta = (meta, space = "") => {
4420
4439
  const parts = [];
4421
4440
  if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
@@ -4424,6 +4443,11 @@ var formatMmlMeta = (meta, space = "") => {
4424
4443
  if (meta.drumVolume !== void 0)
4425
4444
  parts.push(`#drumvolume=${meta.drumVolume}`);
4426
4445
  if (meta.mode) parts.push(`#mode=${meta.mode}`);
4446
+ if (meta.trackInstruments) {
4447
+ for (const [idx, name] of Object.entries(meta.trackInstruments)) {
4448
+ if (name) parts.push(`#t${idx}inst=${name}`);
4449
+ }
4450
+ }
4427
4451
  return parts.join(space);
4428
4452
  };
4429
4453
  var parseMML = (mml, options = {}) => {
@@ -5045,7 +5069,7 @@ var DAW_CSS = `
5045
5069
  top: 0;
5046
5070
  z-index: 20;
5047
5071
  display: flex;
5048
- flex-wrap: nowrap;
5072
+ flex-wrap: wrap;
5049
5073
  align-items: center;
5050
5074
  gap: var(--dtm-gap);
5051
5075
  padding: 6px;
@@ -5055,18 +5079,19 @@ var DAW_CSS = `
5055
5079
  inset 0 0 0 2px var(--c-black),
5056
5080
  0 0 0 2px var(--dtm-success),
5057
5081
  4px 4px 0 var(--c-black);
5082
+ }
5083
+ .dtm-topbar-row1 {
5084
+ display: flex;
5085
+ align-items: center;
5086
+ gap: var(--dtm-gap);
5087
+ flex-basis: 100%;
5088
+ min-width: 0;
5058
5089
  overflow-x: auto;
5059
5090
  scrollbar-width: none;
5060
5091
  }
5061
- .dtm-topbar::-webkit-scrollbar {
5062
- display: none;
5063
- }
5064
- .dtm-topbar > * {
5065
- flex-shrink: 0;
5066
- }
5067
- .dtm-topbar > .dtm-grow {
5068
- flex-shrink: 1;
5069
- }
5092
+ .dtm-topbar-row1::-webkit-scrollbar { display: none; }
5093
+ .dtm-topbar-row1 > * { flex-shrink: 0; }
5094
+ .dtm-topbar-row1 > .dtm-grow { flex-shrink: 1; }
5070
5095
 
5071
5096
  /* PLAY\u30DC\u30BF\u30F3 \u2014 \u30B2\u30FC\u30E0\u306E\u300C\u6C7A\u5B9A\u30DC\u30BF\u30F3\u300D\u7684\u5B58\u5728\u611F */
5072
5097
  .dtm-play {
@@ -5256,46 +5281,39 @@ var DAW_CSS = `
5256
5281
  .dtm-modebtn--active { background: var(--dtm-primary); color: var(--dtm-pfg); }
5257
5282
  .dtm-modebtn:not(.dtm-modebtn--active):active { background: var(--dtm-border2); }
5258
5283
 
5259
- /* \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 */
5284
+ /* \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 */
5260
5285
  .dtm-tracks {
5286
+ flex-basis: 100%;
5261
5287
  display: flex;
5262
- flex-wrap: wrap;
5263
- gap: var(--dtm-gap);
5288
+ flex-wrap: nowrap;
5289
+ gap: 3px;
5264
5290
  }
5265
5291
  .dtm-pill {
5266
5292
  --dtm-pill-color: var(--dtm-primary);
5267
5293
  display: inline-flex;
5268
5294
  align-items: center;
5269
- gap: 8px;
5270
- flex: 1 1 auto;
5271
5295
  justify-content: center;
5272
- min-height: 42px;
5273
- padding: 0 12px;
5274
- border: 2px solid var(--dtm-border2);
5275
- background: var(--dtm-deep);
5276
- color: var(--dtm-muted);
5296
+ flex: 1 1 0;
5297
+ min-width: 0;
5298
+ height: 26px;
5299
+ padding: 0;
5300
+ border: 2px solid var(--c-black);
5301
+ background: color-mix(in srgb, var(--dtm-pill-color) 40%, black);
5302
+ color: var(--c-white);
5277
5303
  font-family: var(--dtm-font);
5278
- font-size: 13px;
5279
- text-transform: uppercase;
5280
- letter-spacing: .1em;
5304
+ font-size: 11px;
5305
+ font-weight: bold;
5281
5306
  cursor: pointer;
5282
- box-shadow: 3px 3px 0 var(--c-black);
5283
- }
5284
- .dtm-pill .dtm-dot {
5285
- width: 8px; height: 8px;
5286
- background: var(--dtm-pill-color);
5287
- flex: 0 0 auto;
5288
- box-shadow: 1px 1px 0 var(--c-black);
5307
+ box-shadow: 2px 2px 0 var(--c-black);
5308
+ opacity: 0.7;
5289
5309
  }
5290
- /* \u30A2\u30AF\u30C6\u30A3\u30D6\u9078\u629E = \u91D1\u8272\u30CF\u30A4\u30E9\u30A4\u30C8 + \u30AB\u30FC\u30BD\u30EB */
5310
+ /* \u30A2\u30AF\u30C6\u30A3\u30D6\u9078\u629E = \u4E0D\u900F\u660E + \u91D1\u67A0 */
5291
5311
  .dtm-pill--active {
5312
+ opacity: 1;
5292
5313
  border-color: var(--dtm-gold);
5293
- color: var(--dtm-gold);
5294
- background: var(--dtm-surface);
5295
- box-shadow: 0 0 0 2px var(--dtm-gold), 3px 3px 0 var(--c-black);
5314
+ box-shadow: 0 0 0 1px var(--dtm-gold), 2px 2px 0 var(--c-black);
5296
5315
  }
5297
- .dtm-pill--active::before { content: "\u25BA "; font-size: 10px; }
5298
- .dtm-pill:not(.dtm-pill--active):active { transform: translate(3px,3px); box-shadow: none; }
5316
+ .dtm-pill:not(.dtm-pill--active):active { transform: translate(2px,2px); box-shadow: none; }
5299
5317
 
5300
5318
  /* \u2500\u2500\u2500 \u30D4\u30A2\u30CE\u30ED\u30FC\u30EB\uFF08\u30C8\u30E9\u30C3\u30AB\u30FC\u98A8\uFF09 \u2500\u2500\u2500 */
5301
5319
  .dtm-roll-wrap { display: flex; gap: var(--dtm-gap); }
@@ -5439,7 +5457,7 @@ var DAW_CSS = `
5439
5457
  }
5440
5458
  .dtm-overlay[hidden] { display: none; }
5441
5459
  .dtm-overlay::before {
5442
- content: 'NOW LOADING';
5460
+ content: '\u30ED\u30FC\u30C9\u4E2D';
5443
5461
  font-family: var(--dtm-font);
5444
5462
  font-size: 13px;
5445
5463
  color: var(--dtm-primary);
@@ -5632,6 +5650,15 @@ var DAW_CSS = `
5632
5650
  justify-content: center;
5633
5651
  }
5634
5652
 
5653
+ .dtm-confirm-footer {
5654
+ padding: 8px 12px;
5655
+ border-top: 2px solid var(--c-black);
5656
+ background: var(--dtm-deep);
5657
+ display: flex;
5658
+ gap: 8px;
5659
+ justify-content: flex-end;
5660
+ }
5661
+
5635
5662
  .dtm-modal {
5636
5663
  max-width: 500px;
5637
5664
  width: 100%;
@@ -6654,9 +6681,11 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6654
6681
  const mmlInfoItem = makeMenuItem("MML\u66F8\u5F0F\u3068\u306F");
6655
6682
  const embedItem = makeMenuItem("\u57CB\u3081\u8FBC\u3080");
6656
6683
  const copyMmlItem = makeMenuItem("MML\u30B3\u30D4\u30FC");
6657
- menuDropdown.appendChild(showMmlItem);
6658
- menuDropdown.appendChild(mmlInfoItem);
6659
- menuDropdown.appendChild(embedItem);
6684
+ if (!options._skipInfoModals) {
6685
+ menuDropdown.appendChild(showMmlItem);
6686
+ menuDropdown.appendChild(mmlInfoItem);
6687
+ menuDropdown.appendChild(embedItem);
6688
+ }
6660
6689
  menuDropdown.appendChild(copyMmlItem);
6661
6690
  menuContainer.appendChild(menuDropdown);
6662
6691
  mmlHeader.appendChild(menuContainer);
@@ -6810,6 +6839,8 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6810
6839
  volume: trackVolume,
6811
6840
  // 解説モーダル内の試聴サンプルは規約同意を要求しない。
6812
6841
  skipConsent: true,
6842
+ // 再帰的なモーダル生成を防ぐ。
6843
+ _skipInfoModals: true,
6813
6844
  onStop: () => {
6814
6845
  if (activeSampleBtn === el) resetSampleBtn(el);
6815
6846
  }
@@ -6819,65 +6850,67 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6819
6850
  });
6820
6851
  }
6821
6852
  };
6822
- showMmlItem.addEventListener("click", (e) => {
6823
- e.stopPropagation();
6824
- toggleMenu(false);
6825
- const modalBody = openInfoModal("MML\u3092\u8868\u793A");
6826
- const desc = doc.createElement("p");
6827
- 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";
6828
- desc.style.marginBottom = "8px";
6829
- modalBody.appendChild(desc);
6830
- const sourceMml = options.getMml?.() ?? mml;
6831
- const displayMml = sourceMml.split(";").map((s) => s.trim()).filter((s) => s.length > 0).join(";\n");
6832
- const pre = doc.createElement("pre");
6833
- pre.textContent = displayMml;
6834
- pre.style.whiteSpace = "pre-wrap";
6835
- pre.style.wordBreak = "break-all";
6836
- pre.style.cursor = "text";
6837
- pre.addEventListener("click", () => {
6838
- const range = doc.createRange();
6839
- range.selectNodeContents(pre);
6840
- const sel = doc.defaultView?.getSelection();
6841
- sel?.removeAllRanges();
6842
- sel?.addRange(range);
6843
- });
6844
- modalBody.appendChild(pre);
6845
- appendCopyButton(modalBody, sourceMml);
6846
- });
6847
- mmlInfoItem.addEventListener("click", (e) => {
6848
- e.stopPropagation();
6849
- toggleMenu(false);
6850
- const modalBody = openInfoModal("MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC");
6851
- modalBody.innerHTML = MML_INFO_HTML;
6852
- wireSampleButtons(modalBody);
6853
- });
6854
- embedItem.addEventListener("click", async (e) => {
6855
- e.stopPropagation();
6856
- toggleMenu(false);
6857
- const modalBody = openInfoModal("\u57CB\u3081\u8FBC\u307F");
6858
- const loading = doc.createElement("p");
6859
- loading.textContent = "\u751F\u6210\u4E2D...";
6860
- modalBody.appendChild(loading);
6861
- try {
6862
- const embedBase = options.embedUrl ?? "https://onjmin.github.io/dtm/demo/embed.html";
6863
- const payload = await encodeMml(mml);
6864
- const url2 = `${embedBase}#${payload}`;
6865
- const snippet = `<iframe src="${url2}" width="100%" height="260" frameborder="0" loading="lazy" title="@onjmin/dtm player"></iframe>`;
6866
- if (!modalBody.isConnected) return;
6867
- loading.remove();
6853
+ if (!options._skipInfoModals) {
6854
+ showMmlItem.addEventListener("click", (e) => {
6855
+ e.stopPropagation();
6856
+ toggleMenu(false);
6857
+ const modalBody = openInfoModal("MML\u3092\u8868\u793A");
6868
6858
  const desc = doc.createElement("p");
6869
- 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";
6859
+ 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";
6860
+ desc.style.marginBottom = "8px";
6861
+ modalBody.appendChild(desc);
6862
+ const sourceMml = options.getMml?.() ?? mml;
6863
+ const displayMml = sourceMml.split(";").map((s) => s.trim()).filter((s) => s.length > 0).join(";\n");
6870
6864
  const pre = doc.createElement("pre");
6871
- pre.textContent = snippet;
6865
+ pre.textContent = displayMml;
6872
6866
  pre.style.whiteSpace = "pre-wrap";
6873
6867
  pre.style.wordBreak = "break-all";
6874
- modalBody.append(desc, pre);
6875
- appendCopyButton(modalBody, snippet);
6876
- } catch (err2) {
6877
- console.error("[dtm] failed to generate embed snippet", err2);
6878
- if (modalBody.isConnected) loading.textContent = "\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
6879
- }
6880
- });
6868
+ pre.style.cursor = "text";
6869
+ pre.addEventListener("click", () => {
6870
+ const range = doc.createRange();
6871
+ range.selectNodeContents(pre);
6872
+ const sel = doc.defaultView?.getSelection();
6873
+ sel?.removeAllRanges();
6874
+ sel?.addRange(range);
6875
+ });
6876
+ modalBody.appendChild(pre);
6877
+ appendCopyButton(modalBody, sourceMml);
6878
+ });
6879
+ mmlInfoItem.addEventListener("click", (e) => {
6880
+ e.stopPropagation();
6881
+ toggleMenu(false);
6882
+ const modalBody = openInfoModal("MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC");
6883
+ modalBody.innerHTML = MML_INFO_HTML;
6884
+ wireSampleButtons(modalBody);
6885
+ });
6886
+ embedItem.addEventListener("click", async (e) => {
6887
+ e.stopPropagation();
6888
+ toggleMenu(false);
6889
+ const modalBody = openInfoModal("\u57CB\u3081\u8FBC\u307F");
6890
+ const loading = doc.createElement("p");
6891
+ loading.textContent = "\u751F\u6210\u4E2D...";
6892
+ modalBody.appendChild(loading);
6893
+ try {
6894
+ const embedBase = options.embedUrl ?? "https://onjmin.github.io/dtm/demo/embed.html";
6895
+ const payload = await encodeMml(mml);
6896
+ const url2 = `${embedBase}#${payload}`;
6897
+ const snippet = `<iframe src="${url2}" width="100%" height="260" frameborder="0" loading="lazy" title="@onjmin/dtm player"></iframe>`;
6898
+ if (!modalBody.isConnected) return;
6899
+ loading.remove();
6900
+ const desc = doc.createElement("p");
6901
+ 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";
6902
+ const pre = doc.createElement("pre");
6903
+ pre.textContent = snippet;
6904
+ pre.style.whiteSpace = "pre-wrap";
6905
+ pre.style.wordBreak = "break-all";
6906
+ modalBody.append(desc, pre);
6907
+ appendCopyButton(modalBody, snippet);
6908
+ } catch (err2) {
6909
+ console.error("[dtm] failed to generate embed snippet", err2);
6910
+ if (modalBody.isConnected) loading.textContent = "\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
6911
+ }
6912
+ });
6913
+ }
6881
6914
  copyMmlItem.addEventListener("click", async (e) => {
6882
6915
  e.stopPropagation();
6883
6916
  const success = await copyToClipboard(doc, options.getMml?.() ?? mml);
@@ -7474,8 +7507,8 @@ var MIDI_INFO_HTML = `
7474
7507
 
7475
7508
  <h4>3. \u30E2\u30FC\u30C9\u306B\u3088\u308B\u53D6\u308A\u8FBC\u307F\u65B9\u306E\u9055\u3044</h4>
7476
7509
  <ul>
7477
- <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>
7478
- <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>
7510
+ <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>
7511
+ <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>
7479
7512
  </ul>
7480
7513
 
7481
7514
  <h4>4. MIDI\u30D5\u30A1\u30A4\u30EB\u3092\u624B\u306B\u5165\u308C\u308B</h4>
@@ -7527,105 +7560,105 @@ var TRACKS_SIMPLE = [
7527
7560
  var TRACKS_ADVANCED = [
7528
7561
  {
7529
7562
  id: "t0",
7530
- name: "TRACK 01",
7563
+ name: "\u30C8\u30E9\u30C3\u30AF1",
7531
7564
  color: [41, 173, 255],
7532
7565
  instrument: 0,
7533
7566
  volume: 100
7534
7567
  },
7535
7568
  {
7536
7569
  id: "t1",
7537
- name: "TRACK 02",
7570
+ name: "\u30C8\u30E9\u30C3\u30AF2",
7538
7571
  color: [0, 228, 54],
7539
7572
  instrument: 1,
7540
7573
  volume: 100
7541
7574
  },
7542
7575
  {
7543
7576
  id: "t2",
7544
- name: "TRACK 03",
7577
+ name: "\u30C8\u30E9\u30C3\u30AF3",
7545
7578
  color: [255, 119, 168],
7546
7579
  instrument: 2,
7547
7580
  volume: 100
7548
7581
  },
7549
7582
  {
7550
7583
  id: "t3",
7551
- name: "TRACK 04",
7584
+ name: "\u30C8\u30E9\u30C3\u30AF4",
7552
7585
  color: [255, 163, 0],
7553
7586
  instrument: 3,
7554
7587
  volume: 100
7555
7588
  },
7556
7589
  {
7557
7590
  id: "t4",
7558
- name: "TRACK 05",
7591
+ name: "\u30C8\u30E9\u30C3\u30AF5",
7559
7592
  color: [255, 236, 39],
7560
7593
  instrument: 4,
7561
7594
  volume: 100
7562
7595
  },
7563
7596
  {
7564
7597
  id: "t5",
7565
- name: "TRACK 06",
7598
+ name: "\u30C8\u30E9\u30C3\u30AF6",
7566
7599
  color: [131, 118, 156],
7567
7600
  instrument: 5,
7568
7601
  volume: 100
7569
7602
  },
7570
7603
  {
7571
7604
  id: "t6",
7572
- name: "TRACK 07",
7605
+ name: "\u30C8\u30E9\u30C3\u30AF7",
7573
7606
  color: [255, 0, 77],
7574
7607
  instrument: 6,
7575
7608
  volume: 100
7576
7609
  },
7577
7610
  {
7578
7611
  id: "t7",
7579
- name: "TRACK 08",
7612
+ name: "\u30C8\u30E9\u30C3\u30AF8",
7580
7613
  color: [255, 204, 170],
7581
7614
  instrument: 7,
7582
7615
  volume: 100
7583
7616
  },
7584
7617
  {
7585
7618
  id: "t8",
7586
- name: "TRACK 09",
7619
+ name: "\u30C8\u30E9\u30C3\u30AF9",
7587
7620
  color: [194, 195, 199],
7588
7621
  instrument: 8,
7589
7622
  volume: 100
7590
7623
  },
7591
7624
  {
7592
7625
  id: "t9",
7593
- name: "TRACK 10",
7626
+ name: "\u30C8\u30E9\u30C3\u30AF10",
7594
7627
  color: [0, 135, 81],
7595
7628
  instrument: 9,
7596
7629
  volume: 100
7597
7630
  },
7598
7631
  {
7599
7632
  id: "t10",
7600
- name: "TRACK 11",
7633
+ name: "\u30C8\u30E9\u30C3\u30AF11",
7601
7634
  color: [171, 82, 54],
7602
7635
  instrument: 10,
7603
7636
  volume: 100
7604
7637
  },
7605
7638
  {
7606
7639
  id: "t11",
7607
- name: "TRACK 12",
7640
+ name: "\u30C8\u30E9\u30C3\u30AF12",
7608
7641
  color: [126, 37, 83],
7609
7642
  instrument: 11,
7610
7643
  volume: 100
7611
7644
  },
7612
7645
  {
7613
7646
  id: "t12",
7614
- name: "TRACK 13",
7647
+ name: "\u30C8\u30E9\u30C3\u30AF13",
7615
7648
  color: [255, 241, 232],
7616
7649
  instrument: 12,
7617
7650
  volume: 100
7618
7651
  },
7619
7652
  {
7620
7653
  id: "t13",
7621
- name: "TRACK 14",
7654
+ name: "\u30C8\u30E9\u30C3\u30AF14",
7622
7655
  color: [120, 200, 255],
7623
7656
  instrument: 13,
7624
7657
  volume: 100
7625
7658
  },
7626
7659
  {
7627
7660
  id: "t14",
7628
- name: "TRACK 15",
7661
+ name: "\u30C8\u30E9\u30C3\u30AF15",
7629
7662
  color: [100, 255, 160],
7630
7663
  instrument: 14,
7631
7664
  volume: 100
@@ -7639,6 +7672,13 @@ var LYRIC_MODEL_LABELS2 = {
7639
7672
  };
7640
7673
  var lyricModelLabel = (model) => LYRIC_MODEL_LABELS2[model] ?? model;
7641
7674
  var clamp3 = (v, min, max) => Math.min(Math.max(v, min), max);
7675
+ var normalizeInstrumentName = (name) => {
7676
+ if (!name) return "";
7677
+ const stripped = name.replace(/\s+/g, "").toLowerCase();
7678
+ return GM_INSTRUMENT_NAMES.find(
7679
+ (n) => n.replace(/\s+/g, "").toLowerCase() === stripped
7680
+ ) ?? name;
7681
+ };
7642
7682
  var mountDAW = (target, options = {}) => {
7643
7683
  injectStyles();
7644
7684
  const getAudioTime = options.getAudioTime ?? (() => performance.now() / 1e3);
@@ -7678,7 +7718,8 @@ var mountDAW = (target, options = {}) => {
7678
7718
  let snapGridSteps = 12;
7679
7719
  const gridLineSteps = 48;
7680
7720
  let currentOffsetX = 0;
7681
- let currentOffsetY = (104 - 1 - 60) * renderConfig.keyHeight - 215;
7721
+ const _initPitch = options.initialScrollPitch;
7722
+ let currentOffsetY = _initPitch !== void 0 ? (renderConfig.keyCount - 1 - _initPitch) * renderConfig.keyHeight - 215 : (104 - 1 - 60) * renderConfig.keyHeight - 215;
7682
7723
  let playStartStep = 0;
7683
7724
  let isSolo = false;
7684
7725
  let lyricTrackIndices = /* @__PURE__ */ new Set();
@@ -7691,6 +7732,24 @@ var mountDAW = (target, options = {}) => {
7691
7732
  let copiedNotes = [];
7692
7733
  let trackStates = [];
7693
7734
  let suppressPatch = false;
7735
+ let lyricsDebounceTimer = null;
7736
+ const fireLyricsChange = (t) => {
7737
+ if (!options.onLyricsChange) return;
7738
+ const trackId = t.config.id;
7739
+ const data = {
7740
+ lyrics: t.lyrics,
7741
+ model: t.lyricModel,
7742
+ vocalVolume: t.vocalVolume,
7743
+ vocalGate: t.vocalGate,
7744
+ vocalPan: t.vocalPan,
7745
+ vocalOctave: t.vocalOctave
7746
+ };
7747
+ if (lyricsDebounceTimer) clearTimeout(lyricsDebounceTimer);
7748
+ lyricsDebounceTimer = setTimeout(() => {
7749
+ options.onLyricsChange(trackId, data);
7750
+ lyricsDebounceTimer = null;
7751
+ }, 300);
7752
+ };
7694
7753
  const hiddenTracks = /* @__PURE__ */ new Set();
7695
7754
  const audioMutedTracks = /* @__PURE__ */ new Set();
7696
7755
  const createTrackStates = () => {
@@ -7739,7 +7798,8 @@ var mountDAW = (target, options = {}) => {
7739
7798
  vocalVolume: DEFAULT_VOCAL_VOLUME,
7740
7799
  vocalGate: 100,
7741
7800
  vocalPan: 64,
7742
- vocalOctave: 0
7801
+ vocalOctave: 0,
7802
+ trackInstrument: ""
7743
7803
  };
7744
7804
  });
7745
7805
  };
@@ -8445,19 +8505,20 @@ var mountDAW = (target, options = {}) => {
8445
8505
  };
8446
8506
  const updateTrackPanel = () => {
8447
8507
  refs.trackTabs.innerHTML = "";
8448
- for (const t of trackStates) {
8508
+ for (const [i, t] of trackStates.entries()) {
8449
8509
  const [r, g, b] = t.config.color;
8450
8510
  const btn = document.createElement("button");
8451
8511
  btn.className = `dtm-pill ${t.config.id === activeTrackId ? "dtm-pill--active" : ""}`;
8452
8512
  btn.style.setProperty("--dtm-pill-color", `rgb(${r},${g},${b})`);
8453
- btn.innerHTML = `<span class="dtm-dot"></span><span>${t.config.name}</span>`;
8513
+ btn.title = t.config.name;
8514
+ btn.textContent = String(i + 1);
8454
8515
  btn.addEventListener("click", () => switchTrack(t.config.id));
8455
8516
  refs.trackTabs.appendChild(btn);
8456
8517
  }
8457
8518
  const active = getActive();
8458
8519
  refs.trackBody.innerHTML = `
8459
8520
  <div class="dtm-row">
8460
- <span class="dtm-label">velocity</span>
8521
+ <span class="dtm-label">\u30D9\u30ED\u30B7\u30C6\u30A3</span>
8461
8522
  <input type="range" class="dtm-range dtm-grow" data-dtm="track-vol" min="0" max="127" value="${active.volume}">
8462
8523
  <span class="dtm-label" data-dtm="track-vol-label">${active.volume}</span>
8463
8524
  </div>`;
@@ -8472,6 +8533,59 @@ var mountDAW = (target, options = {}) => {
8472
8533
  active.core.setVolume(active.volume);
8473
8534
  volLabel.textContent = String(active.volume);
8474
8535
  });
8536
+ const instRow = document.createElement("div");
8537
+ instRow.className = "dtm-row";
8538
+ instRow.innerHTML = `<span class="dtm-label">\u697D\u5668</span>`;
8539
+ const instSel = document.createElement("select");
8540
+ instSel.className = "dtm-select dtm-grow";
8541
+ const defaultOpt = document.createElement("option");
8542
+ defaultOpt.value = "";
8543
+ defaultOpt.textContent = "\u30C7\u30D5\u30A9\u30EB\u30C8\uFF08\u30D7\u30EA\u30BB\u30C3\u30C8\uFF09";
8544
+ instSel.appendChild(defaultOpt);
8545
+ const GM_GROUPS = [
8546
+ "\u30D4\u30A2\u30CE",
8547
+ "\u30AF\u30ED\u30DE\u30C6\u30A3\u30C3\u30AF\u30D1\u30FC\u30AB\u30C3\u30B7\u30E7\u30F3",
8548
+ "\u30AA\u30EB\u30AC\u30F3",
8549
+ "\u30AE\u30BF\u30FC",
8550
+ "\u30D9\u30FC\u30B9",
8551
+ "\u30B9\u30C8\u30EA\u30F3\u30B0\u30B9",
8552
+ "\u30A2\u30F3\u30B5\u30F3\u30D6\u30EB",
8553
+ "\u30D6\u30E9\u30B9",
8554
+ "\u30EA\u30FC\u30C9\uFF08\u6728\u7BA1\uFF09",
8555
+ "\u30D1\u30A4\u30D7",
8556
+ "\u30B7\u30F3\u30BB\u30EA\u30FC\u30C9",
8557
+ "\u30B7\u30F3\u30BB\u30D1\u30C3\u30C9",
8558
+ "\u30B7\u30F3\u30BB\u30A8\u30D5\u30A7\u30AF\u30C8",
8559
+ "\u30A8\u30B9\u30CB\u30C3\u30AF",
8560
+ "\u30D1\u30FC\u30AB\u30C3\u30B7\u30D6",
8561
+ "\u30B5\u30A6\u30F3\u30C9\u30A8\u30D5\u30A7\u30AF\u30C8"
8562
+ ];
8563
+ GM_GROUPS.forEach((groupName, gi) => {
8564
+ const grp = document.createElement("optgroup");
8565
+ grp.label = groupName;
8566
+ for (let j = 0; j < 8; j++) {
8567
+ const name = GM_INSTRUMENT_NAMES[gi * 8 + j];
8568
+ if (!name) break;
8569
+ const o = document.createElement("option");
8570
+ o.value = name;
8571
+ o.textContent = name;
8572
+ grp.appendChild(o);
8573
+ }
8574
+ instSel.appendChild(grp);
8575
+ });
8576
+ instSel.value = normalizeInstrumentName(active.trackInstrument);
8577
+ const syncInstDisabled = () => {
8578
+ instSel.disabled = !!active.lyricModel;
8579
+ 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" : "";
8580
+ };
8581
+ syncInstDisabled();
8582
+ instSel.addEventListener("change", () => {
8583
+ active.trackInstrument = instSel.value;
8584
+ const trackIndex = trackStates.indexOf(active);
8585
+ options.onTrackInstrumentChange?.(trackIndex, active.trackInstrument);
8586
+ });
8587
+ instRow.appendChild(instSel);
8588
+ refs.trackBody.appendChild(instRow);
8475
8589
  if (isAdvanced || active.config.id !== "chord") {
8476
8590
  const lyricDiv = document.createElement("div");
8477
8591
  lyricDiv.className = "dtm-row";
@@ -8479,7 +8593,7 @@ var mountDAW = (target, options = {}) => {
8479
8593
  lyricDiv.style.alignItems = "stretch";
8480
8594
  lyricDiv.innerHTML = `
8481
8595
  <div class="dtm-row">
8482
- <span class="dtm-label">\u266A \u6B4C\u8A5E</span>
8596
+ <span class="dtm-label">\u266A UTAU</span>
8483
8597
  <select class="dtm-select" data-dtm="lyric-model" aria-label="\u6B4C\u5531\u30E2\u30C7\u30EB"></select>
8484
8598
  <img class="dtm-lyric-icon dtm-hidden" data-dtm="lyric-icon" width="20" height="20" alt="" draggable="false">
8485
8599
  <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">
@@ -8553,7 +8667,7 @@ var mountDAW = (target, options = {}) => {
8553
8667
  o.textContent = label;
8554
8668
  lyricModelSel.appendChild(o);
8555
8669
  };
8556
- addOpt("", "\u306A\u3057");
8670
+ addOpt("", "\u30DC\u30FC\u30AB\u30EB\u306A\u3057");
8557
8671
  for (const m of LYRIC_MODELS) addOpt(m, lyricModelLabel(m));
8558
8672
  if (active.lyricModel && !LYRIC_MODELS.includes(active.lyricModel)) {
8559
8673
  addOpt(active.lyricModel, lyricModelLabel(active.lyricModel));
@@ -8602,21 +8716,27 @@ var mountDAW = (target, options = {}) => {
8602
8716
  lyricModelSel.addEventListener("change", () => {
8603
8717
  active.lyricModel = lyricModelSel.value;
8604
8718
  syncLyricVisibility();
8719
+ syncInstDisabled();
8720
+ fireLyricsChange(active);
8605
8721
  });
8606
8722
  lyricOctaveSel.addEventListener("change", () => {
8607
8723
  active.vocalOctave = Number.parseInt(lyricOctaveSel.value, 10);
8724
+ fireLyricsChange(active);
8608
8725
  });
8609
8726
  lyricInput.addEventListener("input", () => {
8610
8727
  active.lyrics = lyricInput.value;
8611
8728
  updateLyricCount();
8729
+ fireLyricsChange(active);
8612
8730
  });
8613
8731
  lyricVol.addEventListener("input", () => {
8614
8732
  active.vocalVolume = Number.parseInt(lyricVol.value, 10);
8615
8733
  lyricVolLabel.textContent = lyricVol.value;
8734
+ fireLyricsChange(active);
8616
8735
  });
8617
8736
  lyricPan.addEventListener("input", () => {
8618
8737
  active.vocalPan = Number.parseInt(lyricPan.value, 10);
8619
8738
  lyricPanLabel.textContent = fmtPan(active.vocalPan);
8739
+ fireLyricsChange(active);
8620
8740
  });
8621
8741
  lyricPanLabel.style.cursor = "pointer";
8622
8742
  lyricPanLabel.title = "\u30BF\u30C3\u30D7\u3067\u4E2D\u592E(C)\u3078";
@@ -8624,6 +8744,7 @@ var mountDAW = (target, options = {}) => {
8624
8744
  active.vocalPan = 64;
8625
8745
  lyricPan.value = "64";
8626
8746
  lyricPanLabel.textContent = fmtPan(64);
8747
+ fireLyricsChange(active);
8627
8748
  });
8628
8749
  }
8629
8750
  if (active.config.id === "chord" && showChord) {
@@ -8698,13 +8819,19 @@ var mountDAW = (target, options = {}) => {
8698
8819
  const barLimitBars = Number(refs.barLimitSelect.value);
8699
8820
  const limitSteps = barLimitBars > 0 ? barLimitBars * renderConfig.stepsPerBar : Infinity;
8700
8821
  const clipNotes = (notes) => limitSteps === Infinity ? notes : notes.filter((n) => n.startStep < limitSteps);
8822
+ const trackInstrumentsForMeta = {};
8823
+ trackStates.forEach((t, i) => {
8824
+ if (t.trackInstrument) trackInstrumentsForMeta[i] = t.trackInstrument;
8825
+ });
8826
+ const trackInstMeta = Object.keys(trackInstrumentsForMeta).length > 0 ? trackInstrumentsForMeta : void 0;
8701
8827
  const metaLineFull = formatMmlMeta(
8702
8828
  {
8703
8829
  instrument: currentInstrument || void 0,
8704
8830
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
8705
8831
  volume: masterVolume,
8706
8832
  drumVolume,
8707
- mode
8833
+ mode,
8834
+ trackInstruments: trackInstMeta
8708
8835
  },
8709
8836
  " "
8710
8837
  );
@@ -8714,7 +8841,8 @@ var mountDAW = (target, options = {}) => {
8714
8841
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
8715
8842
  volume: masterVolume,
8716
8843
  drumVolume,
8717
- mode
8844
+ mode,
8845
+ trackInstruments: trackInstMeta
8718
8846
  },
8719
8847
  ""
8720
8848
  );
@@ -8872,6 +9000,13 @@ var mountDAW = (target, options = {}) => {
8872
9000
  refs.drumVolume.value = String(meta.drumVolume);
8873
9001
  refs.drumVolumeLabel.textContent = `${meta.drumVolume}%`;
8874
9002
  }
9003
+ trackStates.forEach((t, i) => {
9004
+ const name = normalizeInstrumentName(meta.trackInstruments?.[i] ?? "");
9005
+ if (t.trackInstrument !== name) {
9006
+ t.trackInstrument = name;
9007
+ options.onTrackInstrumentChange?.(i, name);
9008
+ }
9009
+ });
8875
9010
  for (const t of trackStates) {
8876
9011
  t.lyrics = "";
8877
9012
  t.lyricModel = "";
@@ -9031,6 +9166,31 @@ var mountDAW = (target, options = {}) => {
9031
9166
  setLoading(false);
9032
9167
  }, 30);
9033
9168
  };
9169
+ const showConfirmModal = (message) => new Promise((resolve) => {
9170
+ const overlay = document.createElement("div");
9171
+ overlay.className = "dtm-modal-overlay";
9172
+ overlay.innerHTML = `
9173
+ <div class="dtm-modal">
9174
+ <div class="dtm-modal-header">
9175
+ <span class="dtm-modal-title">\u30E2\u30FC\u30C9\u306E\u78BA\u8A8D</span>
9176
+ </div>
9177
+ <div class="dtm-modal-body"><p>${message}</p></div>
9178
+ <div class="dtm-confirm-footer">
9179
+ <button class="dtm-btn dtm-btn--ghost dtm-confirm-no">\u3044\u3044\u3048\uFF08\u3053\u306E\u307E\u307E\u8AAD\u307F\u8FBC\u3080\uFF09</button>
9180
+ <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>
9181
+ </div>
9182
+ </div>`;
9183
+ const close = (result) => {
9184
+ overlay.remove();
9185
+ resolve(result);
9186
+ };
9187
+ overlay.querySelector(".dtm-confirm-yes").addEventListener("click", () => close(true));
9188
+ overlay.querySelector(".dtm-confirm-no").addEventListener("click", () => close(false));
9189
+ overlay.addEventListener("click", (e) => {
9190
+ if (e.target === overlay) close(false);
9191
+ });
9192
+ document.body.appendChild(overlay);
9193
+ });
9034
9194
  const wireEvents = () => {
9035
9195
  refs.playBtn.addEventListener("click", togglePlay);
9036
9196
  refs.playBtn.disabled = false;
@@ -9150,10 +9310,25 @@ var mountDAW = (target, options = {}) => {
9150
9310
  "click",
9151
9311
  () => copy(refs.outputMini.textContent ?? "", refs.copyMiniBtn)
9152
9312
  );
9153
- refs.mmlLoadBtn.addEventListener(
9154
- "click",
9155
- () => overlayDuring(() => loadMML(refs.mmlInput.value))
9156
- );
9313
+ refs.mmlLoadBtn.addEventListener("click", async () => {
9314
+ const mml = refs.mmlInput.value;
9315
+ if (!isAdvanced && options.onRequestAdvancedMode) {
9316
+ const { mergedTrackCount } = parseMML(mml, {
9317
+ stepsPerBar: renderConfig.stepsPerBar,
9318
+ clampTrackCount: trackStates.length
9319
+ });
9320
+ if (mergedTrackCount > 0) {
9321
+ const confirmed = await showConfirmModal(
9322
+ "\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"
9323
+ );
9324
+ if (confirmed) {
9325
+ options.onRequestAdvancedMode(mml);
9326
+ return;
9327
+ }
9328
+ }
9329
+ }
9330
+ overlayDuring(() => loadMML(mml));
9331
+ });
9157
9332
  let activeSamplePlayer = null;
9158
9333
  let activeSampleButton = null;
9159
9334
  const collapseActiveSample = () => {
@@ -9246,6 +9421,7 @@ var mountDAW = (target, options = {}) => {
9246
9421
  singingVoices: options.singingVoices,
9247
9422
  drumPatterns: options.drumPatterns,
9248
9423
  volume: masterVolume,
9424
+ _skipInfoModals: true,
9249
9425
  onStop: () => {
9250
9426
  if (activeSampleButton === htmlBtn) {
9251
9427
  htmlBtn.textContent = "\u25B6 \u8A66\u8074";
@@ -9327,7 +9503,7 @@ var mountDAW = (target, options = {}) => {
9327
9503
  refs.overlay.hidden = true;
9328
9504
  setLoading(false);
9329
9505
  });
9330
- refs.midiLoadBtn.addEventListener("click", () => {
9506
+ refs.midiLoadBtn.addEventListener("click", async () => {
9331
9507
  if (!pendingMidi) return;
9332
9508
  const selected = [];
9333
9509
  const btns = refs.midiTrackSelection.querySelectorAll("button");
@@ -9336,6 +9512,19 @@ var mountDAW = (target, options = {}) => {
9336
9512
  selected.push(detectedTracks[i].index);
9337
9513
  });
9338
9514
  if (selected.length === 0) return;
9515
+ if (!isAdvanced && options.onRequestAdvancedMode && selected.length > trackStates.length) {
9516
+ const confirmed = await showConfirmModal(
9517
+ "\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"
9518
+ );
9519
+ if (confirmed) {
9520
+ const midi = pendingMidi;
9521
+ const sel = selected.slice();
9522
+ options.onRequestAdvancedMode(void 0, (newDaw) => {
9523
+ newDaw.applyMidiParsed?.(midi, sel);
9524
+ });
9525
+ return;
9526
+ }
9527
+ }
9339
9528
  overlayDuring(() => applyMidiSelection(pendingMidi, selected));
9340
9529
  });
9341
9530
  };
@@ -9472,6 +9661,9 @@ var mountDAW = (target, options = {}) => {
9472
9661
  },
9473
9662
  loadMML,
9474
9663
  loadMIDI,
9664
+ applyMidiParsed: (midi, selectedIndices) => {
9665
+ overlayDuring(() => applyMidiSelection(midi, selectedIndices));
9666
+ },
9475
9667
  exportMIDI: exportMIDI2,
9476
9668
  setBpm,
9477
9669
  getPlaybackState: () => playbackState,
@@ -9506,6 +9698,37 @@ var mountDAW = (target, options = {}) => {
9506
9698
  if (audible) audioMutedTracks.delete(trackId);
9507
9699
  else audioMutedTracks.add(trackId);
9508
9700
  },
9701
+ applyLyrics: (trackId, data) => {
9702
+ const t = trackStates.find((s) => s.config.id === trackId);
9703
+ if (!t) return;
9704
+ t.lyrics = data.lyrics;
9705
+ t.lyricModel = data.model;
9706
+ t.vocalVolume = data.vocalVolume;
9707
+ t.vocalGate = data.vocalGate;
9708
+ t.vocalPan = data.vocalPan;
9709
+ t.vocalOctave = data.vocalOctave;
9710
+ },
9711
+ applyTrackInstrument: (trackIndex, instrumentName) => {
9712
+ const t = trackStates[trackIndex];
9713
+ if (!t) return;
9714
+ const name = normalizeInstrumentName(instrumentName);
9715
+ t.trackInstrument = name;
9716
+ if (t.config.id === activeTrackId) updateTrackPanel();
9717
+ },
9718
+ noteToCanvas: (step, pitch) => {
9719
+ const canvas = getGridCanvas();
9720
+ const x = step * renderConfig.stepWidth - currentOffsetX;
9721
+ const y = (renderConfig.keyCount - 1 - pitch) * renderConfig.keyHeight - currentOffsetY;
9722
+ const onScreen = x >= 0 && x <= canvas.width && y >= 0 && y <= canvas.height;
9723
+ let side = null;
9724
+ if (!onScreen) {
9725
+ if (x < 0) side = "left";
9726
+ else if (x > canvas.width) side = "right";
9727
+ else if (y < 0) side = "top";
9728
+ else side = "bottom";
9729
+ }
9730
+ return { x, y, onScreen, side };
9731
+ },
9509
9732
  destroy: () => {
9510
9733
  sequencer.stop();
9511
9734
  options.singingVoices?.stopStream();
@@ -10156,7 +10379,7 @@ var SoundFont = class _SoundFont {
10156
10379
  const { buffer, _param } = zone;
10157
10380
  if (!buffer || !_param) return;
10158
10381
  src.buffer = buffer;
10159
- g.gain.value = volume;
10382
+ g.gain.setValueAtTime(volume, _when);
10160
10383
  src.playbackRate.setValueAtTime(_param.playbackRate, 0);
10161
10384
  Object.assign(src, _param.src);
10162
10385
  const _duration = duration + _SoundFont.afterTime;
@@ -10208,6 +10431,12 @@ var adjustZone = async (ctx, zone) => {
10208
10431
  }
10209
10432
  } else if (zone.file) {
10210
10433
  const buf = Uint8Array.from(atob(zone.file), (c) => c.charCodeAt(0)).buffer;
10434
+ if (ctx.state !== "running") {
10435
+ try {
10436
+ await ctx.resume();
10437
+ } catch {
10438
+ }
10439
+ }
10211
10440
  zone.buffer = await ctx.decodeAudioData(buf);
10212
10441
  }
10213
10442
  for (const [k, v] of [
@@ -10425,6 +10654,14 @@ var createDtmStudio = async (options = {}) => {
10425
10654
  }
10426
10655
  })();
10427
10656
  let nameToKey = {};
10657
+ const resolveNameToKey = (name) => {
10658
+ if (nameToKey[name]) return nameToKey[name];
10659
+ const stripped = name.replace(/\s+/g, "").toLowerCase();
10660
+ const canonical = Object.keys(nameToKey).find(
10661
+ (k) => k.replace(/\s+/g, "").toLowerCase() === stripped
10662
+ );
10663
+ return canonical ? nameToKey[canonical] : void 0;
10664
+ };
10428
10665
  const soundFonts = /* @__PURE__ */ new Map();
10429
10666
  const loadingByKey = /* @__PURE__ */ new Map();
10430
10667
  const loadInstrument = (instrumentKey) => {
@@ -10510,7 +10747,7 @@ var createDtmStudio = async (options = {}) => {
10510
10747
  if (opts.label !== null) {
10511
10748
  const lab = doc.createElement("span");
10512
10749
  lab.className = "dtm-controlbar-label";
10513
- lab.textContent = opts.label ?? "INSTRUMENT";
10750
+ lab.textContent = opts.label ?? "\u697D\u5668\u30D7\u30EA\u30BB\u30C3\u30C8";
10514
10751
  wrapper.appendChild(lab);
10515
10752
  }
10516
10753
  const select = doc.createElement("select");
@@ -10575,7 +10812,13 @@ var createDtmStudio = async (options = {}) => {
10575
10812
  const mountedPlayers = [];
10576
10813
  const mountedModeSwitches = [];
10577
10814
  const mountEditor = (target, opts = {}) => {
10578
- const { preset, presetUI, onInstrumentChange, ...dawOverrides } = opts;
10815
+ const {
10816
+ preset,
10817
+ presetUI,
10818
+ onInstrumentChange,
10819
+ onTrackInstrumentChange: externalOnTrackInstrumentChange,
10820
+ ...dawOverrides
10821
+ } = opts;
10579
10822
  const tracks = dawOverrides.tracks ?? TRACKS_SIMPLE;
10580
10823
  const trackIds = tracks.map((t) => t.id);
10581
10824
  const presetKey = preset && INSTRUMENT_PRESETS[preset] ? preset : defaultPreset;
@@ -10583,14 +10826,29 @@ var createDtmStudio = async (options = {}) => {
10583
10826
  const initialPreset = meta.instrument && INSTRUMENT_PRESETS[meta.instrument] ? meta.instrument : presetKey;
10584
10827
  let editorPreset = initialPreset;
10585
10828
  const isAdvancedMode = dawOverrides.mode === "advanced";
10829
+ const trackInstOverrides = /* @__PURE__ */ new Map();
10830
+ if (meta.trackInstruments) {
10831
+ for (const [idxStr, name] of Object.entries(meta.trackInstruments)) {
10832
+ const idx = Number(idxStr);
10833
+ const key = resolveNameToKey(name);
10834
+ if (key) trackInstOverrides.set(idx, key);
10835
+ }
10836
+ }
10586
10837
  const playNote = (e) => {
10587
- const sf2 = resolveSoundFont(
10588
- editorPreset,
10589
- e.trackId,
10590
- isAdvancedMode ? "advanced" : "simple"
10591
- );
10592
- if (!sf2) return;
10593
- sf2.play({
10838
+ const trackIdx = tracks.findIndex((t) => t.id === e.trackId);
10839
+ const overrideKey = trackIdx >= 0 ? trackInstOverrides.get(trackIdx) : void 0;
10840
+ let sfInst;
10841
+ if (overrideKey) {
10842
+ sfInst = soundFonts.get(overrideKey);
10843
+ } else {
10844
+ sfInst = resolveSoundFont(
10845
+ editorPreset,
10846
+ e.trackId,
10847
+ isAdvancedMode ? "advanced" : "simple"
10848
+ );
10849
+ }
10850
+ if (!sfInst) return;
10851
+ sfInst.play({
10594
10852
  ctx: audioCtx,
10595
10853
  destination: masterGain,
10596
10854
  pitch: e.pitch,
@@ -10607,6 +10865,17 @@ var createDtmStudio = async (options = {}) => {
10607
10865
  }
10608
10866
  onInstrumentChange?.(key);
10609
10867
  };
10868
+ const handleTrackInstrumentChange = async (trackIndex, instrumentName) => {
10869
+ if (!instrumentName) {
10870
+ trackInstOverrides.delete(trackIndex);
10871
+ return;
10872
+ }
10873
+ await listReady;
10874
+ const key = resolveNameToKey(instrumentName);
10875
+ if (!key) return;
10876
+ trackInstOverrides.set(trackIndex, key);
10877
+ await loadInstrument(key);
10878
+ };
10610
10879
  const base = {
10611
10880
  getAudioTime: () => audioCtx.currentTime,
10612
10881
  onResumeAudio: resumeAudio,
@@ -10615,6 +10884,10 @@ var createDtmStudio = async (options = {}) => {
10615
10884
  singingVoices,
10616
10885
  parseMidi,
10617
10886
  onInstrumentChange: handleInstrumentChange,
10887
+ onTrackInstrumentChange: (idx, name) => {
10888
+ void handleTrackInstrumentChange(idx, name);
10889
+ externalOnTrackInstrumentChange?.(idx, name);
10890
+ },
10618
10891
  ...dawOverrides
10619
10892
  };
10620
10893
  const daw = mountDAW(target, base);
@@ -10662,6 +10935,10 @@ var createDtmStudio = async (options = {}) => {
10662
10935
  presetSelect.setValue(name);
10663
10936
  }
10664
10937
  },
10938
+ applyTrackInstrument: (trackIndex, instrumentName) => {
10939
+ daw.applyTrackInstrument(trackIndex, instrumentName);
10940
+ void handleTrackInstrumentChange(trackIndex, instrumentName);
10941
+ },
10665
10942
  destroy
10666
10943
  };
10667
10944
  };
@@ -10669,8 +10946,8 @@ var createDtmStudio = async (options = {}) => {
10669
10946
  const doc = target.ownerDocument;
10670
10947
  const tracksFor = opts.tracksFor ?? ((m) => m === "advanced" ? TRACKS_ADVANCED : TRACKS_SIMPLE);
10671
10948
  const labels = {
10672
- simple: opts.labels?.simple ?? "\u30B7\u30F3\u30D7\u30EB",
10673
- advanced: opts.labels?.advanced ?? "\u30A2\u30C9\u30D0\u30F3\u30B9"
10949
+ simple: opts.labels?.simple ?? "\u521D\u5FC3\u8005",
10950
+ advanced: opts.labels?.advanced ?? "\u4E0A\u7D1A\u8005"
10674
10951
  };
10675
10952
  const editorOptionsFor = (mode) => typeof opts.editorOptions === "function" ? opts.editorOptions(mode) : opts.editorOptions ?? {};
10676
10953
  let currentMode = opts.mode ?? "simple";
@@ -10680,7 +10957,7 @@ var createDtmStudio = async (options = {}) => {
10680
10957
  if (opts.label !== null) {
10681
10958
  const lab = doc.createElement("span");
10682
10959
  lab.className = "dtm-controlbar-label";
10683
- lab.textContent = opts.label ?? "MODE";
10960
+ lab.textContent = opts.label ?? "\u30E2\u30FC\u30C9";
10684
10961
  wrapper.appendChild(lab);
10685
10962
  }
10686
10963
  const seg = doc.createElement("div");
@@ -10711,7 +10988,15 @@ var createDtmStudio = async (options = {}) => {
10711
10988
  ...editorOpts,
10712
10989
  mode,
10713
10990
  tracks: tracksFor(mode),
10714
- initialMML: mml ?? editorOpts.initialMML
10991
+ initialMML: mml ?? editorOpts.initialMML,
10992
+ onRequestAdvancedMode: (pendingMml, applyMidi) => {
10993
+ doUnmount();
10994
+ currentMode = "advanced";
10995
+ updateButtons();
10996
+ opts.onChange?.("advanced");
10997
+ doMount("advanced", pendingMml);
10998
+ if (applyMidi && daw) applyMidi(daw);
10999
+ }
10715
11000
  });
10716
11001
  attachWrapper();
10717
11002
  opts.onMount?.(daw, mode);
@@ -10761,24 +11046,44 @@ var createDtmStudio = async (options = {}) => {
10761
11046
  (idx) => getRoleForTrackIndex(idx, isAdvancedMode ? "advanced" : "simple")
10762
11047
  );
10763
11048
  const loadTrackIds = trackIds.length > 0 ? trackIds : [...TRACK_ROLES];
10764
- void loadPreset(
10765
- playerPreset,
10766
- loadTrackIds,
10767
- isAdvancedMode ? "advanced" : "simple"
10768
- );
10769
- const playPlayerNote = (e) => {
10770
- const idx = Number(e.trackId);
10771
- const role = getRoleForTrackIndex(
10772
- idx,
10773
- isAdvancedMode ? "advanced" : "simple"
10774
- );
10775
- const sf2 = resolveSoundFont(
11049
+ const playerTrackInstKeys = /* @__PURE__ */ new Map();
11050
+ const loadPlayerTrackInstruments = async () => {
11051
+ if (!meta.trackInstruments) return;
11052
+ await listReady;
11053
+ for (const [idxStr, name] of Object.entries(meta.trackInstruments)) {
11054
+ const key = resolveNameToKey(name);
11055
+ if (!key) continue;
11056
+ playerTrackInstKeys.set(Number(idxStr), key);
11057
+ await loadInstrument(key);
11058
+ }
11059
+ };
11060
+ void Promise.all([
11061
+ loadPreset(
10776
11062
  playerPreset,
10777
- role,
11063
+ loadTrackIds,
10778
11064
  isAdvancedMode ? "advanced" : "simple"
10779
- );
10780
- if (!sf2) return;
10781
- sf2.play({
11065
+ ),
11066
+ loadPlayerTrackInstruments()
11067
+ ]);
11068
+ const playPlayerNote = (e) => {
11069
+ const idx = Number(e.trackId);
11070
+ const overrideKey = playerTrackInstKeys.get(idx);
11071
+ let sfInst;
11072
+ if (overrideKey) {
11073
+ sfInst = soundFonts.get(overrideKey);
11074
+ } else {
11075
+ const role = getRoleForTrackIndex(
11076
+ idx,
11077
+ isAdvancedMode ? "advanced" : "simple"
11078
+ );
11079
+ sfInst = resolveSoundFont(
11080
+ playerPreset,
11081
+ role,
11082
+ isAdvancedMode ? "advanced" : "simple"
11083
+ );
11084
+ }
11085
+ if (!sfInst) return;
11086
+ sfInst.play({
10782
11087
  ctx: audioCtx,
10783
11088
  destination: masterGain,
10784
11089
  pitch: e.pitch,
@@ -10837,6 +11142,7 @@ var createDtmStudio = async (options = {}) => {
10837
11142
  DRUM_FONT,
10838
11143
  DRUM_KEYS,
10839
11144
  DRUM_PATTERNS,
11145
+ GM_INSTRUMENT_NAMES,
10840
11146
  INSTRUMENT_PRESETS,
10841
11147
  KOE_BASE_URL,
10842
11148
  KOE_VOICEBANKS,