@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.mjs CHANGED
@@ -146,6 +146,7 @@ async function buildNameToKeyMapping() {
146
146
  }
147
147
  return nameToKey;
148
148
  }
149
+ var GM_INSTRUMENT_NAMES = FONT_NAME_SURIKOV.trim().split("\n").map((line) => line.slice(line.indexOf(" ") + 1));
149
150
 
150
151
  // node_modules/.pnpm/@onjmin+chord-parser@1.0.2/node_modules/@onjmin/chord-parser/dist/index.mjs
151
152
  var SHARP_NAMES = [
@@ -1223,14 +1224,17 @@ var buildUI = (target, options) => {
1223
1224
  target.innerHTML = `
1224
1225
  <div class="dtm-daw" data-dtm="root">
1225
1226
  <div class="dtm-topbar" data-dtm="transport">
1226
- <button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
1227
- <button class="dtm-play" data-dtm="play" disabled>${icon("play")}<span>\u8A66\u8074</span></button>
1228
- <button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
1229
- <label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
1230
- <span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
1231
- <span class="dtm-grow"></span>
1232
- <span class="dtm-label">BPM</span>
1233
- <input type="number" class="dtm-input dtm-input--num" data-dtm="bpm" value="${defaultBpm}" min="20" max="300">
1227
+ <div class="dtm-topbar-row1">
1228
+ <button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
1229
+ <button class="dtm-play" data-dtm="play" disabled>${icon("play")}<span>\u8A66\u8074</span></button>
1230
+ <button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
1231
+ <label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
1232
+ <span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
1233
+ <span class="dtm-grow"></span>
1234
+ <span class="dtm-label">BPM</span>
1235
+ <input type="number" class="dtm-input dtm-input--num" data-dtm="bpm" value="${defaultBpm}" min="20" max="300">
1236
+ </div>
1237
+ <div class="dtm-tracks" data-dtm="track-tabs"></div>
1234
1238
  </div>
1235
1239
 
1236
1240
  <div class="dtm-tooldock">
@@ -1253,8 +1257,6 @@ var buildUI = (target, options) => {
1253
1257
  </select>
1254
1258
  </div>
1255
1259
 
1256
- <div class="dtm-tracks" data-dtm="track-tabs"></div>
1257
-
1258
1260
  <div class="dtm-roll-wrap">
1259
1261
  <div class="dtm-roll" data-dtm="roll">
1260
1262
  <div data-dtm="wrapper" style="position:absolute;inset:0;"></div>
@@ -4285,6 +4287,7 @@ var PITCH_MAP2 = {
4285
4287
  };
4286
4288
  var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
4287
4289
  var META_DIRECTIVE = /#(inst|drum|volume|drumvolume|mode)=([\w-]+)/gi;
4290
+ var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
4288
4291
  var parseMmlMeta = (mml) => {
4289
4292
  const meta = {};
4290
4293
  for (const m of mml.matchAll(META_DIRECTIVE)) {
@@ -4303,9 +4306,17 @@ var parseMmlMeta = (mml) => {
4303
4306
  }
4304
4307
  }
4305
4308
  }
4309
+ for (const m of mml.matchAll(TRACK_INST_DIRECTIVE)) {
4310
+ const idx = Number.parseInt(m[1], 10);
4311
+ const name = m[2].trim();
4312
+ if (!Number.isNaN(idx) && name) {
4313
+ meta.trackInstruments ??= {};
4314
+ meta.trackInstruments[idx] = name;
4315
+ }
4316
+ }
4306
4317
  return meta;
4307
4318
  };
4308
- var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "");
4319
+ var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "").replace(TRACK_INST_DIRECTIVE, "");
4309
4320
  var formatMmlMeta = (meta, space = "") => {
4310
4321
  const parts = [];
4311
4322
  if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
@@ -4314,6 +4325,11 @@ var formatMmlMeta = (meta, space = "") => {
4314
4325
  if (meta.drumVolume !== void 0)
4315
4326
  parts.push(`#drumvolume=${meta.drumVolume}`);
4316
4327
  if (meta.mode) parts.push(`#mode=${meta.mode}`);
4328
+ if (meta.trackInstruments) {
4329
+ for (const [idx, name] of Object.entries(meta.trackInstruments)) {
4330
+ if (name) parts.push(`#t${idx}inst=${name}`);
4331
+ }
4332
+ }
4317
4333
  return parts.join(space);
4318
4334
  };
4319
4335
  var parseMML = (mml, options = {}) => {
@@ -4935,7 +4951,7 @@ var DAW_CSS = `
4935
4951
  top: 0;
4936
4952
  z-index: 20;
4937
4953
  display: flex;
4938
- flex-wrap: nowrap;
4954
+ flex-wrap: wrap;
4939
4955
  align-items: center;
4940
4956
  gap: var(--dtm-gap);
4941
4957
  padding: 6px;
@@ -4945,18 +4961,19 @@ var DAW_CSS = `
4945
4961
  inset 0 0 0 2px var(--c-black),
4946
4962
  0 0 0 2px var(--dtm-success),
4947
4963
  4px 4px 0 var(--c-black);
4964
+ }
4965
+ .dtm-topbar-row1 {
4966
+ display: flex;
4967
+ align-items: center;
4968
+ gap: var(--dtm-gap);
4969
+ flex-basis: 100%;
4970
+ min-width: 0;
4948
4971
  overflow-x: auto;
4949
4972
  scrollbar-width: none;
4950
4973
  }
4951
- .dtm-topbar::-webkit-scrollbar {
4952
- display: none;
4953
- }
4954
- .dtm-topbar > * {
4955
- flex-shrink: 0;
4956
- }
4957
- .dtm-topbar > .dtm-grow {
4958
- flex-shrink: 1;
4959
- }
4974
+ .dtm-topbar-row1::-webkit-scrollbar { display: none; }
4975
+ .dtm-topbar-row1 > * { flex-shrink: 0; }
4976
+ .dtm-topbar-row1 > .dtm-grow { flex-shrink: 1; }
4960
4977
 
4961
4978
  /* PLAY\u30DC\u30BF\u30F3 \u2014 \u30B2\u30FC\u30E0\u306E\u300C\u6C7A\u5B9A\u30DC\u30BF\u30F3\u300D\u7684\u5B58\u5728\u611F */
4962
4979
  .dtm-play {
@@ -5146,46 +5163,39 @@ var DAW_CSS = `
5146
5163
  .dtm-modebtn--active { background: var(--dtm-primary); color: var(--dtm-pfg); }
5147
5164
  .dtm-modebtn:not(.dtm-modebtn--active):active { background: var(--dtm-border2); }
5148
5165
 
5149
- /* \u2500\u2500\u2500 \u30C8\u30E9\u30C3\u30AF\u30D4\u30EB\uFF08\u30AD\u30E3\u30E9\u30AF\u30BF\u30FC\u9078\u629E\u30DC\u30BF\u30F3\uFF09 \u2500\u2500\u2500 */
5166
+ /* \u2500\u2500\u2500 \u30C8\u30E9\u30C3\u30AF\u30D4\u30EB\uFF08\u756A\u53F7\u30DC\u30BF\u30F3\u3001\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u30D0\u30FC2\u884C\u76EE\uFF09 \u2500\u2500\u2500 */
5150
5167
  .dtm-tracks {
5168
+ flex-basis: 100%;
5151
5169
  display: flex;
5152
- flex-wrap: wrap;
5153
- gap: var(--dtm-gap);
5170
+ flex-wrap: nowrap;
5171
+ gap: 3px;
5154
5172
  }
5155
5173
  .dtm-pill {
5156
5174
  --dtm-pill-color: var(--dtm-primary);
5157
5175
  display: inline-flex;
5158
5176
  align-items: center;
5159
- gap: 8px;
5160
- flex: 1 1 auto;
5161
5177
  justify-content: center;
5162
- min-height: 42px;
5163
- padding: 0 12px;
5164
- border: 2px solid var(--dtm-border2);
5165
- background: var(--dtm-deep);
5166
- color: var(--dtm-muted);
5178
+ flex: 1 1 0;
5179
+ min-width: 0;
5180
+ height: 26px;
5181
+ padding: 0;
5182
+ border: 2px solid var(--c-black);
5183
+ background: color-mix(in srgb, var(--dtm-pill-color) 40%, black);
5184
+ color: var(--c-white);
5167
5185
  font-family: var(--dtm-font);
5168
- font-size: 13px;
5169
- text-transform: uppercase;
5170
- letter-spacing: .1em;
5186
+ font-size: 11px;
5187
+ font-weight: bold;
5171
5188
  cursor: pointer;
5172
- box-shadow: 3px 3px 0 var(--c-black);
5173
- }
5174
- .dtm-pill .dtm-dot {
5175
- width: 8px; height: 8px;
5176
- background: var(--dtm-pill-color);
5177
- flex: 0 0 auto;
5178
- box-shadow: 1px 1px 0 var(--c-black);
5189
+ box-shadow: 2px 2px 0 var(--c-black);
5190
+ opacity: 0.7;
5179
5191
  }
5180
- /* \u30A2\u30AF\u30C6\u30A3\u30D6\u9078\u629E = \u91D1\u8272\u30CF\u30A4\u30E9\u30A4\u30C8 + \u30AB\u30FC\u30BD\u30EB */
5192
+ /* \u30A2\u30AF\u30C6\u30A3\u30D6\u9078\u629E = \u4E0D\u900F\u660E + \u91D1\u67A0 */
5181
5193
  .dtm-pill--active {
5194
+ opacity: 1;
5182
5195
  border-color: var(--dtm-gold);
5183
- color: var(--dtm-gold);
5184
- background: var(--dtm-surface);
5185
- box-shadow: 0 0 0 2px var(--dtm-gold), 3px 3px 0 var(--c-black);
5196
+ box-shadow: 0 0 0 1px var(--dtm-gold), 2px 2px 0 var(--c-black);
5186
5197
  }
5187
- .dtm-pill--active::before { content: "\u25BA "; font-size: 10px; }
5188
- .dtm-pill:not(.dtm-pill--active):active { transform: translate(3px,3px); box-shadow: none; }
5198
+ .dtm-pill:not(.dtm-pill--active):active { transform: translate(2px,2px); box-shadow: none; }
5189
5199
 
5190
5200
  /* \u2500\u2500\u2500 \u30D4\u30A2\u30CE\u30ED\u30FC\u30EB\uFF08\u30C8\u30E9\u30C3\u30AB\u30FC\u98A8\uFF09 \u2500\u2500\u2500 */
5191
5201
  .dtm-roll-wrap { display: flex; gap: var(--dtm-gap); }
@@ -5329,7 +5339,7 @@ var DAW_CSS = `
5329
5339
  }
5330
5340
  .dtm-overlay[hidden] { display: none; }
5331
5341
  .dtm-overlay::before {
5332
- content: 'NOW LOADING';
5342
+ content: '\u30ED\u30FC\u30C9\u4E2D';
5333
5343
  font-family: var(--dtm-font);
5334
5344
  font-size: 13px;
5335
5345
  color: var(--dtm-primary);
@@ -5522,6 +5532,15 @@ var DAW_CSS = `
5522
5532
  justify-content: center;
5523
5533
  }
5524
5534
 
5535
+ .dtm-confirm-footer {
5536
+ padding: 8px 12px;
5537
+ border-top: 2px solid var(--c-black);
5538
+ background: var(--dtm-deep);
5539
+ display: flex;
5540
+ gap: 8px;
5541
+ justify-content: flex-end;
5542
+ }
5543
+
5525
5544
  .dtm-modal {
5526
5545
  max-width: 500px;
5527
5546
  width: 100%;
@@ -6544,9 +6563,11 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6544
6563
  const mmlInfoItem = makeMenuItem("MML\u66F8\u5F0F\u3068\u306F");
6545
6564
  const embedItem = makeMenuItem("\u57CB\u3081\u8FBC\u3080");
6546
6565
  const copyMmlItem = makeMenuItem("MML\u30B3\u30D4\u30FC");
6547
- menuDropdown.appendChild(showMmlItem);
6548
- menuDropdown.appendChild(mmlInfoItem);
6549
- menuDropdown.appendChild(embedItem);
6566
+ if (!options._skipInfoModals) {
6567
+ menuDropdown.appendChild(showMmlItem);
6568
+ menuDropdown.appendChild(mmlInfoItem);
6569
+ menuDropdown.appendChild(embedItem);
6570
+ }
6550
6571
  menuDropdown.appendChild(copyMmlItem);
6551
6572
  menuContainer.appendChild(menuDropdown);
6552
6573
  mmlHeader.appendChild(menuContainer);
@@ -6700,6 +6721,8 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6700
6721
  volume: trackVolume,
6701
6722
  // 解説モーダル内の試聴サンプルは規約同意を要求しない。
6702
6723
  skipConsent: true,
6724
+ // 再帰的なモーダル生成を防ぐ。
6725
+ _skipInfoModals: true,
6703
6726
  onStop: () => {
6704
6727
  if (activeSampleBtn === el) resetSampleBtn(el);
6705
6728
  }
@@ -6709,65 +6732,67 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6709
6732
  });
6710
6733
  }
6711
6734
  };
6712
- showMmlItem.addEventListener("click", (e) => {
6713
- e.stopPropagation();
6714
- toggleMenu(false);
6715
- const modalBody = openInfoModal("MML\u3092\u8868\u793A");
6716
- const desc = doc.createElement("p");
6717
- desc.textContent = "\u3053\u306EMML\u3092\u30B3\u30D4\u30FC\u3057\u3066\u3001\u4ED6\u306E\u30D7\u30EC\u30A4\u30E4\u30FC\u3084\u5171\u6709URL\u306B\u8CBC\u308A\u4ED8\u3051\u3066\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002";
6718
- desc.style.marginBottom = "8px";
6719
- modalBody.appendChild(desc);
6720
- const sourceMml = options.getMml?.() ?? mml;
6721
- const displayMml = sourceMml.split(";").map((s) => s.trim()).filter((s) => s.length > 0).join(";\n");
6722
- const pre = doc.createElement("pre");
6723
- pre.textContent = displayMml;
6724
- pre.style.whiteSpace = "pre-wrap";
6725
- pre.style.wordBreak = "break-all";
6726
- pre.style.cursor = "text";
6727
- pre.addEventListener("click", () => {
6728
- const range = doc.createRange();
6729
- range.selectNodeContents(pre);
6730
- const sel = doc.defaultView?.getSelection();
6731
- sel?.removeAllRanges();
6732
- sel?.addRange(range);
6733
- });
6734
- modalBody.appendChild(pre);
6735
- appendCopyButton(modalBody, sourceMml);
6736
- });
6737
- mmlInfoItem.addEventListener("click", (e) => {
6738
- e.stopPropagation();
6739
- toggleMenu(false);
6740
- const modalBody = openInfoModal("MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC");
6741
- modalBody.innerHTML = MML_INFO_HTML;
6742
- wireSampleButtons(modalBody);
6743
- });
6744
- embedItem.addEventListener("click", async (e) => {
6745
- e.stopPropagation();
6746
- toggleMenu(false);
6747
- const modalBody = openInfoModal("\u57CB\u3081\u8FBC\u307F");
6748
- const loading = doc.createElement("p");
6749
- loading.textContent = "\u751F\u6210\u4E2D...";
6750
- modalBody.appendChild(loading);
6751
- try {
6752
- const embedBase = options.embedUrl ?? "https://onjmin.github.io/dtm/demo/embed.html";
6753
- const payload = await encodeMml(mml);
6754
- const url2 = `${embedBase}#${payload}`;
6755
- const snippet = `<iframe src="${url2}" width="100%" height="260" frameborder="0" loading="lazy" title="@onjmin/dtm player"></iframe>`;
6756
- if (!modalBody.isConnected) return;
6757
- loading.remove();
6735
+ if (!options._skipInfoModals) {
6736
+ showMmlItem.addEventListener("click", (e) => {
6737
+ e.stopPropagation();
6738
+ toggleMenu(false);
6739
+ const modalBody = openInfoModal("MML\u3092\u8868\u793A");
6758
6740
  const desc = doc.createElement("p");
6759
- desc.textContent = "\u3053\u306EHTML\u3092\u30D6\u30ED\u30B0\u3084\u30B5\u30A4\u30C8\u306B\u8CBC\u308A\u4ED8\u3051\u308B\u3068\u3001\u30D7\u30EC\u30A4\u30E4\u30FC\u3092\u305D\u306E\u307E\u307E\u57CB\u3081\u8FBC\u3081\u307E\u3059\u3002";
6741
+ 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";
6742
+ desc.style.marginBottom = "8px";
6743
+ modalBody.appendChild(desc);
6744
+ const sourceMml = options.getMml?.() ?? mml;
6745
+ const displayMml = sourceMml.split(";").map((s) => s.trim()).filter((s) => s.length > 0).join(";\n");
6760
6746
  const pre = doc.createElement("pre");
6761
- pre.textContent = snippet;
6747
+ pre.textContent = displayMml;
6762
6748
  pre.style.whiteSpace = "pre-wrap";
6763
6749
  pre.style.wordBreak = "break-all";
6764
- modalBody.append(desc, pre);
6765
- appendCopyButton(modalBody, snippet);
6766
- } catch (err2) {
6767
- console.error("[dtm] failed to generate embed snippet", err2);
6768
- if (modalBody.isConnected) loading.textContent = "\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
6769
- }
6770
- });
6750
+ pre.style.cursor = "text";
6751
+ pre.addEventListener("click", () => {
6752
+ const range = doc.createRange();
6753
+ range.selectNodeContents(pre);
6754
+ const sel = doc.defaultView?.getSelection();
6755
+ sel?.removeAllRanges();
6756
+ sel?.addRange(range);
6757
+ });
6758
+ modalBody.appendChild(pre);
6759
+ appendCopyButton(modalBody, sourceMml);
6760
+ });
6761
+ mmlInfoItem.addEventListener("click", (e) => {
6762
+ e.stopPropagation();
6763
+ toggleMenu(false);
6764
+ const modalBody = openInfoModal("MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC");
6765
+ modalBody.innerHTML = MML_INFO_HTML;
6766
+ wireSampleButtons(modalBody);
6767
+ });
6768
+ embedItem.addEventListener("click", async (e) => {
6769
+ e.stopPropagation();
6770
+ toggleMenu(false);
6771
+ const modalBody = openInfoModal("\u57CB\u3081\u8FBC\u307F");
6772
+ const loading = doc.createElement("p");
6773
+ loading.textContent = "\u751F\u6210\u4E2D...";
6774
+ modalBody.appendChild(loading);
6775
+ try {
6776
+ const embedBase = options.embedUrl ?? "https://onjmin.github.io/dtm/demo/embed.html";
6777
+ const payload = await encodeMml(mml);
6778
+ const url2 = `${embedBase}#${payload}`;
6779
+ const snippet = `<iframe src="${url2}" width="100%" height="260" frameborder="0" loading="lazy" title="@onjmin/dtm player"></iframe>`;
6780
+ if (!modalBody.isConnected) return;
6781
+ loading.remove();
6782
+ const desc = doc.createElement("p");
6783
+ 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";
6784
+ const pre = doc.createElement("pre");
6785
+ pre.textContent = snippet;
6786
+ pre.style.whiteSpace = "pre-wrap";
6787
+ pre.style.wordBreak = "break-all";
6788
+ modalBody.append(desc, pre);
6789
+ appendCopyButton(modalBody, snippet);
6790
+ } catch (err2) {
6791
+ console.error("[dtm] failed to generate embed snippet", err2);
6792
+ if (modalBody.isConnected) loading.textContent = "\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
6793
+ }
6794
+ });
6795
+ }
6771
6796
  copyMmlItem.addEventListener("click", async (e) => {
6772
6797
  e.stopPropagation();
6773
6798
  const success = await copyToClipboard(doc, options.getMml?.() ?? mml);
@@ -7364,8 +7389,8 @@ var MIDI_INFO_HTML = `
7364
7389
 
7365
7390
  <h4>3. \u30E2\u30FC\u30C9\u306B\u3088\u308B\u53D6\u308A\u8FBC\u307F\u65B9\u306E\u9055\u3044</h4>
7366
7391
  <ul>
7367
- <li><strong>SIMPLE</strong>: \u5404\u30C8\u30E9\u30C3\u30AF\u306E\u7279\u5FB4\u304B\u3089\u3001\u30E1\u30ED\u30C7\u30A3\u30FC\u30FB\u30B5\u30D6\u30E1\u30ED\u30FB\u30D9\u30FC\u30B9\u30FB\u4F34\u594F\u306E4\u3064\u306E\u5F79\u5272\u306B\u81EA\u52D5\u3067\u632F\u308A\u5206\u3051\u3089\u308C\u307E\u3059\u3002</li>
7368
- <li><strong>ADVANCED</strong>: MIDI\u306E\u30C8\u30E9\u30C3\u30AF\u69CB\u6210\u304C\u305D\u306E\u307E\u307E\u53CD\u6620\u3055\u308C\u307E\u3059\uFF081\u5BFE1\uFF09\u3002</li>
7392
+ <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>
7393
+ <li><strong>\u4E0A\u7D1A\u8005\u30E2\u30FC\u30C9</strong>: MIDI\u306E\u30C8\u30E9\u30C3\u30AF\u69CB\u6210\u304C\u305D\u306E\u307E\u307E\u53CD\u6620\u3055\u308C\u307E\u3059\uFF081\u5BFE1\uFF09\u3002</li>
7369
7394
  </ul>
7370
7395
 
7371
7396
  <h4>4. MIDI\u30D5\u30A1\u30A4\u30EB\u3092\u624B\u306B\u5165\u308C\u308B</h4>
@@ -7417,105 +7442,105 @@ var TRACKS_SIMPLE = [
7417
7442
  var TRACKS_ADVANCED = [
7418
7443
  {
7419
7444
  id: "t0",
7420
- name: "TRACK 01",
7445
+ name: "\u30C8\u30E9\u30C3\u30AF1",
7421
7446
  color: [41, 173, 255],
7422
7447
  instrument: 0,
7423
7448
  volume: 100
7424
7449
  },
7425
7450
  {
7426
7451
  id: "t1",
7427
- name: "TRACK 02",
7452
+ name: "\u30C8\u30E9\u30C3\u30AF2",
7428
7453
  color: [0, 228, 54],
7429
7454
  instrument: 1,
7430
7455
  volume: 100
7431
7456
  },
7432
7457
  {
7433
7458
  id: "t2",
7434
- name: "TRACK 03",
7459
+ name: "\u30C8\u30E9\u30C3\u30AF3",
7435
7460
  color: [255, 119, 168],
7436
7461
  instrument: 2,
7437
7462
  volume: 100
7438
7463
  },
7439
7464
  {
7440
7465
  id: "t3",
7441
- name: "TRACK 04",
7466
+ name: "\u30C8\u30E9\u30C3\u30AF4",
7442
7467
  color: [255, 163, 0],
7443
7468
  instrument: 3,
7444
7469
  volume: 100
7445
7470
  },
7446
7471
  {
7447
7472
  id: "t4",
7448
- name: "TRACK 05",
7473
+ name: "\u30C8\u30E9\u30C3\u30AF5",
7449
7474
  color: [255, 236, 39],
7450
7475
  instrument: 4,
7451
7476
  volume: 100
7452
7477
  },
7453
7478
  {
7454
7479
  id: "t5",
7455
- name: "TRACK 06",
7480
+ name: "\u30C8\u30E9\u30C3\u30AF6",
7456
7481
  color: [131, 118, 156],
7457
7482
  instrument: 5,
7458
7483
  volume: 100
7459
7484
  },
7460
7485
  {
7461
7486
  id: "t6",
7462
- name: "TRACK 07",
7487
+ name: "\u30C8\u30E9\u30C3\u30AF7",
7463
7488
  color: [255, 0, 77],
7464
7489
  instrument: 6,
7465
7490
  volume: 100
7466
7491
  },
7467
7492
  {
7468
7493
  id: "t7",
7469
- name: "TRACK 08",
7494
+ name: "\u30C8\u30E9\u30C3\u30AF8",
7470
7495
  color: [255, 204, 170],
7471
7496
  instrument: 7,
7472
7497
  volume: 100
7473
7498
  },
7474
7499
  {
7475
7500
  id: "t8",
7476
- name: "TRACK 09",
7501
+ name: "\u30C8\u30E9\u30C3\u30AF9",
7477
7502
  color: [194, 195, 199],
7478
7503
  instrument: 8,
7479
7504
  volume: 100
7480
7505
  },
7481
7506
  {
7482
7507
  id: "t9",
7483
- name: "TRACK 10",
7508
+ name: "\u30C8\u30E9\u30C3\u30AF10",
7484
7509
  color: [0, 135, 81],
7485
7510
  instrument: 9,
7486
7511
  volume: 100
7487
7512
  },
7488
7513
  {
7489
7514
  id: "t10",
7490
- name: "TRACK 11",
7515
+ name: "\u30C8\u30E9\u30C3\u30AF11",
7491
7516
  color: [171, 82, 54],
7492
7517
  instrument: 10,
7493
7518
  volume: 100
7494
7519
  },
7495
7520
  {
7496
7521
  id: "t11",
7497
- name: "TRACK 12",
7522
+ name: "\u30C8\u30E9\u30C3\u30AF12",
7498
7523
  color: [126, 37, 83],
7499
7524
  instrument: 11,
7500
7525
  volume: 100
7501
7526
  },
7502
7527
  {
7503
7528
  id: "t12",
7504
- name: "TRACK 13",
7529
+ name: "\u30C8\u30E9\u30C3\u30AF13",
7505
7530
  color: [255, 241, 232],
7506
7531
  instrument: 12,
7507
7532
  volume: 100
7508
7533
  },
7509
7534
  {
7510
7535
  id: "t13",
7511
- name: "TRACK 14",
7536
+ name: "\u30C8\u30E9\u30C3\u30AF14",
7512
7537
  color: [120, 200, 255],
7513
7538
  instrument: 13,
7514
7539
  volume: 100
7515
7540
  },
7516
7541
  {
7517
7542
  id: "t14",
7518
- name: "TRACK 15",
7543
+ name: "\u30C8\u30E9\u30C3\u30AF15",
7519
7544
  color: [100, 255, 160],
7520
7545
  instrument: 14,
7521
7546
  volume: 100
@@ -7529,6 +7554,13 @@ var LYRIC_MODEL_LABELS2 = {
7529
7554
  };
7530
7555
  var lyricModelLabel = (model) => LYRIC_MODEL_LABELS2[model] ?? model;
7531
7556
  var clamp3 = (v, min, max) => Math.min(Math.max(v, min), max);
7557
+ var normalizeInstrumentName = (name) => {
7558
+ if (!name) return "";
7559
+ const stripped = name.replace(/\s+/g, "").toLowerCase();
7560
+ return GM_INSTRUMENT_NAMES.find(
7561
+ (n) => n.replace(/\s+/g, "").toLowerCase() === stripped
7562
+ ) ?? name;
7563
+ };
7532
7564
  var mountDAW = (target, options = {}) => {
7533
7565
  injectStyles();
7534
7566
  const getAudioTime = options.getAudioTime ?? (() => performance.now() / 1e3);
@@ -7568,7 +7600,8 @@ var mountDAW = (target, options = {}) => {
7568
7600
  let snapGridSteps = 12;
7569
7601
  const gridLineSteps = 48;
7570
7602
  let currentOffsetX = 0;
7571
- let currentOffsetY = (104 - 1 - 60) * renderConfig.keyHeight - 215;
7603
+ const _initPitch = options.initialScrollPitch;
7604
+ let currentOffsetY = _initPitch !== void 0 ? (renderConfig.keyCount - 1 - _initPitch) * renderConfig.keyHeight - 215 : (104 - 1 - 60) * renderConfig.keyHeight - 215;
7572
7605
  let playStartStep = 0;
7573
7606
  let isSolo = false;
7574
7607
  let lyricTrackIndices = /* @__PURE__ */ new Set();
@@ -7581,6 +7614,24 @@ var mountDAW = (target, options = {}) => {
7581
7614
  let copiedNotes = [];
7582
7615
  let trackStates = [];
7583
7616
  let suppressPatch = false;
7617
+ let lyricsDebounceTimer = null;
7618
+ const fireLyricsChange = (t) => {
7619
+ if (!options.onLyricsChange) return;
7620
+ const trackId = t.config.id;
7621
+ const data = {
7622
+ lyrics: t.lyrics,
7623
+ model: t.lyricModel,
7624
+ vocalVolume: t.vocalVolume,
7625
+ vocalGate: t.vocalGate,
7626
+ vocalPan: t.vocalPan,
7627
+ vocalOctave: t.vocalOctave
7628
+ };
7629
+ if (lyricsDebounceTimer) clearTimeout(lyricsDebounceTimer);
7630
+ lyricsDebounceTimer = setTimeout(() => {
7631
+ options.onLyricsChange(trackId, data);
7632
+ lyricsDebounceTimer = null;
7633
+ }, 300);
7634
+ };
7584
7635
  const hiddenTracks = /* @__PURE__ */ new Set();
7585
7636
  const audioMutedTracks = /* @__PURE__ */ new Set();
7586
7637
  const createTrackStates = () => {
@@ -7629,7 +7680,8 @@ var mountDAW = (target, options = {}) => {
7629
7680
  vocalVolume: DEFAULT_VOCAL_VOLUME,
7630
7681
  vocalGate: 100,
7631
7682
  vocalPan: 64,
7632
- vocalOctave: 0
7683
+ vocalOctave: 0,
7684
+ trackInstrument: ""
7633
7685
  };
7634
7686
  });
7635
7687
  };
@@ -8335,19 +8387,20 @@ var mountDAW = (target, options = {}) => {
8335
8387
  };
8336
8388
  const updateTrackPanel = () => {
8337
8389
  refs.trackTabs.innerHTML = "";
8338
- for (const t of trackStates) {
8390
+ for (const [i, t] of trackStates.entries()) {
8339
8391
  const [r, g, b] = t.config.color;
8340
8392
  const btn = document.createElement("button");
8341
8393
  btn.className = `dtm-pill ${t.config.id === activeTrackId ? "dtm-pill--active" : ""}`;
8342
8394
  btn.style.setProperty("--dtm-pill-color", `rgb(${r},${g},${b})`);
8343
- btn.innerHTML = `<span class="dtm-dot"></span><span>${t.config.name}</span>`;
8395
+ btn.title = t.config.name;
8396
+ btn.textContent = String(i + 1);
8344
8397
  btn.addEventListener("click", () => switchTrack(t.config.id));
8345
8398
  refs.trackTabs.appendChild(btn);
8346
8399
  }
8347
8400
  const active = getActive();
8348
8401
  refs.trackBody.innerHTML = `
8349
8402
  <div class="dtm-row">
8350
- <span class="dtm-label">velocity</span>
8403
+ <span class="dtm-label">\u30D9\u30ED\u30B7\u30C6\u30A3</span>
8351
8404
  <input type="range" class="dtm-range dtm-grow" data-dtm="track-vol" min="0" max="127" value="${active.volume}">
8352
8405
  <span class="dtm-label" data-dtm="track-vol-label">${active.volume}</span>
8353
8406
  </div>`;
@@ -8362,6 +8415,59 @@ var mountDAW = (target, options = {}) => {
8362
8415
  active.core.setVolume(active.volume);
8363
8416
  volLabel.textContent = String(active.volume);
8364
8417
  });
8418
+ const instRow = document.createElement("div");
8419
+ instRow.className = "dtm-row";
8420
+ instRow.innerHTML = `<span class="dtm-label">\u697D\u5668</span>`;
8421
+ const instSel = document.createElement("select");
8422
+ instSel.className = "dtm-select dtm-grow";
8423
+ const defaultOpt = document.createElement("option");
8424
+ defaultOpt.value = "";
8425
+ defaultOpt.textContent = "\u30C7\u30D5\u30A9\u30EB\u30C8\uFF08\u30D7\u30EA\u30BB\u30C3\u30C8\uFF09";
8426
+ instSel.appendChild(defaultOpt);
8427
+ const GM_GROUPS = [
8428
+ "\u30D4\u30A2\u30CE",
8429
+ "\u30AF\u30ED\u30DE\u30C6\u30A3\u30C3\u30AF\u30D1\u30FC\u30AB\u30C3\u30B7\u30E7\u30F3",
8430
+ "\u30AA\u30EB\u30AC\u30F3",
8431
+ "\u30AE\u30BF\u30FC",
8432
+ "\u30D9\u30FC\u30B9",
8433
+ "\u30B9\u30C8\u30EA\u30F3\u30B0\u30B9",
8434
+ "\u30A2\u30F3\u30B5\u30F3\u30D6\u30EB",
8435
+ "\u30D6\u30E9\u30B9",
8436
+ "\u30EA\u30FC\u30C9\uFF08\u6728\u7BA1\uFF09",
8437
+ "\u30D1\u30A4\u30D7",
8438
+ "\u30B7\u30F3\u30BB\u30EA\u30FC\u30C9",
8439
+ "\u30B7\u30F3\u30BB\u30D1\u30C3\u30C9",
8440
+ "\u30B7\u30F3\u30BB\u30A8\u30D5\u30A7\u30AF\u30C8",
8441
+ "\u30A8\u30B9\u30CB\u30C3\u30AF",
8442
+ "\u30D1\u30FC\u30AB\u30C3\u30B7\u30D6",
8443
+ "\u30B5\u30A6\u30F3\u30C9\u30A8\u30D5\u30A7\u30AF\u30C8"
8444
+ ];
8445
+ GM_GROUPS.forEach((groupName, gi) => {
8446
+ const grp = document.createElement("optgroup");
8447
+ grp.label = groupName;
8448
+ for (let j = 0; j < 8; j++) {
8449
+ const name = GM_INSTRUMENT_NAMES[gi * 8 + j];
8450
+ if (!name) break;
8451
+ const o = document.createElement("option");
8452
+ o.value = name;
8453
+ o.textContent = name;
8454
+ grp.appendChild(o);
8455
+ }
8456
+ instSel.appendChild(grp);
8457
+ });
8458
+ instSel.value = normalizeInstrumentName(active.trackInstrument);
8459
+ const syncInstDisabled = () => {
8460
+ instSel.disabled = !!active.lyricModel;
8461
+ 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" : "";
8462
+ };
8463
+ syncInstDisabled();
8464
+ instSel.addEventListener("change", () => {
8465
+ active.trackInstrument = instSel.value;
8466
+ const trackIndex = trackStates.indexOf(active);
8467
+ options.onTrackInstrumentChange?.(trackIndex, active.trackInstrument);
8468
+ });
8469
+ instRow.appendChild(instSel);
8470
+ refs.trackBody.appendChild(instRow);
8365
8471
  if (isAdvanced || active.config.id !== "chord") {
8366
8472
  const lyricDiv = document.createElement("div");
8367
8473
  lyricDiv.className = "dtm-row";
@@ -8369,7 +8475,7 @@ var mountDAW = (target, options = {}) => {
8369
8475
  lyricDiv.style.alignItems = "stretch";
8370
8476
  lyricDiv.innerHTML = `
8371
8477
  <div class="dtm-row">
8372
- <span class="dtm-label">\u266A \u6B4C\u8A5E</span>
8478
+ <span class="dtm-label">\u266A UTAU</span>
8373
8479
  <select class="dtm-select" data-dtm="lyric-model" aria-label="\u6B4C\u5531\u30E2\u30C7\u30EB"></select>
8374
8480
  <img class="dtm-lyric-icon dtm-hidden" data-dtm="lyric-icon" width="20" height="20" alt="" draggable="false">
8375
8481
  <select class="dtm-select" data-dtm="lyric-octave" aria-label="\u30AA\u30AF\u30BF\u30FC\u30D6\uFF08\u97F3\u6E90\u306E\u5F97\u610F\u97F3\u57DF\u306B\u5408\u308F\u305B\u308B\uFF09" title="\u30AA\u30AF\u30BF\u30FC\u30D6">
@@ -8443,7 +8549,7 @@ var mountDAW = (target, options = {}) => {
8443
8549
  o.textContent = label;
8444
8550
  lyricModelSel.appendChild(o);
8445
8551
  };
8446
- addOpt("", "\u306A\u3057");
8552
+ addOpt("", "\u30DC\u30FC\u30AB\u30EB\u306A\u3057");
8447
8553
  for (const m of LYRIC_MODELS) addOpt(m, lyricModelLabel(m));
8448
8554
  if (active.lyricModel && !LYRIC_MODELS.includes(active.lyricModel)) {
8449
8555
  addOpt(active.lyricModel, lyricModelLabel(active.lyricModel));
@@ -8492,21 +8598,27 @@ var mountDAW = (target, options = {}) => {
8492
8598
  lyricModelSel.addEventListener("change", () => {
8493
8599
  active.lyricModel = lyricModelSel.value;
8494
8600
  syncLyricVisibility();
8601
+ syncInstDisabled();
8602
+ fireLyricsChange(active);
8495
8603
  });
8496
8604
  lyricOctaveSel.addEventListener("change", () => {
8497
8605
  active.vocalOctave = Number.parseInt(lyricOctaveSel.value, 10);
8606
+ fireLyricsChange(active);
8498
8607
  });
8499
8608
  lyricInput.addEventListener("input", () => {
8500
8609
  active.lyrics = lyricInput.value;
8501
8610
  updateLyricCount();
8611
+ fireLyricsChange(active);
8502
8612
  });
8503
8613
  lyricVol.addEventListener("input", () => {
8504
8614
  active.vocalVolume = Number.parseInt(lyricVol.value, 10);
8505
8615
  lyricVolLabel.textContent = lyricVol.value;
8616
+ fireLyricsChange(active);
8506
8617
  });
8507
8618
  lyricPan.addEventListener("input", () => {
8508
8619
  active.vocalPan = Number.parseInt(lyricPan.value, 10);
8509
8620
  lyricPanLabel.textContent = fmtPan(active.vocalPan);
8621
+ fireLyricsChange(active);
8510
8622
  });
8511
8623
  lyricPanLabel.style.cursor = "pointer";
8512
8624
  lyricPanLabel.title = "\u30BF\u30C3\u30D7\u3067\u4E2D\u592E(C)\u3078";
@@ -8514,6 +8626,7 @@ var mountDAW = (target, options = {}) => {
8514
8626
  active.vocalPan = 64;
8515
8627
  lyricPan.value = "64";
8516
8628
  lyricPanLabel.textContent = fmtPan(64);
8629
+ fireLyricsChange(active);
8517
8630
  });
8518
8631
  }
8519
8632
  if (active.config.id === "chord" && showChord) {
@@ -8588,13 +8701,19 @@ var mountDAW = (target, options = {}) => {
8588
8701
  const barLimitBars = Number(refs.barLimitSelect.value);
8589
8702
  const limitSteps = barLimitBars > 0 ? barLimitBars * renderConfig.stepsPerBar : Infinity;
8590
8703
  const clipNotes = (notes) => limitSteps === Infinity ? notes : notes.filter((n) => n.startStep < limitSteps);
8704
+ const trackInstrumentsForMeta = {};
8705
+ trackStates.forEach((t, i) => {
8706
+ if (t.trackInstrument) trackInstrumentsForMeta[i] = t.trackInstrument;
8707
+ });
8708
+ const trackInstMeta = Object.keys(trackInstrumentsForMeta).length > 0 ? trackInstrumentsForMeta : void 0;
8591
8709
  const metaLineFull = formatMmlMeta(
8592
8710
  {
8593
8711
  instrument: currentInstrument || void 0,
8594
8712
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
8595
8713
  volume: masterVolume,
8596
8714
  drumVolume,
8597
- mode
8715
+ mode,
8716
+ trackInstruments: trackInstMeta
8598
8717
  },
8599
8718
  " "
8600
8719
  );
@@ -8604,7 +8723,8 @@ var mountDAW = (target, options = {}) => {
8604
8723
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
8605
8724
  volume: masterVolume,
8606
8725
  drumVolume,
8607
- mode
8726
+ mode,
8727
+ trackInstruments: trackInstMeta
8608
8728
  },
8609
8729
  ""
8610
8730
  );
@@ -8762,6 +8882,13 @@ var mountDAW = (target, options = {}) => {
8762
8882
  refs.drumVolume.value = String(meta.drumVolume);
8763
8883
  refs.drumVolumeLabel.textContent = `${meta.drumVolume}%`;
8764
8884
  }
8885
+ trackStates.forEach((t, i) => {
8886
+ const name = normalizeInstrumentName(meta.trackInstruments?.[i] ?? "");
8887
+ if (t.trackInstrument !== name) {
8888
+ t.trackInstrument = name;
8889
+ options.onTrackInstrumentChange?.(i, name);
8890
+ }
8891
+ });
8765
8892
  for (const t of trackStates) {
8766
8893
  t.lyrics = "";
8767
8894
  t.lyricModel = "";
@@ -8921,6 +9048,31 @@ var mountDAW = (target, options = {}) => {
8921
9048
  setLoading(false);
8922
9049
  }, 30);
8923
9050
  };
9051
+ const showConfirmModal = (message) => new Promise((resolve) => {
9052
+ const overlay = document.createElement("div");
9053
+ overlay.className = "dtm-modal-overlay";
9054
+ overlay.innerHTML = `
9055
+ <div class="dtm-modal">
9056
+ <div class="dtm-modal-header">
9057
+ <span class="dtm-modal-title">\u30E2\u30FC\u30C9\u306E\u78BA\u8A8D</span>
9058
+ </div>
9059
+ <div class="dtm-modal-body"><p>${message}</p></div>
9060
+ <div class="dtm-confirm-footer">
9061
+ <button class="dtm-btn dtm-btn--ghost dtm-confirm-no">\u3044\u3044\u3048\uFF08\u3053\u306E\u307E\u307E\u8AAD\u307F\u8FBC\u3080\uFF09</button>
9062
+ <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>
9063
+ </div>
9064
+ </div>`;
9065
+ const close = (result) => {
9066
+ overlay.remove();
9067
+ resolve(result);
9068
+ };
9069
+ overlay.querySelector(".dtm-confirm-yes").addEventListener("click", () => close(true));
9070
+ overlay.querySelector(".dtm-confirm-no").addEventListener("click", () => close(false));
9071
+ overlay.addEventListener("click", (e) => {
9072
+ if (e.target === overlay) close(false);
9073
+ });
9074
+ document.body.appendChild(overlay);
9075
+ });
8924
9076
  const wireEvents = () => {
8925
9077
  refs.playBtn.addEventListener("click", togglePlay);
8926
9078
  refs.playBtn.disabled = false;
@@ -9040,10 +9192,25 @@ var mountDAW = (target, options = {}) => {
9040
9192
  "click",
9041
9193
  () => copy(refs.outputMini.textContent ?? "", refs.copyMiniBtn)
9042
9194
  );
9043
- refs.mmlLoadBtn.addEventListener(
9044
- "click",
9045
- () => overlayDuring(() => loadMML(refs.mmlInput.value))
9046
- );
9195
+ refs.mmlLoadBtn.addEventListener("click", async () => {
9196
+ const mml = refs.mmlInput.value;
9197
+ if (!isAdvanced && options.onRequestAdvancedMode) {
9198
+ const { mergedTrackCount } = parseMML(mml, {
9199
+ stepsPerBar: renderConfig.stepsPerBar,
9200
+ clampTrackCount: trackStates.length
9201
+ });
9202
+ if (mergedTrackCount > 0) {
9203
+ const confirmed = await showConfirmModal(
9204
+ "\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"
9205
+ );
9206
+ if (confirmed) {
9207
+ options.onRequestAdvancedMode(mml);
9208
+ return;
9209
+ }
9210
+ }
9211
+ }
9212
+ overlayDuring(() => loadMML(mml));
9213
+ });
9047
9214
  let activeSamplePlayer = null;
9048
9215
  let activeSampleButton = null;
9049
9216
  const collapseActiveSample = () => {
@@ -9136,6 +9303,7 @@ var mountDAW = (target, options = {}) => {
9136
9303
  singingVoices: options.singingVoices,
9137
9304
  drumPatterns: options.drumPatterns,
9138
9305
  volume: masterVolume,
9306
+ _skipInfoModals: true,
9139
9307
  onStop: () => {
9140
9308
  if (activeSampleButton === htmlBtn) {
9141
9309
  htmlBtn.textContent = "\u25B6 \u8A66\u8074";
@@ -9217,7 +9385,7 @@ var mountDAW = (target, options = {}) => {
9217
9385
  refs.overlay.hidden = true;
9218
9386
  setLoading(false);
9219
9387
  });
9220
- refs.midiLoadBtn.addEventListener("click", () => {
9388
+ refs.midiLoadBtn.addEventListener("click", async () => {
9221
9389
  if (!pendingMidi) return;
9222
9390
  const selected = [];
9223
9391
  const btns = refs.midiTrackSelection.querySelectorAll("button");
@@ -9226,6 +9394,19 @@ var mountDAW = (target, options = {}) => {
9226
9394
  selected.push(detectedTracks[i].index);
9227
9395
  });
9228
9396
  if (selected.length === 0) return;
9397
+ if (!isAdvanced && options.onRequestAdvancedMode && selected.length > trackStates.length) {
9398
+ const confirmed = await showConfirmModal(
9399
+ "\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"
9400
+ );
9401
+ if (confirmed) {
9402
+ const midi = pendingMidi;
9403
+ const sel = selected.slice();
9404
+ options.onRequestAdvancedMode(void 0, (newDaw) => {
9405
+ newDaw.applyMidiParsed?.(midi, sel);
9406
+ });
9407
+ return;
9408
+ }
9409
+ }
9229
9410
  overlayDuring(() => applyMidiSelection(pendingMidi, selected));
9230
9411
  });
9231
9412
  };
@@ -9362,6 +9543,9 @@ var mountDAW = (target, options = {}) => {
9362
9543
  },
9363
9544
  loadMML,
9364
9545
  loadMIDI,
9546
+ applyMidiParsed: (midi, selectedIndices) => {
9547
+ overlayDuring(() => applyMidiSelection(midi, selectedIndices));
9548
+ },
9365
9549
  exportMIDI: exportMIDI2,
9366
9550
  setBpm,
9367
9551
  getPlaybackState: () => playbackState,
@@ -9396,6 +9580,37 @@ var mountDAW = (target, options = {}) => {
9396
9580
  if (audible) audioMutedTracks.delete(trackId);
9397
9581
  else audioMutedTracks.add(trackId);
9398
9582
  },
9583
+ applyLyrics: (trackId, data) => {
9584
+ const t = trackStates.find((s) => s.config.id === trackId);
9585
+ if (!t) return;
9586
+ t.lyrics = data.lyrics;
9587
+ t.lyricModel = data.model;
9588
+ t.vocalVolume = data.vocalVolume;
9589
+ t.vocalGate = data.vocalGate;
9590
+ t.vocalPan = data.vocalPan;
9591
+ t.vocalOctave = data.vocalOctave;
9592
+ },
9593
+ applyTrackInstrument: (trackIndex, instrumentName) => {
9594
+ const t = trackStates[trackIndex];
9595
+ if (!t) return;
9596
+ const name = normalizeInstrumentName(instrumentName);
9597
+ t.trackInstrument = name;
9598
+ if (t.config.id === activeTrackId) updateTrackPanel();
9599
+ },
9600
+ noteToCanvas: (step, pitch) => {
9601
+ const canvas = getGridCanvas();
9602
+ const x = step * renderConfig.stepWidth - currentOffsetX;
9603
+ const y = (renderConfig.keyCount - 1 - pitch) * renderConfig.keyHeight - currentOffsetY;
9604
+ const onScreen = x >= 0 && x <= canvas.width && y >= 0 && y <= canvas.height;
9605
+ let side = null;
9606
+ if (!onScreen) {
9607
+ if (x < 0) side = "left";
9608
+ else if (x > canvas.width) side = "right";
9609
+ else if (y < 0) side = "top";
9610
+ else side = "bottom";
9611
+ }
9612
+ return { x, y, onScreen, side };
9613
+ },
9399
9614
  destroy: () => {
9400
9615
  sequencer.stop();
9401
9616
  options.singingVoices?.stopStream();
@@ -10046,7 +10261,7 @@ var SoundFont = class _SoundFont {
10046
10261
  const { buffer, _param } = zone;
10047
10262
  if (!buffer || !_param) return;
10048
10263
  src.buffer = buffer;
10049
- g.gain.value = volume;
10264
+ g.gain.setValueAtTime(volume, _when);
10050
10265
  src.playbackRate.setValueAtTime(_param.playbackRate, 0);
10051
10266
  Object.assign(src, _param.src);
10052
10267
  const _duration = duration + _SoundFont.afterTime;
@@ -10314,6 +10529,14 @@ var createDtmStudio = async (options = {}) => {
10314
10529
  }
10315
10530
  })();
10316
10531
  let nameToKey = {};
10532
+ const resolveNameToKey = (name) => {
10533
+ if (nameToKey[name]) return nameToKey[name];
10534
+ const stripped = name.replace(/\s+/g, "").toLowerCase();
10535
+ const canonical = Object.keys(nameToKey).find(
10536
+ (k) => k.replace(/\s+/g, "").toLowerCase() === stripped
10537
+ );
10538
+ return canonical ? nameToKey[canonical] : void 0;
10539
+ };
10317
10540
  const soundFonts = /* @__PURE__ */ new Map();
10318
10541
  const loadingByKey = /* @__PURE__ */ new Map();
10319
10542
  const loadInstrument = (instrumentKey) => {
@@ -10399,7 +10622,7 @@ var createDtmStudio = async (options = {}) => {
10399
10622
  if (opts.label !== null) {
10400
10623
  const lab = doc.createElement("span");
10401
10624
  lab.className = "dtm-controlbar-label";
10402
- lab.textContent = opts.label ?? "INSTRUMENT";
10625
+ lab.textContent = opts.label ?? "\u697D\u5668\u30D7\u30EA\u30BB\u30C3\u30C8";
10403
10626
  wrapper.appendChild(lab);
10404
10627
  }
10405
10628
  const select = doc.createElement("select");
@@ -10464,7 +10687,13 @@ var createDtmStudio = async (options = {}) => {
10464
10687
  const mountedPlayers = [];
10465
10688
  const mountedModeSwitches = [];
10466
10689
  const mountEditor = (target, opts = {}) => {
10467
- const { preset, presetUI, onInstrumentChange, ...dawOverrides } = opts;
10690
+ const {
10691
+ preset,
10692
+ presetUI,
10693
+ onInstrumentChange,
10694
+ onTrackInstrumentChange: externalOnTrackInstrumentChange,
10695
+ ...dawOverrides
10696
+ } = opts;
10468
10697
  const tracks = dawOverrides.tracks ?? TRACKS_SIMPLE;
10469
10698
  const trackIds = tracks.map((t) => t.id);
10470
10699
  const presetKey = preset && INSTRUMENT_PRESETS[preset] ? preset : defaultPreset;
@@ -10472,14 +10701,29 @@ var createDtmStudio = async (options = {}) => {
10472
10701
  const initialPreset = meta.instrument && INSTRUMENT_PRESETS[meta.instrument] ? meta.instrument : presetKey;
10473
10702
  let editorPreset = initialPreset;
10474
10703
  const isAdvancedMode = dawOverrides.mode === "advanced";
10704
+ const trackInstOverrides = /* @__PURE__ */ new Map();
10705
+ if (meta.trackInstruments) {
10706
+ for (const [idxStr, name] of Object.entries(meta.trackInstruments)) {
10707
+ const idx = Number(idxStr);
10708
+ const key = resolveNameToKey(name);
10709
+ if (key) trackInstOverrides.set(idx, key);
10710
+ }
10711
+ }
10475
10712
  const playNote = (e) => {
10476
- const sf2 = resolveSoundFont(
10477
- editorPreset,
10478
- e.trackId,
10479
- isAdvancedMode ? "advanced" : "simple"
10480
- );
10481
- if (!sf2) return;
10482
- sf2.play({
10713
+ const trackIdx = tracks.findIndex((t) => t.id === e.trackId);
10714
+ const overrideKey = trackIdx >= 0 ? trackInstOverrides.get(trackIdx) : void 0;
10715
+ let sfInst;
10716
+ if (overrideKey) {
10717
+ sfInst = soundFonts.get(overrideKey);
10718
+ } else {
10719
+ sfInst = resolveSoundFont(
10720
+ editorPreset,
10721
+ e.trackId,
10722
+ isAdvancedMode ? "advanced" : "simple"
10723
+ );
10724
+ }
10725
+ if (!sfInst) return;
10726
+ sfInst.play({
10483
10727
  ctx: audioCtx,
10484
10728
  destination: masterGain,
10485
10729
  pitch: e.pitch,
@@ -10496,6 +10740,17 @@ var createDtmStudio = async (options = {}) => {
10496
10740
  }
10497
10741
  onInstrumentChange?.(key);
10498
10742
  };
10743
+ const handleTrackInstrumentChange = async (trackIndex, instrumentName) => {
10744
+ if (!instrumentName) {
10745
+ trackInstOverrides.delete(trackIndex);
10746
+ return;
10747
+ }
10748
+ await listReady;
10749
+ const key = resolveNameToKey(instrumentName);
10750
+ if (!key) return;
10751
+ trackInstOverrides.set(trackIndex, key);
10752
+ await loadInstrument(key);
10753
+ };
10499
10754
  const base = {
10500
10755
  getAudioTime: () => audioCtx.currentTime,
10501
10756
  onResumeAudio: resumeAudio,
@@ -10504,6 +10759,10 @@ var createDtmStudio = async (options = {}) => {
10504
10759
  singingVoices,
10505
10760
  parseMidi,
10506
10761
  onInstrumentChange: handleInstrumentChange,
10762
+ onTrackInstrumentChange: (idx, name) => {
10763
+ void handleTrackInstrumentChange(idx, name);
10764
+ externalOnTrackInstrumentChange?.(idx, name);
10765
+ },
10507
10766
  ...dawOverrides
10508
10767
  };
10509
10768
  const daw = mountDAW(target, base);
@@ -10551,6 +10810,10 @@ var createDtmStudio = async (options = {}) => {
10551
10810
  presetSelect.setValue(name);
10552
10811
  }
10553
10812
  },
10813
+ applyTrackInstrument: (trackIndex, instrumentName) => {
10814
+ daw.applyTrackInstrument(trackIndex, instrumentName);
10815
+ void handleTrackInstrumentChange(trackIndex, instrumentName);
10816
+ },
10554
10817
  destroy
10555
10818
  };
10556
10819
  };
@@ -10558,8 +10821,8 @@ var createDtmStudio = async (options = {}) => {
10558
10821
  const doc = target.ownerDocument;
10559
10822
  const tracksFor = opts.tracksFor ?? ((m) => m === "advanced" ? TRACKS_ADVANCED : TRACKS_SIMPLE);
10560
10823
  const labels = {
10561
- simple: opts.labels?.simple ?? "\u30B7\u30F3\u30D7\u30EB",
10562
- advanced: opts.labels?.advanced ?? "\u30A2\u30C9\u30D0\u30F3\u30B9"
10824
+ simple: opts.labels?.simple ?? "\u521D\u5FC3\u8005",
10825
+ advanced: opts.labels?.advanced ?? "\u4E0A\u7D1A\u8005"
10563
10826
  };
10564
10827
  const editorOptionsFor = (mode) => typeof opts.editorOptions === "function" ? opts.editorOptions(mode) : opts.editorOptions ?? {};
10565
10828
  let currentMode = opts.mode ?? "simple";
@@ -10569,7 +10832,7 @@ var createDtmStudio = async (options = {}) => {
10569
10832
  if (opts.label !== null) {
10570
10833
  const lab = doc.createElement("span");
10571
10834
  lab.className = "dtm-controlbar-label";
10572
- lab.textContent = opts.label ?? "MODE";
10835
+ lab.textContent = opts.label ?? "\u30E2\u30FC\u30C9";
10573
10836
  wrapper.appendChild(lab);
10574
10837
  }
10575
10838
  const seg = doc.createElement("div");
@@ -10600,7 +10863,15 @@ var createDtmStudio = async (options = {}) => {
10600
10863
  ...editorOpts,
10601
10864
  mode,
10602
10865
  tracks: tracksFor(mode),
10603
- initialMML: mml ?? editorOpts.initialMML
10866
+ initialMML: mml ?? editorOpts.initialMML,
10867
+ onRequestAdvancedMode: (pendingMml, applyMidi) => {
10868
+ doUnmount();
10869
+ currentMode = "advanced";
10870
+ updateButtons();
10871
+ opts.onChange?.("advanced");
10872
+ doMount("advanced", pendingMml);
10873
+ if (applyMidi && daw) applyMidi(daw);
10874
+ }
10604
10875
  });
10605
10876
  attachWrapper();
10606
10877
  opts.onMount?.(daw, mode);
@@ -10650,24 +10921,44 @@ var createDtmStudio = async (options = {}) => {
10650
10921
  (idx) => getRoleForTrackIndex(idx, isAdvancedMode ? "advanced" : "simple")
10651
10922
  );
10652
10923
  const loadTrackIds = trackIds.length > 0 ? trackIds : [...TRACK_ROLES];
10653
- void loadPreset(
10654
- playerPreset,
10655
- loadTrackIds,
10656
- isAdvancedMode ? "advanced" : "simple"
10657
- );
10658
- const playPlayerNote = (e) => {
10659
- const idx = Number(e.trackId);
10660
- const role = getRoleForTrackIndex(
10661
- idx,
10662
- isAdvancedMode ? "advanced" : "simple"
10663
- );
10664
- const sf2 = resolveSoundFont(
10924
+ const playerTrackInstKeys = /* @__PURE__ */ new Map();
10925
+ const loadPlayerTrackInstruments = async () => {
10926
+ if (!meta.trackInstruments) return;
10927
+ await listReady;
10928
+ for (const [idxStr, name] of Object.entries(meta.trackInstruments)) {
10929
+ const key = resolveNameToKey(name);
10930
+ if (!key) continue;
10931
+ playerTrackInstKeys.set(Number(idxStr), key);
10932
+ await loadInstrument(key);
10933
+ }
10934
+ };
10935
+ void Promise.all([
10936
+ loadPreset(
10665
10937
  playerPreset,
10666
- role,
10938
+ loadTrackIds,
10667
10939
  isAdvancedMode ? "advanced" : "simple"
10668
- );
10669
- if (!sf2) return;
10670
- sf2.play({
10940
+ ),
10941
+ loadPlayerTrackInstruments()
10942
+ ]);
10943
+ const playPlayerNote = (e) => {
10944
+ const idx = Number(e.trackId);
10945
+ const overrideKey = playerTrackInstKeys.get(idx);
10946
+ let sfInst;
10947
+ if (overrideKey) {
10948
+ sfInst = soundFonts.get(overrideKey);
10949
+ } else {
10950
+ const role = getRoleForTrackIndex(
10951
+ idx,
10952
+ isAdvancedMode ? "advanced" : "simple"
10953
+ );
10954
+ sfInst = resolveSoundFont(
10955
+ playerPreset,
10956
+ role,
10957
+ isAdvancedMode ? "advanced" : "simple"
10958
+ );
10959
+ }
10960
+ if (!sfInst) return;
10961
+ sfInst.play({
10671
10962
  ctx: audioCtx,
10672
10963
  destination: masterGain,
10673
10964
  pitch: e.pitch,
@@ -10725,6 +11016,7 @@ export {
10725
11016
  DRUM_FONT,
10726
11017
  DRUM_KEYS,
10727
11018
  DRUM_PATTERNS,
11019
+ GM_INSTRUMENT_NAMES,
10728
11020
  INSTRUMENT_PRESETS,
10729
11021
  KOE_BASE_URL,
10730
11022
  KOE_VOICEBANKS,