@onjmin/dtm 0.1.36 → 0.1.37

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>
@@ -4395,6 +4398,7 @@ var PITCH_MAP2 = {
4395
4398
  };
4396
4399
  var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
4397
4400
  var META_DIRECTIVE = /#(inst|drum|volume|drumvolume|mode)=([\w-]+)/gi;
4401
+ var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
4398
4402
  var parseMmlMeta = (mml) => {
4399
4403
  const meta = {};
4400
4404
  for (const m of mml.matchAll(META_DIRECTIVE)) {
@@ -4413,9 +4417,17 @@ var parseMmlMeta = (mml) => {
4413
4417
  }
4414
4418
  }
4415
4419
  }
4420
+ for (const m of mml.matchAll(TRACK_INST_DIRECTIVE)) {
4421
+ const idx = Number.parseInt(m[1], 10);
4422
+ const name = m[2].trim();
4423
+ if (!Number.isNaN(idx) && name) {
4424
+ meta.trackInstruments ??= {};
4425
+ meta.trackInstruments[idx] = name;
4426
+ }
4427
+ }
4416
4428
  return meta;
4417
4429
  };
4418
- var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "");
4430
+ var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "").replace(TRACK_INST_DIRECTIVE, "");
4419
4431
  var formatMmlMeta = (meta, space = "") => {
4420
4432
  const parts = [];
4421
4433
  if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
@@ -4424,6 +4436,11 @@ var formatMmlMeta = (meta, space = "") => {
4424
4436
  if (meta.drumVolume !== void 0)
4425
4437
  parts.push(`#drumvolume=${meta.drumVolume}`);
4426
4438
  if (meta.mode) parts.push(`#mode=${meta.mode}`);
4439
+ if (meta.trackInstruments) {
4440
+ for (const [idx, name] of Object.entries(meta.trackInstruments)) {
4441
+ if (name) parts.push(`#t${idx}inst=${name}`);
4442
+ }
4443
+ }
4427
4444
  return parts.join(space);
4428
4445
  };
4429
4446
  var parseMML = (mml, options = {}) => {
@@ -5045,7 +5062,7 @@ var DAW_CSS = `
5045
5062
  top: 0;
5046
5063
  z-index: 20;
5047
5064
  display: flex;
5048
- flex-wrap: nowrap;
5065
+ flex-wrap: wrap;
5049
5066
  align-items: center;
5050
5067
  gap: var(--dtm-gap);
5051
5068
  padding: 6px;
@@ -5055,18 +5072,19 @@ var DAW_CSS = `
5055
5072
  inset 0 0 0 2px var(--c-black),
5056
5073
  0 0 0 2px var(--dtm-success),
5057
5074
  4px 4px 0 var(--c-black);
5075
+ }
5076
+ .dtm-topbar-row1 {
5077
+ display: flex;
5078
+ align-items: center;
5079
+ gap: var(--dtm-gap);
5080
+ flex-basis: 100%;
5081
+ min-width: 0;
5058
5082
  overflow-x: auto;
5059
5083
  scrollbar-width: none;
5060
5084
  }
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
- }
5085
+ .dtm-topbar-row1::-webkit-scrollbar { display: none; }
5086
+ .dtm-topbar-row1 > * { flex-shrink: 0; }
5087
+ .dtm-topbar-row1 > .dtm-grow { flex-shrink: 1; }
5070
5088
 
5071
5089
  /* PLAY\u30DC\u30BF\u30F3 \u2014 \u30B2\u30FC\u30E0\u306E\u300C\u6C7A\u5B9A\u30DC\u30BF\u30F3\u300D\u7684\u5B58\u5728\u611F */
5072
5090
  .dtm-play {
@@ -5256,46 +5274,39 @@ var DAW_CSS = `
5256
5274
  .dtm-modebtn--active { background: var(--dtm-primary); color: var(--dtm-pfg); }
5257
5275
  .dtm-modebtn:not(.dtm-modebtn--active):active { background: var(--dtm-border2); }
5258
5276
 
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 */
5277
+ /* \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
5278
  .dtm-tracks {
5279
+ flex-basis: 100%;
5261
5280
  display: flex;
5262
- flex-wrap: wrap;
5263
- gap: var(--dtm-gap);
5281
+ flex-wrap: nowrap;
5282
+ gap: 3px;
5264
5283
  }
5265
5284
  .dtm-pill {
5266
5285
  --dtm-pill-color: var(--dtm-primary);
5267
5286
  display: inline-flex;
5268
5287
  align-items: center;
5269
- gap: 8px;
5270
- flex: 1 1 auto;
5271
5288
  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);
5289
+ flex: 1 1 0;
5290
+ min-width: 0;
5291
+ height: 26px;
5292
+ padding: 0;
5293
+ border: 2px solid var(--c-black);
5294
+ background: color-mix(in srgb, var(--dtm-pill-color) 40%, black);
5295
+ color: var(--c-white);
5277
5296
  font-family: var(--dtm-font);
5278
- font-size: 13px;
5279
- text-transform: uppercase;
5280
- letter-spacing: .1em;
5297
+ font-size: 11px;
5298
+ font-weight: bold;
5281
5299
  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);
5300
+ box-shadow: 2px 2px 0 var(--c-black);
5301
+ opacity: 0.7;
5289
5302
  }
5290
- /* \u30A2\u30AF\u30C6\u30A3\u30D6\u9078\u629E = \u91D1\u8272\u30CF\u30A4\u30E9\u30A4\u30C8 + \u30AB\u30FC\u30BD\u30EB */
5303
+ /* \u30A2\u30AF\u30C6\u30A3\u30D6\u9078\u629E = \u4E0D\u900F\u660E + \u91D1\u67A0 */
5291
5304
  .dtm-pill--active {
5305
+ opacity: 1;
5292
5306
  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);
5307
+ box-shadow: 0 0 0 1px var(--dtm-gold), 2px 2px 0 var(--c-black);
5296
5308
  }
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; }
5309
+ .dtm-pill:not(.dtm-pill--active):active { transform: translate(2px,2px); box-shadow: none; }
5299
5310
 
5300
5311
  /* \u2500\u2500\u2500 \u30D4\u30A2\u30CE\u30ED\u30FC\u30EB\uFF08\u30C8\u30E9\u30C3\u30AB\u30FC\u98A8\uFF09 \u2500\u2500\u2500 */
5301
5312
  .dtm-roll-wrap { display: flex; gap: var(--dtm-gap); }
@@ -5439,7 +5450,7 @@ var DAW_CSS = `
5439
5450
  }
