@onjmin/dtm 0.1.87 → 0.1.88

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
@@ -2320,7 +2320,7 @@ var parseLyrics = (mml) => {
2320
2320
  let delay = 0;
2321
2321
  let gender = 50;
2322
2322
  let breathiness = 50;
2323
- let octaveDouble = false;
2323
+ let octaveUnison = "none";
2324
2324
  const modelMatch = rest.match(
2325
2325
  /^([a-z_][a-z0-9_]*?)(?=(?:[vqpobrghew]-?\d)|[^a-z0-9_]|$)(?::(\d+))?/i
2326
2326
  );
@@ -2398,9 +2398,9 @@ var parseLyrics = (mml) => {
2398
2398
  rest = rest.substring(eMatch[0].length).trim();
2399
2399
  continue;
2400
2400
  }
2401
- const wMatch = rest.match(/^w([01])/i);
2401
+ const wMatch = rest.match(/^w([0-3])/i);
2402
2402
  if (wMatch) {
2403
- octaveDouble = wMatch[1] === "1";
2403
+ octaveUnison = ["none", "down", "up", "both"][Number.parseInt(wMatch[1], 10)];
2404
2404
  metaTokens.push(wMatch[0]);
2405
2405
  rest = rest.substring(wMatch[0].length).trim();
2406
2406
  continue;
@@ -2424,7 +2424,7 @@ var parseLyrics = (mml) => {
2424
2424
  delay,
2425
2425
  gender,
2426
2426
  breathiness,
2427
- octaveDouble,
2427
+ octaveUnison,
2428
2428
  syllables,
2429
2429
  metaText: metaTokens.join(" "),
2430
2430
  ...lineBreaks.length > 0 ? { lineBreaks } : {}
@@ -3021,8 +3021,19 @@ var createKoeVoice = async (ctx, destination, options) => {
3021
3021
  var PREWARM_NOTES = 3;
3022
3022
  var STREAM_LOOKAHEAD_SEC = 1.5;
3023
3023
  var STREAM_POLL_MS = 100;
3024
- var OCTAVE_DOUBLE_SEMITONES = -12;
3025
- var OCTAVE_DOUBLE_PEAK_SCALE = 0.6;
3024
+ var OCTAVE_UNISON_PEAK_SCALE = 0.6;
3025
+ var octaveUnisonOffsets = (mode) => {
3026
+ switch (mode) {
3027
+ case "down":
3028
+ return [-12];
3029
+ case "up":
3030
+ return [12];
3031
+ case "both":
3032
+ return [-12, 12];
3033
+ default:
3034
+ return [];
3035
+ }
3036
+ };
3026
3037
  var FALLBACK_MODEL = "klatt";
3027
3038
  var createSingingVoices = (ctx, destination, options = {}) => {
3028
3039
  const catalog = {};
@@ -3117,12 +3128,12 @@ var createSingingVoices = (ctx, destination, options = {}) => {
3117
3128
  vibrato: track.vibrato,
3118
3129
  expr
3119
3130
  });
3120
- if (track.octaveDouble) {
3131
+ for (const offset of octaveUnisonOffsets(track.octaveUnison)) {
3121
3132
  tasks.push({
3122
3133
  model: m,
3123
3134
  note,
3124
3135
  prevVowel,
3125
- pitch: note.pitch + OCTAVE_DOUBLE_SEMITONES,
3136
+ pitch: note.pitch + offset,
3126
3137
  vibrato: track.vibrato,
3127
3138
  expr
3128
3139
  });
@@ -3237,11 +3248,8 @@ var createSingingVoices = (ctx, destination, options = {}) => {
3237
3248
  }
3238
3249
  };
3239
3250
  dispatchNote(note.pitch, 1);
3240
- if (track.octaveDouble) {
3241
- dispatchNote(
3242
- note.pitch + OCTAVE_DOUBLE_SEMITONES,
3243
- OCTAVE_DOUBLE_PEAK_SCALE
3244
- );
3251
+ for (const offset of octaveUnisonOffsets(track.octaveUnison)) {
3252
+ dispatchNote(note.pitch + offset, OCTAVE_UNISON_PEAK_SCALE);
3245
3253
  }
3246
3254
  if (!(model.renderToCache && model.scheduleCached)) {
3247
3255
  await new Promise((resolve) => setTimeout(resolve, 0));
@@ -3306,6 +3314,7 @@ var META_DIRECTIVE = /#(inst|drum|drumfont|volume|drumvolume|reverb|reverbdecay|
3306
3314
  var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
3307
3315
  var TRACK_COMP_DIRECTIVE = /#t(\d+)comp=(\d+)/gi;
3308
3316
  var TRACK_WIDTH_DIRECTIVE = /#t(\d+)width=(\d+)/gi;
3317
+ var TRACK_REVERBSEND_DIRECTIVE = /#t(\d+)rev=(\d+)/gi;
3309
3318
  var TRACK_EQLOW_DIRECTIVE = /#t(\d+)eqlo=(-?\d+)/gi;
3310
3319
  var TRACK_EQMID_DIRECTIVE = /#t(\d+)eqmid=(-?\d+)/gi;
3311
3320
  var TRACK_EQHIGH_DIRECTIVE = /#t(\d+)eqhi=(-?\d+)/gi;
@@ -3372,6 +3381,14 @@ var parseMmlMeta = (mml) => {
3372
3381
  meta.trackWidth[idx] = val;
3373
3382
  }
3374
3383
  }
3384
+ for (const m of mml.matchAll(TRACK_REVERBSEND_DIRECTIVE)) {
3385
+ const idx = Number.parseInt(m[1], 10);
3386
+ const val = clamp2(Number.parseInt(m[2], 10), 0, 100);
3387
+ if (!Number.isNaN(idx) && !Number.isNaN(val)) {
3388
+ meta.trackReverbSend ??= {};
3389
+ meta.trackReverbSend[idx] = val;
3390
+ }
3391
+ }
3375
3392
  for (const m of mml.matchAll(TRACK_EQLOW_DIRECTIVE)) {
3376
3393
  const idx = Number.parseInt(m[1], 10);
3377
3394
  const val = clamp2(Number.parseInt(m[2], 10), -12, 12);
@@ -3398,7 +3415,7 @@ var parseMmlMeta = (mml) => {
3398
3415
  }
3399
3416
  return meta;
3400
3417
  };
3401
- var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "").replace(TRACK_INST_DIRECTIVE, "").replace(TRACK_COMP_DIRECTIVE, "").replace(TRACK_WIDTH_DIRECTIVE, "").replace(TRACK_EQLOW_DIRECTIVE, "").replace(TRACK_EQMID_DIRECTIVE, "").replace(TRACK_EQHIGH_DIRECTIVE, "");
3418
+ var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "").replace(TRACK_INST_DIRECTIVE, "").replace(TRACK_COMP_DIRECTIVE, "").replace(TRACK_WIDTH_DIRECTIVE, "").replace(TRACK_REVERBSEND_DIRECTIVE, "").replace(TRACK_EQLOW_DIRECTIVE, "").replace(TRACK_EQMID_DIRECTIVE, "").replace(TRACK_EQHIGH_DIRECTIVE, "");
3402
3419
  var formatMmlMeta = (meta, space = "") => {
3403
3420
  const parts = [];
3404
3421
  if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
@@ -3437,6 +3454,11 @@ var formatMmlMeta = (meta, space = "") => {
3437
3454
  if (val !== 100) parts.push(`#t${idx}width=${val}`);
3438
3455
  }
3439
3456
  }
3457
+ if (meta.trackReverbSend) {
3458
+ for (const [idx, val] of Object.entries(meta.trackReverbSend)) {
3459
+ if (val !== 0) parts.push(`#t${idx}rev=${val}`);
3460
+ }
3461
+ }
3440
3462
  if (meta.trackEqLow) {
3441
3463
  for (const [idx, val] of Object.entries(meta.trackEqLow)) {
3442
3464
  if (val !== 0) parts.push(`#t${idx}eqlo=${val}`);
@@ -7752,11 +7774,14 @@ var DAW_CSS = `
7752
7774
  user-select: none;
7753
7775
  }
7754
7776
  .dtm-player-balloon {
7755
- position: absolute;
7756
- bottom: calc(100% + 6px);
7757
- left: 50%;
7758
- transform: translateX(-50%);
7759
- z-index: 100;
7777
+ /* body \u76F4\u4E0B\u306B\u751F\u6210\u3055\u308C\u3001JS(positionBalloon)\u304C\u6BCE\u56DE left/top \u3092\u30A2\u30F3\u30AB\u30FC\u306E\u753B\u9762\u5EA7\u6A19\u3067
7778
+ \u8A2D\u5B9A\u3059\u308B\u3002overflow:hidden \u306A\u7956\u5148\uFF08\u6295\u7A3F\u30AB\u30FC\u30C9\u7B49\uFF09\u306B\u30AF\u30EA\u30C3\u30D7\u3055\u308C\u306A\u3044\u3088\u3046 fixed \u3067\u6D6E\u304B\u305B\u308B\u3002
7779
+ top \u306F\u30A2\u30F3\u30AB\u30FC\u4E0A\u7AEF-6px\u3001transform \u3067\u81EA\u8EAB\u306E\u5DE6\u4E0B\u89D2\u3092\u305D\u306E\u70B9\u306B\u5408\u308F\u305B\u308B\u3002 */
7780
+ position: fixed;
7781
+ left: 0;
7782
+ top: 0;
7783
+ transform: translate(-50%, -100%);
7784
+ z-index: 1000;
7760
7785
  display: none;
7761
7786
  pointer-events: none;
7762
7787
  font-family: var(--dtm-font);
@@ -7785,8 +7810,8 @@ var DAW_CSS = `
7785
7810
  animation: dtm-balloon-fade-in 0.1s steps(2);
7786
7811
  }
7787
7812
  @keyframes dtm-balloon-fade-in {
7788
- from { opacity: 0; transform: translateX(-50%) translateY(4px); }
7789
- to { opacity: 1; transform: translateX(-50%) translateY(0); }
7813
+ from { opacity: 0; transform: translate(-50%, -100%) translateY(4px); }
7814
+ to { opacity: 1; transform: translate(-50%, -100%) translateY(0); }
7790
7815
  }
7791
7816
  @keyframes dtm-emoji-jump {
7792
7817
  0% { transform: translateY(0); }
@@ -8209,6 +8234,27 @@ var LYRIC_MODEL_LABELS = {
8209
8234
  };
8210
8235
  var activeBalloonEl = null;
8211
8236
  var activeBalloonTimer = null;
8237
+ var balloonAnchors = /* @__PURE__ */ new WeakMap();
8238
+ var positionBalloon = (balloonEl) => {
8239
+ const anchor = balloonAnchors.get(balloonEl);
8240
+ if (!anchor) return;
8241
+ const rect = anchor.getBoundingClientRect();
8242
+ balloonEl.style.left = `${rect.left + rect.width / 2}px`;
8243
+ balloonEl.style.top = `${rect.top - 6}px`;
8244
+ };
8245
+ var balloonListenersBound = false;
8246
+ var ensureBalloonListeners = () => {
8247
+ if (balloonListenersBound || typeof window === "undefined") return;
8248
+ balloonListenersBound = true;
8249
+ const reposition = () => {
8250
+ if (activeBalloonEl) positionBalloon(activeBalloonEl);
8251
+ };
8252
+ window.addEventListener("scroll", reposition, {
8253
+ capture: true,
8254
+ passive: true
8255
+ });
8256
+ window.addEventListener("resize", reposition, { passive: true });
8257
+ };
8212
8258
  var hideActiveBalloon = () => {
8213
8259
  if (activeBalloonEl) {
8214
8260
  activeBalloonEl.classList.remove("dtm-player-balloon--visible");
@@ -8220,6 +8266,7 @@ var hideActiveBalloon = () => {
8220
8266
  }
8221
8267
  };
8222
8268
  var showBalloon = (balloonEl) => {
8269
+ positionBalloon(balloonEl);
8223
8270
  if (activeBalloonEl === balloonEl) {
8224
8271
  if (activeBalloonTimer) {
8225
8272
  clearTimeout(activeBalloonTimer);
@@ -8484,6 +8531,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
8484
8531
  const doc = target.ownerDocument ?? document;
8485
8532
  const root = doc.createElement("div");
8486
8533
  root.className = "dtm-daw dtm-player";
8534
+ const playerBalloons = [];
8487
8535
  const head = doc.createElement("div");
8488
8536
  head.className = "dtm-player-head";
8489
8537
  const playBtn = doc.createElement("button");
@@ -8829,7 +8877,10 @@ var mountMmlPlayer = (target, mml, options = {}) => {
8829
8877
  balloon.className = "dtm-player-balloon";
8830
8878
  const modelKey = lt.model.toLowerCase();
8831
8879
  balloon.textContent = LYRIC_MODEL_LABELS[modelKey] ?? lt.model;
8832
- em.appendChild(balloon);
8880
+ balloonAnchors.set(balloon, em);
8881
+ doc.body.appendChild(balloon);
8882
+ playerBalloons.push(balloon);
8883
+ ensureBalloonListeners();
8833
8884
  em.addEventListener("mouseenter", () => {
8834
8885
  showBalloon(balloon);
8835
8886
  });
@@ -9290,7 +9341,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
9290
9341
  delaySend: (lt.delay ?? 0) / 100,
9291
9342
  gender: (lt.gender ?? 50) / 100,
9292
9343
  breathiness: (lt.breathiness ?? 50) / 100,
9293
- octaveDouble: lt.octaveDouble,
9344
+ octaveUnison: lt.octaveUnison,
9294
9345
  notes
9295
9346
  };
9296
9347
  });
@@ -9426,9 +9477,10 @@ var mountMmlPlayer = (target, mml, options = {}) => {
9426
9477
  }
9427
9478
  for (const t of blinkTimers) clearTimeout(t);
9428
9479
  clearJumpTimers();
9429
- if (activeBalloonEl && root.contains(activeBalloonEl)) {
9480
+ if (activeBalloonEl && playerBalloons.includes(activeBalloonEl)) {
9430
9481
  hideActiveBalloon();
9431
9482
  }
9483
+ for (const balloon of playerBalloons) balloon.remove();
9432
9484
  root.remove();
9433
9485
  consentOverlayEl?.remove();
9434
9486
  closeInfoModal();
@@ -13130,14 +13182,22 @@ var VIBRATO_INFO_HTML = `
13130
13182
  <p>\u30D4\u30C3\u30C1\u3092\u63FA\u3089\u3059\u52B9\u679C\u306A\u306E\u3067\u3001\u58F0\u8CEA\u305D\u306E\u3082\u306E\u3092\u5909\u3048\u308B\u30B8\u30A7\u30F3\u30C0\u30FC/\u30D6\u30EC\u30B7\u30CD\u30B9\u3068\u306F\u72EC\u7ACB\u3057\u3066\u7D44\u307F\u5408\u308F\u305B\u3089\u308C\u307E\u3059\u3002\u901F\u3055\u30FB\u6DF1\u3055\u306F\u8ABF\u6574\u3067\u304D\u307E\u305B\u3093\uFF08\u6B4C\u3068\u3057\u3066\u7834\u7DBB\u3057\u306B\u304F\u3044\u63A7\u3048\u3081\u306A\u91CF\u306B\u56FA\u5B9A\uFF09\u3002\u66F2\u3084\u7B87\u6240\u3054\u3068\u306B\u639B\u3051\u305F\u3044/\u639B\u3051\u305F\u304F\u306A\u3044\u304C\u3042\u308B\u5834\u5408\u306F\u3001\u30C8\u30E9\u30C3\u30AF\u3092\u5206\u3051\u3066\u6B4C\u8A5E\u3092\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\u3002</p>
13131
13183
  </div>
13132
13184
  `;
13133
- var OCTAVE_DOUBLE_INFO_HTML = `
13185
+ var OCTAVE_UNISON_TOKEN = {
13186
+ none: "",
13187
+ down: "w1",
13188
+ up: "w2",
13189
+ both: "w3"
13190
+ };
13191
+ var OCTAVE_UNISON_INFO_HTML = `
13134
13192
  <div class="dtm-modal-body-content">
13135
13193
  <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
13136
- <p>ON\u306B\u3059\u308B\u3068\u3001\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u5404\u97F3\u7BC0\u30921\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0B\uFF08\u63A7\u3048\u3081\u306A\u97F3\u91CF\uFF09\u3067\u540C\u6642\u306B\u91CD\u306D\u3066\u767A\u97F3\u3057\u307E\u3059\u3002\u58F0\u306B\u539A\u307F\u30FB\u30D1\u30EF\u30FC\u3092\u8DB3\u3059\u300C\u30AA\u30AF\u30BF\u30FC\u30D6\u30C0\u30D6\u30EA\u30F3\u30B0\u300D\u3068\u3044\u3046\u5B9A\u756A\u306E\u30DC\u30FC\u30AB\u30EB\u52A0\u5DE5\u3067\u3059\u3002</p>
13194
+ <p>\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u5404\u97F3\u7BC0\u306B\u3001\u3082\u30461\u58F0\u30921\u30AA\u30AF\u30BF\u30FC\u30D6\u4E0A/\u4E0B/\u4E21\u65B9\uFF08\u63A7\u3048\u3081\u306A\u97F3\u91CF\uFF09\u3067\u540C\u6642\u306B\u91CD\u306D\u3066\u767A\u97F3\u3057\u307E\u3059\u3002\u58F0\u306B\u539A\u307F\u30FB\u30D1\u30EF\u30FC\u3092\u8DB3\u3059\u300C\u30AA\u30AF\u30BF\u30FC\u30D6\u30E6\u30CB\u30BE\u30F3\uFF08\u30C0\u30D6\u30EA\u30F3\u30B0\uFF09\u300D\u3068\u3044\u3046\u5B9A\u756A\u306E\u30DC\u30FC\u30AB\u30EB\u52A0\u5DE5\u3067\u3059\u3002</p>
13195
+ <h4>\u4E0B\u30FB\u4E0A\u30FB\u4E21\u65B9\u306E\u9055\u3044</h4>
13196
+ <p>\u300C\u4E0B\u300D\u306F\u58F0\u306B\u91CD\u307F\u30FB\u30D1\u30EF\u30FC\u3092\u8DB3\u3059\u5B9A\u756A\u306E\u4F7F\u3044\u65B9\uFF08\u30ED\u30C3\u30AF/EDM\u306E\u30D0\u30C3\u30AD\u30F3\u30B0\u7B49\uFF09\u3002\u300C\u4E0A\u300D\u306F\u592A\u3055\u3067\u306F\u306A\u304F\u714C\u3073\u3084\u304B\u3055\u30FB\u53EF\u6190\u3055\u3092\u8DB3\u3059\u4F7F\u3044\u65B9\uFF08\u30A2\u30CB\u30BD\u30F3/\u30CF\u30E2\u30EA\u7B49\uFF09\u3002\u300C\u4E21\u65B9\u300D\u306F\u4E0A\u4E0B\u540C\u6642\u306B\u91CD\u306D\u308B\u7279\u6B8A\u306A\u52B9\u679C\u3067\u3001\u30AD\u30E3\u30E9\u30AF\u30BF\u30FC\u6027\u3092\u5F37\u304F\u51FA\u3057\u305F\u3044\u3068\u304D\u306B\u3002</p>
13137
13197
  <h4>\u4F7F\u3044\u3069\u3053\u308D</h4>
13138
13198
  <p>\u30B5\u30D3\u306E\u6C7A\u3081\u306E\u30D5\u30EC\u30FC\u30BA\u3001\u30ED\u30DC\u30C3\u30C8\u7684/\u30C0\u30FC\u30AF\u306A\u8CEA\u611F\u306E\u6F14\u51FA\u3001\u30E6\u30CB\u30BE\u30F3\u30CF\u30FC\u30E2\u30CB\u30FC\u306A\u3069\u3067\u3088\u304F\u4F7F\u308F\u308C\u307E\u3059\u3002\u66F2\u5168\u4F53\u306B\u639B\u3051\u3063\u3071\u306A\u3057\u306B\u3059\u308B\u3068\u5E38\u306B\u304F\u3069\u3044\u5370\u8C61\u306B\u306A\u308A\u3084\u3059\u3044\u306E\u3067\u3001\u30C8\u30E9\u30C3\u30AF\u3092\u5206\u3051\u3066\u4F7F\u3044\u305F\u3044\u7B87\u6240\u3060\u3051\u306B\u7D5E\u308B\u306E\u304C\u304A\u3059\u3059\u3081\u3067\u3059\u3002</p>
13139
13199
  <h4>\u4ED6\u306E\u8A2D\u5B9A\u3068\u306E\u517C\u306D\u5408\u3044</h4>
13140
- <p>\u91CD\u306D\u308B\u58F0\u306B\u3082\u540C\u3058\u30D3\u30D6\u30E9\u30FC\u30C8/\u30B8\u30A7\u30F3\u30C0\u30FC/\u30D6\u30EC\u30B7\u30CD\u30B9/\u30EA\u30D0\u30FC\u30D6\u9001\u308A/\u30C7\u30A3\u30EC\u30A4\u9001\u308A\u306E\u8A2D\u5B9A\u304C\u305D\u306E\u307E\u307E\u9069\u7528\u3055\u308C\u307E\u3059\u3002\u97F3\u91CF\u6BD4\u30FB\u97F3\u7A0B\u5DEE\uFF081\u30AA\u30AF\u30BF\u30FC\u30D6\u56FA\u5B9A\uFF09\u306F\u8ABF\u6574\u3067\u304D\u307E\u305B\u3093\u3002\u5408\u6210\u304C\u5358\u7D14\u306B2\u500D\u8D70\u308B\u305F\u3081\u3001\u97F3\u6570\u306E\u591A\u3044\u30C8\u30E9\u30C3\u30AF\u3067\u306F\u3084\u3084\u91CD\u304F\u306A\u308A\u307E\u3059\u3002</p>
13200
+ <p>\u91CD\u306D\u308B\u58F0\u306B\u3082\u540C\u3058\u30D3\u30D6\u30E9\u30FC\u30C8/\u30B8\u30A7\u30F3\u30C0\u30FC/\u30D6\u30EC\u30B7\u30CD\u30B9/\u30EA\u30D0\u30FC\u30D6\u9001\u308A/\u30C7\u30A3\u30EC\u30A4\u9001\u308A\u306E\u8A2D\u5B9A\u304C\u305D\u306E\u307E\u307E\u9069\u7528\u3055\u308C\u307E\u3059\u3002\u97F3\u91CF\u6BD4\u30FB\u97F3\u7A0B\u5DEE\uFF081\u30AA\u30AF\u30BF\u30FC\u30D6\u56FA\u5B9A\uFF09\u306F\u8ABF\u6574\u3067\u304D\u307E\u305B\u3093\u3002\u300C\u4E21\u65B9\u300D\u306F\u5408\u6210\u304C3\u500D\u8D70\u308B\u305F\u3081\u3001\u97F3\u6570\u306E\u591A\u3044\u30C8\u30E9\u30C3\u30AF\u3067\u306F\u3084\u3084\u91CD\u304F\u306A\u308A\u307E\u3059\u3002</p>
13141
13201
  </div>
13142
13202
  `;
13143
13203
  var GENDER_INFO_HTML = `
@@ -13252,6 +13312,16 @@ var TRACK_WIDTH_INFO_HTML = `
13252
13312
  <p>\u30B9\u30DE\u30DB\u306E\u30B9\u30D4\u30FC\u30AB\u30FC1\u500B\u306A\u3069\u3001\u30E2\u30CE\u30E9\u30EB\u306B\u8FD1\u3044\u74B0\u5883\u3067\u518D\u751F\u3059\u308B\u3068\u97F3\u304C\u8584\u304F/\u4F4D\u76F8\u304C\u4E71\u308C\u3066\u805E\u3053\u3048\u308B\u3053\u3068\u304C\u3042\u308A\u307E\u3059\uFF08\u5DE6\u53F3\u306E\u5DEE\u5206\u3092\u8A87\u5F35\u3057\u3066\u3044\u308B\u305F\u3081\u3001\u8DB3\u3057\u5408\u308F\u305B\u308B\u3068\u6253\u3061\u6D88\u3057\u5408\u3046\u6210\u5206\u304C\u5897\u3048\u308B\uFF09\u3002\u4E3B\u65CB\u5F8B\u3084\u30DC\u30FC\u30AB\u30EB\u306F\u4E2D\u592E\u4ED8\u8FD1\u3067\u72ED\u3081\u3001\u30D1\u30C3\u30C9\u3084\u30B7\u30F3\u30BB\u306E\u88C5\u98FE\u30D1\u30FC\u30C8\u306F\u5E83\u3052\u308B\u3001\u3068\u3044\u3046\u306E\u304C\u30DF\u30C3\u30AF\u30B9\u306E\u5B9A\u77F3\u3067\u3059\u3002</p>
13253
13313
  </div>
13254
13314
  `;
13315
+ var TRACK_REVERBSEND_INFO_HTML = `
13316
+ <div class="dtm-modal-body-content">
13317
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
13318
+ <p>\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u97F3\u3092\u3001\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\uFF08[reverb]\u30D1\u30CD\u30EB\u306EDecay/Pre Delay/Mix\uFF09\u3078\u3069\u308C\u3060\u3051\u9001\u308B\u304B\u3092 0-100% \u3067\u6C7A\u3081\u307E\u3059\u30020\u306A\u3089\u9001\u3089\u306A\u3044\uFF1D\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306B\u306F\u30EA\u30D0\u30FC\u30D6\u304C\u639B\u304B\u308A\u307E\u305B\u3093\u3002</p>
13319
+ <h4>\u300C\u58F0\u3060\u3051\u30EA\u30D0\u30FC\u30D6\u300D\u3092\u4F5C\u308B\u306B\u306F</h4>
13320
+ <p>\u30EA\u30D0\u30FC\u30D6\u306F\u5168\u30C8\u30E9\u30C3\u30AF\u3078\u4E00\u5F8B\u3067\u639B\u304B\u308B\u308F\u3051\u3067\u306F\u306A\u304F\u3001\u3053\u306E\u9001\u308A\u91CF\u3067\u500B\u5225\u306B\u6C7A\u307E\u308A\u307E\u3059\u3002\u30DC\u30FC\u30AB\u30EB\u306E\u30C8\u30E9\u30C3\u30AF\u3060\u3051\u9001\u308A\u91CF\u3092\u4E0A\u3052\u3001\u4ED6\u306E\u697D\u5668\u30C8\u30E9\u30C3\u30AF\u306F0\u306E\u307E\u307E\u306B\u3059\u308C\u3070\u3001\u58F0\u3060\u3051\u306B\u30EA\u30D0\u30FC\u30D6\u3092\u639B\u3051\u3089\u308C\u307E\u3059\u3002\u4F4E\u57DF\u306E\u697D\u5668\uFF08\u30D9\u30FC\u30B9\u30FB\u30AD\u30C3\u30AF\u7B49\uFF09\u306F\u9001\u3089\u306A\u3044\u3001\u30DC\u30FC\u30AB\u30EB\u3084\u30D1\u30C3\u30C9\u306F\u591A\u3081\u306B\u9001\u308B\u3001\u3068\u3044\u3046\u306E\u304C\u30DF\u30C3\u30AF\u30B9\u306E\u5B9A\u77F3\u3067\u3059\u3002</p>
13321
+ <h4>[reverb]\u30D1\u30CD\u30EB\u306EMix\u3068\u306E\u95A2\u4FC2</h4>
13322
+ <p>Mix\u306F\u300C\u9001\u3089\u308C\u3066\u304D\u305F\u97F3\u3092\u3069\u308C\u3060\u3051\u8FD4\u3059\u304B\u300D\u3068\u3044\u3046\u5168\u4F53\u306E\u8FD4\u308A\u91CF\u3067\u3059\u3002\u5404\u30C8\u30E9\u30C3\u30AF\u306E\u9001\u308A\u91CF\u304C0\u306A\u3089\u3001Mix\u3092\u4E0A\u3052\u3066\u3082\u4F55\u3082\u8FD4\u3063\u3066\u304D\u307E\u305B\u3093\u3002\u307E\u305A\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u9001\u308A\u91CF\u3092\u6C7A\u3081\u3066\u304B\u3089\u3001Mix\u3067\u5168\u4F53\u306E\u639B\u304B\u308A\u5177\u5408\u3092\u5FAE\u8ABF\u6574\u3057\u3066\u304F\u3060\u3055\u3044\u3002</p>
13323
+ </div>
13324
+ `;
13255
13325
  var AUTO_MASTER_INFO_HTML = `
13256
13326
  <div class="dtm-modal-body-content">
13257
13327
  <h4>\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0\u3068\u306F</h4>
@@ -13672,7 +13742,7 @@ var mountDAW = (target, options = {}) => {
13672
13742
  vocalDelay: t.vocalDelay,
13673
13743
  vocalGender: t.vocalGender,
13674
13744
  vocalBreathiness: t.vocalBreathiness,
13675
- vocalOctaveDouble: t.vocalOctaveDouble
13745
+ vocalOctaveUnison: t.vocalOctaveUnison
13676
13746
  };
13677
13747
  if (lyricsDebounceTimer) clearTimeout(lyricsDebounceTimer);
13678
13748
  lyricsDebounceTimer = setTimeout(() => {
@@ -13737,10 +13807,11 @@ var mountDAW = (target, options = {}) => {
13737
13807
  vocalDelay: 0,
13738
13808
  vocalGender: 50,
13739
13809
  vocalBreathiness: 50,
13740
- vocalOctaveDouble: false,
13810
+ vocalOctaveUnison: "none",
13741
13811
  trackInstrument: "",
13742
13812
  trackCompression: 0,
13743
13813
  trackWidth: 100,
13814
+ trackReverbSend: 0,
13744
13815
  trackEqLow: 0,
13745
13816
  trackEqMid: 0,
13746
13817
  trackEqHigh: 0
@@ -13767,7 +13838,7 @@ var mountDAW = (target, options = {}) => {
13767
13838
  delay: t.vocalDelay,
13768
13839
  gender: t.vocalGender,
13769
13840
  breathiness: t.vocalBreathiness,
13770
- octaveDouble: t.vocalOctaveDouble,
13841
+ octaveUnison: t.vocalOctaveUnison,
13771
13842
  syllables
13772
13843
  });
13773
13844
  });
@@ -14577,7 +14648,7 @@ var mountDAW = (target, options = {}) => {
14577
14648
  delaySend: (lt.delay ?? 0) / 100,
14578
14649
  gender: (lt.gender ?? 50) / 100,
14579
14650
  breathiness: (lt.breathiness ?? 50) / 100,
14580
- octaveDouble: lt.octaveDouble,
14651
+ octaveUnison: lt.octaveUnison,
14581
14652
  notes
14582
14653
  };
14583
14654
  }) : [];
@@ -14739,6 +14810,12 @@ var mountDAW = (target, options = {}) => {
14739
14810
  <span class="dtm-label" data-dtm="track-width-label"></span>
14740
14811
  <button class="dtm-infobtn" data-dtm="track-width-info" title="\u30B9\u30C6\u30EC\u30AA\u5E45\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14741
14812
  </div>
14813
+ <div class="dtm-row">
14814
+ <span class="dtm-label">\u30EA\u30D0\u30FC\u30D6\u9001\u308A</span>
14815
+ <input type="range" class="dtm-range dtm-grow" data-dtm="track-reverb-send" min="0" max="100" aria-label="\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\u3078\u306E\u9001\u308A\u91CF\uFF080=\u639B\u304B\u3089\u306A\u3044\uFF09">
14816
+ <span class="dtm-label" data-dtm="track-reverb-send-label"></span>
14817
+ <button class="dtm-infobtn" data-dtm="track-reverb-send-info" title="\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14818
+ </div>
14742
14819
  </details>`;
14743
14820
  const volInput = refs.trackBody.querySelector(
14744
14821
  '[data-dtm="track-vol"]'
@@ -14843,6 +14920,28 @@ var mountDAW = (target, options = {}) => {
14843
14920
  trackWidthInfo.addEventListener("click", () => {
14844
14921
  showModal("\u30B9\u30C6\u30EC\u30AA\u5E45\u306E\u89E3\u8AAC", TRACK_WIDTH_INFO_HTML);
14845
14922
  });
14923
+ const trackReverbSendInput = refs.trackBody.querySelector(
14924
+ '[data-dtm="track-reverb-send"]'
14925
+ );
14926
+ const trackReverbSendLabel = refs.trackBody.querySelector(
14927
+ '[data-dtm="track-reverb-send-label"]'
14928
+ );
14929
+ const trackReverbSendInfo = refs.trackBody.querySelector(
14930
+ '[data-dtm="track-reverb-send-info"]'
14931
+ );
14932
+ trackReverbSendInput.value = String(active.trackReverbSend);
14933
+ trackReverbSendLabel.textContent = `${active.trackReverbSend}%`;
14934
+ trackReverbSendInput.addEventListener("input", () => {
14935
+ active.trackReverbSend = Number.parseInt(trackReverbSendInput.value, 10);
14936
+ trackReverbSendLabel.textContent = `${active.trackReverbSend}%`;
14937
+ options.onTrackReverbSendChange?.(
14938
+ active.config.id,
14939
+ active.trackReverbSend
14940
+ );
14941
+ });
14942
+ trackReverbSendInfo.addEventListener("click", () => {
14943
+ showModal("\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u306E\u89E3\u8AAC", TRACK_REVERBSEND_INFO_HTML);
14944
+ });
14846
14945
  const instRow = document.createElement("div");
14847
14946
  instRow.className = "dtm-row";
14848
14947
  instRow.innerHTML = `<span class="dtm-label">\u697D\u5668</span>`;
@@ -14979,10 +15078,14 @@ var mountDAW = (target, options = {}) => {
14979
15078
  <button class="dtm-infobtn" data-dtm="lyric-vibrato-info" title="\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14980
15079
  </div>
14981
15080
  <div class="dtm-row">
14982
- <span class="dtm-label" style="display:inline-flex;align-items:center;gap:2px">
14983
- <input type="checkbox" data-dtm="lyric-octave-double" aria-label="\u30AA\u30AF\u30BF\u30FC\u30D6\u30C0\u30D6\u30EB">\u30AA\u30AF\u30BF\u30FC\u30D6\u30C0\u30D6\u30EB
14984
- </span>
14985
- <button class="dtm-infobtn" data-dtm="lyric-octave-double-info" title="\u30AA\u30AF\u30BF\u30FC\u30D6\u30C0\u30D6\u30EB\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
15081
+ <span class="dtm-label">\u30AA\u30AF\u30BF\u30FC\u30D6\u30E6\u30CB\u30BE\u30F3</span>
15082
+ <select class="dtm-select dtm-grow" data-dtm="lyric-octave-unison" aria-label="\u30AA\u30AF\u30BF\u30FC\u30D6\u30E6\u30CB\u30BE\u30F3\uFF08\u3082\u30461\u58F0\u3092\u4E0A/\u4E0B\u306B\u91CD\u306D\u308B\uFF09">
15083
+ <option value="none">\u306A\u3057</option>
15084
+ <option value="down">\u4E0B (-1oct)</option>
15085
+ <option value="up">\u4E0A (+1oct)</option>
15086
+ <option value="both">\u4E0A\u4E0B\u4E21\u65B9</option>
15087
+ </select>
15088
+ <button class="dtm-infobtn" data-dtm="lyric-octave-unison-info" title="\u30AA\u30AF\u30BF\u30FC\u30D6\u30E6\u30CB\u30BE\u30F3\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14986
15089
  </div>
14987
15090
  </details>
14988
15091
  <textarea class="dtm-textarea" data-dtm="lyric-input" rows="2" placeholder="\u3072\u3089\u304C\u306A\u30FB\u30AB\u30BF\u30AB\u30CA\u3067\u6B4C\u8A5E\uFF08\u4F8B: \u3069\u308C\u307F\u3075\u3041\u305D\u3089\u3057\u3069\uFF09"></textarea>
@@ -15075,14 +15178,14 @@ var mountDAW = (target, options = {}) => {
15075
15178
  lyricVibratoInfo.addEventListener("click", () => {
15076
15179
  showModal("\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u89E3\u8AAC", VIBRATO_INFO_HTML);
15077
15180
  });
15078
- const lyricOctaveDouble = lyricDiv.querySelector(
15079
- '[data-dtm="lyric-octave-double"]'
15181
+ const lyricOctaveUnison = lyricDiv.querySelector(
15182
+ '[data-dtm="lyric-octave-unison"]'
15080
15183
  );
15081
- const lyricOctaveDoubleInfo = lyricDiv.querySelector(
15082
- '[data-dtm="lyric-octave-double-info"]'
15184
+ const lyricOctaveUnisonInfo = lyricDiv.querySelector(
15185
+ '[data-dtm="lyric-octave-unison-info"]'
15083
15186
  );
15084
- lyricOctaveDoubleInfo.addEventListener("click", () => {
15085
- showModal("\u30AA\u30AF\u30BF\u30FC\u30D6\u30C0\u30D6\u30EB\u89E3\u8AAC", OCTAVE_DOUBLE_INFO_HTML);
15187
+ lyricOctaveUnisonInfo.addEventListener("click", () => {
15188
+ showModal("\u30AA\u30AF\u30BF\u30FC\u30D6\u30E6\u30CB\u30BE\u30F3\u89E3\u8AAC", OCTAVE_UNISON_INFO_HTML);
15086
15189
  });
15087
15190
  const lyricTerms = lyricDiv.querySelector(
15088
15191
  '[data-dtm="lyric-terms"]'
@@ -15154,7 +15257,7 @@ var mountDAW = (target, options = {}) => {
15154
15257
  lyricBreathiness.value = String(active.vocalBreathiness);
15155
15258
  lyricBreathinessLabel.textContent = `${active.vocalBreathiness}`;
15156
15259
  lyricVibrato.checked = active.vocalVibrato;
15157
- lyricOctaveDouble.checked = active.vocalOctaveDouble;
15260
+ lyricOctaveUnison.value = active.vocalOctaveUnison;
15158
15261
  const updateLyricCount = () => {
15159
15262
  const n = normalizeLyrics(lyricInput.value).length;
15160
15263
  lyricCount.textContent = active.lyricModel && n > 0 ? `${n}\u97F3\u7BC0` : "";
@@ -15276,8 +15379,8 @@ var mountDAW = (target, options = {}) => {
15276
15379
  active.vocalVibrato = lyricVibrato.checked;
15277
15380
  fireLyricsChange(active);
15278
15381
  });
15279
- lyricOctaveDouble.addEventListener("change", () => {
15280
- active.vocalOctaveDouble = lyricOctaveDouble.checked;
15382
+ lyricOctaveUnison.addEventListener("change", () => {
15383
+ active.vocalOctaveUnison = lyricOctaveUnison.value;
15281
15384
  fireLyricsChange(active);
15282
15385
  });
15283
15386
  lyricInput.addEventListener("input", () => {
@@ -15408,6 +15511,7 @@ var mountDAW = (target, options = {}) => {
15408
15511
  const trackInstrumentsForMeta = {};
15409
15512
  const trackCompressionForMeta = {};
15410
15513
  const trackWidthForMeta = {};
15514
+ const trackReverbSendForMeta = {};
15411
15515
  const trackEqLowForMeta = {};
15412
15516
  const trackEqMidForMeta = {};
15413
15517
  const trackEqHighForMeta = {};
@@ -15416,6 +15520,8 @@ var mountDAW = (target, options = {}) => {
15416
15520
  if (t.trackCompression !== 0)
15417
15521
  trackCompressionForMeta[i2] = t.trackCompression;
15418
15522
  if (t.trackWidth !== 100) trackWidthForMeta[i2] = t.trackWidth;
15523
+ if (t.trackReverbSend !== 0)
15524
+ trackReverbSendForMeta[i2] = t.trackReverbSend;
15419
15525
  if (t.trackEqLow !== 0) trackEqLowForMeta[i2] = t.trackEqLow;
15420
15526
  if (t.trackEqMid !== 0) trackEqMidForMeta[i2] = t.trackEqMid;
15421
15527
  if (t.trackEqHigh !== 0) trackEqHighForMeta[i2] = t.trackEqHigh;
@@ -15423,6 +15529,7 @@ var mountDAW = (target, options = {}) => {
15423
15529
  const trackInstMeta = Object.keys(trackInstrumentsForMeta).length > 0 ? trackInstrumentsForMeta : void 0;
15424
15530
  const trackCompMeta = Object.keys(trackCompressionForMeta).length > 0 ? trackCompressionForMeta : void 0;
15425
15531
  const trackWidthMeta = Object.keys(trackWidthForMeta).length > 0 ? trackWidthForMeta : void 0;
15532
+ const trackReverbSendMeta = Object.keys(trackReverbSendForMeta).length > 0 ? trackReverbSendForMeta : void 0;
15426
15533
  const trackEqLowMeta = Object.keys(trackEqLowForMeta).length > 0 ? trackEqLowForMeta : void 0;
15427
15534
  const trackEqMidMeta = Object.keys(trackEqMidForMeta).length > 0 ? trackEqMidForMeta : void 0;
15428
15535
  const trackEqHighMeta = Object.keys(trackEqHighForMeta).length > 0 ? trackEqHighForMeta : void 0;
@@ -15443,6 +15550,7 @@ var mountDAW = (target, options = {}) => {
15443
15550
  trackInstruments: trackInstMeta,
15444
15551
  trackCompression: trackCompMeta,
15445
15552
  trackWidth: trackWidthMeta,
15553
+ trackReverbSend: trackReverbSendMeta,
15446
15554
  trackEqLow: trackEqLowMeta,
15447
15555
  trackEqMid: trackEqMidMeta,
15448
15556
  trackEqHigh: trackEqHighMeta
@@ -15466,6 +15574,7 @@ var mountDAW = (target, options = {}) => {
15466
15574
  trackInstruments: trackInstMeta,
15467
15575
  trackCompression: trackCompMeta,
15468
15576
  trackWidth: trackWidthMeta,
15577
+ trackReverbSend: trackReverbSendMeta,
15469
15578
  trackEqLow: trackEqLowMeta,
15470
15579
  trackEqMid: trackEqMidMeta,
15471
15580
  trackEqHigh: trackEqHighMeta
@@ -15521,7 +15630,7 @@ var mountDAW = (target, options = {}) => {
15521
15630
  del: t.vocalDelay,
15522
15631
  gen: t.vocalGender,
15523
15632
  bre: t.vocalBreathiness,
15524
- dbl: t.vocalOctaveDouble
15633
+ uni: t.vocalOctaveUnison
15525
15634
  })).filter(
15526
15635
  (x2) => x2.model.length > 0 && x2.text.length > 0 && x2.notes.length > 0
15527
15636
  ).map((x2) => {
@@ -15535,7 +15644,7 @@ var mountDAW = (target, options = {}) => {
15535
15644
  x2.del === 0 ? "" : `e${x2.del}`,
15536
15645
  x2.gen === 50 ? "" : `g${x2.gen}`,
15537
15646
  x2.bre === 50 ? "" : `h${x2.bre}`,
15538
- x2.dbl ? "w1" : ""
15647
+ OCTAVE_UNISON_TOKEN[x2.uni]
15539
15648
  ].filter((s) => s.length > 0).join(" ");
15540
15649
  const head = params ? `${x2.model} ${params}` : x2.model;
15541
15650
  return `@@${x2.i} ${head} ${x2.text}`;
@@ -15735,6 +15844,11 @@ var mountDAW = (target, options = {}) => {
15735
15844
  t.trackWidth = width;
15736
15845
  options.onTrackWidthChange?.(t.config.id, width);
15737
15846
  }
15847
+ const reverbSend = meta.trackReverbSend?.[i2] ?? 0;
15848
+ if (t.trackReverbSend !== reverbSend) {
15849
+ t.trackReverbSend = reverbSend;
15850
+ options.onTrackReverbSendChange?.(t.config.id, reverbSend);
15851
+ }
15738
15852
  const eqLow = meta.trackEqLow?.[i2] ?? 0;
15739
15853
  if (t.trackEqLow !== eqLow) {
15740
15854
  t.trackEqLow = eqLow;
@@ -15772,7 +15886,7 @@ var mountDAW = (target, options = {}) => {
15772
15886
  active.vocalDelay = 0;
15773
15887
  active.vocalGender = 50;
15774
15888
  active.vocalBreathiness = 50;
15775
- active.vocalOctaveDouble = false;
15889
+ active.vocalOctaveUnison = "none";
15776
15890
  } else {
15777
15891
  for (const t of trackStates) {
15778
15892
  t.lyrics = "";
@@ -15786,7 +15900,7 @@ var mountDAW = (target, options = {}) => {
15786
15900
  t.vocalDelay = 0;
15787
15901
  t.vocalGender = 50;
15788
15902
  t.vocalBreathiness = 50;
15789
- t.vocalOctaveDouble = false;
15903
+ t.vocalOctaveUnison = "none";
15790
15904
  }
15791
15905
  }
15792
15906
  lyrics?.forEach((lt) => {
@@ -15804,7 +15918,7 @@ var mountDAW = (target, options = {}) => {
15804
15918
  t.vocalDelay = lt.delay ?? 0;
15805
15919
  t.vocalGender = lt.gender ?? 50;
15806
15920
  t.vocalBreathiness = lt.breathiness ?? 50;
15807
- t.vocalOctaveDouble = lt.octaveDouble ?? false;
15921
+ t.vocalOctaveUnison = lt.octaveUnison ?? "none";
15808
15922
  });
15809
15923
  for (const p of placements) {
15810
15924
  if (applyActiveOnly && p.trackIndex !== activeTrackIndex) continue;
@@ -15888,7 +16002,7 @@ var mountDAW = (target, options = {}) => {
15888
16002
  active.vocalDelay = 0;
15889
16003
  active.vocalGender = 50;
15890
16004
  active.vocalBreathiness = 50;
15891
- active.vocalOctaveDouble = false;
16005
+ active.vocalOctaveUnison = "none";
15892
16006
  } else {
15893
16007
  clearAll();
15894
16008
  for (const t of trackStates) t.core.setLoadMode(true);
@@ -15904,7 +16018,7 @@ var mountDAW = (target, options = {}) => {
15904
16018
  t.vocalDelay = 0;
15905
16019
  t.vocalGender = 50;
15906
16020
  t.vocalBreathiness = 50;
15907
- t.vocalOctaveDouble = false;
16021
+ t.vocalOctaveUnison = "none";
15908
16022
  }
15909
16023
  }
15910
16024
  const { placements, bpm: parsedBpm } = isAdvanced ? extractMidiPlacementsByTrack(
@@ -16204,6 +16318,10 @@ var mountDAW = (target, options = {}) => {
16204
16318
  t.trackWidth = 115;
16205
16319
  options.onTrackCompressionChange?.(t.config.id, comp);
16206
16320
  options.onTrackWidthChange?.(t.config.id, 115);
16321
+ if (!t.lyricModel) {
16322
+ t.trackReverbSend = 15;
16323
+ options.onTrackReverbSendChange?.(t.config.id, 15);
16324
+ }
16207
16325
  if (t.lyricModel) {
16208
16326
  t.vocalVibrato = true;
16209
16327
  t.vocalReverb = isMainVocal ? 15 : 30;
@@ -17264,7 +17382,7 @@ var mountDAW = (target, options = {}) => {
17264
17382
  t.vocalDelay = data.vocalDelay ?? 0;
17265
17383
  t.vocalGender = data.vocalGender ?? 50;
17266
17384
  t.vocalBreathiness = data.vocalBreathiness ?? 50;
17267
- t.vocalOctaveDouble = data.vocalOctaveDouble ?? false;
17385
+ t.vocalOctaveUnison = data.vocalOctaveUnison ?? "none";
17268
17386
  },
17269
17387
  applyTrackInstrument: (trackIndex, instrumentName) => {
17270
17388
  const t = trackStates[trackIndex];
@@ -17386,7 +17504,7 @@ var playSingingMML = async (mml, options = {}) => {
17386
17504
  delaySend: (lt.delay ?? 0) / 100,
17387
17505
  gender: (lt.gender ?? 50) / 100,
17388
17506
  breathiness: (lt.breathiness ?? 50) / 100,
17389
- octaveDouble: lt.octaveDouble,
17507
+ octaveUnison: lt.octaveUnison,
17390
17508
  notes
17391
17509
  };
17392
17510
  });
@@ -18080,6 +18198,10 @@ var createChannelStrip = (ctx, destination, options = {}) => {
18080
18198
  eqHigh.connect(compressor);
18081
18199
  compressor.connect(splitter);
18082
18200
  merger.connect(destination);
18201
+ const reverbSendGain = ctx.createGain();
18202
+ reverbSendGain.gain.value = clamp5(options.reverbSend ?? 0, 0, 100) / 100;
18203
+ merger.connect(reverbSendGain);
18204
+ if (options.reverbBus) reverbSendGain.connect(options.reverbBus);
18083
18205
  return {
18084
18206
  input,
18085
18207
  setEqLow: (db) => {
@@ -18105,6 +18227,13 @@ var createChannelStrip = (ctx, destination, options = {}) => {
18105
18227
  },
18106
18228
  setCompression: applyCompression,
18107
18229
  setWidth,
18230
+ setReverbSend: (amount) => {
18231
+ reverbSendGain.gain.setTargetAtTime(
18232
+ clamp5(amount, 0, 100) / 100,
18233
+ ctx.currentTime,
18234
+ 0.02
18235
+ );
18236
+ },
18108
18237
  dispose: () => {
18109
18238
  input.disconnect();
18110
18239
  eqLow.disconnect();
@@ -18121,6 +18250,7 @@ var createChannelStrip = (ctx, destination, options = {}) => {
18121
18250
  outL.disconnect();
18122
18251
  outR.disconnect();
18123
18252
  merger.disconnect();
18253
+ reverbSendGain.disconnect();
18124
18254
  }
18125
18255
  };
18126
18256
  };
@@ -18343,7 +18473,6 @@ var createDtmStudio = async (options = {}) => {
18343
18473
  reverbConvolver.normalize = true;
18344
18474
  const reverbWetGain = audioCtx.createGain();
18345
18475
  reverbWetGain.gain.value = reverbAmountToGain(options.reverbAmount ?? 0);
18346
- masterGain.connect(reverbPreDelay);
18347
18476
  reverbPreDelay.connect(reverbConvolver);
18348
18477
  reverbConvolver.connect(reverbWetGain);
18349
18478
  const setReverbAmount = (amount) => {
@@ -18409,7 +18538,9 @@ var createDtmStudio = async (options = {}) => {
18409
18538
  const getChannelStrip = (trackId) => {
18410
18539
  let strip = channelStrips.get(trackId);
18411
18540
  if (!strip) {
18412
- strip = createChannelStrip(audioCtx, masterGain);
18541
+ strip = createChannelStrip(audioCtx, masterGain, {
18542
+ reverbBus: reverbPreDelay
18543
+ });
18413
18544
  channelStrips.set(trackId, strip);
18414
18545
  }
18415
18546
  return strip;
@@ -18446,7 +18577,8 @@ var createDtmStudio = async (options = {}) => {
18446
18577
  voicebanks,
18447
18578
  worldlineScriptUrl: options.worldlineScriptUrl,
18448
18579
  // ボーカルトラック個別のリバーブセンド(`r`トークン)先。マスタリバーブの
18449
- // Convolver 入力へ直接センドする(masterGain 経由のドライ段は素通り)。
18580
+ // Convolver 入力へ直接センドする(PreDelayは経由しない。チャンネルストリップの
18581
+ // reverbSend とは別経路の、音節単位でより細かく制御できるセンド)。
18450
18582
  reverbBus: reverbConvolver,
18451
18583
  // ボーカルトラック個別のディレイセンド(`e`トークン)先。
18452
18584
  delayBus: delayBus.input,
@@ -18744,6 +18876,7 @@ var createDtmStudio = async (options = {}) => {
18744
18876
  onScheduleFade: scheduleFade,
18745
18877
  onTrackCompressionChange: (trackId, amount) => getChannelStrip(trackId).setCompression(amount),
18746
18878
  onTrackWidthChange: (trackId, width) => getChannelStrip(trackId).setWidth(width),
18879
+ onTrackReverbSendChange: (trackId, amount) => getChannelStrip(trackId).setReverbSend(amount),
18747
18880
  onTrackEqLowChange: (trackId, db) => getChannelStrip(trackId).setEqLow(db),
18748
18881
  onTrackEqMidChange: (trackId, db) => getChannelStrip(trackId).setEqMid(db),
18749
18882
  onTrackEqHighChange: (trackId, db) => getChannelStrip(trackId).setEqHigh(db),