@onjmin/dtm 0.1.84 → 0.1.86

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
@@ -2186,8 +2186,11 @@ var parseLyrics = (mml) => {
2186
2186
  let octave = 0;
2187
2187
  let vibrato = false;
2188
2188
  let reverb = 0;
2189
+ let delay = 0;
2190
+ let gender = 50;
2191
+ let breathiness = 50;
2189
2192
  const modelMatch = rest.match(
2190
- /^([a-z_][a-z0-9_]*?)(?=(?:[vqpobr]-?\d)|[^a-z0-9_]|$)(?::(\d+))?/i
2193
+ /^([a-z_][a-z0-9_]*?)(?=(?:[vqpobrghe]-?\d)|[^a-z0-9_]|$)(?::(\d+))?/i
2191
2194
  );
2192
2195
  let model = "";
2193
2196
  const metaTokens = [];
@@ -2242,6 +2245,27 @@ var parseLyrics = (mml) => {
2242
2245
  rest = rest.substring(rMatch[0].length).trim();
2243
2246
  continue;
2244
2247
  }
2248
+ const gMatch = rest.match(/^g(\d+)/i);
2249
+ if (gMatch) {
2250
+ gender = clamp(Number.parseInt(gMatch[1], 10), 0, 100);
2251
+ metaTokens.push(gMatch[0]);
2252
+ rest = rest.substring(gMatch[0].length).trim();
2253
+ continue;
2254
+ }
2255
+ const hMatch = rest.match(/^h(\d+)/i);
2256
+ if (hMatch) {
2257
+ breathiness = clamp(Number.parseInt(hMatch[1], 10), 0, 100);
2258
+ metaTokens.push(hMatch[0]);
2259
+ rest = rest.substring(hMatch[0].length).trim();
2260
+ continue;
2261
+ }
2262
+ const eMatch = rest.match(/^e(\d+)/i);
2263
+ if (eMatch) {
2264
+ delay = clamp(Number.parseInt(eMatch[1], 10), 0, 100);
2265
+ metaTokens.push(eMatch[0]);
2266
+ rest = rest.substring(eMatch[0].length).trim();
2267
+ continue;
2268
+ }
2245
2269
  break;
2246
2270
  }
2247
2271
  const lyricLines = [rest];