5440
5451
  .dtm-overlay[hidden] { display: none; }
5441
5452
  .dtm-overlay::before {
5442
- content: 'NOW LOADING';
5453
+ content: '\u30ED\u30FC\u30C9\u4E2D';
5443
5454
  font-family: var(--dtm-font);
5444
5455
  font-size: 13px;
5445
5456
  color: var(--dtm-primary);
@@ -5632,6 +5643,15 @@ var DAW_CSS = `
5632
5643
  justify-content: center;
5633
5644
  }
5634
5645
 
5646
+ .dtm-confirm-footer {
5647
+ padding: 8px 12px;
5648
+ border-top: 2px solid var(--c-black);
5649
+ background: var(--dtm-deep);
5650
+ display: flex;
5651
+ gap: 8px;
5652
+ justify-content: flex-end;
5653
+ }
5654
+
5635
5655
  .dtm-modal {
5636
5656
  max-width: 500px;
5637
5657
  width: 100%;
@@ -6654,9 +6674,11 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6654
6674
  const mmlInfoItem = makeMenuItem("MML\u66F8\u5F0F\u3068\u306F");
6655
6675
  const embedItem = makeMenuItem("\u57CB\u3081\u8FBC\u3080");
6656
6676
  const copyMmlItem = makeMenuItem("MML\u30B3\u30D4\u30FC");
6657
- menuDropdown.appendChild(showMmlItem);
6658
- menuDropdown.appendChild(mmlInfoItem);
6659
- menuDropdown.appendChild(embedItem);
6677
+ if (!options._skipInfoModals) {
6678
+ menuDropdown.appendChild(showMmlItem);
6679
+ menuDropdown.appendChild(mmlInfoItem);
6680
+ menuDropdown.appendChild(embedItem);
6681
+ }
6660
6682
  menuDropdown.appendChild(copyMmlItem);
6661
6683
  menuContainer.appendChild(menuDropdown);
6662
6684
  mmlHeader.appendChild(menuContainer);
@@ -6810,6 +6832,8 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6810
6832
  volume: trackVolume,
6811
6833
  // 解説モーダル内の試聴サンプルは規約同意を要求しない。
6812
6834
  skipConsent: true,
6835
+ // 再帰的なモーダル生成を防ぐ。
6836
+ _skipInfoModals: true,
6813
6837
  onStop: () => {
6814
6838
  if (activeSampleBtn === el) resetSampleBtn(el);
6815
6839
  }
@@ -6819,65 +6843,67 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6819
6843
  });
6820
6844
  }
6821
6845
  };
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();
6846
+ if (!options._skipInfoModals) {
6847
+ showMmlItem.addEventListener("click", (e) => {
6848
+ e.stopPropagation();
6849
+ toggleMenu(false);
6850
+ const modalBody = openInfoModal("MML\u3092\u8868\u793A");
6868
6851
  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";
6852
+ 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";
6853
+ desc.style.marginBottom = "8px";
6854
+ modalBody.appendChild(desc);
6855
+ const sourceMml = options.getMml?.() ?? mml;
6856
+ const displayMml = sourceMml.split(";").map((s) => s.trim()).filter((s) => s.length > 0).join(";\n");
6870
6857
  const pre = doc.createElement("pre");
6871
- pre.textContent = snippet;
6858
+ pre.textContent = displayMml;
6872
6859
  pre.style.whiteSpace = "pre-wrap";
6873
6860
  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
- });
6861
+ pre.style.cursor = "text";
6862
+ pre.addEventListener("click", () => {
6863
+ const range = doc.createRange();
6864
+ range.selectNodeContents(pre);
6865
+ const sel = doc.defaultView?.getSelection();
6866
+ sel?.removeAllRanges();
6867
+ sel?.addRange(range);
6868
+ });
6869
+ modalBody.appendChild(pre);
6870
+ appendCopyButton(modalBody, sourceMml);
6871
+ });
6872
+ mmlInfoItem.addEventListener("click", (e) => {
6873
+ e.stopPropagation();
6874
+ toggleMenu(false);
6875
+ const modalBody = openInfoModal("MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC");
6876
+ modalBody.innerHTML = MML_INFO_HTML;
6877
+ wireSampleButtons(modalBody);
6878
+ });
6879
+ embedItem.addEventListener("click", async (e) => {
6880
+ e.stopPropagation();
6881
+ toggleMenu(false);
6882
+ const modalBody = openInfoModal("\u57CB\u3081\u8FBC\u307F");
6883
+ const loading = doc.createElement("p");
6884
+ loading.textContent = "\u751F\u6210\u4E2D...";
6885
+ modalBody.appendChild(loading);
6886
+ try {
6887
+ const embedBase = options.embedUrl ?? "https://onjmin.github.io/dtm/demo/embed.html";
6888
+ const payload = await encodeMml(mml);
6889
+ const url2 = `${embedBase}#${payload}`;
6890
+ const snippet = `<iframe src="${url2}" width="100%" height="260" frameborder="0" loading="lazy" title="@onjmin/dtm player"></iframe>`;
6891
+ if (!modalBody.isConnected) return;
6892
+ loading.remove();
6893
+ const desc = doc.createElement("p");
6894
+ 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";
6895
+ const pre = doc.createElement("pre");
6896
+ pre.textContent = snippet;
6897
+ pre.style.whiteSpace = "pre-wrap";
6898
+ pre.style.wordBreak = "break-all";
6899
+ modalBody.append(desc, pre);
6900
+ appendCopyButton(modalBody, snippet);
6901
+ } catch (err2) {
6902
+ console.error("[dtm] failed to generate embed snippet", err2);
6903
+ if (modalBody.isConnected) loading.textContent = "\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
6904
+ }
6905
+ });
6906
+ }
6881
6907
  copyMmlItem.addEventListener("click", async (e) => {
6882
6908
  e.stopPropagation();
6883
6909
  const success = await copyToClipboard(doc, options.getMml?.() ?? mml);
@@ -7474,8 +7500,8 @@ var MIDI_INFO_HTML = `
7474
7500
 
7475
7501
  <h4>3. \u30E2\u30FC\u30C9\u306B\u3088\u308B\u53D6\u308A\u8FBC\u307F\u65B9\u306E\u9055\u3044</h4>
7476
7502
  <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>
7503
+ <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>
7504
+ <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
7505
  </ul>
7480
7506
 
7481
7507
  <h4>4. MIDI\u30D5\u30A1\u30A4\u30EB\u3092\u624B\u306B\u5165\u308C\u308B</h4>
@@ -7527,105 +7553,105 @@ var TRACKS_SIMPLE = [
7527
7553
  var TRACKS_ADVANCED = [
7528
7554
  {
7529
7555
  id: "t0",
7530
- name: "TRACK 01",
7556
+ name: "\u30C8\u30E9\u30C3\u30AF1",
7531
7557
  color: [41, 173, 255],
7532
7558
  instrument: 0,
7533
7559
  volume: 100
7534
7560
  },
7535
7561
  {
7536
7562
  id: "t1",
7537
- name: "TRACK 02",
7563
+ name: "\u30C8\u30E9\u30C3\u30AF2",
7538
7564
  color: [0, 228, 54],
7539
7565
  instrument: 1,
7540
7566
  volume: 100
7541
7567
  },
7542
7568
  {
7543
7569
  id: "t2",
7544
- name: "TRACK 03",
7570
+ name: "\u30C8\u30E9\u30C3\u30AF3",
7545
7571
  color: [255, 119, 168],
7546
7572
  instrument: 2,
7547
7573
  volume: 100
7548
7574
  },
7549
7575
  {
7550
7576
  id: "t3",
7551
- name: "TRACK 04",
7577
+ name: "\u30C8\u30E9\u30C3\u30AF4",
7552
7578
  color: [255, 163, 0],
7553
7579
  instrument: 3,
7554
7580
  volume: 100
7555
7581
  },
7556
7582
  {
7557
7583
  id: "t4",
7558
- name: "TRACK 05",
7584
+ name: "\u30C8\u30E9\u30C3\u30AF5",
7559
7585
  color: [255, 236, 39],
7560
7586
  instrument: 4,
7561
7587
  volume: 100
7562
7588
  },
7563
7589
  {
7564
7590
  id: "t5",
7565
- name: "TRACK 06",
7591
+ name: "\u30C8\u30E9\u30C3\u30AF6",
7566
7592
  color: [131, 118, 156],
7567
7593
  instrument: 5,
7568
7594
  volume: 100
7569
7595
  },
7570
7596
  {
7571
7597
  id: "t6",
7572
- name: "TRACK 07",
7598
+ name: "\u30C8\u30E9\u30C3\u30AF7",
7573
7599
  color: [255, 0, 77],
7574
7600
  instrument: 6,
7575
7601
  volume: 100
7576
7602
  },
7577
7603
  {
7578
7604
  id: "t7",
7579
- name: "TRACK 08",
7605
+ name: "\u30C8\u30E9\u30C3\u30AF8",
7580
7606
  color: [255, 204, 170],
7581
7607
  instrument: 7,
7582
7608
  volume: 100
7583
7609
  },
7584
7610
  {
7585
7611
  id: "t8",
7586
- name: "TRACK 09",
7612
+ name: "\u30C8\u30E9\u30C3\u30AF9",
7587
7613
  color: [194, 195, 199],
7588
7614
  instrument: 8,
7589
7615
  volume: 100
7590
7616
  },
7591
7617
  {
7592
7618
  id: "t9",
7593
- name: "TRACK 10",
7619
+ name: "\u30C8\u30E9\u30C3\u30AF10",
7594
7620
  color: [0, 135, 81],
7595
7621
  instrument: 9,
7596
7622
  volume: 100
7597
7623
  },
7598
7624
  {
7599
7625
  id: "t10",
7600
- name: "TRACK 11",
7626
+ name: "\u30C8\u30E9\u30C3\u30AF11",
7601
7627
  color: [171, 82, 54],
7602
7628
  instrument: 10,
7603
7629
  volume: 100
7604
7630
  },
7605
7631
  {
7606
7632
  id: "t11",
7607
- name: "TRACK 12",
7633
+ name: "\u30C8\u30E9\u30C3\u30AF12",
7608
7634
  color: [126, 37, 83],
7609
7635
  instrument: 11,
7610
7636
  volume: 100
7611
7637
  },
7612
7638
  {
7613
7639
  id: "t12",
7614
- name: "TRACK 13",
7640
+ name: "\u30C8\u30E9\u30C3\u30AF13",
7615
7641
  color: [255, 241, 232],
7616
7642
  instrument: 12,
7617
7643
  volume: 100
7618
7644
  },
7619
7645
  {
7620
7646
  id: "t13",
7621
- name: "TRACK 14",
7647
+ name: "\u30C8\u30E9\u30C3\u30AF14",
7622
7648
  color: [120, 200, 255],
7623
7649
  instrument: 13,
7624
7650
  volume: 100
7625
7651
  },
7626
7652
  {
7627
7653
  id: "t14",
7628
- name: "TRACK 15",
7654
+ name: "\u30C8\u30E9\u30C3\u30AF15",
7629
7655
  color: [100, 255, 160],
7630
7656
  instrument: 14,
7631
7657
  volume: 100
@@ -7639,6 +7665,13 @@ var LYRIC_MODEL_LABELS2 = {
7639
7665
  };
7640
7666
  var lyricModelLabel = (model) => LYRIC_MODEL_LABELS2[model] ?? model;
7641
7667
  var clamp3 = (v, min, max) => Math.min(Math.max(v, min), max);
7668
+ var normalizeInstrumentName = (name) => {
7669
+ if (!name) return "";
7670
+ const stripped = name.replace(/\s+/g, "").toLowerCase();
7671
+ return GM_INSTRUMENT_NAMES.find(
7672
+ (n) => n.replace(/\s+/g, "").toLowerCase() === stripped
7673
+ ) ?? name;
7674
+ };
7642
7675
  var mountDAW = (target, options = {}) => {
7643
7676
  injectStyles();
7644
7677
  const getAudioTime = options.getAudioTime ?? (() => performance.now() / 1e3);
@@ -7678,7 +7711,8 @@ var mountDAW = (target, options = {}) => {
7678
7711
  let snapGridSteps = 12;
7679
7712
  const gridLineSteps = 48;
7680
7713
  let currentOffsetX = 0;
7681
- let currentOffsetY = (104 - 1 - 60) * renderConfig.keyHeight - 215;
7714
+ const _initPitch = options.initialScrollPitch;
7715
+ let currentOffsetY = _initPitch !== void 0 ? (renderConfig.keyCount - 1 - _initPitch) * renderConfig.keyHeight - 215 : (104 - 1 - 60) * renderConfig.keyHeight - 215;
7682
7716
  let playStartStep = 0;
7683
7717
  let isSolo = false;
7684
7718
  let lyricTrackIndices = /* @__PURE__ */ new Set();
@@ -7691,6 +7725,24 @@ var mountDAW = (target, options = {}) => {
7691
7725
  let copiedNotes = [];
7692
7726
  let trackStates = [];
7693
7727
  let suppressPatch = false;
7728
+ let lyricsDebounceTimer = null;
7729
+ const fireLyricsChange = (t) => {
7730
+ if (!options.onLyricsChange) return;
7731
+ const trackId = t.config.id;
7732
+ const data = {
7733
+ lyrics: t.lyrics,
7734
+ model: t.lyricModel,
7735
+ vocalVolume: t.vocalVolume,
7736
+ vocalGate: t.vocalGate,
7737
+ vocalPan: t.vocalPan,
7738
+ vocalOctave: t.vocalOctave
7739
+ };
7740
+ if (lyricsDebounceTimer) clearTimeout(lyricsDebounceTimer);
7741
+ lyricsDebounceTimer = setTimeout(() => {
7742
+ options.onLyricsChange(trackId, data);
7743
+ lyricsDebounceTimer = null;
7744
+ }, 300);
7745
+ };
7694
7746
  const hiddenTracks = /* @__PURE__ */ new Set();
7695
7747
  const audioMutedTracks = /* @__PURE__ */ new Set();
7696
7748
  const createTrackStates = () => {
@@ -7739,7 +7791,8 @@ var mountDAW = (target, options = {}) => {
7739
7791
  vocalVolume: DEFAULT_VOCAL_VOLUME,
7740
7792
  vocalGate: 100,
7741
7793
  vocalPan: 64,
7742
- vocalOctave: 0
7794
+ vocalOctave: 0,
7795
+ trackInstrument: ""
7743
7796
  };
7744
7797
  });
7745
7798
  };
@@ -8445,19 +8498,20 @@ var mountDAW = (target, options = {}) => {
8445
8498
  };
8446
8499
  const updateTrackPanel = () => {
8447
8500
  refs.trackTabs.innerHTML = "";
8448
- for (const t of trackStates) {
8501
+ for (const [i, t] of trackStates.entries()) {
8449
8502
  const [r, g, b] = t.config.color;
8450
8503
  const btn = document.createElement("button");
8451
8504
  btn.className = `dtm-pill ${t.config.id === activeTrackId ? "dtm-pill--active" : ""}`;
8452
8505
  btn.style.setProperty("--dtm-pill-color", `rgb(${r},${g},${b})`);
8453
- btn.innerHTML = `<span class="dtm-dot"></span><span>${t.config.name}</span>`;
8506
+ btn.title = t.config.name;
8507
+ btn.textContent = String(i + 1);
8454
8508
  btn.addEventListener("click", () => switchTrack(t.config.id));
8455
8509
  refs.trackTabs.appendChild(btn);
8456
8510
  }
8457
8511
  const active = getActive();
8458
8512
  refs.trackBody.innerHTML = `
8459
8513
  <div class="dtm-row">
8460
- <span class="dtm-label">velocity</span>
8514
+ <span class="dtm-label">\u30D9\u30ED\u30B7\u30C6\u30A3</span>
8461
8515
  <input type="range" class="dtm-range dtm-grow" data-dtm="track-vol" min="0" max="127" value="${active.volume}">
8462
8516
  <span class="dtm-label" data-dtm="track-vol-label">${active.volume}</span>
8463
8517
  </div>`;
@@ -8472,6 +8526,59 @@ var mountDAW = (target, options = {}) => {
8472
8526
  active.core.setVolume(active.volume);
8473
8527
  volLabel.textContent = String(active.volume);
8474
8528
  });
8529
+ const instRow = document.createElement("div");
8530
+ instRow.className = "dtm-row";
8531
+ instRow.innerHTML = `<span class="dtm-label">\u697D\u5668</span>`;
8532
+ const instSel = document.createElement("select");
8533
+ instSel.className = "dtm-select dtm-grow";
8534
+ const defaultOpt = document.createElement("option");
8535
+ defaultOpt.value = "";
8536
+ defaultOpt.textContent = "\u30C7\u30D5\u30A9\u30EB\u30C8\uFF08\u30D7\u30EA\u30BB\u30C3\u30C8\uFF09";
8537
+ instSel.appendChild(defaultOpt);
8538
+ const GM_GROUPS = [
8539
+ "\u30D4\u30A2\u30CE",
8540
+ "\u30AF\u30ED\u30DE\u30C6\u30A3\u30C3\u30AF\u30D1\u30FC\u30AB\u30C3\u30B7\u30E7\u30F3",
8541
+ "\u30AA\u30EB\u30AC\u30F3",
8542
+ "\u30AE\u30BF\u30FC",
8543
+ "\u30D9\u30FC\u30B9",
8544
+ "\u30B9\u30C8\u30EA\u30F3\u30B0\u30B9",
8545
+ "\u30A2\u30F3\u30B5\u30F3\u30D6\u30EB",
8546
+ "\u30D6\u30E9\u30B9",
8547
+ "\u30EA\u30FC\u30C9\uFF08\u6728\u7BA1\uFF09",
8548
+ "\u30D1\u30A4\u30D7",
8549
+ "\u30B7\u30F3\u30BB\u30EA\u30FC\u30C9",
8550
+ "\u30B7\u30F3\u30BB\u30D1\u30C3\u30C9",
8551
+ "\u30B7\u30F3\u30BB\u30A8\u30D5\u30A7\u30AF\u30C8",
8552
+ "\u30A8\u30B9\u30CB\u30C3\u30AF",
8553
+ "\u30D1\u30FC\u30AB\u30C3\u30B7\u30D6",
8554
+ "\u30B5\u30A6\u30F3\u30C9\u30A8\u30D5\u30A7\u30AF\u30C8"
8555
+ ];
8556
+ GM_GROUPS.forEach((groupName, gi) => {
8557
+ const grp = document.createElement("optgroup");
8558
+ grp.label = groupName;
8559
+ for (let j = 0; j < 8; j++) {
8560
+ const name = GM_INSTRUMENT_NAMES[gi * 8 + j];
8561
+ if (!name) break;
8562
+ const o = document.createElement("option");
8563
+ o.value = name;
8564
+ o.textContent = name;
8565
+ grp.appendChild(o);
8566
+ }
8567
+ instSel.appendChild(grp);
8568
+ });
8569
+ instSel.value = normalizeInstrumentName(active.trackInstrument);
8570
+ const syncInstDisabled = () => {
8571
+ instSel.disabled = !!active.lyricModel;
8572
+ 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" : "";
8573
+ };
8574
+ syncInstDisabled();
8575
+ instSel.addEventListener("change", () => {
8576
+ active.trackInstrument = instSel.value;
8577
+ const trackIndex = trackStates.indexOf(active);
8578
+ options.onTrackInstrumentChange?.(trackIndex, active.trackInstrument);
8579
+ });
8580
+ instRow.appendChild(instSel);
8581
+ refs.trackBody.appendChild(instRow);
8475
8582
  if (isAdvanced || active.config.id !== "chord") {
8476
8583
  const lyricDiv = document.createElement("div");
8477
8584
  lyricDiv.className = "dtm-row";
@@ -8479,7 +8586,7 @@ var mountDAW = (target, options = {}) => {
8479
8586
  lyricDiv.style.alignItems = "stretch";
8480
8587
  lyricDiv.innerHTML = `
8481
8588
  <div class="dtm-row">
8482
- <span class="dtm-label">\u266A \u6B4C\u8A5E</span>
8589
+ <span class="dtm-label">\u266A UTAU</span>
8483
8590
  <select class="dtm-select" data-dtm="lyric-model" aria-label="\u6B4C\u5531\u30E2\u30C7\u30EB"></select>
8484
8591
  <img class="dtm-lyric-icon dtm-hidden" data-dtm="lyric-icon" width="20" height="20" alt="" draggable="false">
8485
8592
  <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 +8660,7 @@ var mountDAW = (target, options = {}) => {
8553
8660
  o.textContent = label;
8554
8661
  lyricModelSel.appendChild(o);
8555
8662
  };
8556
- addOpt("", "\u306A\u3057");
8663
+ addOpt("", "\u30DC\u30FC\u30AB\u30EB\u306A\u3057");
8557
8664
  for (const m of LYRIC_MODELS) addOpt(m, lyricModelLabel(m));
8558
8665
  if (active.lyricModel && !LYRIC_MODELS.includes(active.lyricModel)) {
8559
8666
  addOpt(active.lyricModel, lyricModelLabel(active.lyricModel));
@@ -8602,21 +8709,27 @@ var mountDAW = (target, options = {}) => {
8602
8709
  lyricModelSel.addEventListener("change", () => {
8603
8710
  active.lyricModel = lyricModelSel.value;
8604
8711
  syncLyricVisibility();
8712
+ syncInstDisabled();
8713
+ fireLyricsChange(active);
8605
8714
  });
8606
8715
  lyricOctaveSel.addEventListener("change", () => {
8607
8716
  active.vocalOctave = Number.parseInt(lyricOctaveSel.value, 10);
8717
+ fireLyricsChange(active);
8608
8718
  });
8609
8719
  lyricInput.addEventListener("input", () => {
8610
8720
  active.lyrics = lyricInput.value;
8611
8721
  updateLyricCount();
8722
+ fireLyricsChange(active);
8612
8723
  });
8613
8724
  lyricVol.addEventListener("input", () => {
8614
8725
  active.vocalVolume = Number.parseInt(lyricVol.value, 10);
8615
8726
  lyricVolLabel.textContent = lyricVol.value;
8727
+ fireLyricsChange(active);
8616
8728
  });
8617
8729
  lyricPan.addEventListener("input", () => {
8618
8730
  active.vocalPan = Number.parseInt(lyricPan.value, 10);
8619
8731
  lyricPanLabel.textContent = fmtPan(active.vocalPan);
8732
+ fireLyricsChange(active);
8620
8733
  });
8621
8734
  lyricPanLabel.style.cursor = "pointer";
8622
8735
  lyricPanLabel.title = "\u30BF\u30C3\u30D7\u3067\u4E2D\u592E(C)\u3078";
@@ -8624,6 +8737,7 @@ var mountDAW = (target, options = {}) => {
8624
8737
  active.vocalPan = 64;
8625
8738
  lyricPan.value = "64";
8626
8739
  lyricPanLabel.textContent = fmtPan(64);
8740
+ fireLyricsChange(active);
8627
8741
  });
8628
8742
  }
8629
8743
  if (active.config.id === "chord" && showChord) {
@@ -8698,13 +8812,19 @@ var mountDAW = (target, options = {}) => {
8698
8812
  const barLimitBars = Number(refs.barLimitSelect.value);
8699
8813
  const limitSteps = barLimitBars > 0 ? barLimitBars * renderConfig.stepsPerBar : Infinity;
8700
8814
  const clipNotes = (notes) => limitSteps === Infinity ? notes : notes.filter((n) => n.startStep < limitSteps);
8815
+ const trackInstrumentsForMeta = {};
8816
+ trackStates.forEach((t, i) => {
8817
+ if (t.trackInstrument) trackInstrumentsForMeta[i] = t.trackInstrument;
8818
+ });
8819
+ const trackInstMeta = Object.keys(trackInstrumentsForMeta).length > 0 ? trackInstrumentsForMeta : void 0;
8701
8820
  const metaLineFull = formatMmlMeta(
8702
8821
  {
8703
8822
  instrument: currentInstrument || void 0,
8704
8823
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
8705
8824
  volume: masterVolume,
8706
8825
  drumVolume,
8707
- mode
8826
+ mode,
8827
+ trackInstruments: trackInstMeta
8708
8828
  },
8709
8829
  " "
8710
8830
  );
@@ -8714,7 +8834,8 @@ var mountDAW = (target, options = {}) => {
8714
8834
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
8715
8835
  volume: masterVolume,
8716
8836
  drumVolume,
8717
- mode
8837
+ mode,
8838
+ trackInstruments: trackInstMeta
8718
8839
  },
8719
8840
  ""
8720
8841
  );
@@ -8872,6 +8993,13 @@ var mountDAW = (target, options = {}) => {
8872
8993
  refs.drumVolume.value = String(meta.drumVolume);
8873
8994
  refs.drumVolumeLabel.textContent = `${meta.drumVolume}%`;
8874
8995
  }
8996
+ trackStates.forEach((t, i) => {
8997
+ const name = normalizeInstrumentName(meta.trackInstruments?.[i] ?? "");
8998
+ if (t.trackInstrument !== name) {
8999
+ t.trackInstrument = name;
9000
+ options.onTrackInstrumentChange?.(i, name);
9001
+ }
9002
+ });
8875
9003
  for (const t of trackStates) {
8876
9004
  t.lyrics = "";
8877
9005
  t.lyricModel = "";
@@ -9031,6 +9159,31 @@ var mountDAW = (target, options = {}) => {
9031
9159
  setLoading(false);
9032
9160
  }, 30);
9033
9161
  };
9162
+ const showConfirmModal = (message) => new Promise((resolve) => {
9163
+ const overlay = document.createElement("div");
9164
+ overlay.className = "dtm-modal-overlay";
9165
+ overlay.innerHTML = `
9166
+ <div class="dtm-modal">
9167
+ <div class="dtm-modal-header">
9168
+ <span class="dtm-modal-title">\u30E2\u30FC\u30C9\u306E\u78BA\u8A8D</span>
9169
+ </div>
9170
+ <div class="dtm-modal-body"><p>${message}</p></div>
9171
+ <div class="dtm-confirm-footer">
9172
+ <button class="dtm-btn dtm-btn--ghost dtm-confirm-no">\u3044\u3044\u3048\uFF08\u3053\u306E\u307E\u307E\u8AAD\u307F\u8FBC\u3080\uFF09</button>
9173
+ <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>
9174
+ </div>
9175
+ </div>`;
9176
+ const close = (result) => {
9177
+ overlay.remove();
9178
+ resolve(result);
9179
+ };
9180
+ overlay.querySelector(".dtm-confirm-yes").addEventListener("click", () => close(true));
9181
+ overlay.querySelector(".dtm-confirm-no").addEventListener("click", () => close(false));
9182
+ overlay.addEventListener("click", (e) => {
9183
+ if (e.target === overlay) close(false);
9184
+ });
9185
+ document.body.appendChild(overlay);
9186
+ });
9034
9187
  const wireEvents = () => {
9035
9188
  refs.playBtn.addEventListener("click", togglePlay);
9036
9189
  refs.playBtn.disabled = false;
@@ -9150,10 +9303,25 @@ var mountDAW = (target, options = {}) => {
9150
9303
  "click",
9151
9304
  () => copy(refs.outputMini.textContent ?? "", refs.copyMiniBtn)
9152
9305
  );
9153
- refs.mmlLoadBtn.addEventListener(
9154
- "click",
9155
- () => overlayDuring(() => loadMML(refs.mmlInput.value))
9156
- );
9306
+ refs.mmlLoadBtn.addEventListener("click", async () => {
9307
+ const mml = refs.mmlInput.value;
9308
+ if (!isAdvanced && options.onRequestAdvancedMode) {
9309
+ const { mergedTrackCount } = parseMML(mml, {
9310
+ stepsPerBar: renderConfig.stepsPerBar,
9311
+ clampTrackCount: trackStates.length
9312
+ });
9313
+ if (mergedTrackCount > 0) {
9314
+ const confirmed = await showConfirmModal(
9315
+ "\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"
9316
+ );
9317
+ if (confirmed) {
9318
+ options.onRequestAdvancedMode(mml);
9319
+ return;
9320
+ }
9321
+ }
9322
+ }
9323
+ overlayDuring(() => loadMML(mml));
9324
+ });
9157
9325
  let activeSamplePlayer = null;
9158
9326
  let activeSampleButton = null;
9159
9327
  const collapseActiveSample = () => {
@@ -9246,6 +9414,7 @@ var mountDAW = (target, options = {}) => {
9246
9414
  singingVoices: options.singingVoices,
9247
9415
  drumPatterns: options.drumPatterns,
9248
9416
  volume: masterVolume,
9417
+ _skipInfoModals: true,
9249
9418
  onStop: () => {
9250
9419
  if (activeSampleButton === htmlBtn) {
9251
9420
  htmlBtn.textContent = "\u25B6 \u8A66\u8074";
@@ -9327,7 +9496,7 @@ var mountDAW = (target, options = {}) => {
9327
9496
  refs.overlay.hidden = true;
9328
9497
  setLoading(false);
9329
9498
  });
9330
- refs.midiLoadBtn.addEventListener("click", () => {
9499
+ refs.midiLoadBtn.addEventListener("click", async () => {
9331
9500
  if (!pendingMidi) return;
9332
9501
  const selected = [];
9333
9502
  const btns = refs.midiTrackSelection.querySelectorAll("button");
@@ -9336,6 +9505,19 @@ var mountDAW = (target, options = {}) => {
9336
9505
  selected.push(detectedTracks[i].index);
9337
9506
  });
9338
9507
  if (selected.length === 0) return;
9508
+ if (!isAdvanced && options.onRequestAdvancedMode && selected.length > trackStates.length) {
9509
+ const confirmed = await showConfirmModal(
9510
+ "\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"
9511
+ );
9512
+ if (confirmed) {
9513
+ const midi = pendingMidi;
9514
+ const sel = selected.slice();
9515
+ options.onRequestAdvancedMode(void 0, (newDaw) => {
9516
+ newDaw.applyMidiParsed?.(midi, sel);
9517
+ });
9518
+ return;
9519
+ }
9520
+ }
9339
9521
  overlayDuring(() => applyMidiSelection(pendingMidi, selected));
9340
9522
  });
9341
9523
  };
@@ -9472,6 +9654,9 @@ var mountDAW = (target, options = {}) => {
9472
9654
  },
9473
9655
  loadMML,
9474
9656
  loadMIDI,
9657
+ applyMidiParsed: (midi, selectedIndices) => {
9658
+ overlayDuring(() => applyMidiSelection(midi, selectedIndices));
9659
+ },
9475
9660
  exportMIDI: exportMIDI2,
9476
9661
  setBpm,
9477
9662
  getPlaybackState: () => playbackState,
@@ -9506,6 +9691,37 @@ var mountDAW = (target, options = {}) => {
9506
9691
  if (audible) audioMutedTracks.delete(trackId);
9507
9692
  else audioMutedTracks.add(trackId);
9508
9693
  },
9694
+ applyLyrics: (trackId, data) => {
9695
+ const t = trackStates.find((s) => s.config.id === trackId);
9696
+ if (!t) return;
9697
+ t.lyrics = data.lyrics;
9698
+ t.lyricModel = data.model;
9699
+ t.vocalVolume = data.vocalVolume;
9700
+ t.vocalGate = data.vocalGate;
9701
+ t.vocalPan = data.vocalPan;
9702
+ t.vocalOctave = data.vocalOctave;
9703
+ },
9704
+ applyTrackInstrument: (trackIndex, instrumentName) => {
9705
+ const t = trackStates[trackIndex];
9706
+ if (!t) return;
9707
+ const name = normalizeInstrumentName(instrumentName);
9708
+ t.trackInstrument = name;
9709
+ if (t.config.id === activeTrackId) updateTrackPanel();
9710
+ },
9711
+ noteToCanvas: (step, pitch) => {
9712
+ const canvas = getGridCanvas();
9713
+ const x = step * renderConfig.stepWidth - currentOffsetX;
9714
+ const y = (renderConfig.keyCount - 1 - pitch) * renderConfig.keyHeight - currentOffsetY;
9715
+ const onScreen = x >= 0 && x <= canvas.width && y >= 0 && y <= canvas.height;
9716
+ let side = null;
9717
+ if (!onScreen) {
9718
+ if (x < 0) side = "left";
9719
+ else if (x > canvas.width) side = "right";
9720
+ else if (y < 0) side = "top";
9721
+ else side = "bottom";
9722
+ }
9723
+ return { x, y, onScreen, side };
9724
+ },
9509
9725
  destroy: () => {
9510
9726
  sequencer.stop();
9511
9727
  options.singingVoices?.stopStream();
@@ -10156,7 +10372,7 @@ var SoundFont = class _SoundFont {
10156
10372
  const { buffer, _param } = zone;
10157
10373
  if (!buffer || !_param) return;
10158
10374
  src.buffer = buffer;
10159
- g.gain.value = volume;
10375
+ g.gain.setValueAtTime(volume, _when);
10160
10376
  src.playbackRate.setValueAtTime(_param.playbackRate, 0);
10161
10377
  Object.assign(src, _param.src);
10162
10378
  const _duration = duration + _SoundFont.afterTime;
@@ -10425,6 +10641,14 @@ var createDtmStudio = async (options = {}) => {
10425
10641
  }
10426
10642
  })();
10427
10643
  let nameToKey = {};
10644
+ const resolveNameToKey = (name) => {
10645
+ if (nameToKey[name]) return nameToKey[name];
10646
+ const stripped = name.replace(/\s+/g, "").toLowerCase();
10647
+ const canonical = Object.keys(nameToKey).find(
10648
+ (k) => k.replace(/\s+/g, "").toLowerCase() === stripped
10649
+ );
10650
+ return canonical ? nameToKey[canonical] : void 0;
10651
+ };
10428
10652
  const soundFonts = /* @__PURE__ */ new Map();
10429
10653
  const loadingByKey = /* @__PURE__ */ new Map();
10430
10654
  const loadInstrument = (instrumentKey) => {
@@ -10510,7 +10734,7 @@ var createDtmStudio = async (options = {}) => {
10510
10734
  if (opts.label !== null) {
10511
10735
  const lab = doc.createElement("span");
10512
10736
  lab.className = "dtm-controlbar-label";
10513
- lab.textContent = opts.label ?? "INSTRUMENT";
10737
+ lab.textContent = opts.label ?? "\u697D\u5668\u30D7\u30EA\u30BB\u30C3\u30C8";
10514
10738
  wrapper.appendChild(lab);
10515
10739
  }
10516
10740
  const select = doc.createElement("select");
@@ -10575,7 +10799,13 @@ var createDtmStudio = async (options = {}) => {
10575
10799
  const mountedPlayers = [];
10576
10800
  const mountedModeSwitches = [];
10577
10801
  const mountEditor = (target, opts = {}) => {
10578
- const { preset, presetUI, onInstrumentChange, ...dawOverrides } = opts;
10802
+ const {
10803
+ preset,
10804
+ presetUI,
10805
+ onInstrumentChange,
10806
+ onTrackInstrumentChange: externalOnTrackInstrumentChange,
10807
+ ...dawOverrides
10808
+ } = opts;
10579
10809
  const tracks = dawOverrides.tracks ?? TRACKS_SIMPLE;
10580
10810
  const trackIds = tracks.map((t) => t.id);
10581
10811
  const presetKey = preset && INSTRUMENT_PRESETS[preset] ? preset : defaultPreset;
@@ -10583,14 +10813,29 @@ var createDtmStudio = async (options = {}) => {
10583
10813
  const initialPreset = meta.instrument && INSTRUMENT_PRESETS[meta.instrument] ? meta.instrument : presetKey;
10584
10814
  let editorPreset = initialPreset;
10585
10815
  const isAdvancedMode = dawOverrides.mode === "advanced";
10816
+ const trackInstOverrides = /* @__PURE__ */ new Map();
10817
+ if (meta.trackInstruments) {
10818
+ for (const [idxStr, name] of Object.entries(meta.trackInstruments)) {
10819
+ const idx = Number(idxStr);
10820
+ const key = resolveNameToKey(name);
10821
+ if (key) trackInstOverrides.set(idx, key);
10822
+ }
10823
+ }
10586
10824
  const playNote = (e) => {
10587
- const sf2 = resolveSoundFont(
10588
- editorPreset,
10589
- e.trackId,
10590
- isAdvancedMode ? "advanced" : "simple"
10591
- );
10592
- if (!sf2) return;
10593
- sf2.play({
10825
+ const trackIdx = tracks.findIndex((t) => t.id === e.trackId);
10826
+ const overrideKey = trackIdx >= 0 ? trackInstOverrides.get(trackIdx) : void 0;
10827
+ let sfInst;
10828
+ if (overrideKey) {
10829
+ sfInst = soundFonts.get(overrideKey);
10830
+ } else {
10831
+ sfInst = resolveSoundFont(
10832
+ editorPreset,
10833
+ e.trackId,
10834
+ isAdvancedMode ? "advanced" : "simple"
10835
+ );
10836
+ }
10837
+ if (!sfInst) return;
10838
+ sfInst.play({
10594
10839
  ctx: audioCtx,
10595
10840
  destination: masterGain,
10596
10841
  pitch: e.pitch,
@@ -10607,6 +10852,17 @@ var createDtmStudio = async (options = {}) => {
10607
10852
  }
10608
10853
  onInstrumentChange?.(key);
10609
10854
  };
10855
+ const handleTrackInstrumentChange = async (trackIndex, instrumentName) => {
10856
+ if (!instrumentName) {
10857
+ trackInstOverrides.delete(trackIndex);
10858
+ return;
10859
+ }
10860
+ await listReady;
10861
+ const key = resolveNameToKey(instrumentName);
10862
+ if (!key) return;
10863
+ trackInstOverrides.set(trackIndex, key);
10864
+ await loadInstrument(key);
10865
+ };
10610
10866
  const base = {
10611
10867
  getAudioTime: () => audioCtx.currentTime,
10612
10868
  onResumeAudio: resumeAudio,
@@ -10615,6 +10871,10 @@ var createDtmStudio = async (options = {}) => {
10615
10871
  singingVoices,
10616
10872
  parseMidi,
10617
10873
  onInstrumentChange: handleInstrumentChange,
10874
+ onTrackInstrumentChange: (idx, name) => {
10875
+ void handleTrackInstrumentChange(idx, name);
10876
+ externalOnTrackInstrumentChange?.(idx, name);
10877
+ },
10618
10878
  ...dawOverrides
10619
10879
  };
10620
10880
  const daw = mountDAW(target, base);
@@ -10662,6 +10922,10 @@ var createDtmStudio = async (options = {}) => {
10662
10922
  presetSelect.setValue(name);
10663
10923
  }
10664
10924
  },
10925
+ applyTrackInstrument: (trackIndex, instrumentName) => {
10926
+ daw.applyTrackInstrument(trackIndex, instrumentName);
10927
+ void handleTrackInstrumentChange(trackIndex, instrumentName);
10928
+ },
10665
10929
  destroy
10666
10930
  };
10667
10931
  };
@@ -10669,8 +10933,8 @@ var createDtmStudio = async (options = {}) => {
10669
10933
  const doc = target.ownerDocument;
10670
10934
  const tracksFor = opts.tracksFor ?? ((m) => m === "advanced" ? TRACKS_ADVANCED : TRACKS_SIMPLE);
10671
10935
  const labels = {
10672
- simple: opts.labels?.simple ?? "\u30B7\u30F3\u30D7\u30EB",
10673
- advanced: opts.labels?.advanced ?? "\u30A2\u30C9\u30D0\u30F3\u30B9"
10936
+ simple: opts.labels?.simple ?? "\u521D\u5FC3\u8005",
10937
+ advanced: opts.labels?.advanced ?? "\u4E0A\u7D1A\u8005"
10674
10938
  };
10675
10939
  const editorOptionsFor = (mode) => typeof opts.editorOptions === "function" ? opts.editorOptions(mode) : opts.editorOptions ?? {};
10676
10940
  let currentMode = opts.mode ?? "simple";
@@ -10680,7 +10944,7 @@ var createDtmStudio = async (options = {}) => {
10680
10944
  if (opts.label !== null) {
10681
10945
  const lab = doc.createElement("span");
10682
10946
  lab.className = "dtm-controlbar-label";
10683
- lab.textContent = opts.label ?? "MODE";
10947
+ lab.textContent = opts.label ?? "\u30E2\u30FC\u30C9";
10684
10948
  wrapper.appendChild(lab);
10685
10949
  }
10686
10950
  const seg = doc.createElement("div");
@@ -10711,7 +10975,15 @@ var createDtmStudio = async (options = {}) => {
10711
10975
  ...editorOpts,
10712
10976
  mode,
10713
10977
  tracks: tracksFor(mode),
10714
- initialMML: mml ?? editorOpts.initialMML
10978
+ initialMML: mml ?? editorOpts.initialMML,
10979
+ onRequestAdvancedMode: (pendingMml, applyMidi) => {
10980
+ doUnmount();
10981
+ currentMode = "advanced";
10982
+ updateButtons();
10983
+ opts.onChange?.("advanced");
10984
+ doMount("advanced", pendingMml);
10985
+ if (applyMidi && daw) applyMidi(daw);
10986
+ }
10715
10987
  });
10716
10988
  attachWrapper();
10717
10989
  opts.onMount?.(daw, mode);
@@ -10761,24 +11033,44 @@ var createDtmStudio = async (options = {}) => {
10761
11033
  (idx) => getRoleForTrackIndex(idx, isAdvancedMode ? "advanced" : "simple")
10762
11034
  );
10763
11035
  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(
11036
+ const playerTrackInstKeys = /* @__PURE__ */ new Map();
11037
+ const loadPlayerTrackInstruments = async () => {
11038
+ if (!meta.trackInstruments) return;
11039
+ await listReady;
11040
+ for (const [idxStr, name] of Object.entries(meta.trackInstruments)) {
11041
+ const key = resolveNameToKey(name);
11042
+ if (!key) continue;
11043
+ playerTrackInstKeys.set(Number(idxStr), key);
11044
+ await loadInstrument(key);
11045
+ }
11046
+ };
11047
+ void Promise.all([
11048
+ loadPreset(
10776
11049
  playerPreset,
10777
- role,
11050
+ loadTrackIds,
10778
11051
  isAdvancedMode ? "advanced" : "simple"
10779
- );
10780
- if (!sf2) return;
10781
- sf2.play({
11052
+ ),
11053
+ loadPlayerTrackInstruments()
11054
+ ]);
11055
+ const playPlayerNote = (e) => {
11056
+ const idx = Number(e.trackId);
11057
+ const overrideKey = playerTrackInstKeys.get(idx);
11058
+ let sfInst;
11059
+ if (overrideKey) {
11060
+ sfInst = soundFonts.get(overrideKey);
11061
+ } else {
11062
+ const role = getRoleForTrackIndex(
11063
+ idx,
11064
+ isAdvancedMode ? "advanced" : "simple"
11065
+ );
11066
+ sfInst = resolveSoundFont(
11067
+ playerPreset,
11068
+ role,
11069
+ isAdvancedMode ? "advanced" : "simple"
11070
+ );
11071
+ }
11072
+ if (!sfInst) return;
11073
+ sfInst.play({
10782
11074
  ctx: audioCtx,
10783
11075
  destination: masterGain,
10784
11076
  pitch: e.pitch,
@@ -10837,6 +11129,7 @@ var createDtmStudio = async (options = {}) => {
10837
11129
  DRUM_FONT,
10838
11130
  DRUM_KEYS,
10839
11131
  DRUM_PATTERNS,
11132
+ GM_INSTRUMENT_NAMES,
10840
11133
  INSTRUMENT_PRESETS,
10841
11134
  KOE_BASE_URL,
10842
11135
  KOE_VOICEBANKS,