@@ -2258,6 +2282,9 @@ var parseLyrics = (mml) => {
2258
2282
  octave,
2259
2283
  vibrato,
2260
2284
  reverb,
2285
+ delay,
2286
+ gender,
2287
+ breathiness,
2261
2288
  syllables,
2262
2289
  metaText: metaTokens.join(" "),
2263
2290
  ...lineBreaks.length > 0 ? { lineBreaks } : {}
@@ -2343,7 +2370,7 @@ var FORMANTS = {
2343
2370
  N: [250, 1e3]
2344
2371
  };
2345
2372
  var midiToFreq = (m) => 440 * 2 ** ((m - 69) / 12);
2346
- var createKlattVoice = (ctx, destination, reverbBus) => {
2373
+ var createKlattVoice = (ctx, destination, reverbBus, delayBus) => {
2347
2374
  const active = /* @__PURE__ */ new Set();
2348
2375
  const voice = (syllable, e) => {
2349
2376
  const t0 = ctx.currentTime + e.when;
@@ -2353,19 +2380,26 @@ var createKlattVoice = (ctx, destination, reverbBus) => {
2353
2380
  const attack = 0.02;
2354
2381
  const release = 0.06;
2355
2382
  const sustainEnd = t0 + Math.max(attack + 0.02, e.duration);
2383
+ const dest = e.destination ?? destination;
2356
2384
  let panner = null;
2357
- let out = destination;
2385
+ let out = dest;
2358
2386
  if (typeof ctx.createStereoPanner === "function") {
2359
2387
  panner = ctx.createStereoPanner();
2360
2388
  panner.pan.value = Math.max(-1, Math.min(1, e.pan ?? 0));
2361
- panner.connect(destination);
2389
+ panner.connect(dest);
2362
2390
  out = panner;
2363
2391
  }
2364
- let sendGain = null;
2392
+ let reverbSendGain = null;
2365
2393
  if (reverbBus && e.reverbSend && e.reverbSend > 0 && panner) {
2366
- sendGain = ctx.createGain();
2367
- sendGain.gain.value = Math.max(0, Math.min(1, e.reverbSend));
2368
- panner.connect(sendGain).connect(reverbBus);
2394
+ reverbSendGain = ctx.createGain();
2395
+ reverbSendGain.gain.value = Math.max(0, Math.min(1, e.reverbSend));
2396
+ panner.connect(reverbSendGain).connect(reverbBus);
2397
+ }
2398
+ let delaySendGain = null;
2399
+ if (delayBus && e.delaySend && e.delaySend > 0 && panner) {
2400
+ delaySendGain = ctx.createGain();
2401
+ delaySendGain.gain.value = Math.max(0, Math.min(1, e.delaySend));
2402
+ panner.connect(delaySendGain).connect(delayBus);
2369
2403
  }
2370
2404
  const osc = ctx.createOscillator();
2371
2405
  osc.type = "sawtooth";
@@ -2422,7 +2456,8 @@ var createKlattVoice = (ctx, destination, reverbBus) => {
2422
2456
  active.delete(osc);
2423
2457
  osc.disconnect();
2424
2458
  panner?.disconnect();
2425
- sendGain?.disconnect();
2459
+ reverbSendGain?.disconnect();
2460
+ delaySendGain?.disconnect();
2426
2461
  };
2427
2462
  };
2428
2463
  voice.stopAll = () => {
@@ -2586,7 +2621,7 @@ var createLocalBackend = async (options) => {
2586
2621
  }
2587
2622
  return p;
2588
2623
  };
2589
- const renderAlias = async (alias, pitch, durationMs, vibrato) => {
2624
+ const renderAlias = async (alias, pitch, durationMs, vibrato, expr) => {
2590
2625
  const pcm = await getPcm(alias);
2591
2626
  if (!pcm || pcm.length === 0) return null;
2592
2627
  const entry = bank.manifest.phonemes[alias];
@@ -2597,7 +2632,9 @@ var createLocalBackend = async (options) => {
2597
2632
  pcm,
2598
2633
  pitch: vibrato ? vibratoPitchCurve(targetHz, lead.preMs) : targetHz,
2599
2634
  durationMs,
2600
- ...lead
2635
+ ...lead,
2636
+ gender: expr?.gender,
2637
+ breathiness: expr?.breathiness
2601
2638
  });
2602
2639
  if (audio) return { pcm: audio, preSec: lead.preMs / 1e3, rate: 1 };
2603
2640
  }
@@ -2662,7 +2699,7 @@ var createWorkerBackend = async (workerUrl, options) => {
2662
2699
  });
2663
2700
  onReady = null;
2664
2701
  onFail = null;
2665
- const renderAlias = (alias, pitch, durationMs, vibrato) => new Promise((resolve) => {
2702
+ const renderAlias = (alias, pitch, durationMs, vibrato, expr) => new Promise((resolve) => {
2666
2703
  const id = ++reqId;
2667
2704
  pending.set(
2668
2705
  id,
@@ -2676,7 +2713,9 @@ var createWorkerBackend = async (workerUrl, options) => {
2676
2713
  alias,
2677
2714
  pitch,
2678
2715
  durationMs,
2679
- vibrato
2716
+ vibrato,
2717
+ gender: expr?.gender,
2718
+ breathiness: expr?.breathiness
2680
2719
  });
2681
2720
  });
2682
2721
  return {
@@ -2705,15 +2744,21 @@ var createKoeVoice = async (ctx, destination, options) => {
2705
2744
  const inflight = /* @__PURE__ */ new Map();
2706
2745
  const active = /* @__PURE__ */ new Set();
2707
2746
  let prevVowel = "";
2708
- const keyOf = (alias, pitch, durationMs, vibrato) => `${alias}|${pitch}|${Math.round(durationMs / 10) * 10}${vibrato ? "|vib" : ""}`;
2709
- const renderInto = (alias, pitch, durationMs, vibrato) => {
2710
- const key = keyOf(alias, pitch, durationMs, vibrato);
2747
+ const keyOf = (alias, pitch, durationMs, vibrato, expr) => `${alias}|${pitch}|${Math.round(durationMs / 10) * 10}${vibrato ? "|vib" : ""}${expr?.gender !== void 0 ? `|g${Math.round(expr.gender * 100)}` : ""}${expr?.breathiness !== void 0 ? `|h${Math.round(expr.breathiness * 100)}` : ""}`;
2748
+ const renderInto = (alias, pitch, durationMs, vibrato, expr) => {
2749
+ const key = keyOf(alias, pitch, durationMs, vibrato, expr);
2711
2750
  const existing = renderCache.get(key);
2712
2751
  if (existing !== void 0) return Promise.resolve(existing);
2713
2752
  const flying = inflight.get(key);
2714
2753
  if (flying) return flying;
2715
2754
  const p = (async () => {
2716
- const out = await backend.renderAlias(alias, pitch, durationMs, vibrato);
2755
+ const out = await backend.renderAlias(
2756
+ alias,
2757
+ pitch,
2758
+ durationMs,
2759
+ vibrato,
2760
+ expr
2761
+ );
2717
2762
  let rendered = null;
2718
2763
  if (out) {
2719
2764
  const buf = ctx.createBuffer(1, out.pcm.length, KOE_SAMPLE_RATE);
@@ -2728,20 +2773,27 @@ var createKoeVoice = async (ctx, destination, options) => {
2728
2773
  return p;
2729
2774
  };
2730
2775
  const LEADCAP_S = 0.09;
2731
- const schedule = (r, t0, peak, pan, reverbSend = 0) => {
2732
- let out = destination;
2776
+ const schedule = (r, t0, peak, pan, reverbSend = 0, delaySend = 0, destOverride) => {
2777
+ const dest = destOverride ?? destination;
2778
+ let out = dest;
2733
2779
  let panner = null;
2734
2780
  if (typeof ctx.createStereoPanner === "function") {
2735
2781
  panner = ctx.createStereoPanner();
2736
2782
  panner.pan.value = Math.max(-1, Math.min(1, pan));
2737
- panner.connect(destination);
2783
+ panner.connect(dest);
2738
2784
  out = panner;
2739
2785
  }
2740
- let sendGain = null;
2786
+ let reverbSendGain = null;
2741
2787
  if (options.reverbBus && reverbSend > 0 && panner) {
2742
- sendGain = ctx.createGain();
2743
- sendGain.gain.value = Math.max(0, Math.min(1, reverbSend));
2744
- panner.connect(sendGain).connect(options.reverbBus);
2788
+ reverbSendGain = ctx.createGain();
2789
+ reverbSendGain.gain.value = Math.max(0, Math.min(1, reverbSend));
2790
+ panner.connect(reverbSendGain).connect(options.reverbBus);
2791
+ }
2792
+ let delaySendGain = null;
2793
+ if (options.delayBus && delaySend > 0 && panner) {
2794
+ delaySendGain = ctx.createGain();
2795
+ delaySendGain.gain.value = Math.max(0, Math.min(1, delaySend));
2796
+ panner.connect(delaySendGain).connect(options.delayBus);
2745
2797
  }
2746
2798
  const src = ctx.createBufferSource();
2747
2799
  src.buffer = r.audio;
@@ -2768,7 +2820,8 @@ var createKoeVoice = async (ctx, destination, options) => {
2768
2820
  src.disconnect();
2769
2821
  env.disconnect();
2770
2822
  panner?.disconnect();
2771
- sendGain?.disconnect();
2823
+ reverbSendGain?.disconnect();
2824
+ delaySendGain?.disconnect();
2772
2825
  };
2773
2826
  };
2774
2827
  const model = (syllable, e) => {
@@ -2787,10 +2840,11 @@ var createKoeVoice = async (ctx, destination, options) => {
2787
2840
  const pan = e.pan ?? 0;
2788
2841
  const durationMs = Math.max(60, e.duration * 1e3);
2789
2842
  void renderInto(alias, e.pitch, durationMs).then((r) => {
2790
- if (r) schedule(r, t0, peak, pan, e.reverbSend);
2843
+ if (r)
2844
+ schedule(r, t0, peak, pan, e.reverbSend, e.delaySend, e.destination);
2791
2845
  });
2792
2846
  };
2793
- model.renderToCache = async (syllable, prevVowelArg, pitch, durationMs, vibrato) => {
2847
+ model.renderToCache = async (syllable, prevVowelArg, pitch, durationMs, vibrato, expr) => {
2794
2848
  if (syllable.consonant === "Q" || syllable.vowel === "") return null;
2795
2849
  const alias = resolveKoeAlias(
2796
2850
  backend.hasAlias,
@@ -2802,12 +2856,12 @@ var createKoeVoice = async (ctx, destination, options) => {
2802
2856
  if (!alias) return null;
2803
2857
  const dMs = Math.max(60, durationMs);
2804
2858
  const vib = !!vibrato && dMs / 1e3 >= VIBRATO_MIN_SEC;
2805
- const r = await renderInto(alias, pitch, dMs, vib);
2806
- return r ? keyOf(alias, pitch, dMs, vib) : null;
2859
+ const r = await renderInto(alias, pitch, dMs, vib, expr);
2860
+ return r ? keyOf(alias, pitch, dMs, vib, expr) : null;
2807
2861
  };
2808
- model.scheduleCached = (key, t0, peak, pan, reverbSend) => {
2862
+ model.scheduleCached = (key, t0, peak, pan, reverbSend, delaySend, dest) => {
2809
2863
  const r = renderCache.get(key);
2810
- if (r) schedule(r, t0, peak, pan, reverbSend);
2864
+ if (r) schedule(r, t0, peak, pan, reverbSend, delaySend, dest);
2811
2865
  };
2812
2866
  model.stopAll = () => {
2813
2867
  for (const src of active) {
@@ -2837,8 +2891,12 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2837
2891
  let streamSession = 0;
2838
2892
  const masterGain = ctx.createGain();
2839
2893
  masterGain.connect(destination);
2894
+ let masterVolumeScalar = 1;
2840
2895
  const loaded = /* @__PURE__ */ new Map([
2841
- [FALLBACK_MODEL, createKlattVoice(ctx, masterGain, options.reverbBus)]
2896
+ [
2897
+ FALLBACK_MODEL,
2898
+ createKlattVoice(ctx, masterGain, options.reverbBus, options.delayBus)
2899
+ ]
2842
2900
  ]);
2843
2901
  const loading = /* @__PURE__ */ new Map();
2844
2902
  const load2 = (model) => {
@@ -2859,7 +2917,8 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2859
2917
  worldlineScriptUrl: options.worldlineScriptUrl,
2860
2918
  lightweight: options.lightweight,
2861
2919
  voiceWorkerUrl: options.voiceWorkerUrl,
2862
- reverbBus: options.reverbBus
2920
+ reverbBus: options.reverbBus,
2921
+ delayBus: options.delayBus
2863
2922
  })
2864
2923
  ))().then((v) => {
2865
2924
  loaded.set(m, v);
@@ -2901,10 +2960,14 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2901
2960
  const m = loaded.get(track.model.toLowerCase());
2902
2961
  if (!m?.renderToCache) continue;
2903
2962
  let n = 0;
2963
+ const expr = {
2964
+ gender: track.gender,
2965
+ breathiness: track.breathiness
2966
+ };
2904
2967
  forEachSungNote(track, (note, prevVowel) => {
2905
2968
  if (n >= count && note.startSec >= STREAM_LOOKAHEAD_SEC) return;
2906
2969
  n++;
2907
- tasks.push({ model: m, note, prevVowel, vibrato: track.vibrato });
2970
+ tasks.push({ model: m, note, prevVowel, vibrato: track.vibrato, expr });
2908
2971
  });
2909
2972
  }
2910
2973
  const total = tasks.length;
@@ -2920,7 +2983,8 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2920
2983
  task.prevVowel,
2921
2984
  task.note.pitch,
2922
2985
  task.note.durationSec * 1e3,
2923
- task.vibrato
2986
+ task.vibrato,
2987
+ task.expr
2924
2988
  ) ?? Promise.resolve(null));
2925
2989
  done++;
2926
2990
  onProgress?.(done, total);
@@ -2966,13 +3030,24 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2966
3030
  prevVowel,
2967
3031
  note.pitch,
2968
3032
  note.durationSec * 1e3,
2969
- track.vibrato
3033
+ track.vibrato,
3034
+ { gender: track.gender, breathiness: track.breathiness }
2970
3035
  );
2971
3036
  if (session !== streamSession) return;
2972
3037
  if (key) {
2973
3038
  const delay = ctx.currentTime - t0;
2974
3039
  if (delay < 0.05) {
2975
- scheduleCached(key, t0, peak, track.pan, track.reverbSend);
3040
+ const dest = options.getTrackDestination?.(track.id ?? "");
3041
+ const effPeak = dest ? peak * masterVolumeScalar : peak;
3042
+ scheduleCached(
3043
+ key,
3044
+ t0,
3045
+ effPeak,
3046
+ track.pan,
3047
+ track.reverbSend,
3048
+ track.delaySend,
3049
+ dest
3050
+ );
2976
3051
  opts?.onScheduled?.(track, note, t0);
2977
3052
  } else {
2978
3053
  console.warn(
@@ -2984,15 +3059,19 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2984
3059
  })();
2985
3060
  } else {
2986
3061
  const when = t0 - ctx.currentTime;
3062
+ const dest = options.getTrackDestination?.(track.id ?? "");
3063
+ const effPeak = dest ? peak * masterVolumeScalar : peak;
2987
3064
  model(note.syllable, {
2988
- trackId: "",
3065
+ trackId: track.id ?? "",
2989
3066
  pitch: note.pitch,
2990
3067
  velocity: 100,
2991
- volume: peak,
3068
+ volume: effPeak,
2992
3069
  when,
2993
3070
  duration: note.durationSec,
2994
3071
  pan: track.pan,
2995
- reverbSend: track.reverbSend
3072
+ reverbSend: track.reverbSend,
3073
+ delaySend: track.delaySend,
3074
+ destination: dest
2996
3075
  });
2997
3076
  opts?.onScheduled?.(track, note, t0);
2998
3077
  await new Promise((resolve) => setTimeout(resolve, 0));
@@ -3017,7 +3096,9 @@ var createSingingVoices = (ctx, destination, options = {}) => {
3017
3096
  for (const v of loaded.values()) v.reset?.();
3018
3097
  };
3019
3098
  const setVolume = (gain) => {
3020
- masterGain.gain.value = Math.max(0, gain);
3099
+ const g = Math.max(0, gain);
3100
+ masterGain.gain.value = g;
3101
+ masterVolumeScalar = g;
3021
3102
  };
3022
3103
  return {
3023
3104
  loadModels,
@@ -3051,8 +3132,13 @@ var PITCH_MAP = {
3051
3132
  b: 11
3052
3133
  };
3053
3134
  var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
3054
- var META_DIRECTIVE = /#(inst|drum|drumfont|volume|drumvolume|mode)=([\w-]+)/gi;
3135
+ var META_DIRECTIVE = /#(inst|drum|drumfont|volume|drumvolume|reverb|reverbdecay|reverbpredelay|delay|delaydiv|mode)=([\w-]+)/gi;
3055
3136
  var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
3137
+ var TRACK_COMP_DIRECTIVE = /#t(\d+)comp=(\d+)/gi;
3138
+ var TRACK_WIDTH_DIRECTIVE = /#t(\d+)width=(\d+)/gi;
3139
+ var TRACK_EQLOW_DIRECTIVE = /#t(\d+)eqlo=(-?\d+)/gi;
3140
+ var TRACK_EQMID_DIRECTIVE = /#t(\d+)eqmid=(-?\d+)/gi;
3141
+ var TRACK_EQHIGH_DIRECTIVE = /#t(\d+)eqhi=(-?\d+)/gi;
3056
3142
  var parseMmlMeta = (mml) => {
3057
3143
  const meta = {};
3058
3144
  for (const m of mml.matchAll(META_DIRECTIVE)) {
@@ -3066,6 +3152,20 @@ var parseMmlMeta = (mml) => {
3066
3152
  } else if (key === "drumvolume") {
3067
3153
  const dv = Number.parseInt(m[2], 10);
3068
3154
  if (!Number.isNaN(dv)) meta.drumVolume = dv;
3155
+ } else if (key === "reverb") {
3156
+ const rv = Number.parseInt(m[2], 10);
3157
+ if (!Number.isNaN(rv)) meta.reverb = clamp2(rv, 0, 100);
3158
+ } else if (key === "reverbdecay") {
3159
+ const rd = Number.parseInt(m[2], 10);
3160
+ if (!Number.isNaN(rd)) meta.reverbDecay = clamp2(rd, 3, 40);
3161
+ } else if (key === "reverbpredelay") {
3162
+ const rp = Number.parseInt(m[2], 10);
3163
+ if (!Number.isNaN(rp)) meta.reverbPreDelay = clamp2(rp, 0, 150);
3164
+ } else if (key === "delay") {
3165
+ const dv = Number.parseInt(m[2], 10);
3166
+ if (!Number.isNaN(dv)) meta.delay = clamp2(dv, 0, 100);
3167
+ } else if (key === "delaydiv") {
3168
+ if (["4", "8", "8d", "16"].includes(m[2])) meta.delayDivision = m[2];
3069
3169
  } else if (key === "mode") {
3070
3170
  if (m[2] === "simple" || m[2] === "advanced") {
3071
3171
  meta.mode = m[2];
@@ -3080,9 +3180,49 @@ var parseMmlMeta = (mml) => {
3080
3180
  meta.trackInstruments[idx] = name;
3081
3181
  }
3082
3182
  }
3183
+ for (const m of mml.matchAll(TRACK_COMP_DIRECTIVE)) {
3184
+ const idx = Number.parseInt(m[1], 10);
3185
+ const val = clamp2(Number.parseInt(m[2], 10), 0, 100);
3186
+ if (!Number.isNaN(idx) && !Number.isNaN(val)) {
3187
+ meta.trackCompression ??= {};
3188
+ meta.trackCompression[idx] = val;
3189
+ }
3190
+ }
3191
+ for (const m of mml.matchAll(TRACK_WIDTH_DIRECTIVE)) {
3192
+ const idx = Number.parseInt(m[1], 10);
3193
+ const val = clamp2(Number.parseInt(m[2], 10), 0, 200);
3194
+ if (!Number.isNaN(idx) && !Number.isNaN(val)) {
3195
+ meta.trackWidth ??= {};
3196
+ meta.trackWidth[idx] = val;
3197
+ }
3198
+ }
3199
+ for (const m of mml.matchAll(TRACK_EQLOW_DIRECTIVE)) {
3200
+ const idx = Number.parseInt(m[1], 10);
3201
+ const val = clamp2(Number.parseInt(m[2], 10), -12, 12);
3202
+ if (!Number.isNaN(idx) && !Number.isNaN(val)) {
3203
+ meta.trackEqLow ??= {};
3204
+ meta.trackEqLow[idx] = val;
3205
+ }
3206
+ }
3207
+ for (const m of mml.matchAll(TRACK_EQMID_DIRECTIVE)) {
3208
+ const idx = Number.parseInt(m[1], 10);
3209
+ const val = clamp2(Number.parseInt(m[2], 10), -12, 12);
3210
+ if (!Number.isNaN(idx) && !Number.isNaN(val)) {
3211
+ meta.trackEqMid ??= {};
3212
+ meta.trackEqMid[idx] = val;
3213
+ }
3214
+ }
3215
+ for (const m of mml.matchAll(TRACK_EQHIGH_DIRECTIVE)) {
3216
+ const idx = Number.parseInt(m[1], 10);
3217
+ const val = clamp2(Number.parseInt(m[2], 10), -12, 12);
3218
+ if (!Number.isNaN(idx) && !Number.isNaN(val)) {
3219
+ meta.trackEqHigh ??= {};
3220
+ meta.trackEqHigh[idx] = val;
3221
+ }
3222
+ }
3083
3223
  return meta;
3084
3224
  };
3085
- var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "").replace(TRACK_INST_DIRECTIVE, "");
3225
+ 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, "");
3086
3226
  var formatMmlMeta = (meta, space = "") => {
3087
3227
  const parts = [];
3088
3228
  if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
@@ -3091,12 +3231,47 @@ var formatMmlMeta = (meta, space = "") => {
3091
3231
  if (meta.volume !== void 0) parts.push(`#volume=${meta.volume}`);
3092
3232
  if (meta.drumVolume !== void 0)
3093
3233
  parts.push(`#drumvolume=${meta.drumVolume}`);
3234
+ if (meta.reverb !== void 0 && meta.reverb !== 0)
3235
+ parts.push(`#reverb=${meta.reverb}`);
3236
+ if (meta.reverbDecay !== void 0 && meta.reverbDecay !== 22)
3237
+ parts.push(`#reverbdecay=${meta.reverbDecay}`);
3238
+ if (meta.reverbPreDelay !== void 0 && meta.reverbPreDelay !== 0)
3239
+ parts.push(`#reverbpredelay=${meta.reverbPreDelay}`);
3240
+ if (meta.delay !== void 0 && meta.delay !== 0)
3241
+ parts.push(`#delay=${meta.delay}`);
3242
+ if (meta.delayDivision && meta.delayDivision !== "8")
3243
+ parts.push(`#delaydiv=${meta.delayDivision}`);
3094
3244
  if (meta.mode) parts.push(`#mode=${meta.mode}`);
3095
3245
  if (meta.trackInstruments) {
3096
3246
  for (const [idx, name] of Object.entries(meta.trackInstruments)) {
3097
3247
  if (name) parts.push(`#t${idx}inst=${name}`);
3098
3248
  }
3099
3249
  }
3250
+ if (meta.trackCompression) {
3251
+ for (const [idx, val] of Object.entries(meta.trackCompression)) {
3252
+ if (val !== 0) parts.push(`#t${idx}comp=${val}`);
3253
+ }
3254
+ }
3255
+ if (meta.trackWidth) {
3256
+ for (const [idx, val] of Object.entries(meta.trackWidth)) {
3257
+ if (val !== 100) parts.push(`#t${idx}width=${val}`);
3258
+ }
3259
+ }
3260
+ if (meta.trackEqLow) {
3261
+ for (const [idx, val] of Object.entries(meta.trackEqLow)) {
3262
+ if (val !== 0) parts.push(`#t${idx}eqlo=${val}`);
3263
+ }
3264
+ }
3265
+ if (meta.trackEqMid) {
3266
+ for (const [idx, val] of Object.entries(meta.trackEqMid)) {
3267
+ if (val !== 0) parts.push(`#t${idx}eqmid=${val}`);
3268
+ }
3269
+ }
3270
+ if (meta.trackEqHigh) {
3271
+ for (const [idx, val] of Object.entries(meta.trackEqHigh)) {
3272
+ if (val !== 0) parts.push(`#t${idx}eqhi=${val}`);
3273
+ }
3274
+ }
3100
3275
  return parts.join(space);
3101
3276
  };
3102
3277
  var parseMML = (mml, options = {}) => {
@@ -6838,6 +7013,23 @@ var DAW_CSS = `
6838
7013
  @keyframes dtm-blink { 0%,100%{opacity:1} 50%{opacity:0} }
6839
7014
  .dtm-blink { animation: dtm-blink 1s steps(1) infinite; }
6840
7015
 
7016
+ /* \u2500\u2500\u2500 \u97F3\u5272\u308C\u691C\u77E5\u30D0\u30C3\u30B8 \u2500\u2500\u2500 \u30AF\u30EA\u30C3\u30D7\u767A\u751F\u4E2D\u3060\u3051\u8868\u793A\u3055\u308C\u308B\u8B66\u544A\u30DC\u30BF\u30F3\u3002\u30AF\u30EA\u30C3\u30AF\u3067\u6D88\u305B\u308B\u3002 */
7017
+ .dtm-clip-badge {
7018
+ flex: 0 0 auto;
7019
+ min-height: 24px;
7020
+ padding: 0 8px;
7021
+ border: 2px solid var(--c-black);
7022
+ background: var(--dtm-danger);
7023
+ color: var(--c-white);
7024
+ font-family: var(--dtm-font);
7025
+ font-size: 10px;
7026
+ letter-spacing: .1em;
7027
+ cursor: pointer;
7028
+ box-shadow: 0 0 0 2px var(--dtm-danger), 2px 2px 0 var(--c-black);
7029
+ animation: dtm-blink .5s steps(1) infinite;
7030
+ }
7031
+ .dtm-clip-badge:active { transform: translate(2px,2px); box-shadow: none; }
7032
+
6841
7033
  /* \u2500\u2500\u2500 \u30A4\u30F3\u30D5\u30A9\u30DC\u30BF\u30F3 \u2500\u2500\u2500 */
6842
7034
  .dtm-infobtn {
6843
7035
  display: inline-flex;
@@ -7113,6 +7305,37 @@ var DAW_CSS = `
7113
7305
  object-fit: cover;
7114
7306
  image-rendering: pixelated;
7115
7307
  }
7308
+ /* \u6B4C\u8A5E\u30C8\u30E9\u30C3\u30AF\u306E\u300C\u8A73\u7D30\u8A2D\u5B9A\u300D\u2014 \u5E38\u7528\u3057\u306A\u3044\u30D1\u30E9\u30E1\u30FC\u30BF\uFF08\u30AA\u30AF\u30BF\u30FC\u30D6/\u5B9A\u4F4D/\u30EA\u30D0\u30FC\u30D6\u9001\u308A/
7309
+ \u30B8\u30A7\u30F3\u30C0\u30FC/\u30D6\u30EC\u30B7\u30CD\u30B9/\u30D3\u30D6\u30E9\u30FC\u30C8\uFF09\u3092\u7573\u3093\u3067\u304A\u304F\u8EFD\u91CF\u306A details\u3002dtm-panel \u307B\u3069
7310
+ \u4EF0\u3005\u3057\u304F\u305B\u305A\u3001\u63A7\u3048\u3081\u306A\u4ED5\u5207\u308A\u7DDA\uFF0B\u5C0F\u3055\u3044\u25B6\u30DE\u30FC\u30AB\u30FC\u3060\u3051\u4ED8\u3051\u308B\u3002 */
7311
+ .dtm-advanced {
7312
+ border-top: 1px dashed var(--dtm-border2);
7313
+ padding-top: 6px;
7314
+ display: flex;
7315
+ flex-direction: column;
7316
+ gap: 6px;
7317
+ }
7318
+ .dtm-advanced > summary {
7319
+ list-style: none;
7320
+ cursor: pointer;
7321
+ font-size: 11px;
7322
+ letter-spacing: .08em;
7323
+ color: var(--dtm-text);
7324
+ display: flex;
7325
+ align-items: center;
7326
+ gap: 6px;
7327
+ min-height: 28px;
7328
+ padding: 0 8px;
7329
+ border: 2px solid var(--dtm-border2);
7330
+ background: var(--dtm-deep);
7331
+ box-shadow: 2px 2px 0 var(--c-black);
7332
+ width: fit-content;
7333
+ }
7334
+ .dtm-advanced > summary:active { transform: translate(2px,2px); box-shadow: none; }
7335
+ .dtm-advanced > summary::-webkit-details-marker { display: none; }
7336
+ .dtm-advanced > summary::before { content: "\u25B6"; font-size: 9px; color: var(--dtm-accent); }
7337
+ .dtm-advanced[open] > summary::before { content: "\u25BC"; }
7338
+ .dtm-advanced[open] > summary { border-color: var(--dtm-primary); color: var(--dtm-primary); }
7116
7339
 
7117
7340
  /* \u2500\u2500\u2500 \u5E83\u5E45\u62E1\u5F35 \u2500\u2500\u2500 */
7118
7341
  @media (min-width: 768px) {
@@ -8884,6 +9107,9 @@ var mountMmlPlayer = (target, mml, options = {}) => {
8884
9107
  pan: panToStereo(lt.pan ?? DEFAULT_PAN),
8885
9108
  vibrato: lt.vibrato,
8886
9109
  reverbSend: (lt.reverb ?? 0) / 100,
9110
+ delaySend: (lt.delay ?? 0) / 100,
9111
+ gender: (lt.gender ?? 50) / 100,
9112
+ breathiness: (lt.breathiness ?? 50) / 100,
8887
9113
  notes
8888
9114
  };
8889
9115
  });
@@ -10561,6 +10787,71 @@ var mountChordPlayer = (target, chords, options = {}) => {
10561
10787
  };
10562
10788
  };
10563
10789
 
10790
+ // src/delay.ts
10791
+ var DELAY_DIVISIONS = [
10792
+ { value: "4", label: "4\u5206", beats: 1 },
10793
+ { value: "8", label: "8\u5206", beats: 0.5 },
10794
+ { value: "8d", label: "\u4ED8\u70B98\u5206", beats: 0.75 },
10795
+ { value: "16", label: "16\u5206", beats: 0.25 }
10796
+ ];
10797
+ var clamp3 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
10798
+ var divisionToSeconds = (division, bpm) => {
10799
+ const beats = DELAY_DIVISIONS.find((d) => d.value === division)?.beats ?? 0.5;
10800
+ const safeBpm = bpm > 0 ? bpm : 120;
10801
+ return 60 / safeBpm * beats;
10802
+ };
10803
+ var DELAY_MAX_WET = 0.45;
10804
+ var delayAmountToGain = (amount) => clamp3(amount, 0, 100) / 100 * DELAY_MAX_WET;
10805
+ var FEEDBACK_GAIN = 0.3;
10806
+ var MAX_DELAY_SEC = 2;
10807
+ var createDelayBus = (ctx, destination, options = {}) => {
10808
+ const input = ctx.createGain();
10809
+ const delayNode = ctx.createDelay(MAX_DELAY_SEC);
10810
+ const feedback = ctx.createGain();
10811
+ feedback.gain.value = FEEDBACK_GAIN;
10812
+ const wetGain = ctx.createGain();
10813
+ wetGain.gain.value = delayAmountToGain(options.amount ?? 0);
10814
+ input.connect(delayNode);
10815
+ delayNode.connect(feedback);
10816
+ feedback.connect(delayNode);
10817
+ delayNode.connect(wetGain);
10818
+ wetGain.connect(destination);
10819
+ let bpm = options.bpm ?? 120;
10820
+ let division = options.division ?? "8";
10821
+ const applyTime = () => {
10822
+ delayNode.delayTime.setTargetAtTime(
10823
+ divisionToSeconds(division, bpm),
10824
+ ctx.currentTime,
10825
+ 0.05
10826
+ );
10827
+ };
10828
+ applyTime();
10829
+ return {
10830
+ input,
10831
+ setAmount: (amount) => {
10832
+ wetGain.gain.setTargetAtTime(
10833
+ delayAmountToGain(amount),
10834
+ ctx.currentTime,
10835
+ 0.02
10836
+ );
10837
+ },
10838
+ setDivision: (d) => {
10839
+ division = d;
10840
+ applyTime();
10841
+ },
10842
+ setBpm: (b) => {
10843
+ bpm = b;
10844
+ applyTime();
10845
+ },
10846
+ dispose: () => {
10847
+ input.disconnect();
10848
+ delayNode.disconnect();
10849
+ feedback.disconnect();
10850
+ wetGain.disconnect();
10851
+ }
10852
+ };
10853
+ };
10854
+
10564
10855
  // src/daw-ui.ts
10565
10856
  var q = (root, sel) => root.querySelector(sel);
10566
10857
  var buildUI = (target, options) => {
@@ -10585,6 +10876,7 @@ var buildUI = (target, options) => {
10585
10876
  <button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
10586
10877
  <label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
10587
10878
  <span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
10879
+ <button class="dtm-clip-badge dtm-hidden" data-dtm="clip-badge" title="\u97F3\u5272\u308C\u691C\u77E5\uFF08\u30AF\u30EA\u30C3\u30AF\u3067\u6D88\u3059\uFF09">CLIP</button>
10588
10880
  <span class="dtm-grow"></span>
10589
10881
  <span class="dtm-label">BPM</span>
10590
10882
  <input type="number" class="dtm-input dtm-input--num" data-dtm="bpm" value="${defaultBpm}" min="20" max="300">
@@ -10622,18 +10914,8 @@ var buildUI = (target, options) => {
10622
10914
  <div class="dtm-hscroll" data-dtm="hscroll"><div class="dtm-hscroll-thumb" data-dtm="hscroll-thumb"></div></div>
10623
10915
 
10624
10916
  <details class="dtm-panel" open>
10625
- <summary>\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A</summary>
10917
+ <summary>\u500B\u5225\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A</summary>
10626
10918
  <div class="dtm-panel-body">
10627
- <div class="dtm-row">
10628
- <span class="dtm-label">\u5168\u4F53\u97F3\u91CF</span>
10629
- <input type="range" class="dtm-range dtm-grow" data-dtm="master-volume" value="50" min="0" max="100">
10630
- <span class="dtm-label" data-dtm="master-volume-label">50%</span>
10631
- </div>
10632
- <div class="dtm-row">
10633
- <span class="dtm-label">\u30EA\u30D0\u30FC\u30D6</span>
10634
- <input type="range" class="dtm-range dtm-grow" data-dtm="reverb-amount" value="0" min="0" max="100" aria-label="\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\uFF08\u5168\u30C8\u30E9\u30C3\u30AF\u3078\u4E00\u5F8B\u306B\u639B\u304B\u308B\u6B8B\u97FF\uFF09" title="\u30EA\u30D0\u30FC\u30D6">
10635
- <span class="dtm-label" data-dtm="reverb-amount-label">0%</span>
10636
- </div>
10637
10919
  <div class="dtm-track-body" data-dtm="track-body"></div>
10638
10920
  </div>
10639
10921
  </details>
@@ -10666,6 +10948,48 @@ var buildUI = (target, options) => {
10666
10948
  </div>
10667
10949
  </details>
10668
10950
 
10951
+ <details class="dtm-panel" open>
10952
+ <summary>\u5168\u4F53\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A</summary>
10953
+ <div class="dtm-panel-body">
10954
+ <div data-dtm="preset-select-slot"></div>
10955
+ <div class="dtm-row">
10956
+ <span class="dtm-label">\u5168\u4F53\u97F3\u91CF</span>
10957
+ <input type="range" class="dtm-range dtm-grow" data-dtm="master-volume" value="50" min="0" max="100">
10958
+ <span class="dtm-label" data-dtm="master-volume-label">50%</span>
10959
+ </div>
10960
+ <div class="dtm-row">
10961
+ <button class="dtm-btn dtm-btn--ghost dtm-btn--xs" data-dtm="auto-master" title="\u97F3\u5727\u30FB\u30B9\u30C6\u30EC\u30AA\u5E45\u30FB\u30EA\u30D0\u30FC\u30D6\u3092\u5546\u696D\u66F2\u5BC4\u308A\u306E\u5024\u306B\u4E00\u62EC\u8A2D\u5B9A\u3057\u307E\u3059">\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0</button>
10962
+ <button class="dtm-infobtn" data-dtm="auto-master-info" title="\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
10963
+ <span class="dtm-grow"></span>
10964
+ </div>
10965
+ <div class="dtm-row">
10966
+ <span class="dtm-label">\u30EA\u30D0\u30FC\u30D6</span>
10967
+ <input type="range" class="dtm-range dtm-grow" data-dtm="reverb-amount" value="0" min="0" max="100" aria-label="\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\uFF08\u5168\u30C8\u30E9\u30C3\u30AF\u3078\u4E00\u5F8B\u306B\u639B\u304B\u308B\u6B8B\u97FF\uFF09">
10968
+ <span class="dtm-label" data-dtm="reverb-amount-label">0%</span>
10969
+ <button class="dtm-infobtn" data-dtm="reverb-amount-info" title="\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
10970
+ </div>
10971
+ <div class="dtm-row">
10972
+ <span class="dtm-label">Decay</span>
10973
+ <input type="range" class="dtm-range dtm-grow" data-dtm="reverb-decay" value="22" min="3" max="40" step="1" aria-label="\u30EA\u30D0\u30FC\u30D6\u306EDecay\uFF08\u6B8B\u97FF\u306E\u9577\u3055\u3001\u79D2\uFF09">
10974
+ <span class="dtm-label" data-dtm="reverb-decay-label">2.2s</span>
10975
+ </div>
10976
+ <div class="dtm-row">
10977
+ <span class="dtm-label">Pre Delay</span>
10978
+ <input type="range" class="dtm-range dtm-grow" data-dtm="reverb-predelay" value="0" min="0" max="150" step="5" aria-label="\u30EA\u30D0\u30FC\u30D6\u306EPre Delay\uFF08\u539F\u97F3\u304B\u3089\u6B8B\u97FF\u304C\u7ACB\u3061\u4E0A\u304C\u308B\u307E\u3067\u306E\u9045\u5EF6\u3001ms\uFF09">
10979
+ <span class="dtm-label" data-dtm="reverb-predelay-label">0ms</span>
10980
+ </div>
10981
+ <div class="dtm-row">
10982
+ <span class="dtm-label">\u30C7\u30A3\u30EC\u30A4</span>
10983
+ <input type="range" class="dtm-range dtm-grow" data-dtm="delay-amount" value="0" min="0" max="100" aria-label="\u30DE\u30B9\u30BF\u30C7\u30A3\u30EC\u30A4\uFF08\u30C6\u30F3\u30DD\u540C\u671F\u3057\u305F\u30A8\u30B3\u30FC\uFF09">
10984
+ <span class="dtm-label" data-dtm="delay-amount-label">0%</span>
10985
+ <select class="dtm-select" data-dtm="delay-division" aria-label="\u30C7\u30A3\u30EC\u30A4\u306E\u97F3\u4FA1\uFF08\u30C6\u30F3\u30DD\u306B\u540C\u671F\uFF09">
10986
+ ${DELAY_DIVISIONS.map((d) => `<option value="${d.value}">${d.label}</option>`).join("")}
10987
+ </select>
10988
+ <button class="dtm-infobtn" data-dtm="delay-amount-info" title="\u30DE\u30B9\u30BF\u30C7\u30A3\u30EC\u30A4\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
10989
+ </div>
10990
+ </div>
10991
+ </details>
10992
+
10669
10993
  <details class="dtm-panel">
10670
10994
  <summary>\u30C9\u30E9\u30E0\u8A2D\u5B9A</summary>
10671
10995
  <div class="dtm-panel-body">
@@ -10831,6 +11155,7 @@ var buildUI = (target, options) => {
10831
11155
  prevBarBtn: sel("prev-bar"),
10832
11156
  nextBarBtn: sel("next-bar"),
10833
11157
  soloCheckbox: sel("solo"),
11158
+ clipBadge: sel("clip-badge"),
10834
11159
  toolPen: sel("tool-pen"),
10835
11160
  toolSelect: sel("tool-select"),
10836
11161
  toolEraser: sel("tool-eraser"),
@@ -10859,6 +11184,17 @@ var buildUI = (target, options) => {
10859
11184
  masterVolumeLabel: sel("master-volume-label"),
10860
11185
  reverbAmount: sel("reverb-amount"),
10861
11186
  reverbAmountLabel: sel("reverb-amount-label"),
11187
+ reverbAmountInfoBtn: sel("reverb-amount-info"),
11188
+ reverbDecay: sel("reverb-decay"),
11189
+ reverbDecayLabel: sel("reverb-decay-label"),
11190
+ reverbPreDelay: sel("reverb-predelay"),
11191
+ reverbPreDelayLabel: sel("reverb-predelay-label"),
11192
+ delayAmount: sel("delay-amount"),
11193
+ delayAmountLabel: sel("delay-amount-label"),
11194
+ delayAmountInfoBtn: sel("delay-amount-info"),
11195
+ delayDivision: sel("delay-division"),
11196
+ autoMasterBtn: sel("auto-master"),
11197
+ autoMasterInfoBtn: sel("auto-master-info"),
10862
11198
  trackTabs: sel("track-tabs"),
10863
11199
  trackBody: sel("track-body"),
10864
11200
  drumSelect: sel("drum-select"),
@@ -12490,6 +12826,38 @@ var isChordHeavyTrack = (notes, threshold = 0.6) => {
12490
12826
  return chordNotes / notes.length >= threshold;
12491
12827
  };
12492
12828
 
12829
+ // src/reverb.ts
12830
+ var DEFAULT_REVERB_DECAY_SEC = 2.2;
12831
+ var MIN_REVERB_DECAY_SEC = 0.3;
12832
+ var MAX_REVERB_DECAY_SEC = 4;
12833
+ var IMPULSE_DECAY_CURVE = 2.5;
12834
+ var DEFAULT_REVERB_PREDELAY_MS = 0;
12835
+ var MIN_REVERB_PREDELAY_MS = 0;
12836
+ var MAX_REVERB_PREDELAY_MS = 150;
12837
+ var createReverbImpulse = (ctx, decaySec = DEFAULT_REVERB_DECAY_SEC) => {
12838
+ const rate = ctx.sampleRate;
12839
+ const length = Math.max(
12840
+ 1,
12841
+ Math.floor(
12842
+ rate * Math.max(
12843
+ MIN_REVERB_DECAY_SEC,
12844
+ Math.min(MAX_REVERB_DECAY_SEC, decaySec)
12845
+ )
12846
+ )
12847
+ );
12848
+ const impulse = ctx.createBuffer(2, length, rate);
12849
+ for (let ch = 0; ch < impulse.numberOfChannels; ch++) {
12850
+ const data = impulse.getChannelData(ch);
12851
+ for (let i2 = 0; i2 < length; i2++) {
12852
+ const envelope = (1 - i2 / length) ** IMPULSE_DECAY_CURVE;
12853
+ data[i2] = (Math.random() * 2 - 1) * envelope;
12854
+ }
12855
+ }
12856
+ return impulse;
12857
+ };
12858
+ var REVERB_MAX_WET = 0.6;
12859
+ var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100 * REVERB_MAX_WET;
12860
+
12493
12861
  // src/daw.ts
12494
12862
  var CHORD_INFO_HTML2 = `
12495
12863
  <div class="dtm-modal-body-content">
@@ -12523,10 +12891,125 @@ var CHORD_INFO_HTML2 = `
12523
12891
  `;
12524
12892
  var VIBRATO_INFO_HTML = `
12525
12893
  <div class="dtm-modal-body-content">
12526
- <h4>\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u3068\u306F</h4>
12894
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
12527
12895
  <p>ON\u306B\u3059\u308B\u3068\u3001\u4E00\u5B9A\u306E\u9577\u3055\uFF08\u7D040.35\u79D2\uFF09\u4EE5\u4E0A\u306E\u97F3\u7B26\uFF08\u30ED\u30F3\u30B0\u30C8\u30FC\u30F3\uFF09\u306B\u3060\u3051\u3001\u81EA\u52D5\u3067\u30D4\u30C3\u30C1\u304C\u5C0F\u523B\u307F\u306B\u63FA\u308C\u308B\u6B4C\u5531\u8868\u73FE\uFF08\u30D3\u30D6\u30E9\u30FC\u30C8\uFF09\u304C\u639B\u304B\u308A\u307E\u3059\u3002</p>
12528
- <p style="margin-top:4px;"><small>\u77ED\u3044\u97F3\u7B26\u306B\u306F\u639B\u304B\u308A\u307E\u305B\u3093\u30021\u5468\u671F\u3082\u63FA\u308C\u304D\u3089\u306A\u3044\u3046\u3061\u306B\u6B21\u306E\u97F3\u3078\u79FB\u3063\u3066\u3057\u307E\u3044\u3001\u30D3\u30D6\u30E9\u30FC\u30C8\u3068\u3044\u3046\u3088\u308A\u5358\u306A\u308B\u97F3\u7A0B\u306E\u30D6\u30EC\u3068\u3057\u3066\u4E0D\u81EA\u7136\u306B\u805E\u3053\u3048\u308B\u305F\u3081\u3067\u3059\u3002</small></p>
12529
- <p style="margin-top:4px;"><small>\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\u3057\u3066\u3044\u307E\u3059\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</small></p>
12896
+ <h4>\u77ED\u3044\u97F3\u7B26\u306B\u639B\u304B\u3089\u306A\u3044\u7406\u7531</h4>
12897
+ <p>1\u5468\u671F\u3082\u63FA\u308C\u304D\u3089\u306A\u3044\u3046\u3061\u306B\u6B21\u306E\u97F3\u3078\u79FB\u3063\u3066\u3057\u307E\u3044\u3001\u30D3\u30D6\u30E9\u30FC\u30C8\u3068\u3044\u3046\u3088\u308A\u5358\u306A\u308B\u97F3\u7A0B\u306E\u30D6\u30EC\u3068\u3057\u3066\u4E0D\u81EA\u7136\u306B\u805E\u3053\u3048\u308B\u305F\u3081\u3067\u3059\u3002</p>
12898
+ <h4>\u4ED6\u306E\u8A2D\u5B9A\u3068\u306E\u517C\u306D\u5408\u3044</h4>
12899
+ <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>
12900
+ </div>
12901
+ `;
12902
+ var GENDER_INFO_HTML = `
12903
+ <div class="dtm-modal-body-content">
12904
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
12905
+ <p>\u30D4\u30C3\u30C1\uFF08\u97F3\u306E\u9AD8\u3055\uFF09\u306F\u305D\u306E\u307E\u307E\u306B\u3001\u58F0\u306E\u592A\u3055/\u7D30\u3055\uFF08\u30D5\u30A9\u30EB\u30DE\u30F3\u30C8\uFF1D\u58F0\u9053\u306E\u5171\u9CF4\u3001\u5E74\u9F62\u30FB\u6027\u5225\u611F\u306E\u5370\u8C61\uFF09\u3060\u3051\u3092\u52D5\u304B\u3057\u307E\u3059\u300250\u304C\u7121\u5909\u5316\u300150\u672A\u6E80\u3067\u4F4E\u3081/\u592A\u3081\uFF08\u5927\u4EBA\u3073\u308B\uFF09\u300150\u8D85\u3067\u9AD8\u3081/\u7D30\u3081\uFF08\u82E5\u304F/\u660E\u308B\u304F\uFF09\u306B\u5BC4\u308A\u307E\u3059\u3002</p>
12906
+ <h4>\u30AA\u30AF\u30BF\u30FC\u30D6\u30B7\u30D5\u30C8\u3068\u306E\u9055\u3044</h4>
12907
+ <p>\u30AA\u30AF\u30BF\u30FC\u30D6\u306F\u97F3\u7A0B\u305D\u306E\u3082\u306E\u3092\u4E0A\u4E0B\u3055\u305B\u307E\u3059\u304C\u3001\u30B8\u30A7\u30F3\u30C0\u30FC\u306F\u97F3\u7A0B\u3092\u5909\u3048\u305A\u306B\u58F0\u8272\u3060\u3051\u3092\u52D5\u304B\u3057\u307E\u3059\u3002\u300C\u9AD8\u3044\u58F0\u306E\u307E\u307E\u5927\u4EBA\u3073\u3055\u305B\u308B\u300D\u300C\u4F4E\u3044\u58F0\u306E\u307E\u307E\u82E5\u3005\u3057\u304F\u3059\u308B\u300D\u3068\u3044\u3063\u305F\u3001\u97F3\u7A0B\u3068\u58F0\u8CEA\u3092\u5225\u3005\u306B\u8ABF\u6574\u3057\u305F\u3044\u3068\u304D\u306B\u4F7F\u3044\u307E\u3059\u3002</p>
12908
+ <h4>\u4ED6\u306E\u8A2D\u5B9A\u3068\u306E\u517C\u306D\u5408\u3044</h4>
12909
+ <p>\u30D6\u30EC\u30B7\u30CD\u30B9\u3068\u7D44\u307F\u5408\u308F\u305B\u3066\u58F0\u306E\u30AD\u30E3\u30E9\u30AF\u30BF\u30FC\u3092\u4F5C\u308A\u307E\u3059\uFF08\u4F8B: \u4F4E\u3081+\u606F\u591A\u3081\u3067\u6E0B\u3044/\u5927\u4EBA\u3063\u307D\u3044\u5370\u8C61\u3001\u9AD8\u3081+\u606F\u5C11\u306A\u3081\u3067\u5143\u6C17/\u82E5\u3005\u3057\u3044\u5370\u8C61\uFF09\u3002koe\u97F3\u6E90\uFF08UTAU\u7531\u6765\u306E.koe\u97F3\u6E90\uFF09\u9650\u5B9A\u306E\u52B9\u679C\u3067\u3001klatt\uFF08\u5185\u8535\u306E\u7C21\u6613\u5408\u6210\uFF09\u3067\u306F\u5909\u5316\u3057\u307E\u305B\u3093\u3002</p>
12910
+ </div>
12911
+ `;
12912
+ var BREATHINESS_INFO_HTML = `
12913
+ <div class="dtm-modal-body-content">
12914
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
12915
+ <p>\u606F\u6210\u5206\u306E\u91CF\u3067\u3059\u300250\u304C\u7121\u5909\u5316\u3001\u5927\u304D\u3044\u307B\u3069\u606F\u3063\u307D\u304F\uFF08\u3055\u3055\u3084\u304D\u5BC4\u308A\uFF09\u3001\u5C0F\u3055\u3044\u307B\u3069\u82AF\u306E\u3042\u308B\u58F0\u306B\u306A\u308A\u307E\u3059\u3002</p>
12916
+ <h4>\u4E0A\u3052\u3059\u304E\u308B\u3068\u3069\u3046\u306A\u308B\u304B</h4>
12917
+ <p>\u30ED\u30F3\u30B0\u30C8\u30FC\u30F3\u3067\u97F3\u7A0B\u611F\u304C\u8584\u308C\u3066\u805E\u3053\u3048\u307E\u3059\uFF08\u606F\u306E\u97F3\u304C\u30D4\u30C3\u30C1\u611F\u3092\u96A0\u3059\u305F\u3081\uFF09\u3002\u56C1\u304F\u3088\u3046\u306A\u30D0\u30E9\u30FC\u30C9\u8868\u73FE\u306B\u306F\u52B9\u679C\u7684\u3067\u3059\u304C\u3001\u4E0A\u3052\u3059\u304E\u308B\u3068\u30E1\u30ED\u30C7\u30A3\u304C\u4F1D\u308F\u308A\u306B\u304F\u304F\u306A\u308A\u307E\u3059\u3002</p>
12918
+ <h4>\u4ED6\u306E\u8A2D\u5B9A\u3068\u306E\u517C\u306D\u5408\u3044</h4>
12919
+ <p>\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u3068\u7D44\u307F\u5408\u308F\u305B\u308B\u3068\u3001\u63FA\u308C\u306A\u304C\u3089\u606F\u3063\u307D\u3044\u3001\u3088\u308A\u30A8\u30E2\u30FC\u30B7\u30E7\u30CA\u30EB\u306A\u8868\u73FE\u306B\u306A\u308A\u3084\u3059\u3044\u3067\u3059\u3002koe\u97F3\u6E90\uFF08UTAU\u7531\u6765\u306E.koe\u97F3\u6E90\uFF09\u9650\u5B9A\u306E\u52B9\u679C\u3067\u3001klatt\uFF08\u5185\u8535\u306E\u7C21\u6613\u5408\u6210\uFF09\u3067\u306F\u5909\u5316\u3057\u307E\u305B\u3093\u3002</p>
12920
+ </div>
12921
+ `;
12922
+ var LYRIC_REVERB_INFO_HTML = `
12923
+ <div class="dtm-modal-body-content">
12924
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
12925
+ <p>\u3053\u306E\u30DC\u30FC\u30AB\u30EB\u30C8\u30E9\u30C3\u30AF\u304B\u3089\u3001\u66F2\u5168\u4F53\u306B\u639B\u304B\u308B\u300C\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\u300D\u3078\u3069\u308C\u3060\u3051\u97F3\u3092\u9001\u308B\u304B\u3092\u6C7A\u3081\u307E\u3059\uFF080\u3067\u9001\u3089\u306A\u3044\uFF1D\u30C9\u30E9\u30A4\u3001100\u3067\u76EE\u4E00\u676F\u9001\u308B\uFF09\u3002</p>
12926
+ <h4>\u30DE\u30B9\u30BF\u306E\u300C\u30EA\u30D0\u30FC\u30D6\u300D\u3064\u307E\u307F\u3068\u306E\u95A2\u4FC2\uFF08\u91CD\u8981\uFF09</h4>
12927
+ <p>\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A\u30D1\u30CD\u30EB\u306B\u3042\u308B\u30DE\u30B9\u30BF\u306E\u300C\u30EA\u30D0\u30FC\u30D6\u300D\u3064\u307E\u307F\u304C0%\u3060\u3068\u3001\u3053\u3053\u3092\u3044\u304F\u3089\u4E0A\u3052\u3066\u3082\u7121\u97F3\u306E\u307E\u307E\u3067\u3059\u3002\u300C\u9001\u308A\u91CF\uFF08\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u304C\u3069\u308C\u3060\u3051\u63D0\u4F9B\u3059\u308B\u304B\uFF09\u300D\u3068\u300C\u30DE\u30B9\u30BF\u306E\u6B8B\u97FF\u8A2D\u5B9A\uFF08\u5B9F\u969B\u306B\u3069\u3093\u306A\u97FF\u304D\u304C\u639B\u304B\u308B\u304B\uFF09\u300D\u306E\u4E8C\u6BB5\u69CB\u3048\u306B\u306A\u3063\u3066\u3044\u308B\u305F\u3081\u3067\u3059\u3002\u4E21\u65B9\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002</p>
12928
+ <h4>\u639B\u3051\u3059\u304E\u308B\u3068\u3069\u3046\u306A\u308B\u304B</h4>
12929
+ <p>\u6B8B\u97FF\u3067\u97F3\u304C\u6EF2\u307F\u3001\u6B4C\u8A5E\u304C\u805E\u304D\u53D6\u308A\u306B\u304F\u304F\u306A\u308A\u307E\u3059\u3002\u30C9\u30E9\u30A4\u306A\u30DC\u30FC\u30AB\u30EB\u306F\u300C\u8FD1\u3044\u300D\u300C\u524D\u306B\u51FA\u308B\u300D\u5370\u8C61\u3001\u30EA\u30D0\u30FC\u30D6\u305F\u3063\u3077\u308A\u306E\u30DC\u30FC\u30AB\u30EB\u306F\u300C\u5965\u884C\u304D\u304C\u3042\u308B\u300D\u300C\u5E7B\u60F3\u7684\u300D\u306A\u5370\u8C61\u306B\u306A\u308A\u307E\u3059\u3002\u8868\u73FE\u3057\u305F\u3044\u8DDD\u96E2\u611F\u306B\u5408\u308F\u305B\u3066\u8ABF\u6574\u3057\u3066\u304F\u3060\u3055\u3044\u3002</p>
12930
+ </div>
12931
+ `;
12932
+ var LYRIC_DELAY_INFO_HTML = `
12933
+ <div class="dtm-modal-body-content">
12934
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
12935
+ <p>\u3053\u306E\u30DC\u30FC\u30AB\u30EB\u30C8\u30E9\u30C3\u30AF\u304B\u3089\u3001\u66F2\u5168\u4F53\u306B\u639B\u304B\u308B\u300C\u30DE\u30B9\u30BF\u30C7\u30A3\u30EC\u30A4\u300D\u3078\u3069\u308C\u3060\u3051\u97F3\u3092\u9001\u308B\u304B\u3092\u6C7A\u3081\u307E\u3059\uFF080\u3067\u9001\u3089\u306A\u3044\u3001100\u3067\u76EE\u4E00\u676F\u9001\u308B\uFF09\u3002</p>
12936
+ <h4>\u30DE\u30B9\u30BF\u306E\u300C\u30C7\u30A3\u30EC\u30A4\u300D\u3064\u307E\u307F\u3068\u306E\u95A2\u4FC2\uFF08\u91CD\u8981\uFF09</h4>
12937
+ <p>\u30DE\u30B9\u30BF\u306E\u300C\u30C7\u30A3\u30EC\u30A4\u300D\u3064\u307E\u307F\u304C0%\u3060\u3068\u3001\u3053\u3053\u3092\u3044\u304F\u3089\u4E0A\u3052\u3066\u3082\u7121\u97F3\u306E\u307E\u307E\u3067\u3059\u3002\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u3068\u540C\u3058\u4E8C\u6BB5\u69CB\u3048\u3067\u3059\u3002</p>
12938
+ <h4>\u4F7F\u3044\u3069\u3053\u308D</h4>
12939
+ <p>\u66F2\u5168\u4F53\u306B\u3046\u3063\u3059\u3089\u639B\u3051\u308B\u30EA\u30D0\u30FC\u30D6\u3068\u9055\u3044\u3001\u30C7\u30A3\u30EC\u30A4\u306F\u30CF\u30C3\u30AD\u30EA\u805E\u3053\u3048\u308B\u7E70\u308A\u8FD4\u3057\u306A\u306E\u3067\u3001A\u30E1\u30ED\u306F0%\u30FB\u30B5\u30D3\u306E\u8A9E\u5C3E\u3060\u3051\u9001\u308A\u91CF\u3092\u4E0A\u3052\u308B\u3001\u3068\u3044\u3063\u305F\u30E1\u30EA\u30CF\u30EA\u306E\u3042\u308B\u4F7F\u3044\u65B9\u304C\u52B9\u679C\u7684\u3067\u3059\u3002\u30D0\u30C3\u30AD\u30F3\u30B0\u5168\u4F53\u306B\u5F37\u304F\u639B\u3051\u308B\u3068\u30EA\u30BA\u30E0\u304C\u6FC1\u308A\u307E\u3059\u3002</p>
12940
+ </div>
12941
+ `;
12942
+ var MASTER_REVERB_INFO_HTML = `
12943
+ <div class="dtm-modal-body-content">
12944
+ <h4>\u30EA\u30D0\u30FC\u30D6\uFF08Mix\uFF09</h4>
12945
+ <p>\u66F2\u5168\u4F53\u306B\u4E00\u5F8B\u3067\u639B\u304B\u308B\u6B8B\u97FF\uFF08\u90E8\u5C4B\u9CF4\u308A\u30FB\u7A7A\u9593\u306E\u97FF\u304D\uFF09\u306E\u91CF\u3067\u3059\u30020%\u3067\u5B8C\u5168\u306B\u30C9\u30E9\u30A4\uFF08\u97FF\u304D\u306A\u3057\uFF09\u3001\u4E0A\u3052\u308B\u307B\u3069\u5E83\u3044\u7A7A\u9593\u3067\u9CF4\u3063\u3066\u3044\u308B\u3088\u3046\u306A\u5965\u884C\u304D\u304C\u51FA\u307E\u3059\u3002\u639B\u3051\u3059\u304E\u308B\u3068\u30DF\u30C3\u30AF\u30B9\u5168\u4F53\u306E\u8F2A\u90ED\u304C\u307C\u3084\u3051\u3001\u9060\u304F\u30FB\u3053\u3082\u3063\u305F\u5370\u8C61\u306B\u306A\u308A\u307E\u3059\u300210\u301C30%\u7A0B\u5EA6\u304C\u51FA\u767A\u70B9\u306E\u76EE\u5B89\u3067\u3059\u3002</p>
12946
+ <h4>Decay\uFF08\u6B8B\u97FF\u306E\u9577\u3055\uFF09</h4>
12947
+ <p>\u30EA\u30D0\u30FC\u30D6\u304C\u9CF4\u308A\u7D9A\u3051\u308B\u9577\u3055\u3067\u3059\u3002\u9577\u3044\u307B\u3069\u5E83\u3044\u30DB\u30FC\u30EB\u306E\u3088\u3046\u306A\u7A7A\u9593\u306E\u5370\u8C61\u306B\u306A\u308A\u3001\u77ED\u3044\u3068\u72ED\u3044\u90E8\u5C4B\u306E\u3088\u3046\u306A\u5BC6\u7740\u611F\u306B\u306A\u308A\u307E\u3059\u3002\u76EE\u5B89\u3068\u3057\u3066\u3001\u901F\u3044\u66F2\u3067\u306F0.6\u301C1.4\u79D2\u3001\u9045\u3044\u66F2\u30FB\u30D0\u30E9\u30FC\u30C9\u3067\u306F1.8\u301C4.0\u79D2\u7A0B\u5EA6\u304C\u3088\u304F\u4F7F\u308F\u308C\u307E\u3059\u3002</p>
12948
+ <h4>Pre Delay\uFF08\u539F\u97F3\u3068\u30EA\u30D0\u30FC\u30D6\u306E\u9593\u9694\uFF09</h4>
12949
+ <p>\u539F\u97F3\u304C\u9CF4\u3063\u3066\u304B\u3089\u6B8B\u97FF\u304C\u7ACB\u3061\u4E0A\u304C\u308B\u307E\u3067\u306E\u9045\u5EF6\u3067\u3059\u30020ms\u3060\u3068\u539F\u97F3\u3068\u6B8B\u97FF\u304C\u540C\u6642\u306B\u59CB\u307E\u308A\u97F3\u50CF\u304C\u307C\u3084\u3051\u304C\u3061\u3067\u3059\u304C\u3001\u5C11\u3057\uFF08\u6570\u5341ms\u7A0B\u5EA6\uFF09\u5165\u308C\u308B\u3068\u539F\u97F3\u306E\u8F2A\u90ED\u30FB\u30A2\u30BF\u30C3\u30AF\u611F\u3092\u4FDD\u3063\u305F\u307E\u307E\u5965\u884C\u304D\u3092\u8DB3\u305B\u307E\u3059\u3002\u7279\u306B\u30DC\u30FC\u30AB\u30EB\u3067\u52B9\u679C\u7684\u3067\u3059\u3002</p>
12950
+ <h4>\u5404\u30C8\u30E9\u30C3\u30AF\u306E\u300C\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u300D\u3068\u306E\u95A2\u4FC2\uFF08\u91CD\u8981\uFF09</h4>
12951
+ <p>\u6B4C\u8A5E\u30C8\u30E9\u30C3\u30AF\u306B\u306F\u305D\u308C\u305E\u308C\u300C\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u300D\u3068\u3044\u3046\u500B\u5225\u306E\u3064\u307E\u307F\u304C\u3042\u308A\u3001\u305D\u3061\u3089\u304C0%\u306E\u30C8\u30E9\u30C3\u30AF\u306F\u3053\u306E\u30DE\u30B9\u30BF\u306E\u5024\u3092\u3044\u304F\u3089\u4E0A\u3052\u3066\u3082\u7121\u97F3\u306E\u307E\u307E\u3067\u3059\u3002\u9006\u306B\u3053\u306E\u30DE\u30B9\u30BF\u306EMix\u304C0%\u306A\u3089\u3001\u3069\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u9001\u308A\u91CF\u3092\u4E0A\u3052\u3066\u3082\u52B9\u679C\u304C\u51FA\u307E\u305B\u3093\u3002\u300C\u30DE\u30B9\u30BF\uFF1D\u6B8B\u97FF\u306E\u8CEA\u30FB\u91CF\u30FB\u30BF\u30A4\u30DF\u30F3\u30B0\u305D\u306E\u3082\u306E\u300D\u300C\u30C8\u30E9\u30C3\u30AF\u9001\u308A\uFF1D\u305D\u306E\u30C8\u30E9\u30C3\u30AF\u3092\u3069\u308C\u3060\u3051\u6DF7\u305C\u308B\u304B\u300D\u3068\u3044\u3046\u4E8C\u6BB5\u69CB\u3048\u3067\u3059\u3002\u697D\u5668\u30C8\u30E9\u30C3\u30AF\u306B\u306F\u73FE\u72B6\u30BB\u30F3\u30C9\u6A5F\u80FD\u304C\u7121\u3044\u305F\u3081\u3001\u5E38\u306B\u4E00\u5F8B\u3067\u639B\u304B\u308A\u307E\u3059\u3002</p>
12952
+ </div>
12953
+ `;
12954
+ var MASTER_DELAY_INFO_HTML = `
12955
+ <div class="dtm-modal-body-content">
12956
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
12957
+ <p>\u96E2\u6563\u3057\u305F\u30A8\u30B3\u30FC\uFF08\u97F3\u306E\u7E70\u308A\u8FD4\u3057\uFF09\u3092\u4ED8\u52A0\u3057\u307E\u3059\u3002\u66F2\u306EBPM\u306B\u81EA\u52D5\u540C\u671F\u3059\u308B\u97F3\u4FA1\uFF088\u5206\u30FB\u4ED8\u70B98\u5206\u306A\u3069\uFF09\u3092\u9078\u3093\u3067\u4F7F\u3046\u306E\u304C\u57FA\u672C\u3067\u3001\u7121\u95A2\u4FC2\u306A\u79D2\u6570\u3067\u9CF4\u3089\u3059\u3068\u30EA\u30BA\u30E0\u304C\u5D29\u308C\u3066\u805E\u3053\u3048\u307E\u3059\u3002</p>
12958
+ <h4>\u30EA\u30D0\u30FC\u30D6\u3068\u306E\u9055\u3044</h4>
12959
+ <p>\u30EA\u30D0\u30FC\u30D6\u306F\u62E1\u6563\u3057\u305F\u6B8B\u97FF\u3067\u7A7A\u9593\u5168\u4F53\u306B\u8584\u304F\u639B\u3051\u308B\u306E\u304C\u57FA\u672C\u3067\u3059\u304C\u3001\u30C7\u30A3\u30EC\u30A4\u306F\u30CF\u30C3\u30AD\u30EA\u805E\u3053\u3048\u308B\u7E70\u308A\u8FD4\u3057\u306A\u306E\u3067\u300C\u3053\u3053\u305E\u300D\u3068\u3044\u3046\u5834\u9762\uFF08\u30EA\u30FC\u30C9\u30DC\u30FC\u30AB\u30EB\u306E\u8A9E\u5C3E\u3001\u30AE\u30BF\u30FC\u30BD\u30ED\u3001\u30B7\u30F3\u30BB\u306E\u30D5\u30EC\u30FC\u30BA\u7B49\uFF09\u306B\u30EF\u30F3\u30DD\u30A4\u30F3\u30C8\u3067\u4F7F\u3046\u306E\u304C\u5B9A\u756A\u3067\u3059\u3002\u30D0\u30C3\u30AD\u30F3\u30B0\u5168\u4F53\u306B\u5F37\u304F\u639B\u3051\u308B\u3068\u30EA\u30BA\u30E0\u304C\u6FC1\u308A\u307E\u3059\u3002</p>
12960
+ <h4>\u5404\u30C8\u30E9\u30C3\u30AF\u306E\u300C\u30C7\u30A3\u30EC\u30A4\u9001\u308A\u300D\u3068\u306E\u95A2\u4FC2</h4>
12961
+ <p>\u30EA\u30D0\u30FC\u30D6\u3068\u540C\u3058\u4E8C\u6BB5\u69CB\u3048\u3067\u3059\u3002\u6B4C\u8A5E\u30C8\u30E9\u30C3\u30AF\u500B\u5225\u306E\u300C\u30C7\u30A3\u30EC\u30A4\u9001\u308A\u300D\u304C0%\u306A\u3089\u3053\u306E\u30DE\u30B9\u30BF\u306E\u5024\u3092\u4E0A\u3052\u3066\u3082\u7121\u97F3\u3001\u9006\u306B\u3053\u306E\u30DE\u30B9\u30BF\u304C0%\u306A\u3089\u3069\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u9001\u308A\u91CF\u3092\u4E0A\u3052\u3066\u3082\u52B9\u679C\u304C\u51FA\u307E\u305B\u3093\u3002</p>
12962
+ </div>
12963
+ `;
12964
+ var TRACK_EQ_INFO_HTML = `
12965
+ <div class="dtm-modal-body-content">
12966
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
12967
+ <p>\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u5468\u6CE2\u6570\u5E2F\u57DF\u3054\u3068\u306E\u97F3\u91CF\u3092\u8ABF\u6574\u3057\u307E\u3059\u3002\u4F4E\u57DF\u30FB\u4E2D\u57DF\u30FB\u9AD8\u57DF\u306E3\u30D0\u30F3\u30C9\u3001\u305D\u308C\u305E\u308C-12dB\uFF08\u524A\u308B\uFF09\u301C+12dB\uFF08\u6301\u3061\u4E0A\u3052\u308B\uFF09\u30010dB\u304C\u7121\u5909\u5316\u3067\u3059\u3002</p>
12968
+ <h4>\u4F7F\u3044\u3069\u3053\u308D\uFF08\u5E2F\u57DF\u306E\u68F2\u307F\u5206\u3051\uFF09</h4>
12969
+ <p>\u8907\u6570\u306E\u97F3\u304C\u540C\u3058\u5E2F\u57DF\u3067\u9CF4\u3063\u3066\u3044\u308B\u3068\u6FC1\u3063\u3066\u805E\u3053\u3048\u307E\u3059\u3002\u4F8B\u3048\u3070\u3001\u30DC\u30FC\u30AB\u30EB\u3068\u30EA\u30FC\u30C9\u697D\u5668\u304C\u4E2D\u57DF\u3067\u3076\u3064\u304B\u308B\u306A\u3089\u7247\u65B9\u306E\u4E2D\u57DF\u3092\u8EFD\u304F\u524A\u308B\u3001\u30D9\u30FC\u30B9\u3068\u30D0\u30B9\u30C9\u30E9\u30E0\u306E\u4F4E\u57DF\u304C\u3076\u3064\u304B\u308B\u306A\u3089\u7247\u65B9\u3060\u3051\u4F4E\u57DF\u3092\u524A\u308B\u3001\u3068\u3044\u3063\u305F\u300C\u5E2F\u57DF\u306E\u4EA4\u901A\u6574\u7406\u300D\u304CEQ\u306E\u4E3B\u306A\u4ED5\u4E8B\u3067\u3059\u3002</p>
12970
+ <h4>\u4ED6\u306E\u8A2D\u5B9A\u3068\u306E\u517C\u306D\u5408\u3044\uFF08\u9806\u5E8F\u304C\u91CD\u8981\uFF09</h4>
12971
+ <p>\u3053\u306E\u30C1\u30E3\u30F3\u30CD\u30EB\u30B9\u30C8\u30EA\u30C3\u30D7\u3067\u306F EQ \u2192 \u97F3\u5727\u5F37\u5316\uFF08\u30B3\u30F3\u30D7\u30EC\u30C3\u30B5\u30FC\uFF09 \u2192 \u30B9\u30C6\u30EC\u30AA\u5E45 \u306E\u9806\u3067\u51E6\u7406\u3055\u308C\u307E\u3059\u3002\u3053\u308C\u306F\u4E00\u822C\u7684\u306A\u30DF\u30C3\u30AF\u30B9\u306E\u5B9A\u77F3\u3068\u540C\u3058\u3067\u3059\u3002\u4E0D\u8981\u306A\u5E2F\u57DF\uFF08\u4F4E\u57DF\u306E\u3053\u3082\u308A\u3001\u8033\u969C\u308A\u306A\u9AD8\u57DF\u7B49\uFF09\u3092\u5148\u306BEQ\u3067\u524A\u3063\u3066\u304B\u3089\u30B3\u30F3\u30D7\u30EC\u30C3\u30B5\u30FC\u3092\u639B\u3051\u308B\u3068\u3001\u30B3\u30F3\u30D7\u304C\u672C\u5F53\u306B\u5FC5\u8981\u306A\u97F3\u3060\u3051\u306B\u53CD\u5FDC\u3059\u308B\u3088\u3046\u306B\u306A\u308A\u3001\u97F3\u5727\u5F37\u5316\u306E\u52B9\u304D\u304C\u826F\u304F\u306A\u308A\u307E\u3059\u3002\u9806\u5E8F\u3092\u5909\u3048\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093\u3002</p>
12972
+ </div>
12973
+ `;
12974
+ var TRACK_COMPRESSION_INFO_HTML = `
12975
+ <div class="dtm-modal-body-content">
12976
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
12977
+ <p>\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306B\u30B3\u30F3\u30D7\u30EC\u30C3\u30B5\u30FC\u3092\u639B\u3051\u3001\u97F3\u91CF\u306E\u5927\u5C0F\u5DEE\u3092\u7E2E\u3081\u3066\u300C\u524D\u306B\u51FA\u308B\u300D\u300C\u805E\u3053\u3048\u3084\u3059\u3044\u300D\u97F3\u306B\u3057\u307E\u3059\u3002\u5E02\u8CA9\u66F2\u306E\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0\u3084\u30DF\u30C3\u30AF\u30B9\u3067\u5B9A\u756A\u306E\u51E6\u7406\u3067\u3059\u30020\u3067\u7121\u5727\u7E2E\u3001100\u306B\u8FD1\u3065\u304F\u307B\u3069\u5F37\u304F\u5727\u7E2E\u3055\u308C\u307E\u3059\u3002</p>
12978
+ <h4>\u639B\u3051\u3059\u304E\u308B\u3068\u3069\u3046\u306A\u308B\u304B</h4>
12979
+ <p>\u5F37\u5F31\u306E\u8868\u60C5\uFF08\u30C0\u30A4\u30CA\u30DF\u30AF\u30B9\uFF09\u304C\u5931\u308F\u308C\u3066\u5358\u8ABF\u306B\u805E\u3053\u3048\u307E\u3059\u3002\u30DC\u30FC\u30AB\u30EB\u3084\u30EA\u30FC\u30C9\u697D\u5668\u306F\u63A7\u3048\u3081\uFF0820\u301C40\u7A0B\u5EA6\uFF09\u3001\u30C9\u30E9\u30E0\u3084\u30D9\u30FC\u30B9\u306F\u3084\u3084\u5F37\u3081\u3001\u304C\u4E00\u822C\u7684\u306A\u76EE\u5B89\u3067\u3059\u3002</p>
12980
+ <h4>\u4ED6\u306E\u8A2D\u5B9A\u3068\u306E\u517C\u306D\u5408\u3044</h4>
12981
+ <p>\u30DE\u30B9\u30BF\u306E\u300C\u5B89\u5168\u30EA\u30DF\u30C3\u30BF\u30FC\u300D\uFF08\u5E38\u6642ON\u3001[reverb]\u30D1\u30CD\u30EB\u306E\u5916\u5074\u3067\u81EA\u52D5\u7684\u306B\u50CD\u304F\u4FDD\u967A\uFF09\u3068\u306F\u5225\u7269\u3067\u3059\u3002\u3042\u3061\u3089\u306F\u97F3\u5272\u308C\u3092\u7269\u7406\u7684\u306B\u9632\u3050\u305F\u3081\u306E\u6700\u7D42\u9632\u885B\u30E9\u30A4\u30F3\u3067\u3001\u5E38\u306B\u63A7\u3048\u3081\u306B\u52D5\u3044\u3066\u3044\u307E\u3059\u3002\u3053\u3061\u3089\u306E\u30B3\u30F3\u30D7\u30EC\u30C3\u30B5\u30FC\u306F\u97F3\u4F5C\u308A\uFF08\u8868\u73FE\uFF09\u306E\u305F\u3081\u306E\u51E6\u7406\u3067\u3001\u30C8\u30E9\u30C3\u30AF\u3054\u3068\u306B\u597D\u304D\u306A\u3060\u3051\u5F37\u304F/\u5F31\u304F\u639B\u3051\u3089\u308C\u307E\u3059\u3002\u300C\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0\u300D\u306F\u5168\u30C8\u30E9\u30C3\u30AF\u4E00\u5F8B35%\u3092\u5F53\u3066\u308B\u3060\u3051\u306A\u306E\u3067\u3001\u30DC\u30FC\u30AB\u30EB\u306A\u3069\u76EE\u7ACB\u305F\u305B\u305F\u3044\u30D1\u30FC\u30C8\u306F\u5F8C\u3067\u500B\u5225\u306B\u4E0B\u3052\u308B\u3068\u8F2A\u90ED\u304C\u51FA\u3084\u3059\u304F\u306A\u308A\u307E\u3059\u3002</p>
12982
+ </div>
12983
+ `;
12984
+ var TRACK_WIDTH_INFO_HTML = `
12985
+ <div class="dtm-modal-body-content">
12986
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
12987
+ <p>\u5DE6\u53F3\u306E\u5E83\u304C\u308A\u3092\u8ABF\u6574\u3057\u307E\u3059\u3002100\u304C\u539F\u97F3\u306E\u307E\u307E\u30010\u3067\u5B8C\u5168\u30E2\u30CE\u30E9\u30EB\uFF08\u5DE6\u53F3\u304C\u540C\u3058\u97F3\uFF09\u3001100\u3092\u8D85\u3048\u308B\u3068\u5DE6\u53F3\u306E\u9055\u3044\u304C\u8A87\u5F35\u3055\u308C\u3066\u5E83\u304F\u805E\u3053\u3048\u307E\u3059\u3002</p>
12988
+ <h4>\u300C\u5B9A\u4F4D\uFF08\u30D1\u30F3\uFF09\u300D\u3068\u306E\u9055\u3044</h4>
12989
+ <p>\u5B9A\u4F4D\u306F\u300C\u97F3\u3092\u3069\u3053\u306B\u7F6E\u304F\u304B\u300D\uFF08\u5DE6\u5BC4\u308A/\u4E2D\u592E/\u53F3\u5BC4\u308A\uFF09\u3001\u30B9\u30C6\u30EC\u30AA\u5E45\u306F\u300C\u305D\u306E\u97F3\u81EA\u4F53\u304C\u3069\u308C\u3060\u3051\u5E83\u304C\u3063\u3066\u805E\u3053\u3048\u308B\u304B\u300D\u3067\u3001\u5F79\u5272\u304C\u7570\u306A\u308A\u307E\u3059\u3002\u4E21\u65B9\u3092\u5F37\u304F\u4F7F\u3046\u3068\u5B9A\u4F4D\u304C\u307C\u3084\u3051\u3066\u66D6\u6627\u306B\u306A\u308A\u304C\u3061\u3067\u3059\u3002</p>
12990
+ <h4>\u5E83\u3052\u3059\u304E\u308B\u3068\u3069\u3046\u306A\u308B\u304B</h4>
12991
+ <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>
12992
+ </div>
12993
+ `;
12994
+ var AUTO_MASTER_INFO_HTML = `
12995
+ <div class="dtm-modal-body-content">
12996
+ <h4>\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0\u3068\u306F</h4>
12997
+ <p>\u5E02\u8CA9\u66F2\u3067\u3088\u304F\u4F7F\u308F\u308C\u308B\u5024\u3092\u76EE\u5B89\u306B\u3001\u4EE5\u4E0B\u3092\u307E\u3068\u3081\u3066\u8A2D\u5B9A\u3059\u308B\u30DC\u30BF\u30F3\u3067\u3059\u3002\u66F2\u306EBPM\u3092\u898B\u3066\u4E00\u90E8\u306E\u5024\u3092\u81EA\u52D5\u8ABF\u6574\u3057\u307E\u3059\u3002</p>
12998
+ <ul>
12999
+ <li>\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6 Mix: 20%</li>
13000
+ <li>\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6 Decay: <strong>\u73FE\u5728\u306EBPM\u306B\u5FDC\u3058\u3066\u81EA\u52D5\u8A08\u7B97</strong>\uFF08\u901F\u3044\u66F2\u307B\u3069\u77ED\u304F0.9\u79D2\u5BC4\u308A\u3001\u9045\u3044\u66F2\u307B\u3069\u9577\u304F3.0\u79D2\u5BC4\u308A\uFF09</li>
13001
+ <li>\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6 Pre Delay: 20ms\uFF08\u539F\u97F3\u306E\u8F2A\u90ED\u3092\u4FDD\u3063\u305F\u307E\u307E\u5965\u884C\u304D\u3092\u8DB3\u3059\u5B9A\u756A\u5024\uFF09</li>
13002
+ <li>\u5404\u30C8\u30E9\u30C3\u30AF\u306E\u97F3\u5727\u5F37\u5316: \u697D\u566835%\u30FB\u6B4C\u8A5E\u306E\u3042\u308B\u30C8\u30E9\u30C3\u30AF\u306F25%\uFF08\u5F37\u304F\u6F70\u3059\u3068\u8868\u60C5\u304C\u5931\u308F\u308C\u308B\u305F\u3081\u63A7\u3048\u3081\u306B\uFF09</li>
13003
+ <li>\u5168\u30C8\u30E9\u30C3\u30AF\u306E\u30B9\u30C6\u30EC\u30AA\u5E45: 115%\uFF08\u308F\u305A\u304B\u306B\u5E83\u3052\u308B\uFF09</li>
13004
+ <li>\u6B4C\u8A5E\u306E\u3042\u308B\u30C8\u30E9\u30C3\u30AF: \u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8ON</li>
13005
+ </ul>
13006
+ <h4>\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB\u306E\u81EA\u52D5\u5224\u5B9A</h4>
13007
+ <p>\u6B4C\u8A5E\u306E\u3042\u308B\u30C8\u30E9\u30C3\u30AF\u304C\u8907\u6570\u3042\u308B\u5834\u5408\uFF08\u30CF\u30E2\u30EA\u30FB\u30B3\u30FC\u30E9\u30B9\u30FB\u639B\u3051\u58F0\u7B49\uFF09\u3001\u767A\u97F3\u6642\u9593\u304C\u6700\u3082\u9577\u3044\u30C8\u30E9\u30C3\u30AF\u3092\u300C\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB\u300D\u3068\u307F\u306A\u3057\u3001\u4ED6\u3068\u306F\u9055\u3046\u6271\u3044\u306B\u3057\u307E\u3059\u3002</p>
13008
+ <ul>
13009
+ <li>\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB: \u97F3\u5727\u5F37\u531630%\u30FB\u30EA\u30D0\u30FC\u30D6\u9001\u308A15%\uFF08\u524D\u306B\u51FA\u3059\uFF09</li>
13010
+ <li>\u305D\u308C\u4EE5\u5916\u306E\u30DC\u30FC\u30AB\u30EB: \u97F3\u5727\u5F37\u531625%\u30FB\u30EA\u30D0\u30FC\u30D6\u9001\u308A30%\uFF08\u5965\u3078\u99B4\u67D3\u307E\u305B\u308B\uFF09</li>
13011
+ </ul>
13012
+ <p style="margin-top:4px;"><small>\u300C\u524D\u306B\u51FA\u3057\u305F\u3044\u97F3\u306F\u30EA\u30D0\u30FC\u30D6\u5C11\u306A\u3081\u3001\u5965\u306B\u7F6E\u304D\u305F\u3044\u97F3\u306F\u30EA\u30D0\u30FC\u30D6\u591A\u3081\u300D\u3068\u3044\u3046\u5B9A\u77F3\u306B\u57FA\u3065\u3044\u3066\u3044\u307E\u3059\u3002EQ\u306F\u66F2\u30FB\u30D1\u30FC\u30C8\u3054\u3068\u306B\u4F55\u304C\u6FC1\u3063\u3066\u3044\u308B\u304B\u6B21\u7B2C\u3067\u81EA\u52D5\u5316\u306B\u5411\u304B\u306A\u3044\u305F\u3081\u5BFE\u8C61\u5916\u3067\u3059\u3002\u300C\u3044\u3044\u611F\u3058\u306E\u521D\u671F\u5024\u300D\u3092\u4E00\u62EC\u3067\u5F53\u3066\u308B\u3060\u3051\u3067\u3001\u66F2\u3084\u597D\u307F\u306B\u5FDC\u3058\u305F\u5FAE\u8ABF\u6574\u307E\u3067\u306F\u884C\u3044\u307E\u305B\u3093\u3002\u65E2\u5B58\u306E\u8A2D\u5B9A\u306F\u4E0A\u66F8\u304D\u3055\u308C\u308B\u306E\u3067\u3001\u6C17\u306B\u5165\u3089\u306A\u3051\u308C\u3070\u5404\u30B9\u30E9\u30A4\u30C0\u30FC\u304B\u3089\u500B\u5225\u306B\u623B\u3057\u3066\u304F\u3060\u3055\u3044\u3002</small></p>
12530
13013
  </div>
12531
13014
  `;
12532
13015
  var MIDI_INFO_HTML = `
@@ -12762,7 +13245,7 @@ var deriveCustomVocalKeyFromUrl = (url2) => {
12762
13245
  if (name && /^[0-9]/.test(name)) name = `_${name}`;
12763
13246
  return CUSTOM_VOCAL_KEY_RE.test(name) ? name : "";
12764
13247
  };
12765
- var clamp3 = (v, min, max) => Math.min(Math.max(v, min), max);
13248
+ var clamp4 = (v, min, max) => Math.min(Math.max(v, min), max);
12766
13249
  var normalizeInstrumentName = (name) => {
12767
13250
  if (!name) return "";
12768
13251
  const stripped = name.replace(/\s+/g, "").toLowerCase();
@@ -12802,6 +13285,17 @@ var mountDAW = (target, options = {}) => {
12802
13285
  refs.masterVolumeLabel.textContent = `${options.masterVolume ?? 50}%`;
12803
13286
  refs.reverbAmount.value = String(options.reverbAmount ?? 0);
12804
13287
  refs.reverbAmountLabel.textContent = `${options.reverbAmount ?? 0}%`;
13288
+ {
13289
+ const initDecay = options.reverbDecay ?? DEFAULT_REVERB_DECAY_SEC;
13290
+ const initPreDelay = options.reverbPreDelay ?? DEFAULT_REVERB_PREDELAY_MS;
13291
+ refs.reverbDecay.value = String(Math.round(initDecay * 10));
13292
+ refs.reverbDecayLabel.textContent = `${initDecay.toFixed(1)}s`;
13293
+ refs.reverbPreDelay.value = String(initPreDelay);
13294
+ refs.reverbPreDelayLabel.textContent = `${initPreDelay}ms`;
13295
+ }
13296
+ refs.delayAmount.value = String(options.delayAmount ?? 0);
13297
+ refs.delayAmountLabel.textContent = `${options.delayAmount ?? 0}%`;
13298
+ refs.delayDivision.value = options.delayDivision ?? "8";
12805
13299
  refs.drumVolume.value = String(options.drumVolume ?? 80);
12806
13300
  refs.drumVolumeLabel.textContent = `${options.drumVolume ?? 80}%`;
12807
13301
  const renderConfig = {
@@ -12818,7 +13312,16 @@ var mountDAW = (target, options = {}) => {
12818
13312
  let masterVolume = options.masterVolume ?? 50;
12819
13313
  options.singingVoices?.setVolume(masterVolume / 100);
12820
13314
  let reverbAmount = options.reverbAmount ?? 0;
13315
+ let reverbDecay = options.reverbDecay ?? DEFAULT_REVERB_DECAY_SEC;
13316
+ let reverbPreDelay = options.reverbPreDelay ?? DEFAULT_REVERB_PREDELAY_MS;
13317
+ let delayAmount = options.delayAmount ?? 0;
13318
+ let delayDivision = options.delayDivision ?? "8";
13319
+ let unsubscribeClip;
12821
13320
  options.onReverbChange?.(reverbAmount);
13321
+ options.onReverbDecayChange?.(reverbDecay);
13322
+ options.onReverbPreDelayChange?.(reverbPreDelay);
13323
+ options.onDelayChange?.(delayAmount);
13324
+ options.onDelayDivisionChange?.(delayDivision);
12822
13325
  let drumVolume = options.drumVolume ?? 80;
12823
13326
  let currentDrumPattern = refs.drumSelect.value;
12824
13327
  let currentDrumFont = options.drumFont ?? "FluidR3_GM_sf2_file:0";
@@ -12898,7 +13401,10 @@ var mountDAW = (target, options = {}) => {
12898
13401
  vocalPan: t.vocalPan,
12899
13402
  vocalOctave: t.vocalOctave,
12900
13403
  vocalVibrato: t.vocalVibrato,
12901
- vocalReverb: t.vocalReverb
13404
+ vocalReverb: t.vocalReverb,
13405
+ vocalDelay: t.vocalDelay,
13406
+ vocalGender: t.vocalGender,
13407
+ vocalBreathiness: t.vocalBreathiness
12902
13408
  };
12903
13409
  if (lyricsDebounceTimer) clearTimeout(lyricsDebounceTimer);
12904
13410
  lyricsDebounceTimer = setTimeout(() => {
@@ -12960,7 +13466,15 @@ var mountDAW = (target, options = {}) => {
12960
13466
  vocalOctave: 0,
12961
13467
  vocalVibrato: false,
12962
13468
  vocalReverb: 0,
12963
- trackInstrument: ""
13469
+ vocalDelay: 0,
13470
+ vocalGender: 50,
13471
+ vocalBreathiness: 50,
13472
+ trackInstrument: "",
13473
+ trackCompression: 0,
13474
+ trackWidth: 100,
13475
+ trackEqLow: 0,
13476
+ trackEqMid: 0,
13477
+ trackEqHigh: 0
12964
13478
  };
12965
13479
  });
12966
13480
  };
@@ -12981,6 +13495,9 @@ var mountDAW = (target, options = {}) => {
12981
13495
  octave: t.vocalOctave,
12982
13496
  vibrato: t.vocalVibrato,
12983
13497
  reverb: t.vocalReverb,
13498
+ delay: t.vocalDelay,
13499
+ gender: t.vocalGender,
13500
+ breathiness: t.vocalBreathiness,
12984
13501
  syllables
12985
13502
  });
12986
13503
  });
@@ -13101,7 +13618,7 @@ var mountDAW = (target, options = {}) => {
13101
13618
  const thumbW = Math.max(40, canvas.width / totalContentWidth * sbW);
13102
13619
  const ratio = currentOffsetX / maxOffsetX;
13103
13620
  refs.hScrollThumb.style.width = `${thumbW}px`;
13104
- refs.hScrollThumb.style.left = `${clamp3(ratio * (sbW - thumbW), 0, sbW - thumbW)}px`;
13621
+ refs.hScrollThumb.style.left = `${clamp4(ratio * (sbW - thumbW), 0, sbW - thumbW)}px`;
13105
13622
  }
13106
13623
  const totalHeight = renderConfig.keyCount * renderConfig.keyHeight;
13107
13624
  const sbH = refs.vScroll.clientHeight;
@@ -13171,9 +13688,9 @@ var mountDAW = (target, options = {}) => {
13171
13688
  if (maxOffsetX <= 0) return;
13172
13689
  const rect = refs.hScroll.getBoundingClientRect();
13173
13690
  const thumbW = Number.parseFloat(refs.hScrollThumb.style.width) || 40;
13174
- const x2 = clamp3(clientX - rect.left - thumbW / 2, 0, rect.width - thumbW);
13691
+ const x2 = clamp4(clientX - rect.left - thumbW / 2, 0, rect.width - thumbW);
13175
13692
  const ratio = x2 / (rect.width - thumbW);
13176
- currentOffsetX = clamp3(ratio * maxOffsetX, 0, maxOffsetX);
13693
+ currentOffsetX = clamp4(ratio * maxOffsetX, 0, maxOffsetX);
13177
13694
  setDrawOffset(currentOffsetX, currentOffsetY);
13178
13695
  redrawAll();
13179
13696
  };
@@ -13182,9 +13699,9 @@ var mountDAW = (target, options = {}) => {
13182
13699
  if (maxOffset <= 0) return;
13183
13700
  const rect = refs.vScroll.getBoundingClientRect();
13184
13701
  const thumbH = Number.parseFloat(refs.vScrollThumb.style.height) || 40;
13185
- const y = clamp3(clientY - rect.top - thumbH / 2, 0, rect.height - thumbH);
13702
+ const y = clamp4(clientY - rect.top - thumbH / 2, 0, rect.height - thumbH);
13186
13703
  const ratio = y / (rect.height - thumbH);
13187
- currentOffsetY = clamp3(ratio * maxOffset, 0, maxOffset);
13704
+ currentOffsetY = clamp4(ratio * maxOffset, 0, maxOffset);
13188
13705
  setDrawOffset(currentOffsetX, currentOffsetY);
13189
13706
  redrawAll();
13190
13707
  };
@@ -13231,8 +13748,8 @@ var mountDAW = (target, options = {}) => {
13231
13748
  if (dx !== 0 || dy !== 0) {
13232
13749
  const maxOffsetX = getMaxOffsetX();
13233
13750
  const maxOffsetY = getMaxOffsetY();
13234
- currentOffsetX = clamp3(currentOffsetX + dx, 0, maxOffsetX);
13235
- currentOffsetY = clamp3(currentOffsetY + dy, 0, maxOffsetY);
13751
+ currentOffsetX = clamp4(currentOffsetX + dx, 0, maxOffsetX);
13752
+ currentOffsetY = clamp4(currentOffsetY + dy, 0, maxOffsetY);
13236
13753
  setDrawOffset(currentOffsetX, currentOffsetY);
13237
13754
  onPointerMove(lastMoveEvent);
13238
13755
  }
@@ -13622,12 +14139,12 @@ var mountDAW = (target, options = {}) => {
13622
14139
  "wheel",
13623
14140
  (event) => {
13624
14141
  event.preventDefault();
13625
- currentOffsetY = clamp3(
14142
+ currentOffsetY = clamp4(
13626
14143
  currentOffsetY + event.deltaY,
13627
14144
  0,
13628
14145
  getMaxOffsetY()
13629
14146
  );
13630
- currentOffsetX = clamp3(
14147
+ currentOffsetX = clamp4(
13631
14148
  currentOffsetX + event.deltaX,
13632
14149
  0,
13633
14150
  getMaxOffsetX()
@@ -13666,7 +14183,7 @@ var mountDAW = (target, options = {}) => {
13666
14183
  const centerStep = (currentOffsetX + canvas.width / 2) / renderConfig.stepWidth;
13667
14184
  renderConfig.stepWidth = BASE_STEP_WIDTH * (zoomX * 2) / 100;
13668
14185
  refs.zoomXLabel.textContent = `${zoomX}%`;
13669
- currentOffsetX = clamp3(
14186
+ currentOffsetX = clamp4(
13670
14187
  centerStep * renderConfig.stepWidth - canvas.width / 2,
13671
14188
  0,
13672
14189
  getMaxOffsetX()
@@ -13679,7 +14196,7 @@ var mountDAW = (target, options = {}) => {
13679
14196
  const centerKey = (currentOffsetY + canvas.height / 2) / renderConfig.keyHeight;
13680
14197
  renderConfig.keyHeight = BASE_KEY_HEIGHT * zoomY / 100;
13681
14198
  refs.zoomYLabel.textContent = `${zoomY}%`;
13682
- currentOffsetY = clamp3(
14199
+ currentOffsetY = clamp4(
13683
14200
  centerKey * renderConfig.keyHeight - canvas.height / 2,
13684
14201
  0,
13685
14202
  getMaxOffsetY()
@@ -13729,7 +14246,7 @@ var mountDAW = (target, options = {}) => {
13729
14246
  const threshold = currentOffsetX / renderConfig.stepWidth + visibleSteps - 4;
13730
14247
  if (currentPlayStep > threshold) {
13731
14248
  const visibleBars = Math.round(visibleSteps / renderConfig.stepsPerBar);
13732
- currentOffsetX = clamp3(
14249
+ currentOffsetX = clamp4(
13733
14250
  currentOffsetX + visibleBars * renderConfig.stepsPerBar * renderConfig.stepWidth,
13734
14251
  0,
13735
14252
  getMaxOffsetX()
@@ -13785,6 +14302,9 @@ var mountDAW = (target, options = {}) => {
13785
14302
  pan: panToStereo(lt.pan ?? DEFAULT_PAN),
13786
14303
  vibrato: lt.vibrato,
13787
14304
  reverbSend: (lt.reverb ?? 0) / 100,
14305
+ delaySend: (lt.delay ?? 0) / 100,
14306
+ gender: (lt.gender ?? 50) / 100,
14307
+ breathiness: (lt.breathiness ?? 50) / 100,
13788
14308
  notes
13789
14309
  };
13790
14310
  }) : [];
@@ -13812,7 +14332,7 @@ var mountDAW = (target, options = {}) => {
13812
14332
  }
13813
14333
  if (playbackState !== "paused") {
13814
14334
  const canvas = getGridCanvas();
13815
- currentOffsetX = clamp3(
14335
+ currentOffsetX = clamp4(
13816
14336
  playStartStep * renderConfig.stepWidth - canvas.width * 0.5,
13817
14337
  0,
13818
14338
  getMaxOffsetX()
@@ -13890,7 +14410,38 @@ var mountDAW = (target, options = {}) => {
13890
14410
  <span class="dtm-label">\u30D9\u30ED\u30B7\u30C6\u30A3</span>
13891
14411
  <input type="range" class="dtm-range dtm-grow" data-dtm="track-vol" min="0" max="127" value="${active.volume}">
13892
14412
  <span class="dtm-label" data-dtm="track-vol-label">${active.volume}</span>
13893
- </div>`;
14413
+ </div>
14414
+ <details class="dtm-advanced" data-dtm="track-fx-advanced">
14415
+ <summary>\u8A73\u7D30\u8A2D\u5B9A\uFF08EQ\u30FB\u97F3\u5727\u30FB\u30B9\u30C6\u30EC\u30AA\u5E45\uFF09</summary>
14416
+ <div class="dtm-row">
14417
+ <span class="dtm-label">EQ\u4F4E\u57DF</span>
14418
+ <input type="range" class="dtm-range dtm-grow" data-dtm="track-eq-low" min="-12" max="12" step="1" aria-label="\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306EEQ\u4F4E\u57DF\u30B2\u30A4\u30F3\uFF08dB\uFF09">
14419
+ <span class="dtm-label" data-dtm="track-eq-low-label"></span>
14420
+ <button class="dtm-infobtn" data-dtm="track-eq-info" title="EQ\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14421
+ </div>
14422
+ <div class="dtm-row">
14423
+ <span class="dtm-label">EQ\u4E2D\u57DF</span>
14424
+ <input type="range" class="dtm-range dtm-grow" data-dtm="track-eq-mid" min="-12" max="12" step="1" aria-label="\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306EEQ\u4E2D\u57DF\u30B2\u30A4\u30F3\uFF08dB\uFF09">
14425
+ <span class="dtm-label" data-dtm="track-eq-mid-label"></span>
14426
+ </div>
14427
+ <div class="dtm-row">
14428
+ <span class="dtm-label">EQ\u9AD8\u57DF</span>
14429
+ <input type="range" class="dtm-range dtm-grow" data-dtm="track-eq-high" min="-12" max="12" step="1" aria-label="\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306EEQ\u9AD8\u57DF\u30B2\u30A4\u30F3\uFF08dB\uFF09">
14430
+ <span class="dtm-label" data-dtm="track-eq-high-label"></span>
14431
+ </div>
14432
+ <div class="dtm-row">
14433
+ <span class="dtm-label">\u97F3\u5727\u5F37\u5316</span>
14434
+ <input type="range" class="dtm-range dtm-grow" data-dtm="track-comp" min="0" max="100" aria-label="\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u30B3\u30F3\u30D7\u30EC\u30C3\u30B5\u30FC\u91CF\uFF08\u97F3\u5727\u5F37\u5316\uFF09">
14435
+ <span class="dtm-label" data-dtm="track-comp-label"></span>
14436
+ <button class="dtm-infobtn" data-dtm="track-comp-info" title="\u97F3\u5727\u5F37\u5316\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14437
+ </div>
14438
+ <div class="dtm-row">
14439
+ <span class="dtm-label">\u30B9\u30C6\u30EC\u30AA\u5E45</span>
14440
+ <input type="range" class="dtm-range dtm-grow" data-dtm="track-width" min="0" max="200" aria-label="\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u30B9\u30C6\u30EC\u30AA\u5E45\uFF08100=\u539F\u97F3\u30010=\u30E2\u30CE\u30E9\u30EB\uFF09">
14441
+ <span class="dtm-label" data-dtm="track-width-label"></span>
14442
+ <button class="dtm-infobtn" data-dtm="track-width-info" title="\u30B9\u30C6\u30EC\u30AA\u5E45\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14443
+ </div>
14444
+ </details>`;
13894
14445
  const volInput = refs.trackBody.querySelector(
13895
14446
  '[data-dtm="track-vol"]'
13896
14447
  );
@@ -13907,6 +14458,93 @@ var mountDAW = (target, options = {}) => {
13907
14458
  volInput.title = active.lyricModel ? "\u6B4C\u8A5E\u30E2\u30FC\u30C9\u306E\u3068\u304D\u306F\u30D9\u30ED\u30B7\u30C6\u30A3\u304C\u518D\u751F\u306B\u53CD\u6620\u3055\u308C\u307E\u305B\u3093\uFF08\u58F0\u91CF\u3067\u8ABF\u6574\u3057\u3066\u304F\u3060\u3055\u3044\uFF09" : "";
13908
14459
  };
13909
14460
  syncVelocityDisabled();
14461
+ const trackEqLowInput = refs.trackBody.querySelector(
14462
+ '[data-dtm="track-eq-low"]'
14463
+ );
14464
+ const trackEqLowLabel = refs.trackBody.querySelector(
14465
+ '[data-dtm="track-eq-low-label"]'
14466
+ );
14467
+ const trackEqMidInput = refs.trackBody.querySelector(
14468
+ '[data-dtm="track-eq-mid"]'
14469
+ );
14470
+ const trackEqMidLabel = refs.trackBody.querySelector(
14471
+ '[data-dtm="track-eq-mid-label"]'
14472
+ );
14473
+ const trackEqHighInput = refs.trackBody.querySelector(
14474
+ '[data-dtm="track-eq-high"]'
14475
+ );
14476
+ const trackEqHighLabel = refs.trackBody.querySelector(
14477
+ '[data-dtm="track-eq-high-label"]'
14478
+ );
14479
+ const trackEqInfo = refs.trackBody.querySelector(
14480
+ '[data-dtm="track-eq-info"]'
14481
+ );
14482
+ const fmtDb = (db) => db > 0 ? `+${db}dB` : `${db}dB`;
14483
+ trackEqLowInput.value = String(active.trackEqLow);
14484
+ trackEqLowLabel.textContent = fmtDb(active.trackEqLow);
14485
+ trackEqMidInput.value = String(active.trackEqMid);
14486
+ trackEqMidLabel.textContent = fmtDb(active.trackEqMid);
14487
+ trackEqHighInput.value = String(active.trackEqHigh);
14488
+ trackEqHighLabel.textContent = fmtDb(active.trackEqHigh);
14489
+ trackEqLowInput.addEventListener("input", () => {
14490
+ active.trackEqLow = Number.parseInt(trackEqLowInput.value, 10);
14491
+ trackEqLowLabel.textContent = fmtDb(active.trackEqLow);
14492
+ options.onTrackEqLowChange?.(active.config.id, active.trackEqLow);
14493
+ });
14494
+ trackEqMidInput.addEventListener("input", () => {
14495
+ active.trackEqMid = Number.parseInt(trackEqMidInput.value, 10);
14496
+ trackEqMidLabel.textContent = fmtDb(active.trackEqMid);
14497
+ options.onTrackEqMidChange?.(active.config.id, active.trackEqMid);
14498
+ });
14499
+ trackEqHighInput.addEventListener("input", () => {
14500
+ active.trackEqHigh = Number.parseInt(trackEqHighInput.value, 10);
14501
+ trackEqHighLabel.textContent = fmtDb(active.trackEqHigh);
14502
+ options.onTrackEqHighChange?.(active.config.id, active.trackEqHigh);
14503
+ });
14504
+ trackEqInfo.addEventListener("click", () => {
14505
+ showModal("EQ\uFF08\u30A4\u30B3\u30E9\u30A4\u30B6\u30FC\uFF09\u306E\u89E3\u8AAC", TRACK_EQ_INFO_HTML);
14506
+ });
14507
+ const trackCompInput = refs.trackBody.querySelector(
14508
+ '[data-dtm="track-comp"]'
14509
+ );
14510
+ const trackCompLabel = refs.trackBody.querySelector(
14511
+ '[data-dtm="track-comp-label"]'
14512
+ );
14513
+ const trackWidthInput = refs.trackBody.querySelector(
14514
+ '[data-dtm="track-width"]'
14515
+ );
14516
+ const trackWidthLabel = refs.trackBody.querySelector(
14517
+ '[data-dtm="track-width-label"]'
14518
+ );
14519
+ const trackCompInfo = refs.trackBody.querySelector(
14520
+ '[data-dtm="track-comp-info"]'
14521
+ );
14522
+ const trackWidthInfo = refs.trackBody.querySelector(
14523
+ '[data-dtm="track-width-info"]'
14524
+ );
14525
+ trackCompInput.value = String(active.trackCompression);
14526
+ trackCompLabel.textContent = `${active.trackCompression}%`;
14527
+ trackWidthInput.value = String(active.trackWidth);
14528
+ trackWidthLabel.textContent = `${active.trackWidth}%`;
14529
+ trackCompInput.addEventListener("input", () => {
14530
+ active.trackCompression = Number.parseInt(trackCompInput.value, 10);
14531
+ trackCompLabel.textContent = `${active.trackCompression}%`;
14532
+ options.onTrackCompressionChange?.(
14533
+ active.config.id,
14534
+ active.trackCompression
14535
+ );
14536
+ });
14537
+ trackWidthInput.addEventListener("input", () => {
14538
+ active.trackWidth = Number.parseInt(trackWidthInput.value, 10);
14539
+ trackWidthLabel.textContent = `${active.trackWidth}%`;
14540
+ options.onTrackWidthChange?.(active.config.id, active.trackWidth);
14541
+ });
14542
+ trackCompInfo.addEventListener("click", () => {
14543
+ showModal("\u97F3\u5727\u5F37\u5316\u306E\u89E3\u8AAC", TRACK_COMPRESSION_INFO_HTML);
14544
+ });
14545
+ trackWidthInfo.addEventListener("click", () => {
14546
+ showModal("\u30B9\u30C6\u30EC\u30AA\u5E45\u306E\u89E3\u8AAC", TRACK_WIDTH_INFO_HTML);
14547
+ });
13910
14548
  const instRow = document.createElement("div");
13911
14549
  instRow.className = "dtm-row";
13912
14550
  instRow.innerHTML = `<span class="dtm-label">\u697D\u5668</span>`;
@@ -13970,17 +14608,6 @@ var mountDAW = (target, options = {}) => {
13970
14608
  <span class="dtm-label">\u266A UTAU</span>
13971
14609
  <select class="dtm-select" data-dtm="lyric-model" aria-label="\u6B4C\u5531\u30E2\u30C7\u30EB"></select>
13972
14610
  <img class="dtm-lyric-icon dtm-hidden" data-dtm="lyric-icon" width="20" height="20" alt="" draggable="false">
13973
- <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">
13974
- <option value="2">+2 oct</option>
13975
- <option value="1">+1 oct</option>
13976
- <option value="0">\xB10 oct</option>
13977
- <option value="-1">-1 oct</option>
13978
- <option value="-2">-2 oct</option>
13979
- </select>
13980
- <label class="dtm-label" style="display:inline-flex;align-items:center;gap:2px;white-space:nowrap" title="\u30ED\u30F3\u30B0\u30C8\u30FC\u30F3\u306B\u81EA\u52D5\u3067\u30D3\u30D6\u30E9\u30FC\u30C8\u3092\u639B\u3051\u307E\u3059">
13981
- <input type="checkbox" data-dtm="lyric-vibrato" aria-label="\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8">\u30D3\u30D6\u30E9\u30FC\u30C8
13982
- </label>
13983
- <button class="dtm-infobtn" data-dtm="lyric-vibrato-info" title="\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
13984
14611
  <span class="dtm-label dtm-grow" data-dtm="lyric-count" style="text-align:right"></span>
13985
14612
  </div>
13986
14613
  <div class="dtm-row dtm-hidden" data-dtm="lyric-terms" style="font-size:10px;gap:4px;color:var(--dtm-warn)">
@@ -14006,16 +14633,54 @@ var mountDAW = (target, options = {}) => {
14006
14633
  <input type="range" class="dtm-range dtm-grow" data-dtm="lyric-vol" min="0" max="${MAX_VOCAL_VOLUME}" aria-label="\u6B4C\u5531\u306E\u58F0\u91CF\uFF08100=\u7B49\u500D\u3001100\u8D85\u3067\u30D6\u30FC\u30B9\u30C8\u3001\u65E2\u5B9A200\uFF09">
14007
14634
  <span class="dtm-label" data-dtm="lyric-vol-label"></span>
14008
14635
  </div>
14009
- <div class="dtm-row">
14010
- <span class="dtm-label">\u5B9A\u4F4D</span>
14011
- <input type="range" class="dtm-range dtm-grow" data-dtm="lyric-pan" min="0" max="127" aria-label="\u6B4C\u5531\u306E\u30B9\u30C6\u30EC\u30AA\u5B9A\u4F4D\uFF08\u5DE6\u53F3\uFF09">
14012
- <span class="dtm-label" data-dtm="lyric-pan-label"></span>
14013
- </div>
14014
- <div class="dtm-row">
14015
- <span class="dtm-label">\u30EA\u30D0\u30FC\u30D6\u9001\u308A</span>
14016
- <input type="range" class="dtm-range dtm-grow" data-dtm="lyric-reverb" min="0" max="100" aria-label="\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u304B\u3089\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\u3078\u9001\u308B\u91CF\uFF08\u30DE\u30B9\u30BF\u306E\u30EA\u30D0\u30FC\u30D6\u3064\u307E\u307F\u304C0%\u3060\u3068\u7121\u97F3\uFF09" title="\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\u3078\u306E\u30BB\u30F3\u30C9\u91CF">
14017
- <span class="dtm-label" data-dtm="lyric-reverb-label"></span>
14018
- </div>
14636
+ <details class="dtm-advanced" data-dtm="lyric-advanced">
14637
+ <summary>\u8A73\u7D30\u8A2D\u5B9A</summary>
14638
+ <div class="dtm-row">
14639
+ <span class="dtm-label">\u30AA\u30AF\u30BF\u30FC\u30D6</span>
14640
+ <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">
14641
+ <option value="2">+2 oct</option>
14642
+ <option value="1">+1 oct</option>
14643
+ <option value="0">\xB10 oct</option>
14644
+ <option value="-1">-1 oct</option>
14645
+ <option value="-2">-2 oct</option>
14646
+ </select>
14647
+ </div>
14648
+ <div class="dtm-row">
14649
+ <span class="dtm-label">\u5B9A\u4F4D</span>
14650
+ <input type="range" class="dtm-range dtm-grow" data-dtm="lyric-pan" min="0" max="127" aria-label="\u6B4C\u5531\u306E\u30B9\u30C6\u30EC\u30AA\u5B9A\u4F4D\uFF08\u5DE6\u53F3\uFF09">
14651
+ <span class="dtm-label" data-dtm="lyric-pan-label"></span>
14652
+ </div>
14653
+ <div class="dtm-row">
14654
+ <span class="dtm-label">\u30EA\u30D0\u30FC\u30D6\u9001\u308A</span>
14655
+ <input type="range" class="dtm-range dtm-grow" data-dtm="lyric-reverb" min="0" max="100" aria-label="\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u304B\u3089\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\u3078\u9001\u308B\u91CF\uFF08\u30DE\u30B9\u30BF\u306E\u30EA\u30D0\u30FC\u30D6\u3064\u307E\u307F\u304C0%\u3060\u3068\u7121\u97F3\uFF09">
14656
+ <span class="dtm-label" data-dtm="lyric-reverb-label"></span>
14657
+ <button class="dtm-infobtn" data-dtm="lyric-reverb-info" title="\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14658
+ </div>
14659
+ <div class="dtm-row">
14660
+ <span class="dtm-label">\u30C7\u30A3\u30EC\u30A4\u9001\u308A</span>
14661
+ <input type="range" class="dtm-range dtm-grow" data-dtm="lyric-delay" min="0" max="100" aria-label="\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u304B\u3089\u30DE\u30B9\u30BF\u30C7\u30A3\u30EC\u30A4\u3078\u9001\u308B\u91CF\uFF08\u30DE\u30B9\u30BF\u306E\u30C7\u30A3\u30EC\u30A4\u3064\u307E\u307F\u304C0%\u3060\u3068\u7121\u97F3\uFF09">
14662
+ <span class="dtm-label" data-dtm="lyric-delay-label"></span>
14663
+ <button class="dtm-infobtn" data-dtm="lyric-delay-info" title="\u30C7\u30A3\u30EC\u30A4\u9001\u308A\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14664
+ </div>
14665
+ <div class="dtm-row">
14666
+ <span class="dtm-label">\u30B8\u30A7\u30F3\u30C0\u30FC</span>
14667
+ <input type="range" class="dtm-range dtm-grow" data-dtm="lyric-gender" min="0" max="100" aria-label="\u30D5\u30A9\u30EB\u30DE\u30F3\u30C8/\u30B8\u30A7\u30F3\u30C0\u30FC\u30D5\u30A1\u30AF\u30BF\u30FC\uFF08koe\u97F3\u6E90\u9650\u5B9A\uFF09">
14668
+ <span class="dtm-label" data-dtm="lyric-gender-label"></span>
14669
+ <button class="dtm-infobtn" data-dtm="lyric-gender-info" title="\u30B8\u30A7\u30F3\u30C0\u30FC\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14670
+ </div>
14671
+ <div class="dtm-row">
14672
+ <span class="dtm-label">\u30D6\u30EC\u30B7\u30CD\u30B9</span>
14673
+ <input type="range" class="dtm-range dtm-grow" data-dtm="lyric-breathiness" min="0" max="100" aria-label="\u30D6\u30EC\u30B7\u30CD\u30B9\uFF08\u606F\u6210\u5206\u3001koe\u97F3\u6E90\u9650\u5B9A\uFF09">
14674
+ <span class="dtm-label" data-dtm="lyric-breathiness-label"></span>
14675
+ <button class="dtm-infobtn" data-dtm="lyric-breathiness-info" title="\u30D6\u30EC\u30B7\u30CD\u30B9\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14676
+ </div>
14677
+ <div class="dtm-row">
14678
+ <span class="dtm-label" style="display:inline-flex;align-items:center;gap:2px">
14679
+ <input type="checkbox" data-dtm="lyric-vibrato" aria-label="\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8">\u30D3\u30D6\u30E9\u30FC\u30C8
14680
+ </span>
14681
+ <button class="dtm-infobtn" data-dtm="lyric-vibrato-info" title="\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14682
+ </div>
14683
+ </details>
14019
14684
  <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>
14020
14685
  </div>`;
14021
14686
  refs.trackBody.appendChild(lyricDiv);
@@ -14055,6 +14720,48 @@ var mountDAW = (target, options = {}) => {
14055
14720
  const lyricReverbLabel = lyricDiv.querySelector(
14056
14721
  '[data-dtm="lyric-reverb-label"]'
14057
14722
  );
14723
+ const lyricReverbInfo = lyricDiv.querySelector(
14724
+ '[data-dtm="lyric-reverb-info"]'
14725
+ );
14726
+ lyricReverbInfo.addEventListener("click", () => {
14727
+ showModal("\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u306E\u89E3\u8AAC", LYRIC_REVERB_INFO_HTML);
14728
+ });
14729
+ const lyricDelay = lyricDiv.querySelector(
14730
+ '[data-dtm="lyric-delay"]'
14731
+ );
14732
+ const lyricDelayLabel = lyricDiv.querySelector(
14733
+ '[data-dtm="lyric-delay-label"]'
14734
+ );
14735
+ const lyricDelayInfo = lyricDiv.querySelector(
14736
+ '[data-dtm="lyric-delay-info"]'
14737
+ );
14738
+ lyricDelayInfo.addEventListener("click", () => {
14739
+ showModal("\u30C7\u30A3\u30EC\u30A4\u9001\u308A\u306E\u89E3\u8AAC", LYRIC_DELAY_INFO_HTML);
14740
+ });
14741
+ const lyricGender = lyricDiv.querySelector(
14742
+ '[data-dtm="lyric-gender"]'
14743
+ );
14744
+ const lyricGenderLabel = lyricDiv.querySelector(
14745
+ '[data-dtm="lyric-gender-label"]'
14746
+ );
14747
+ const lyricGenderInfo = lyricDiv.querySelector(
14748
+ '[data-dtm="lyric-gender-info"]'
14749
+ );
14750
+ lyricGenderInfo.addEventListener("click", () => {
14751
+ showModal("\u30B8\u30A7\u30F3\u30C0\u30FC\u306E\u89E3\u8AAC", GENDER_INFO_HTML);
14752
+ });
14753
+ const lyricBreathiness = lyricDiv.querySelector(
14754
+ '[data-dtm="lyric-breathiness"]'
14755
+ );
14756
+ const lyricBreathinessLabel = lyricDiv.querySelector(
14757
+ '[data-dtm="lyric-breathiness-label"]'
14758
+ );
14759
+ const lyricBreathinessInfo = lyricDiv.querySelector(
14760
+ '[data-dtm="lyric-breathiness-info"]'
14761
+ );
14762
+ lyricBreathinessInfo.addEventListener("click", () => {
14763
+ showModal("\u30D6\u30EC\u30B7\u30CD\u30B9\u306E\u89E3\u8AAC", BREATHINESS_INFO_HTML);
14764
+ });
14058
14765
  const lyricVibrato = lyricDiv.querySelector(
14059
14766
  '[data-dtm="lyric-vibrato"]'
14060
14767
  );
@@ -14127,6 +14834,12 @@ var mountDAW = (target, options = {}) => {
14127
14834
  lyricPanLabel.textContent = fmtPan(active.vocalPan);
14128
14835
  lyricReverb.value = String(active.vocalReverb);
14129
14836
  lyricReverbLabel.textContent = `${active.vocalReverb}%`;
14837
+ lyricDelay.value = String(active.vocalDelay);
14838
+ lyricDelayLabel.textContent = `${active.vocalDelay}%`;
14839
+ lyricGender.value = String(active.vocalGender);
14840
+ lyricGenderLabel.textContent = `${active.vocalGender}`;
14841
+ lyricBreathiness.value = String(active.vocalBreathiness);
14842
+ lyricBreathinessLabel.textContent = `${active.vocalBreathiness}`;
14130
14843
  lyricVibrato.checked = active.vocalVibrato;
14131
14844
  const updateLyricCount = () => {
14132
14845
  const n = normalizeLyrics(lyricInput.value).length;
@@ -14177,7 +14890,6 @@ var mountDAW = (target, options = {}) => {
14177
14890
  };
14178
14891
  const syncLyricVisibility = () => {
14179
14892
  lyricBody.style.display = active.lyricModel ? "" : "none";
14180
- lyricOctaveSel.style.display = active.lyricModel ? "" : "none";
14181
14893
  updateLyricCount();
14182
14894
  syncLyricTerms();
14183
14895
  syncLyricIcon();
@@ -14278,6 +14990,21 @@ var mountDAW = (target, options = {}) => {
14278
14990
  lyricReverbLabel.textContent = `${active.vocalReverb}%`;
14279
14991
  fireLyricsChange(active);
14280
14992
  });
14993
+ lyricDelay.addEventListener("input", () => {
14994
+ active.vocalDelay = Number.parseInt(lyricDelay.value, 10);
14995
+ lyricDelayLabel.textContent = `${active.vocalDelay}%`;
14996
+ fireLyricsChange(active);
14997
+ });
14998
+ lyricGender.addEventListener("input", () => {
14999
+ active.vocalGender = Number.parseInt(lyricGender.value, 10);
15000
+ lyricGenderLabel.textContent = `${active.vocalGender}`;
15001
+ fireLyricsChange(active);
15002
+ });
15003
+ lyricBreathiness.addEventListener("input", () => {
15004
+ active.vocalBreathiness = Number.parseInt(lyricBreathiness.value, 10);
15005
+ lyricBreathinessLabel.textContent = `${active.vocalBreathiness}`;
15006
+ fireLyricsChange(active);
15007
+ });
14281
15008
  }
14282
15009
  if (active.config.id === "chord" && showChord) {
14283
15010
  const div = document.createElement("div");
@@ -14361,18 +15088,44 @@ var mountDAW = (target, options = {}) => {
14361
15088
  const limitSteps = barLimitBars > 0 ? barLimitBars * renderConfig.stepsPerBar : Infinity;
14362
15089
  const clipNotes = (notes) => limitSteps === Infinity ? notes : notes.filter((n) => n.startStep < limitSteps);
14363
15090
  const trackInstrumentsForMeta = {};
15091
+ const trackCompressionForMeta = {};
15092
+ const trackWidthForMeta = {};
15093
+ const trackEqLowForMeta = {};
15094
+ const trackEqMidForMeta = {};
15095
+ const trackEqHighForMeta = {};
14364
15096
  trackStates.forEach((t, i2) => {
14365
15097
  if (t.trackInstrument) trackInstrumentsForMeta[i2] = t.trackInstrument;
15098
+ if (t.trackCompression !== 0)
15099
+ trackCompressionForMeta[i2] = t.trackCompression;
15100
+ if (t.trackWidth !== 100) trackWidthForMeta[i2] = t.trackWidth;
15101
+ if (t.trackEqLow !== 0) trackEqLowForMeta[i2] = t.trackEqLow;
15102
+ if (t.trackEqMid !== 0) trackEqMidForMeta[i2] = t.trackEqMid;
15103
+ if (t.trackEqHigh !== 0) trackEqHighForMeta[i2] = t.trackEqHigh;
14366
15104
  });
14367
15105
  const trackInstMeta = Object.keys(trackInstrumentsForMeta).length > 0 ? trackInstrumentsForMeta : void 0;
15106
+ const trackCompMeta = Object.keys(trackCompressionForMeta).length > 0 ? trackCompressionForMeta : void 0;
15107
+ const trackWidthMeta = Object.keys(trackWidthForMeta).length > 0 ? trackWidthForMeta : void 0;
15108
+ const trackEqLowMeta = Object.keys(trackEqLowForMeta).length > 0 ? trackEqLowForMeta : void 0;
15109
+ const trackEqMidMeta = Object.keys(trackEqMidForMeta).length > 0 ? trackEqMidForMeta : void 0;
15110
+ const trackEqHighMeta = Object.keys(trackEqHighForMeta).length > 0 ? trackEqHighForMeta : void 0;
14368
15111
  const metaLineFull = formatMmlMeta(
14369
15112
  {
14370
15113
  instrument: currentInstrument || void 0,
14371
15114
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
14372
15115
  volume: masterVolume,
14373
15116
  drumVolume,
15117
+ reverb: reverbAmount,
15118
+ reverbDecay: Math.round(reverbDecay * 10),
15119
+ reverbPreDelay,
15120
+ delay: delayAmount,
15121
+ delayDivision,
14374
15122
  mode,
14375
- trackInstruments: trackInstMeta
15123
+ trackInstruments: trackInstMeta,
15124
+ trackCompression: trackCompMeta,
15125
+ trackWidth: trackWidthMeta,
15126
+ trackEqLow: trackEqLowMeta,
15127
+ trackEqMid: trackEqMidMeta,
15128
+ trackEqHigh: trackEqHighMeta
14376
15129
  },
14377
15130
  " "
14378
15131
  );
@@ -14382,8 +15135,18 @@ var mountDAW = (target, options = {}) => {
14382
15135
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
14383
15136
  volume: masterVolume,
14384
15137
  drumVolume,
15138
+ reverb: reverbAmount,
15139
+ reverbDecay: Math.round(reverbDecay * 10),
15140
+ reverbPreDelay,
15141
+ delay: delayAmount,
15142
+ delayDivision,
14385
15143
  mode,
14386
- trackInstruments: trackInstMeta
15144
+ trackInstruments: trackInstMeta,
15145
+ trackCompression: trackCompMeta,
15146
+ trackWidth: trackWidthMeta,
15147
+ trackEqLow: trackEqLowMeta,
15148
+ trackEqMid: trackEqMidMeta,
15149
+ trackEqHigh: trackEqHighMeta
14387
15150
  },
14388
15151
  ""
14389
15152
  );
@@ -14432,7 +15195,10 @@ var mountDAW = (target, options = {}) => {
14432
15195
  pan: t.vocalPan,
14433
15196
  oct: t.vocalOctave,
14434
15197
  vib: t.vocalVibrato,
14435
- rev: t.vocalReverb
15198
+ rev: t.vocalReverb,
15199
+ del: t.vocalDelay,
15200
+ gen: t.vocalGender,
15201
+ bre: t.vocalBreathiness
14436
15202
  })).filter(
14437
15203
  (x2) => x2.model.length > 0 && x2.text.length > 0 && x2.notes.length > 0
14438
15204
  ).map((x2) => {
@@ -14442,7 +15208,10 @@ var mountDAW = (target, options = {}) => {
14442
15208
  x2.pan === 64 ? "" : `p${x2.pan}`,
14443
15209
  x2.oct === 0 ? "" : `o${x2.oct}`,
14444
15210
  x2.vib ? "b1" : "",
14445
- x2.rev === 0 ? "" : `r${x2.rev}`
15211
+ x2.rev === 0 ? "" : `r${x2.rev}`,
15212
+ x2.del === 0 ? "" : `e${x2.del}`,
15213
+ x2.gen === 50 ? "" : `g${x2.gen}`,
15214
+ x2.bre === 50 ? "" : `h${x2.bre}`
14446
15215
  ].filter((s) => s.length > 0).join(" ");
14447
15216
  const head = params ? `${x2.model} ${params}` : x2.model;
14448
15217
  return `@@${x2.i} ${head} ${x2.text}`;
@@ -14511,7 +15280,7 @@ var mountDAW = (target, options = {}) => {
14511
15280
  const canvas = getGridCanvas();
14512
15281
  const yIndex = renderConfig.keyCount - 1 - (pitch - renderConfig.pitchRangeStart);
14513
15282
  const logicalY = yIndex * renderConfig.keyHeight;
14514
- currentOffsetY = clamp3(
15283
+ currentOffsetY = clamp4(
14515
15284
  logicalY - (canvas.height - renderConfig.keyHeight) / 2,
14516
15285
  0,
14517
15286
  getMaxOffsetY()
@@ -14582,6 +15351,35 @@ var mountDAW = (target, options = {}) => {
14582
15351
  refs.drumVolume.value = String(meta.drumVolume);
14583
15352
  refs.drumVolumeLabel.textContent = `${drumVolume}%`;
14584
15353
  }
15354
+ if (meta.reverb !== void 0) {
15355
+ reverbAmount = meta.reverb;
15356
+ refs.reverbAmount.value = String(meta.reverb);
15357
+ refs.reverbAmountLabel.textContent = `${meta.reverb}%`;
15358
+ options.onReverbChange?.(meta.reverb);
15359
+ }
15360
+ if (meta.reverbDecay !== void 0) {
15361
+ reverbDecay = meta.reverbDecay / 10;
15362
+ refs.reverbDecay.value = String(meta.reverbDecay);
15363
+ refs.reverbDecayLabel.textContent = `${reverbDecay.toFixed(1)}s`;
15364
+ options.onReverbDecayChange?.(reverbDecay);
15365
+ }
15366
+ if (meta.reverbPreDelay !== void 0) {
15367
+ reverbPreDelay = meta.reverbPreDelay;
15368
+ refs.reverbPreDelay.value = String(meta.reverbPreDelay);
15369
+ refs.reverbPreDelayLabel.textContent = `${meta.reverbPreDelay}ms`;
15370
+ options.onReverbPreDelayChange?.(reverbPreDelay);
15371
+ }
15372
+ if (meta.delay !== void 0) {
15373
+ delayAmount = meta.delay;
15374
+ refs.delayAmount.value = String(meta.delay);
15375
+ refs.delayAmountLabel.textContent = `${meta.delay}%`;
15376
+ options.onDelayChange?.(meta.delay);
15377
+ }
15378
+ if (meta.delayDivision && ["4", "8", "8d", "16"].includes(meta.delayDivision)) {
15379
+ delayDivision = meta.delayDivision;
15380
+ refs.delayDivision.value = delayDivision;
15381
+ options.onDelayDivisionChange?.(delayDivision);
15382
+ }
14585
15383
  }
14586
15384
  trackStates.forEach((t, i2) => {
14587
15385
  if (applyActiveOnly && i2 !== activeTrackIndex) return;
@@ -14591,6 +15389,34 @@ var mountDAW = (target, options = {}) => {
14591
15389
  options.onTrackInstrumentChange?.(i2, name);
14592
15390
  }
14593
15391
  });
15392
+ trackStates.forEach((t, i2) => {
15393
+ if (applyActiveOnly && i2 !== activeTrackIndex) return;
15394
+ const comp = meta.trackCompression?.[i2] ?? 0;
15395
+ if (t.trackCompression !== comp) {
15396
+ t.trackCompression = comp;
15397
+ options.onTrackCompressionChange?.(t.config.id, comp);
15398
+ }
15399
+ const width = meta.trackWidth?.[i2] ?? 100;
15400
+ if (t.trackWidth !== width) {
15401
+ t.trackWidth = width;
15402
+ options.onTrackWidthChange?.(t.config.id, width);
15403
+ }
15404
+ const eqLow = meta.trackEqLow?.[i2] ?? 0;
15405
+ if (t.trackEqLow !== eqLow) {
15406
+ t.trackEqLow = eqLow;
15407
+ options.onTrackEqLowChange?.(t.config.id, eqLow);
15408
+ }
15409
+ const eqMid = meta.trackEqMid?.[i2] ?? 0;
15410
+ if (t.trackEqMid !== eqMid) {
15411
+ t.trackEqMid = eqMid;
15412
+ options.onTrackEqMidChange?.(t.config.id, eqMid);
15413
+ }
15414
+ const eqHigh = meta.trackEqHigh?.[i2] ?? 0;
15415
+ if (t.trackEqHigh !== eqHigh) {
15416
+ t.trackEqHigh = eqHigh;
15417
+ options.onTrackEqHighChange?.(t.config.id, eqHigh);
15418
+ }
15419
+ });
14594
15420
  trackStates.forEach((t, i2) => {
14595
15421
  if (applyActiveOnly && i2 !== activeTrackIndex) return;
14596
15422
  const v = trackVelocity.get(i2);
@@ -14609,6 +15435,9 @@ var mountDAW = (target, options = {}) => {
14609
15435
  active.vocalOctave = 0;
14610
15436
  active.vocalVibrato = false;
14611
15437
  active.vocalReverb = 0;
15438
+ active.vocalDelay = 0;
15439
+ active.vocalGender = 50;
15440
+ active.vocalBreathiness = 50;
14612
15441
  } else {
14613
15442
  for (const t of trackStates) {
14614
15443
  t.lyrics = "";
@@ -14619,6 +15448,9 @@ var mountDAW = (target, options = {}) => {
14619
15448
  t.vocalOctave = 0;
14620
15449
  t.vocalVibrato = false;
14621
15450
  t.vocalReverb = 0;
15451
+ t.vocalDelay = 0;
15452
+ t.vocalGender = 50;
15453
+ t.vocalBreathiness = 50;
14622
15454
  }
14623
15455
  }
14624
15456
  lyrics?.forEach((lt) => {
@@ -14633,6 +15465,9 @@ var mountDAW = (target, options = {}) => {
14633
15465
  t.vocalOctave = lt.octave ?? 0;
14634
15466
  t.vocalVibrato = lt.vibrato ?? false;
14635
15467
  t.vocalReverb = lt.reverb ?? 0;
15468
+ t.vocalDelay = lt.delay ?? 0;
15469
+ t.vocalGender = lt.gender ?? 50;
15470
+ t.vocalBreathiness = lt.breathiness ?? 50;
14636
15471
  });
14637
15472
  for (const p of placements) {
14638
15473
  if (applyActiveOnly && p.trackIndex !== activeTrackIndex) continue;
@@ -14713,6 +15548,9 @@ var mountDAW = (target, options = {}) => {
14713
15548
  active.vocalOctave = 0;
14714
15549
  active.vocalVibrato = false;
14715
15550
  active.vocalReverb = 0;
15551
+ active.vocalDelay = 0;
15552
+ active.vocalGender = 50;
15553
+ active.vocalBreathiness = 50;
14716
15554
  } else {
14717
15555
  clearAll();
14718
15556
  for (const t of trackStates) t.core.setLoadMode(true);
@@ -14725,6 +15563,9 @@ var mountDAW = (target, options = {}) => {
14725
15563
  t.vocalOctave = 0;
14726
15564
  t.vocalVibrato = false;
14727
15565
  t.vocalReverb = 0;
15566
+ t.vocalDelay = 0;
15567
+ t.vocalGender = 50;
15568
+ t.vocalBreathiness = 50;
14728
15569
  }
14729
15570
  }
14730
15571
  const { placements, bpm: parsedBpm } = isAdvanced ? extractMidiPlacementsByTrack(
@@ -14773,6 +15614,7 @@ var mountDAW = (target, options = {}) => {
14773
15614
  bpm = value;
14774
15615
  refs.bpmInput.value = String(value);
14775
15616
  for (const t of trackStates) t.core.setTempo(value);
15617
+ options.onBpmChange?.(value);
14776
15618
  };
14777
15619
  let lastUndoTime = 0;
14778
15620
  const undo = () => {
@@ -14859,6 +15701,12 @@ var mountDAW = (target, options = {}) => {
14859
15701
  isSolo = refs.soloCheckbox.checked;
14860
15702
  redrawAll();
14861
15703
  });
15704
+ unsubscribeClip = options.clipMeter?.onClipChange((clipping) => {
15705
+ refs.clipBadge.classList.toggle("dtm-hidden", !clipping);
15706
+ });
15707
+ refs.clipBadge.addEventListener("click", () => {
15708
+ options.clipMeter?.reset();
15709
+ });
14862
15710
  refs.toolPen.addEventListener("click", () => setToolMode("pen"));
14863
15711
  refs.toolSelect.addEventListener("click", () => setToolMode("select"));
14864
15712
  refs.toolEraser.addEventListener("click", () => setToolMode("eraser"));
@@ -14939,6 +15787,81 @@ var mountDAW = (target, options = {}) => {
14939
15787
  refs.reverbAmountLabel.textContent = `${reverbAmount}%`;
14940
15788
  options.onReverbChange?.(reverbAmount);
14941
15789
  });
15790
+ refs.reverbAmountInfoBtn.addEventListener("click", () => {
15791
+ showModal("\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\u306E\u89E3\u8AAC", MASTER_REVERB_INFO_HTML);
15792
+ });
15793
+ refs.reverbDecay.addEventListener("input", () => {
15794
+ const raw = Number.parseInt(refs.reverbDecay.value, 10) || 22;
15795
+ reverbDecay = Math.max(
15796
+ MIN_REVERB_DECAY_SEC,
15797
+ Math.min(MAX_REVERB_DECAY_SEC, raw / 10)
15798
+ );
15799
+ refs.reverbDecayLabel.textContent = `${reverbDecay.toFixed(1)}s`;
15800
+ options.onReverbDecayChange?.(reverbDecay);
15801
+ });
15802
+ refs.reverbPreDelay.addEventListener("input", () => {
15803
+ reverbPreDelay = Number.parseInt(refs.reverbPreDelay.value, 10) || 0;
15804
+ refs.reverbPreDelayLabel.textContent = `${reverbPreDelay}ms`;
15805
+ options.onReverbPreDelayChange?.(reverbPreDelay);
15806
+ });
15807
+ refs.delayAmount.addEventListener("input", () => {
15808
+ delayAmount = Number.parseInt(refs.delayAmount.value, 10) || 0;
15809
+ refs.delayAmountLabel.textContent = `${delayAmount}%`;
15810
+ options.onDelayChange?.(delayAmount);
15811
+ });
15812
+ refs.delayDivision.addEventListener("change", () => {
15813
+ delayDivision = refs.delayDivision.value;
15814
+ options.onDelayDivisionChange?.(delayDivision);
15815
+ });
15816
+ refs.delayAmountInfoBtn.addEventListener("click", () => {
15817
+ showModal("\u30DE\u30B9\u30BF\u30C7\u30A3\u30EC\u30A4\u306E\u89E3\u8AAC", MASTER_DELAY_INFO_HTML);
15818
+ });
15819
+ refs.autoMasterInfoBtn.addEventListener("click", () => {
15820
+ showModal("\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0\u89E3\u8AAC", AUTO_MASTER_INFO_HTML);
15821
+ });
15822
+ refs.autoMasterBtn.addEventListener("click", () => {
15823
+ reverbAmount = 20;
15824
+ refs.reverbAmount.value = "20";
15825
+ refs.reverbAmountLabel.textContent = "20%";
15826
+ options.onReverbChange?.(20);
15827
+ const bpmT = clamp4((bpm - 60) / (180 - 60), 0, 1);
15828
+ reverbDecay = clamp4(
15829
+ 3 - bpmT * (3 - 0.9),
15830
+ MIN_REVERB_DECAY_SEC,
15831
+ MAX_REVERB_DECAY_SEC
15832
+ );
15833
+ refs.reverbDecay.value = String(Math.round(reverbDecay * 10));
15834
+ refs.reverbDecayLabel.textContent = `${reverbDecay.toFixed(1)}s`;
15835
+ options.onReverbDecayChange?.(reverbDecay);
15836
+ reverbPreDelay = 20;
15837
+ refs.reverbPreDelay.value = "20";
15838
+ refs.reverbPreDelayLabel.textContent = "20ms";
15839
+ options.onReverbPreDelayChange?.(20);
15840
+ let mainVocalId = null;
15841
+ let mainVocalScore = -1;
15842
+ for (const t of trackStates) {
15843
+ if (!t.lyricModel) continue;
15844
+ const score = t.core.getNotes().reduce((sum, n) => sum + n.durationSteps, 0);
15845
+ if (score > mainVocalScore) {
15846
+ mainVocalScore = score;
15847
+ mainVocalId = t.config.id;
15848
+ }
15849
+ }
15850
+ for (const t of trackStates) {
15851
+ const isMainVocal = !!t.lyricModel && t.config.id === mainVocalId;
15852
+ const comp = !t.lyricModel ? 35 : isMainVocal ? 30 : 25;
15853
+ t.trackCompression = comp;
15854
+ t.trackWidth = 115;
15855
+ options.onTrackCompressionChange?.(t.config.id, comp);
15856
+ options.onTrackWidthChange?.(t.config.id, 115);
15857
+ if (t.lyricModel) {
15858
+ t.vocalVibrato = true;
15859
+ t.vocalReverb = isMainVocal ? 15 : 30;
15860
+ fireLyricsChange(t);
15861
+ }
15862
+ }
15863
+ updateTrackPanel();
15864
+ });
14942
15865
  refs.drumSelect.addEventListener("change", () => {
14943
15866
  currentDrumPattern = refs.drumSelect.value;
14944
15867
  options.onDrumChange?.(currentDrumPattern);
@@ -15815,7 +16738,7 @@ var mountDAW = (target, options = {}) => {
15815
16738
  currentPlayStep = step;
15816
16739
  playbackState = "paused";
15817
16740
  const canvas = getGridCanvas();
15818
- currentOffsetX = clamp3(
16741
+ currentOffsetX = clamp4(
15819
16742
  step * renderConfig.stepWidth - canvas.width * 0.5,
15820
16743
  0,
15821
16744
  getMaxOffsetX()
@@ -15861,11 +16784,11 @@ var mountDAW = (target, options = {}) => {
15861
16784
  getViewState,
15862
16785
  setViewState: (state) => {
15863
16786
  if (typeof state.zoomX === "number") {
15864
- zoomX = clamp3(state.zoomX, 25, 200);
16787
+ zoomX = clamp4(state.zoomX, 25, 200);
15865
16788
  applyZoomX();
15866
16789
  }
15867
16790
  if (typeof state.zoomY === "number") {
15868
- zoomY = clamp3(state.zoomY, 50, 200);
16791
+ zoomY = clamp4(state.zoomY, 50, 200);
15869
16792
  applyZoomY();
15870
16793
  }
15871
16794
  if (typeof state.decomposeChord === "boolean") {
@@ -15887,28 +16810,52 @@ var mountDAW = (target, options = {}) => {
15887
16810
  forcePauseAt,
15888
16811
  setLoading,
15889
16812
  setMasterVolume: (volume) => {
15890
- masterVolume = clamp3(volume, 0, 100);
16813
+ masterVolume = clamp4(volume, 0, 100);
15891
16814
  refs.masterVolume.value = String(masterVolume);
15892
16815
  refs.masterVolumeLabel.textContent = `${masterVolume}%`;
15893
16816
  options.singingVoices?.setVolume(masterVolume / 100);
15894
16817
  },
15895
16818
  setVolume: (volume) => {
15896
- masterVolume = clamp3(volume, 0, 100);
16819
+ masterVolume = clamp4(volume, 0, 100);
15897
16820
  refs.masterVolume.value = String(masterVolume);
15898
16821
  refs.masterVolumeLabel.textContent = `${masterVolume}%`;
15899
16822
  options.singingVoices?.setVolume(masterVolume / 100);
15900
16823
  },
15901
16824
  setDrumVolume: (volume) => {
15902
- drumVolume = clamp3(volume, 0, 100);
16825
+ drumVolume = clamp4(volume, 0, 100);
15903
16826
  refs.drumVolume.value = String(drumVolume);
15904
16827
  refs.drumVolumeLabel.textContent = `${drumVolume}%`;
15905
16828
  },
15906
16829
  setReverbAmount: (amount) => {
15907
- reverbAmount = clamp3(amount, 0, 100);
16830
+ reverbAmount = clamp4(amount, 0, 100);
15908
16831
  refs.reverbAmount.value = String(reverbAmount);
15909
16832
  refs.reverbAmountLabel.textContent = `${reverbAmount}%`;
15910
16833
  options.onReverbChange?.(reverbAmount);
15911
16834
  },
16835
+ setReverbDecay: (seconds) => {
16836
+ reverbDecay = Math.max(
16837
+ MIN_REVERB_DECAY_SEC,
16838
+ Math.min(MAX_REVERB_DECAY_SEC, seconds)
16839
+ );
16840
+ refs.reverbDecay.value = String(Math.round(reverbDecay * 10));
16841
+ refs.reverbDecayLabel.textContent = `${reverbDecay.toFixed(1)}s`;
16842
+ options.onReverbDecayChange?.(reverbDecay);
16843
+ },
16844
+ setReverbPreDelay: (ms) => {
16845
+ reverbPreDelay = Math.max(
16846
+ MIN_REVERB_PREDELAY_MS,
16847
+ Math.min(MAX_REVERB_PREDELAY_MS, ms)
16848
+ );
16849
+ refs.reverbPreDelay.value = String(reverbPreDelay);
16850
+ refs.reverbPreDelayLabel.textContent = `${reverbPreDelay}ms`;
16851
+ options.onReverbPreDelayChange?.(reverbPreDelay);
16852
+ },
16853
+ setDelayAmount: (amount) => {
16854
+ delayAmount = clamp4(amount, 0, 100);
16855
+ refs.delayAmount.value = String(delayAmount);
16856
+ refs.delayAmountLabel.textContent = `${delayAmount}%`;
16857
+ options.onDelayChange?.(delayAmount);
16858
+ },
15912
16859
  applyPatch: (trackId, added, removed) => {
15913
16860
  const track = trackStates.find((t) => t.config.id === trackId);
15914
16861
  if (!track) return;
@@ -15950,6 +16897,9 @@ var mountDAW = (target, options = {}) => {
15950
16897
  t.vocalOctave = data.vocalOctave;
15951
16898
  t.vocalVibrato = data.vocalVibrato ?? false;
15952
16899
  t.vocalReverb = data.vocalReverb ?? 0;
16900
+ t.vocalDelay = data.vocalDelay ?? 0;
16901
+ t.vocalGender = data.vocalGender ?? 50;
16902
+ t.vocalBreathiness = data.vocalBreathiness ?? 50;
15953
16903
  },
15954
16904
  applyTrackInstrument: (trackIndex, instrumentName) => {
15955
16905
  const t = trackStates[trackIndex];
@@ -15975,6 +16925,7 @@ var mountDAW = (target, options = {}) => {
15975
16925
  destroy: () => {
15976
16926
  sequencer.stop();
15977
16927
  options.singingVoices?.stopStream();
16928
+ unsubscribeClip?.();
15978
16929
  resizeObserver.disconnect();
15979
16930
  document.removeEventListener("pointermove", onPointerMove);
15980
16931
  document.removeEventListener("pointerup", onPointerUp);
@@ -16067,6 +17018,9 @@ var playSingingMML = async (mml, options = {}) => {
16067
17018
  pan: panToStereo(lt.pan ?? DEFAULT_PAN),
16068
17019
  vibrato: lt.vibrato,
16069
17020
  reverbSend: (lt.reverb ?? 0) / 100,
17021
+ delaySend: (lt.delay ?? 0) / 100,
17022
+ gender: (lt.gender ?? 50) / 100,
17023
+ breathiness: (lt.breathiness ?? 50) / 100,
16070
17024
  notes
16071
17025
  };
16072
17026
  });
@@ -16674,24 +17628,191 @@ var isSupported = midiJsonParser.isSupported;
16674
17628
  var parseArrayBuffer = midiJsonParser.parseArrayBuffer;
16675
17629
  URL.revokeObjectURL(url);
16676
17630
 
16677
- // src/reverb.ts
16678
- var IMPULSE_DURATION_SEC = 2.2;
16679
- var IMPULSE_DECAY = 2.5;
16680
- var createReverbImpulse = (ctx) => {
16681
- const rate = ctx.sampleRate;
16682
- const length = Math.max(1, Math.floor(rate * IMPULSE_DURATION_SEC));
16683
- const impulse = ctx.createBuffer(2, length, rate);
16684
- for (let ch = 0; ch < impulse.numberOfChannels; ch++) {
16685
- const data = impulse.getChannelData(ch);
16686
- for (let i2 = 0; i2 < length; i2++) {
16687
- const envelope = (1 - i2 / length) ** IMPULSE_DECAY;
16688
- data[i2] = (Math.random() * 2 - 1) * envelope;
17631
+ // src/channel-strip.ts
17632
+ var clamp5 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
17633
+ var compressionParams = (amount) => {
17634
+ const t = clamp5(amount, 0, 100) / 100;
17635
+ return {
17636
+ threshold: 0 + (-24 - 0) * t,
17637
+ ratio: 1 + (12 - 1) * t,
17638
+ knee: 0 + (6 - 0) * t,
17639
+ attack: 0.02 + (3e-3 - 0.02) * t,
17640
+ release: 0.25 + (0.15 - 0.25) * t
17641
+ };
17642
+ };
17643
+ var EQ_LOW_FREQ = 200;
17644
+ var EQ_MID_FREQ = 1e3;
17645
+ var EQ_HIGH_FREQ = 5e3;
17646
+ var EQ_MID_Q = 1;
17647
+ var EQ_MAX_DB = 12;
17648
+ var createChannelStrip = (ctx, destination, options = {}) => {
17649
+ const input = ctx.createGain();
17650
+ const eqLow = ctx.createBiquadFilter();
17651
+ eqLow.type = "lowshelf";
17652
+ eqLow.frequency.value = EQ_LOW_FREQ;
17653
+ eqLow.gain.value = clamp5(options.eqLow ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
17654
+ const eqMid = ctx.createBiquadFilter();
17655
+ eqMid.type = "peaking";
17656
+ eqMid.frequency.value = EQ_MID_FREQ;
17657
+ eqMid.Q.value = EQ_MID_Q;
17658
+ eqMid.gain.value = clamp5(options.eqMid ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
17659
+ const eqHigh = ctx.createBiquadFilter();
17660
+ eqHigh.type = "highshelf";
17661
+ eqHigh.frequency.value = EQ_HIGH_FREQ;
17662
+ eqHigh.gain.value = clamp5(options.eqHigh ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
17663
+ input.connect(eqLow);
17664
+ eqLow.connect(eqMid);
17665
+ eqMid.connect(eqHigh);
17666
+ const compressor = ctx.createDynamicsCompressor();
17667
+ const applyCompression = (amount) => {
17668
+ const p = compressionParams(amount);
17669
+ const now = ctx.currentTime;
17670
+ compressor.threshold.setValueAtTime(p.threshold, now);
17671
+ compressor.ratio.setValueAtTime(p.ratio, now);
17672
+ compressor.knee.setValueAtTime(p.knee, now);
17673
+ compressor.attack.setValueAtTime(p.attack, now);
17674
+ compressor.release.setValueAtTime(p.release, now);
17675
+ };
17676
+ applyCompression(options.compression ?? 0);
17677
+ const splitter = ctx.createChannelSplitter(2);
17678
+ const mid = ctx.createGain();
17679
+ mid.gain.value = 0.5;
17680
+ splitter.connect(mid, 0);
17681
+ splitter.connect(mid, 1);
17682
+ const sideSum = ctx.createGain();
17683
+ sideSum.gain.value = 1;
17684
+ const sideL = ctx.createGain();
17685
+ sideL.gain.value = 0.5;
17686
+ const sideR = ctx.createGain();
17687
+ sideR.gain.value = -0.5;
17688
+ splitter.connect(sideL, 0);
17689
+ splitter.connect(sideR, 1);
17690
+ sideL.connect(sideSum);
17691
+ sideR.connect(sideSum);
17692
+ const widthGain = ctx.createGain();
17693
+ sideSum.connect(widthGain);
17694
+ const widthInv = ctx.createGain();
17695
+ widthInv.gain.value = -1;
17696
+ widthGain.connect(widthInv);
17697
+ const outL = ctx.createGain();
17698
+ mid.connect(outL);
17699
+ widthGain.connect(outL);
17700
+ const outR = ctx.createGain();
17701
+ mid.connect(outR);
17702
+ widthInv.connect(outR);
17703
+ const merger = ctx.createChannelMerger(2);
17704
+ outL.connect(merger, 0, 0);
17705
+ outR.connect(merger, 0, 1);
17706
+ const setWidth = (width) => {
17707
+ widthGain.gain.setTargetAtTime(
17708
+ clamp5(width, 0, 200) / 100,
17709
+ ctx.currentTime,
17710
+ 0.02
17711
+ );
17712
+ };
17713
+ setWidth(options.width ?? 100);
17714
+ eqHigh.connect(compressor);
17715
+ compressor.connect(splitter);
17716
+ merger.connect(destination);
17717
+ return {
17718
+ input,
17719
+ setEqLow: (db) => {
17720
+ eqLow.gain.setTargetAtTime(
17721
+ clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
17722
+ ctx.currentTime,
17723
+ 0.02
17724
+ );
17725
+ },
17726
+ setEqMid: (db) => {
17727
+ eqMid.gain.setTargetAtTime(
17728
+ clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
17729
+ ctx.currentTime,
17730
+ 0.02
17731
+ );
17732
+ },
17733
+ setEqHigh: (db) => {
17734
+ eqHigh.gain.setTargetAtTime(
17735
+ clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
17736
+ ctx.currentTime,
17737
+ 0.02
17738
+ );
17739
+ },
17740
+ setCompression: applyCompression,
17741
+ setWidth,
17742
+ dispose: () => {
17743
+ input.disconnect();
17744
+ eqLow.disconnect();
17745
+ eqMid.disconnect();
17746
+ eqHigh.disconnect();
17747
+ compressor.disconnect();
17748
+ splitter.disconnect();
17749
+ mid.disconnect();
17750
+ sideSum.disconnect();
17751
+ sideL.disconnect();
17752
+ sideR.disconnect();
17753
+ widthGain.disconnect();
17754
+ widthInv.disconnect();
17755
+ outL.disconnect();
17756
+ outR.disconnect();
17757
+ merger.disconnect();
16689
17758
  }
16690
- }
16691
- return impulse;
17759
+ };
17760
+ };
17761
+
17762
+ // src/clip-meter.ts
17763
+ var createClipMeter = (ctx, source, options = {}) => {
17764
+ const threshold = options.threshold ?? 0.98;
17765
+ const holdMs = options.holdMs ?? 800;
17766
+ const analyser = ctx.createAnalyser();
17767
+ analyser.fftSize = 512;
17768
+ source.connect(analyser);
17769
+ const buf = new Float32Array(analyser.fftSize);
17770
+ let clipping = false;
17771
+ let clipUntil = 0;
17772
+ let peakLevel = 0;
17773
+ const listeners = /* @__PURE__ */ new Set();
17774
+ let rafId = null;
17775
+ const tick = () => {
17776
+ analyser.getFloatTimeDomainData(buf);
17777
+ let peak = 0;
17778
+ let clipped = false;
17779
+ for (let i2 = 0; i2 < buf.length; i2++) {
17780
+ const v = Math.abs(buf[i2]);
17781
+ if (v > peak) peak = v;
17782
+ if (v >= threshold) clipped = true;
17783
+ }
17784
+ peakLevel = peak;
17785
+ const now = performance.now();
17786
+ if (clipped) clipUntil = now + holdMs;
17787
+ const nextClipping = now < clipUntil;
17788
+ if (nextClipping !== clipping) {
17789
+ clipping = nextClipping;
17790
+ for (const cb of listeners) cb(clipping);
17791
+ }
17792
+ rafId = requestAnimationFrame(tick);
17793
+ };
17794
+ rafId = requestAnimationFrame(tick);
17795
+ return {
17796
+ onClipChange: (cb) => {
17797
+ listeners.add(cb);
17798
+ return () => listeners.delete(cb);
17799
+ },
17800
+ getPeakLevel: () => peakLevel,
17801
+ reset: () => {
17802
+ clipUntil = 0;
17803
+ if (clipping) {
17804
+ clipping = false;
17805
+ for (const cb of listeners) cb(false);
17806
+ }
17807
+ },
17808
+ dispose: () => {
17809
+ if (rafId !== null) cancelAnimationFrame(rafId);
17810
+ rafId = null;
17811
+ analyser.disconnect();
17812
+ listeners.clear();
17813
+ }
17814
+ };
16692
17815
  };
16693
- var REVERB_MAX_WET = 0.6;
16694
- var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100 * REVERB_MAX_WET;
16695
17816
 
16696
17817
  // src/sf/SoundFont_drum.ts
16697
17818
  var touch = (map, key, ctor) => {
@@ -16844,18 +17965,20 @@ var createDtmStudio = async (options = {}) => {
16844
17965
  const audioCtx = options.audioContext ?? new AudioContext({ sampleRate: 44100 });
16845
17966
  const masterGain = audioCtx.createGain();
16846
17967
  masterGain.gain.value = options.masterVolume ?? 1;
16847
- masterGain.connect(audioCtx.destination);
16848
17968
  const drumGain = audioCtx.createGain();
16849
17969
  drumGain.gain.value = options.drumVolume ?? 1;
16850
17970
  drumGain.connect(masterGain);
17971
+ let reverbDecaySec = options.reverbDecay ?? DEFAULT_REVERB_DECAY_SEC;
17972
+ const reverbPreDelay = audioCtx.createDelay(MAX_REVERB_PREDELAY_MS / 1e3);
17973
+ reverbPreDelay.delayTime.value = (options.reverbPreDelay ?? DEFAULT_REVERB_PREDELAY_MS) / 1e3;
16851
17974
  const reverbConvolver = audioCtx.createConvolver();
16852
- reverbConvolver.buffer = createReverbImpulse(audioCtx);
17975
+ reverbConvolver.buffer = createReverbImpulse(audioCtx, reverbDecaySec);
16853
17976
  reverbConvolver.normalize = true;
16854
17977
  const reverbWetGain = audioCtx.createGain();
16855
17978
  reverbWetGain.gain.value = reverbAmountToGain(options.reverbAmount ?? 0);
16856
- masterGain.connect(reverbConvolver);
17979
+ masterGain.connect(reverbPreDelay);
17980
+ reverbPreDelay.connect(reverbConvolver);
16857
17981
  reverbConvolver.connect(reverbWetGain);
16858
- reverbWetGain.connect(audioCtx.destination);
16859
17982
  const setReverbAmount = (amount) => {
16860
17983
  reverbWetGain.gain.setTargetAtTime(
16861
17984
  reverbAmountToGain(amount),
@@ -16863,6 +17986,46 @@ var createDtmStudio = async (options = {}) => {
16863
17986
  0.02
16864
17987
  );
16865
17988
  };
17989
+ const setReverbDecay = (seconds) => {
17990
+ reverbDecaySec = Math.max(
17991
+ MIN_REVERB_DECAY_SEC,
17992
+ Math.min(MAX_REVERB_DECAY_SEC, seconds)
17993
+ );
17994
+ reverbConvolver.buffer = createReverbImpulse(audioCtx, reverbDecaySec);
17995
+ };
17996
+ const setReverbPreDelay = (ms) => {
17997
+ reverbPreDelay.delayTime.setTargetAtTime(
17998
+ Math.max(MIN_REVERB_PREDELAY_MS, Math.min(MAX_REVERB_PREDELAY_MS, ms)) / 1e3,
17999
+ audioCtx.currentTime,
18000
+ 0.02
18001
+ );
18002
+ };
18003
+ const finalMix = audioCtx.createGain();
18004
+ masterGain.connect(finalMix);
18005
+ reverbWetGain.connect(finalMix);
18006
+ const delayBus = createDelayBus(audioCtx, finalMix, {
18007
+ amount: options.delayAmount ?? 0,
18008
+ division: options.delayDivision ?? "8"
18009
+ });
18010
+ const setDelayAmount = (amount) => delayBus.setAmount(amount);
18011
+ const safetyLimiter = audioCtx.createDynamicsCompressor();
18012
+ safetyLimiter.threshold.value = -1;
18013
+ safetyLimiter.knee.value = 0;
18014
+ safetyLimiter.ratio.value = 20;
18015
+ safetyLimiter.attack.value = 1e-3;
18016
+ safetyLimiter.release.value = 0.1;
18017
+ finalMix.connect(safetyLimiter);
18018
+ safetyLimiter.connect(options.destination ?? audioCtx.destination);
18019
+ const clipMeter = createClipMeter(audioCtx, finalMix);
18020
+ const channelStrips = /* @__PURE__ */ new Map();
18021
+ const getChannelStrip = (trackId) => {
18022
+ let strip = channelStrips.get(trackId);
18023
+ if (!strip) {
18024
+ strip = createChannelStrip(audioCtx, masterGain);
18025
+ channelStrips.set(trackId, strip);
18026
+ }
18027
+ return strip;
18028
+ };
16866
18029
  const resumeAudio = () => {
16867
18030
  if (audioCtx.state === "closed") return Promise.resolve();
16868
18031
  return audioCtx.resume();
@@ -16896,7 +18059,13 @@ var createDtmStudio = async (options = {}) => {
16896
18059
  worldlineScriptUrl: options.worldlineScriptUrl,
16897
18060
  // ボーカルトラック個別のリバーブセンド(`r`トークン)先。マスタリバーブの
16898
18061
  // Convolver 入力へ直接センドする(masterGain 経由のドライ段は素通り)。
16899
- reverbBus: reverbConvolver
18062
+ reverbBus: reverbConvolver,
18063
+ // ボーカルトラック個別のディレイセンド(`e`トークン)先。
18064
+ delayBus: delayBus.input,
18065
+ // トラック単位チャンネルストリップ(コンプレッサー/ステレオワイド)の入口。
18066
+ // 楽器と同じ getChannelStrip キャッシュを共有するので、演奏トラックと歌詞トラックが
18067
+ // 同じ trackId を指していれば同じ処理が掛かる。
18068
+ getTrackDestination: (trackId) => getChannelStrip(trackId).input
16900
18069
  });
16901
18070
  const listReady = new Promise((resolve) => {
16902
18071
  sfList.init();
@@ -17126,7 +18295,7 @@ var createDtmStudio = async (options = {}) => {
17126
18295
  if (!sfInst) return;
17127
18296
  sfInst.play({
17128
18297
  ctx: audioCtx,
17129
- destination: masterGain,
18298
+ destination: getChannelStrip(e.trackId).input,
17130
18299
  pitch: e.pitch,
17131
18300
  volume: e.volume,
17132
18301
  when: e.when,
@@ -17173,6 +18342,21 @@ var createDtmStudio = async (options = {}) => {
17173
18342
  },
17174
18343
  reverbAmount: options.reverbAmount,
17175
18344
  onReverbChange: setReverbAmount,
18345
+ reverbDecay: options.reverbDecay,
18346
+ onReverbDecayChange: setReverbDecay,
18347
+ reverbPreDelay: options.reverbPreDelay,
18348
+ onReverbPreDelayChange: setReverbPreDelay,
18349
+ delayAmount: options.delayAmount,
18350
+ onDelayChange: setDelayAmount,
18351
+ delayDivision: options.delayDivision,
18352
+ onDelayDivisionChange: (division) => delayBus.setDivision(division),
18353
+ onBpmChange: (bpm) => delayBus.setBpm(bpm),
18354
+ onTrackCompressionChange: (trackId, amount) => getChannelStrip(trackId).setCompression(amount),
18355
+ onTrackWidthChange: (trackId, width) => getChannelStrip(trackId).setWidth(width),
18356
+ onTrackEqLowChange: (trackId, db) => getChannelStrip(trackId).setEqLow(db),
18357
+ onTrackEqMidChange: (trackId, db) => getChannelStrip(trackId).setEqMid(db),
18358
+ onTrackEqHighChange: (trackId, db) => getChannelStrip(trackId).setEqHigh(db),
18359
+ clipMeter,
17176
18360
  ...dawOverrides,
17177
18361
  // dawOverrides で上書きされないよう、スプレッドの後に配置して合成する
17178
18362
  onDrumChange: (name) => {
@@ -17199,17 +18383,22 @@ var createDtmStudio = async (options = {}) => {
17199
18383
  mountedEditors.push(daw);
17200
18384
  const wantPresetUI = presetUI ?? features.presetUI;
17201
18385
  const rollEl = target.querySelector('[data-dtm="roll"]');
18386
+ const presetSlot = target.querySelector(
18387
+ '[data-dtm="preset-select-slot"]'
18388
+ );
17202
18389
  if (wantPresetUI) {
17203
18390
  editorPresetSelects.get(target)?.destroy();
17204
- presetSelect = mountPresetSelect(target, {
18391
+ presetSelect = mountPresetSelect(presetSlot ?? target, {
17205
18392
  getDaw: () => daw,
17206
18393
  getTrackIds: () => trackIds,
17207
18394
  value: initialPreset,
17208
18395
  loadingTarget: rollEl ?? target,
17209
- position: "prepend",
17210
- // 楽器変更時、このエディタの発音解決が使うプリセットも追従させる。
18396
+ position: presetSlot ? "append" : "prepend",
18397
+ // 楽器変更時、このエディタの発音解決が使うプリセットも追従させ、
18398
+ // 外部の onInstrumentChange(呼び出し側の永続化フック等)へも通知する。
18399
+ // #inst= 付きMML読込時の通知経路(handleInstrumentChange)と揃える。
17211
18400
  onChange: (key) => {
17212
- editorPreset = key;
18401
+ handleInstrumentChange(key);
17213
18402
  }
17214
18403
  });
17215
18404
  editorPresetSelects.set(target, presetSelect);
@@ -17392,7 +18581,7 @@ var createDtmStudio = async (options = {}) => {
17392
18581
  if (!sfInst) return;
17393
18582
  sfInst.play({
17394
18583
  ctx: audioCtx,
17395
- destination: masterGain,
18584
+ destination: getChannelStrip(e.trackId).input,
17396
18585
  pitch: e.pitch,
17397
18586
  volume: e.volume,
17398
18587
  when: e.when,
@@ -17478,7 +18667,7 @@ var createDtmStudio = async (options = {}) => {
17478
18667
  if (!sfInst) return;
17479
18668
  sfInst.play({
17480
18669
  ctx: audioCtx,
17481
- destination: masterGain,
18670
+ destination: getChannelStrip(e.trackId).input,
17482
18671
  pitch: e.pitch,
17483
18672
  volume: e.volume,
17484
18673
  when: e.when,
@@ -17557,7 +18746,7 @@ var createDtmStudio = async (options = {}) => {
17557
18746
  if (!sfInst) return;
17558
18747
  sfInst.play({
17559
18748
  ctx: audioCtx,
17560
- destination: masterGain,
18749
+ destination: getChannelStrip(e.trackId).input,
17561
18750
  pitch: e.pitch,
17562
18751
  volume: e.volume,
17563
18752
  when: e.when,
@@ -17596,7 +18785,7 @@ var createDtmStudio = async (options = {}) => {
17596
18785
  if (!sfInst) return;
17597
18786
  sfInst.play({
17598
18787
  ctx: audioCtx,
17599
- destination: masterGain,
18788
+ destination: getChannelStrip(e.trackId).input,
17600
18789
  pitch: e.pitch,
17601
18790
  volume: e.volume,
17602
18791
  when: e.when,
@@ -17652,7 +18841,7 @@ var createDtmStudio = async (options = {}) => {
17652
18841
  if (!sfInst) return;
17653
18842
  sfInst.play({
17654
18843
  ctx: audioCtx,
17655
- destination: masterGain,
18844
+ destination: getChannelStrip("chord").input,
17656
18845
  pitch: e.pitch,
17657
18846
  volume: e.volume,
17658
18847
  when: e.when,
@@ -17702,6 +18891,10 @@ var createDtmStudio = async (options = {}) => {
17702
18891
  mountedPlayers.length = 0;
17703
18892
  mountedChordPlayers.length = 0;
17704
18893
  mountedEditors.length = 0;
18894
+ clipMeter.dispose();
18895
+ delayBus.dispose();
18896
+ for (const strip of channelStrips.values()) strip.dispose();
18897
+ channelStrips.clear();
17705
18898
  void audioCtx.close();
17706
18899
  };
17707
18900
  const setMasterVolume = (volume) => {
@@ -17741,6 +18934,9 @@ var createDtmStudio = async (options = {}) => {
17741
18934
  setMasterVolume,
17742
18935
  setVolume: setMasterVolume,
17743
18936
  setReverbAmount,
18937
+ setReverbDecay,
18938
+ setReverbPreDelay,
18939
+ setDelayAmount,
17744
18940
  dispose
17745
18941
  };
17746
18942
  };