@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.js CHANGED
@@ -2316,8 +2316,11 @@ var parseLyrics = (mml) => {
2316
2316
  let octave = 0;
2317
2317
  let vibrato = false;
2318
2318
  let reverb = 0;
2319
+ let delay = 0;
2320
+ let gender = 50;
2321
+ let breathiness = 50;
2319
2322
  const modelMatch = rest.match(
2320
- /^([a-z_][a-z0-9_]*?)(?=(?:[vqpobr]-?\d)|[^a-z0-9_]|$)(?::(\d+))?/i
2323
+ /^([a-z_][a-z0-9_]*?)(?=(?:[vqpobrghe]-?\d)|[^a-z0-9_]|$)(?::(\d+))?/i
2321
2324
  );
2322
2325
  let model = "";
2323
2326
  const metaTokens = [];
@@ -2372,6 +2375,27 @@ var parseLyrics = (mml) => {
2372
2375
  rest = rest.substring(rMatch[0].length).trim();
2373
2376
  continue;
2374
2377
  }
2378
+ const gMatch = rest.match(/^g(\d+)/i);
2379
+ if (gMatch) {
2380
+ gender = clamp(Number.parseInt(gMatch[1], 10), 0, 100);
2381
+ metaTokens.push(gMatch[0]);
2382
+ rest = rest.substring(gMatch[0].length).trim();
2383
+ continue;
2384
+ }
2385
+ const hMatch = rest.match(/^h(\d+)/i);
2386
+ if (hMatch) {
2387
+ breathiness = clamp(Number.parseInt(hMatch[1], 10), 0, 100);
2388
+ metaTokens.push(hMatch[0]);
2389
+ rest = rest.substring(hMatch[0].length).trim();
2390
+ continue;
2391
+ }
2392
+ const eMatch = rest.match(/^e(\d+)/i);
2393
+ if (eMatch) {
2394
+ delay = clamp(Number.parseInt(eMatch[1], 10), 0, 100);
2395
+ metaTokens.push(eMatch[0]);
2396
+ rest = rest.substring(eMatch[0].length).trim();
2397
+ continue;
2398
+ }
2375
2399
  break;
2376
2400
  }
2377
2401
  const lyricLines = [rest];
@@ -2388,6 +2412,9 @@ var parseLyrics = (mml) => {
2388
2412
  octave,
2389
2413
  vibrato,
2390
2414
  reverb,
2415
+ delay,
2416
+ gender,
2417
+ breathiness,
2391
2418
  syllables,
2392
2419
  metaText: metaTokens.join(" "),
2393
2420
  ...lineBreaks.length > 0 ? { lineBreaks } : {}
@@ -2473,7 +2500,7 @@ var FORMANTS = {
2473
2500
  N: [250, 1e3]
2474
2501
  };
2475
2502
  var midiToFreq = (m) => 440 * 2 ** ((m - 69) / 12);
2476
- var createKlattVoice = (ctx, destination, reverbBus) => {
2503
+ var createKlattVoice = (ctx, destination, reverbBus, delayBus) => {
2477
2504
  const active = /* @__PURE__ */ new Set();
2478
2505
  const voice = (syllable, e) => {
2479
2506
  const t0 = ctx.currentTime + e.when;
@@ -2483,19 +2510,26 @@ var createKlattVoice = (ctx, destination, reverbBus) => {
2483
2510
  const attack = 0.02;
2484
2511
  const release = 0.06;
2485
2512
  const sustainEnd = t0 + Math.max(attack + 0.02, e.duration);
2513
+ const dest = e.destination ?? destination;
2486
2514
  let panner = null;
2487
- let out = destination;
2515
+ let out = dest;
2488
2516
  if (typeof ctx.createStereoPanner === "function") {
2489
2517
  panner = ctx.createStereoPanner();
2490
2518
  panner.pan.value = Math.max(-1, Math.min(1, e.pan ?? 0));
2491
- panner.connect(destination);
2519
+ panner.connect(dest);
2492
2520
  out = panner;
2493
2521
  }
2494
- let sendGain = null;
2522
+ let reverbSendGain = null;
2495
2523
  if (reverbBus && e.reverbSend && e.reverbSend > 0 && panner) {
2496
- sendGain = ctx.createGain();
2497
- sendGain.gain.value = Math.max(0, Math.min(1, e.reverbSend));
2498
- panner.connect(sendGain).connect(reverbBus);
2524
+ reverbSendGain = ctx.createGain();
2525
+ reverbSendGain.gain.value = Math.max(0, Math.min(1, e.reverbSend));
2526
+ panner.connect(reverbSendGain).connect(reverbBus);
2527
+ }
2528
+ let delaySendGain = null;
2529
+ if (delayBus && e.delaySend && e.delaySend > 0 && panner) {
2530
+ delaySendGain = ctx.createGain();
2531
+ delaySendGain.gain.value = Math.max(0, Math.min(1, e.delaySend));
2532
+ panner.connect(delaySendGain).connect(delayBus);
2499
2533
  }
2500
2534
  const osc = ctx.createOscillator();
2501
2535
  osc.type = "sawtooth";
@@ -2552,7 +2586,8 @@ var createKlattVoice = (ctx, destination, reverbBus) => {
2552
2586
  active.delete(osc);
2553
2587
  osc.disconnect();
2554
2588
  panner?.disconnect();
2555
- sendGain?.disconnect();
2589
+ reverbSendGain?.disconnect();
2590
+ delaySendGain?.disconnect();
2556
2591
  };
2557
2592
  };
2558
2593
  voice.stopAll = () => {
@@ -2716,7 +2751,7 @@ var createLocalBackend = async (options) => {
2716
2751
  }
2717
2752
  return p;
2718
2753
  };
2719
- const renderAlias = async (alias, pitch, durationMs, vibrato) => {
2754
+ const renderAlias = async (alias, pitch, durationMs, vibrato, expr) => {
2720
2755
  const pcm = await getPcm(alias);
2721
2756
  if (!pcm || pcm.length === 0) return null;
2722
2757
  const entry = bank.manifest.phonemes[alias];
@@ -2727,7 +2762,9 @@ var createLocalBackend = async (options) => {
2727
2762
  pcm,
2728
2763
  pitch: vibrato ? vibratoPitchCurve(targetHz, lead.preMs) : targetHz,
2729
2764
  durationMs,
2730
- ...lead
2765
+ ...lead,
2766
+ gender: expr?.gender,
2767
+ breathiness: expr?.breathiness
2731
2768
  });
2732
2769
  if (audio) return { pcm: audio, preSec: lead.preMs / 1e3, rate: 1 };
2733
2770
  }
@@ -2792,7 +2829,7 @@ var createWorkerBackend = async (workerUrl, options) => {
2792
2829
  });
2793
2830
  onReady = null;
2794
2831
  onFail = null;
2795
- const renderAlias = (alias, pitch, durationMs, vibrato) => new Promise((resolve) => {
2832
+ const renderAlias = (alias, pitch, durationMs, vibrato, expr) => new Promise((resolve) => {
2796
2833
  const id = ++reqId;
2797
2834
  pending.set(
2798
2835
  id,
@@ -2806,7 +2843,9 @@ var createWorkerBackend = async (workerUrl, options) => {
2806
2843
  alias,
2807
2844
  pitch,
2808
2845
  durationMs,
2809
- vibrato
2846
+ vibrato,
2847
+ gender: expr?.gender,
2848
+ breathiness: expr?.breathiness
2810
2849
  });
2811
2850
  });
2812
2851
  return {
@@ -2835,15 +2874,21 @@ var createKoeVoice = async (ctx, destination, options) => {
2835
2874
  const inflight = /* @__PURE__ */ new Map();
2836
2875
  const active = /* @__PURE__ */ new Set();
2837
2876
  let prevVowel = "";
2838
- const keyOf = (alias, pitch, durationMs, vibrato) => `${alias}|${pitch}|${Math.round(durationMs / 10) * 10}${vibrato ? "|vib" : ""}`;
2839
- const renderInto = (alias, pitch, durationMs, vibrato) => {
2840
- const key = keyOf(alias, pitch, durationMs, vibrato);
2877
+ 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)}` : ""}`;
2878
+ const renderInto = (alias, pitch, durationMs, vibrato, expr) => {
2879
+ const key = keyOf(alias, pitch, durationMs, vibrato, expr);
2841
2880
  const existing = renderCache.get(key);
2842
2881
  if (existing !== void 0) return Promise.resolve(existing);
2843
2882
  const flying = inflight.get(key);
2844
2883
  if (flying) return flying;
2845
2884
  const p = (async () => {
2846
- const out = await backend.renderAlias(alias, pitch, durationMs, vibrato);
2885
+ const out = await backend.renderAlias(
2886
+ alias,
2887
+ pitch,
2888
+ durationMs,
2889
+ vibrato,
2890
+ expr
2891
+ );
2847
2892
  let rendered = null;
2848
2893
  if (out) {
2849
2894
  const buf = ctx.createBuffer(1, out.pcm.length, KOE_SAMPLE_RATE);
@@ -2858,20 +2903,27 @@ var createKoeVoice = async (ctx, destination, options) => {
2858
2903
  return p;
2859
2904
  };
2860
2905
  const LEADCAP_S = 0.09;
2861
- const schedule = (r, t0, peak, pan, reverbSend = 0) => {
2862
- let out = destination;
2906
+ const schedule = (r, t0, peak, pan, reverbSend = 0, delaySend = 0, destOverride) => {
2907
+ const dest = destOverride ?? destination;
2908
+ let out = dest;
2863
2909
  let panner = null;
2864
2910
  if (typeof ctx.createStereoPanner === "function") {
2865
2911
  panner = ctx.createStereoPanner();
2866
2912
  panner.pan.value = Math.max(-1, Math.min(1, pan));
2867
- panner.connect(destination);
2913
+ panner.connect(dest);
2868
2914
  out = panner;
2869
2915
  }
2870
- let sendGain = null;
2916
+ let reverbSendGain = null;
2871
2917
  if (options.reverbBus && reverbSend > 0 && panner) {
2872
- sendGain = ctx.createGain();
2873
- sendGain.gain.value = Math.max(0, Math.min(1, reverbSend));
2874
- panner.connect(sendGain).connect(options.reverbBus);
2918
+ reverbSendGain = ctx.createGain();
2919
+ reverbSendGain.gain.value = Math.max(0, Math.min(1, reverbSend));
2920
+ panner.connect(reverbSendGain).connect(options.reverbBus);
2921
+ }
2922
+ let delaySendGain = null;
2923
+ if (options.delayBus && delaySend > 0 && panner) {
2924
+ delaySendGain = ctx.createGain();
2925
+ delaySendGain.gain.value = Math.max(0, Math.min(1, delaySend));
2926
+ panner.connect(delaySendGain).connect(options.delayBus);
2875
2927
  }
2876
2928
  const src = ctx.createBufferSource();
2877
2929
  src.buffer = r.audio;
@@ -2898,7 +2950,8 @@ var createKoeVoice = async (ctx, destination, options) => {
2898
2950
  src.disconnect();
2899
2951
  env.disconnect();
2900
2952
  panner?.disconnect();
2901
- sendGain?.disconnect();
2953
+ reverbSendGain?.disconnect();
2954
+ delaySendGain?.disconnect();
2902
2955
  };
2903
2956
  };
2904
2957
  const model = (syllable, e) => {
@@ -2917,10 +2970,11 @@ var createKoeVoice = async (ctx, destination, options) => {
2917
2970
  const pan = e.pan ?? 0;
2918
2971
  const durationMs = Math.max(60, e.duration * 1e3);
2919
2972
  void renderInto(alias, e.pitch, durationMs).then((r) => {
2920
- if (r) schedule(r, t0, peak, pan, e.reverbSend);
2973
+ if (r)
2974
+ schedule(r, t0, peak, pan, e.reverbSend, e.delaySend, e.destination);
2921
2975
  });
2922
2976
  };
2923
- model.renderToCache = async (syllable, prevVowelArg, pitch, durationMs, vibrato) => {
2977
+ model.renderToCache = async (syllable, prevVowelArg, pitch, durationMs, vibrato, expr) => {
2924
2978
  if (syllable.consonant === "Q" || syllable.vowel === "") return null;
2925
2979
  const alias = resolveKoeAlias(
2926
2980
  backend.hasAlias,
@@ -2932,12 +2986,12 @@ var createKoeVoice = async (ctx, destination, options) => {
2932
2986
  if (!alias) return null;
2933
2987
  const dMs = Math.max(60, durationMs);
2934
2988
  const vib = !!vibrato && dMs / 1e3 >= VIBRATO_MIN_SEC;
2935
- const r = await renderInto(alias, pitch, dMs, vib);
2936
- return r ? keyOf(alias, pitch, dMs, vib) : null;
2989
+ const r = await renderInto(alias, pitch, dMs, vib, expr);
2990
+ return r ? keyOf(alias, pitch, dMs, vib, expr) : null;
2937
2991
  };
2938
- model.scheduleCached = (key, t0, peak, pan, reverbSend) => {
2992
+ model.scheduleCached = (key, t0, peak, pan, reverbSend, delaySend, dest) => {
2939
2993
  const r = renderCache.get(key);
2940
- if (r) schedule(r, t0, peak, pan, reverbSend);
2994
+ if (r) schedule(r, t0, peak, pan, reverbSend, delaySend, dest);
2941
2995
  };
2942
2996
  model.stopAll = () => {
2943
2997
  for (const src of active) {
@@ -2967,8 +3021,12 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2967
3021
  let streamSession = 0;
2968
3022
  const masterGain = ctx.createGain();
2969
3023
  masterGain.connect(destination);
3024
+ let masterVolumeScalar = 1;
2970
3025
  const loaded = /* @__PURE__ */ new Map([
2971
- [FALLBACK_MODEL, createKlattVoice(ctx, masterGain, options.reverbBus)]
3026
+ [
3027
+ FALLBACK_MODEL,
3028
+ createKlattVoice(ctx, masterGain, options.reverbBus, options.delayBus)
3029
+ ]
2972
3030
  ]);
2973
3031
  const loading = /* @__PURE__ */ new Map();
2974
3032
  const load2 = (model) => {
@@ -2989,7 +3047,8 @@ var createSingingVoices = (ctx, destination, options = {}) => {
2989
3047
  worldlineScriptUrl: options.worldlineScriptUrl,
2990
3048
  lightweight: options.lightweight,
2991
3049
  voiceWorkerUrl: options.voiceWorkerUrl,
2992
- reverbBus: options.reverbBus
3050
+ reverbBus: options.reverbBus,
3051
+ delayBus: options.delayBus
2993
3052
  })
2994
3053
  ))().then((v) => {
2995
3054
  loaded.set(m, v);
@@ -3031,10 +3090,14 @@ var createSingingVoices = (ctx, destination, options = {}) => {
3031
3090
  const m = loaded.get(track.model.toLowerCase());
3032
3091
  if (!m?.renderToCache) continue;
3033
3092
  let n = 0;
3093
+ const expr = {
3094
+ gender: track.gender,
3095
+ breathiness: track.breathiness
3096
+ };
3034
3097
  forEachSungNote(track, (note, prevVowel) => {
3035
3098
  if (n >= count && note.startSec >= STREAM_LOOKAHEAD_SEC) return;
3036
3099
  n++;
3037
- tasks.push({ model: m, note, prevVowel, vibrato: track.vibrato });
3100
+ tasks.push({ model: m, note, prevVowel, vibrato: track.vibrato, expr });
3038
3101
  });
3039
3102
  }
3040
3103
  const total = tasks.length;
@@ -3050,7 +3113,8 @@ var createSingingVoices = (ctx, destination, options = {}) => {
3050
3113
  task.prevVowel,
3051
3114
  task.note.pitch,
3052
3115
  task.note.durationSec * 1e3,
3053
- task.vibrato
3116
+ task.vibrato,
3117
+ task.expr
3054
3118
  ) ?? Promise.resolve(null));
3055
3119
  done++;
3056
3120
  onProgress?.(done, total);
@@ -3096,13 +3160,24 @@ var createSingingVoices = (ctx, destination, options = {}) => {
3096
3160
  prevVowel,
3097
3161
  note.pitch,
3098
3162
  note.durationSec * 1e3,
3099
- track.vibrato
3163
+ track.vibrato,
3164
+ { gender: track.gender, breathiness: track.breathiness }
3100
3165
  );
3101
3166
  if (session !== streamSession) return;
3102
3167
  if (key) {
3103
3168
  const delay = ctx.currentTime - t0;
3104
3169
  if (delay < 0.05) {
3105
- scheduleCached(key, t0, peak, track.pan, track.reverbSend);
3170
+ const dest = options.getTrackDestination?.(track.id ?? "");
3171
+ const effPeak = dest ? peak * masterVolumeScalar : peak;
3172
+ scheduleCached(
3173
+ key,
3174
+ t0,
3175
+ effPeak,
3176
+ track.pan,
3177
+ track.reverbSend,
3178
+ track.delaySend,
3179
+ dest
3180
+ );
3106
3181
  opts?.onScheduled?.(track, note, t0);
3107
3182
  } else {
3108
3183
  console.warn(
@@ -3114,15 +3189,19 @@ var createSingingVoices = (ctx, destination, options = {}) => {
3114
3189
  })();
3115
3190
  } else {
3116
3191
  const when = t0 - ctx.currentTime;
3192
+ const dest = options.getTrackDestination?.(track.id ?? "");
3193
+ const effPeak = dest ? peak * masterVolumeScalar : peak;
3117
3194
  model(note.syllable, {
3118
- trackId: "",
3195
+ trackId: track.id ?? "",
3119
3196
  pitch: note.pitch,
3120
3197
  velocity: 100,
3121
- volume: peak,
3198
+ volume: effPeak,
3122
3199
  when,
3123
3200
  duration: note.durationSec,
3124
3201
  pan: track.pan,
3125
- reverbSend: track.reverbSend
3202
+ reverbSend: track.reverbSend,
3203
+ delaySend: track.delaySend,
3204
+ destination: dest
3126
3205
  });
3127
3206
  opts?.onScheduled?.(track, note, t0);
3128
3207
  await new Promise((resolve) => setTimeout(resolve, 0));
@@ -3147,7 +3226,9 @@ var createSingingVoices = (ctx, destination, options = {}) => {
3147
3226
  for (const v of loaded.values()) v.reset?.();
3148
3227
  };
3149
3228
  const setVolume = (gain) => {
3150
- masterGain.gain.value = Math.max(0, gain);
3229
+ const g = Math.max(0, gain);
3230
+ masterGain.gain.value = g;
3231
+ masterVolumeScalar = g;
3151
3232
  };
3152
3233
  return {
3153
3234
  loadModels,
@@ -3181,8 +3262,13 @@ var PITCH_MAP = {
3181
3262
  b: 11
3182
3263
  };
3183
3264
  var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
3184
- var META_DIRECTIVE = /#(inst|drum|drumfont|volume|drumvolume|mode)=([\w-]+)/gi;
3265
+ var META_DIRECTIVE = /#(inst|drum|drumfont|volume|drumvolume|reverb|reverbdecay|reverbpredelay|delay|delaydiv|mode)=([\w-]+)/gi;
3185
3266
  var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
3267
+ var TRACK_COMP_DIRECTIVE = /#t(\d+)comp=(\d+)/gi;
3268
+ var TRACK_WIDTH_DIRECTIVE = /#t(\d+)width=(\d+)/gi;
3269
+ var TRACK_EQLOW_DIRECTIVE = /#t(\d+)eqlo=(-?\d+)/gi;
3270
+ var TRACK_EQMID_DIRECTIVE = /#t(\d+)eqmid=(-?\d+)/gi;
3271
+ var TRACK_EQHIGH_DIRECTIVE = /#t(\d+)eqhi=(-?\d+)/gi;
3186
3272
  var parseMmlMeta = (mml) => {
3187
3273
  const meta = {};
3188
3274
  for (const m of mml.matchAll(META_DIRECTIVE)) {
@@ -3196,6 +3282,20 @@ var parseMmlMeta = (mml) => {
3196
3282
  } else if (key === "drumvolume") {
3197
3283
  const dv = Number.parseInt(m[2], 10);
3198
3284
  if (!Number.isNaN(dv)) meta.drumVolume = dv;
3285
+ } else if (key === "reverb") {
3286
+ const rv = Number.parseInt(m[2], 10);
3287
+ if (!Number.isNaN(rv)) meta.reverb = clamp2(rv, 0, 100);
3288
+ } else if (key === "reverbdecay") {
3289
+ const rd = Number.parseInt(m[2], 10);
3290
+ if (!Number.isNaN(rd)) meta.reverbDecay = clamp2(rd, 3, 40);
3291
+ } else if (key === "reverbpredelay") {
3292
+ const rp = Number.parseInt(m[2], 10);
3293
+ if (!Number.isNaN(rp)) meta.reverbPreDelay = clamp2(rp, 0, 150);
3294
+ } else if (key === "delay") {
3295
+ const dv = Number.parseInt(m[2], 10);
3296
+ if (!Number.isNaN(dv)) meta.delay = clamp2(dv, 0, 100);
3297
+ } else if (key === "delaydiv") {
3298
+ if (["4", "8", "8d", "16"].includes(m[2])) meta.delayDivision = m[2];
3199
3299
  } else if (key === "mode") {
3200
3300
  if (m[2] === "simple" || m[2] === "advanced") {
3201
3301
  meta.mode = m[2];
@@ -3210,9 +3310,49 @@ var parseMmlMeta = (mml) => {
3210
3310
  meta.trackInstruments[idx] = name;
3211
3311
  }
3212
3312
  }
3313
+ for (const m of mml.matchAll(TRACK_COMP_DIRECTIVE)) {
3314
+ const idx = Number.parseInt(m[1], 10);
3315
+ const val = clamp2(Number.parseInt(m[2], 10), 0, 100);
3316
+ if (!Number.isNaN(idx) && !Number.isNaN(val)) {
3317
+ meta.trackCompression ??= {};
3318
+ meta.trackCompression[idx] = val;
3319
+ }
3320
+ }
3321
+ for (const m of mml.matchAll(TRACK_WIDTH_DIRECTIVE)) {
3322
+ const idx = Number.parseInt(m[1], 10);
3323
+ const val = clamp2(Number.parseInt(m[2], 10), 0, 200);
3324
+ if (!Number.isNaN(idx) && !Number.isNaN(val)) {
3325
+ meta.trackWidth ??= {};
3326
+ meta.trackWidth[idx] = val;
3327
+ }
3328
+ }
3329
+ for (const m of mml.matchAll(TRACK_EQLOW_DIRECTIVE)) {
3330
+ const idx = Number.parseInt(m[1], 10);
3331
+ const val = clamp2(Number.parseInt(m[2], 10), -12, 12);
3332
+ if (!Number.isNaN(idx) && !Number.isNaN(val)) {
3333
+ meta.trackEqLow ??= {};
3334
+ meta.trackEqLow[idx] = val;
3335
+ }
3336
+ }
3337
+ for (const m of mml.matchAll(TRACK_EQMID_DIRECTIVE)) {
3338
+ const idx = Number.parseInt(m[1], 10);
3339
+ const val = clamp2(Number.parseInt(m[2], 10), -12, 12);
3340
+ if (!Number.isNaN(idx) && !Number.isNaN(val)) {
3341
+ meta.trackEqMid ??= {};
3342
+ meta.trackEqMid[idx] = val;
3343
+ }
3344
+ }
3345
+ for (const m of mml.matchAll(TRACK_EQHIGH_DIRECTIVE)) {
3346
+ const idx = Number.parseInt(m[1], 10);
3347
+ const val = clamp2(Number.parseInt(m[2], 10), -12, 12);
3348
+ if (!Number.isNaN(idx) && !Number.isNaN(val)) {
3349
+ meta.trackEqHigh ??= {};
3350
+ meta.trackEqHigh[idx] = val;
3351
+ }
3352
+ }
3213
3353
  return meta;
3214
3354
  };
3215
- var stripMmlMeta = (mml) => mml.replace(META_DIRECTIVE, "").replace(TRACK_INST_DIRECTIVE, "");
3355
+ 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, "");
3216
3356
  var formatMmlMeta = (meta, space = "") => {
3217
3357
  const parts = [];
3218
3358
  if (meta.instrument) parts.push(`#inst=${meta.instrument}`);
@@ -3221,12 +3361,47 @@ var formatMmlMeta = (meta, space = "") => {
3221
3361
  if (meta.volume !== void 0) parts.push(`#volume=${meta.volume}`);
3222
3362
  if (meta.drumVolume !== void 0)
3223
3363
  parts.push(`#drumvolume=${meta.drumVolume}`);
3364
+ if (meta.reverb !== void 0 && meta.reverb !== 0)
3365
+ parts.push(`#reverb=${meta.reverb}`);
3366
+ if (meta.reverbDecay !== void 0 && meta.reverbDecay !== 22)
3367
+ parts.push(`#reverbdecay=${meta.reverbDecay}`);
3368
+ if (meta.reverbPreDelay !== void 0 && meta.reverbPreDelay !== 0)
3369
+ parts.push(`#reverbpredelay=${meta.reverbPreDelay}`);
3370
+ if (meta.delay !== void 0 && meta.delay !== 0)
3371
+ parts.push(`#delay=${meta.delay}`);
3372
+ if (meta.delayDivision && meta.delayDivision !== "8")
3373
+ parts.push(`#delaydiv=${meta.delayDivision}`);
3224
3374
  if (meta.mode) parts.push(`#mode=${meta.mode}`);
3225
3375
  if (meta.trackInstruments) {
3226
3376
  for (const [idx, name] of Object.entries(meta.trackInstruments)) {
3227
3377
  if (name) parts.push(`#t${idx}inst=${name}`);
3228
3378
  }
3229
3379
  }
3380
+ if (meta.trackCompression) {
3381
+ for (const [idx, val] of Object.entries(meta.trackCompression)) {
3382
+ if (val !== 0) parts.push(`#t${idx}comp=${val}`);
3383
+ }
3384
+ }
3385
+ if (meta.trackWidth) {
3386
+ for (const [idx, val] of Object.entries(meta.trackWidth)) {
3387
+ if (val !== 100) parts.push(`#t${idx}width=${val}`);
3388
+ }
3389
+ }
3390
+ if (meta.trackEqLow) {
3391
+ for (const [idx, val] of Object.entries(meta.trackEqLow)) {
3392
+ if (val !== 0) parts.push(`#t${idx}eqlo=${val}`);
3393
+ }
3394
+ }
3395
+ if (meta.trackEqMid) {
3396
+ for (const [idx, val] of Object.entries(meta.trackEqMid)) {
3397
+ if (val !== 0) parts.push(`#t${idx}eqmid=${val}`);
3398
+ }
3399
+ }
3400
+ if (meta.trackEqHigh) {
3401
+ for (const [idx, val] of Object.entries(meta.trackEqHigh)) {
3402
+ if (val !== 0) parts.push(`#t${idx}eqhi=${val}`);
3403
+ }
3404
+ }
3230
3405
  return parts.join(space);
3231
3406
  };
3232
3407
  var parseMML = (mml, options = {}) => {
@@ -6968,6 +7143,23 @@ var DAW_CSS = `
6968
7143
  @keyframes dtm-blink { 0%,100%{opacity:1} 50%{opacity:0} }
6969
7144
  .dtm-blink { animation: dtm-blink 1s steps(1) infinite; }
6970
7145
 
7146
+ /* \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 */
7147
+ .dtm-clip-badge {
7148
+ flex: 0 0 auto;
7149
+ min-height: 24px;
7150
+ padding: 0 8px;
7151
+ border: 2px solid var(--c-black);
7152
+ background: var(--dtm-danger);
7153
+ color: var(--c-white);
7154
+ font-family: var(--dtm-font);
7155
+ font-size: 10px;
7156
+ letter-spacing: .1em;
7157
+ cursor: pointer;
7158
+ box-shadow: 0 0 0 2px var(--dtm-danger), 2px 2px 0 var(--c-black);
7159
+ animation: dtm-blink .5s steps(1) infinite;
7160
+ }
7161
+ .dtm-clip-badge:active { transform: translate(2px,2px); box-shadow: none; }
7162
+
6971
7163
  /* \u2500\u2500\u2500 \u30A4\u30F3\u30D5\u30A9\u30DC\u30BF\u30F3 \u2500\u2500\u2500 */
6972
7164
  .dtm-infobtn {
6973
7165
  display: inline-flex;
@@ -7243,6 +7435,37 @@ var DAW_CSS = `
7243
7435
  object-fit: cover;
7244
7436
  image-rendering: pixelated;
7245
7437
  }
7438
+ /* \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/
7439
+ \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
7440
+ \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 */
7441
+ .dtm-advanced {
7442
+ border-top: 1px dashed var(--dtm-border2);
7443
+ padding-top: 6px;
7444
+ display: flex;
7445
+ flex-direction: column;
7446
+ gap: 6px;
7447
+ }
7448
+ .dtm-advanced > summary {
7449
+ list-style: none;
7450
+ cursor: pointer;
7451
+ font-size: 11px;
7452
+ letter-spacing: .08em;
7453
+ color: var(--dtm-text);
7454
+ display: flex;
7455
+ align-items: center;
7456
+ gap: 6px;
7457
+ min-height: 28px;
7458
+ padding: 0 8px;
7459
+ border: 2px solid var(--dtm-border2);
7460
+ background: var(--dtm-deep);
7461
+ box-shadow: 2px 2px 0 var(--c-black);
7462
+ width: fit-content;
7463
+ }
7464
+ .dtm-advanced > summary:active { transform: translate(2px,2px); box-shadow: none; }
7465
+ .dtm-advanced > summary::-webkit-details-marker { display: none; }
7466
+ .dtm-advanced > summary::before { content: "\u25B6"; font-size: 9px; color: var(--dtm-accent); }
7467
+ .dtm-advanced[open] > summary::before { content: "\u25BC"; }
7468
+ .dtm-advanced[open] > summary { border-color: var(--dtm-primary); color: var(--dtm-primary); }
7246
7469
 
7247
7470
  /* \u2500\u2500\u2500 \u5E83\u5E45\u62E1\u5F35 \u2500\u2500\u2500 */
7248
7471
  @media (min-width: 768px) {
@@ -9014,6 +9237,9 @@ var mountMmlPlayer = (target, mml, options = {}) => {
9014
9237
  pan: panToStereo(lt.pan ?? DEFAULT_PAN),
9015
9238
  vibrato: lt.vibrato,
9016
9239
  reverbSend: (lt.reverb ?? 0) / 100,
9240
+ delaySend: (lt.delay ?? 0) / 100,
9241
+ gender: (lt.gender ?? 50) / 100,
9242
+ breathiness: (lt.breathiness ?? 50) / 100,
9017
9243
  notes
9018
9244
  };
9019
9245
  });
@@ -10691,6 +10917,71 @@ var mountChordPlayer = (target, chords, options = {}) => {
10691
10917
  };
10692
10918
  };
10693
10919
 
10920
+ // src/delay.ts
10921
+ var DELAY_DIVISIONS = [
10922
+ { value: "4", label: "4\u5206", beats: 1 },
10923
+ { value: "8", label: "8\u5206", beats: 0.5 },
10924
+ { value: "8d", label: "\u4ED8\u70B98\u5206", beats: 0.75 },
10925
+ { value: "16", label: "16\u5206", beats: 0.25 }
10926
+ ];
10927
+ var clamp3 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
10928
+ var divisionToSeconds = (division, bpm) => {
10929
+ const beats = DELAY_DIVISIONS.find((d) => d.value === division)?.beats ?? 0.5;
10930
+ const safeBpm = bpm > 0 ? bpm : 120;
10931
+ return 60 / safeBpm * beats;
10932
+ };
10933
+ var DELAY_MAX_WET = 0.45;
10934
+ var delayAmountToGain = (amount) => clamp3(amount, 0, 100) / 100 * DELAY_MAX_WET;
10935
+ var FEEDBACK_GAIN = 0.3;
10936
+ var MAX_DELAY_SEC = 2;
10937
+ var createDelayBus = (ctx, destination, options = {}) => {
10938
+ const input = ctx.createGain();
10939
+ const delayNode = ctx.createDelay(MAX_DELAY_SEC);
10940
+ const feedback = ctx.createGain();
10941
+ feedback.gain.value = FEEDBACK_GAIN;
10942
+ const wetGain = ctx.createGain();
10943
+ wetGain.gain.value = delayAmountToGain(options.amount ?? 0);
10944
+ input.connect(delayNode);
10945
+ delayNode.connect(feedback);
10946
+ feedback.connect(delayNode);
10947
+ delayNode.connect(wetGain);
10948
+ wetGain.connect(destination);
10949
+ let bpm = options.bpm ?? 120;
10950
+ let division = options.division ?? "8";
10951
+ const applyTime = () => {
10952
+ delayNode.delayTime.setTargetAtTime(
10953
+ divisionToSeconds(division, bpm),
10954
+ ctx.currentTime,
10955
+ 0.05
10956
+ );
10957
+ };
10958
+ applyTime();
10959
+ return {
10960
+ input,
10961
+ setAmount: (amount) => {
10962
+ wetGain.gain.setTargetAtTime(
10963
+ delayAmountToGain(amount),
10964
+ ctx.currentTime,
10965
+ 0.02
10966
+ );
10967
+ },
10968
+ setDivision: (d) => {
10969
+ division = d;
10970
+ applyTime();
10971
+ },
10972
+ setBpm: (b) => {
10973
+ bpm = b;
10974
+ applyTime();
10975
+ },
10976
+ dispose: () => {
10977
+ input.disconnect();
10978
+ delayNode.disconnect();
10979
+ feedback.disconnect();
10980
+ wetGain.disconnect();
10981
+ }
10982
+ };
10983
+ };
10984
+
10694
10985
  // src/daw-ui.ts
10695
10986
  var q = (root, sel) => root.querySelector(sel);
10696
10987
  var buildUI = (target, options) => {
@@ -10715,6 +11006,7 @@ var buildUI = (target, options) => {
10715
11006
  <button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
10716
11007
  <label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
10717
11008
  <span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
11009
+ <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>
10718
11010
  <span class="dtm-grow"></span>
10719
11011
  <span class="dtm-label">BPM</span>
10720
11012
  <input type="number" class="dtm-input dtm-input--num" data-dtm="bpm" value="${defaultBpm}" min="20" max="300">
@@ -10752,18 +11044,8 @@ var buildUI = (target, options) => {
10752
11044
  <div class="dtm-hscroll" data-dtm="hscroll"><div class="dtm-hscroll-thumb" data-dtm="hscroll-thumb"></div></div>
10753
11045
 
10754
11046
  <details class="dtm-panel" open>
10755
- <summary>\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A</summary>
11047
+ <summary>\u500B\u5225\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A</summary>
10756
11048
  <div class="dtm-panel-body">
10757
- <div class="dtm-row">
10758
- <span class="dtm-label">\u5168\u4F53\u97F3\u91CF</span>
10759
- <input type="range" class="dtm-range dtm-grow" data-dtm="master-volume" value="50" min="0" max="100">
10760
- <span class="dtm-label" data-dtm="master-volume-label">50%</span>
10761
- </div>
10762
- <div class="dtm-row">
10763
- <span class="dtm-label">\u30EA\u30D0\u30FC\u30D6</span>
10764
- <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">
10765
- <span class="dtm-label" data-dtm="reverb-amount-label">0%</span>
10766
- </div>
10767
11049
  <div class="dtm-track-body" data-dtm="track-body"></div>
10768
11050
  </div>
10769
11051
  </details>
@@ -10796,6 +11078,48 @@ var buildUI = (target, options) => {
10796
11078
  </div>
10797
11079
  </details>
10798
11080
 
11081
+ <details class="dtm-panel" open>
11082
+ <summary>\u5168\u4F53\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A</summary>
11083
+ <div class="dtm-panel-body">
11084
+ <div data-dtm="preset-select-slot"></div>
11085
+ <div class="dtm-row">
11086
+ <span class="dtm-label">\u5168\u4F53\u97F3\u91CF</span>
11087
+ <input type="range" class="dtm-range dtm-grow" data-dtm="master-volume" value="50" min="0" max="100">
11088
+ <span class="dtm-label" data-dtm="master-volume-label">50%</span>
11089
+ </div>
11090
+ <div class="dtm-row">
11091
+ <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>
11092
+ <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>
11093
+ <span class="dtm-grow"></span>
11094
+ </div>
11095
+ <div class="dtm-row">
11096
+ <span class="dtm-label">\u30EA\u30D0\u30FC\u30D6</span>
11097
+ <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">
11098
+ <span class="dtm-label" data-dtm="reverb-amount-label">0%</span>
11099
+ <button class="dtm-infobtn" data-dtm="reverb-amount-info" title="\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
11100
+ </div>
11101
+ <div class="dtm-row">
11102
+ <span class="dtm-label">Decay</span>
11103
+ <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">
11104
+ <span class="dtm-label" data-dtm="reverb-decay-label">2.2s</span>
11105
+ </div>
11106
+ <div class="dtm-row">
11107
+ <span class="dtm-label">Pre Delay</span>
11108
+ <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">
11109
+ <span class="dtm-label" data-dtm="reverb-predelay-label">0ms</span>
11110
+ </div>
11111
+ <div class="dtm-row">
11112
+ <span class="dtm-label">\u30C7\u30A3\u30EC\u30A4</span>
11113
+ <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">
11114
+ <span class="dtm-label" data-dtm="delay-amount-label">0%</span>
11115
+ <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">
11116
+ ${DELAY_DIVISIONS.map((d) => `<option value="${d.value}">${d.label}</option>`).join("")}
11117
+ </select>
11118
+ <button class="dtm-infobtn" data-dtm="delay-amount-info" title="\u30DE\u30B9\u30BF\u30C7\u30A3\u30EC\u30A4\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
11119
+ </div>
11120
+ </div>
11121
+ </details>
11122
+
10799
11123
  <details class="dtm-panel">
10800
11124
  <summary>\u30C9\u30E9\u30E0\u8A2D\u5B9A</summary>
10801
11125
  <div class="dtm-panel-body">
@@ -10961,6 +11285,7 @@ var buildUI = (target, options) => {
10961
11285
  prevBarBtn: sel("prev-bar"),
10962
11286
  nextBarBtn: sel("next-bar"),
10963
11287
  soloCheckbox: sel("solo"),
11288
+ clipBadge: sel("clip-badge"),
10964
11289
  toolPen: sel("tool-pen"),
10965
11290
  toolSelect: sel("tool-select"),
10966
11291
  toolEraser: sel("tool-eraser"),
@@ -10989,6 +11314,17 @@ var buildUI = (target, options) => {
10989
11314
  masterVolumeLabel: sel("master-volume-label"),
10990
11315
  reverbAmount: sel("reverb-amount"),
10991
11316
  reverbAmountLabel: sel("reverb-amount-label"),
11317
+ reverbAmountInfoBtn: sel("reverb-amount-info"),
11318
+ reverbDecay: sel("reverb-decay"),
11319
+ reverbDecayLabel: sel("reverb-decay-label"),
11320
+ reverbPreDelay: sel("reverb-predelay"),
11321
+ reverbPreDelayLabel: sel("reverb-predelay-label"),
11322
+ delayAmount: sel("delay-amount"),
11323
+ delayAmountLabel: sel("delay-amount-label"),
11324
+ delayAmountInfoBtn: sel("delay-amount-info"),
11325
+ delayDivision: sel("delay-division"),
11326
+ autoMasterBtn: sel("auto-master"),
11327
+ autoMasterInfoBtn: sel("auto-master-info"),
10992
11328
  trackTabs: sel("track-tabs"),
10993
11329
  trackBody: sel("track-body"),
10994
11330
  drumSelect: sel("drum-select"),
@@ -12620,6 +12956,38 @@ var isChordHeavyTrack = (notes, threshold = 0.6) => {
12620
12956
  return chordNotes / notes.length >= threshold;
12621
12957
  };
12622
12958
 
12959
+ // src/reverb.ts
12960
+ var DEFAULT_REVERB_DECAY_SEC = 2.2;
12961
+ var MIN_REVERB_DECAY_SEC = 0.3;
12962
+ var MAX_REVERB_DECAY_SEC = 4;
12963
+ var IMPULSE_DECAY_CURVE = 2.5;
12964
+ var DEFAULT_REVERB_PREDELAY_MS = 0;
12965
+ var MIN_REVERB_PREDELAY_MS = 0;
12966
+ var MAX_REVERB_PREDELAY_MS = 150;
12967
+ var createReverbImpulse = (ctx, decaySec = DEFAULT_REVERB_DECAY_SEC) => {
12968
+ const rate = ctx.sampleRate;
12969
+ const length = Math.max(
12970
+ 1,
12971
+ Math.floor(
12972
+ rate * Math.max(
12973
+ MIN_REVERB_DECAY_SEC,
12974
+ Math.min(MAX_REVERB_DECAY_SEC, decaySec)
12975
+ )
12976
+ )
12977
+ );
12978
+ const impulse = ctx.createBuffer(2, length, rate);
12979
+ for (let ch = 0; ch < impulse.numberOfChannels; ch++) {
12980
+ const data = impulse.getChannelData(ch);
12981
+ for (let i2 = 0; i2 < length; i2++) {
12982
+ const envelope = (1 - i2 / length) ** IMPULSE_DECAY_CURVE;
12983
+ data[i2] = (Math.random() * 2 - 1) * envelope;
12984
+ }
12985
+ }
12986
+ return impulse;
12987
+ };
12988
+ var REVERB_MAX_WET = 0.6;
12989
+ var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100 * REVERB_MAX_WET;
12990
+
12623
12991
  // src/daw.ts
12624
12992
  var CHORD_INFO_HTML2 = `
12625
12993
  <div class="dtm-modal-body-content">
@@ -12653,10 +13021,125 @@ var CHORD_INFO_HTML2 = `
12653
13021
  `;
12654
13022
  var VIBRATO_INFO_HTML = `
12655
13023
  <div class="dtm-modal-body-content">
12656
- <h4>\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u3068\u306F</h4>
13024
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
12657
13025
  <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>
12658
- <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>
12659
- <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>
13026
+ <h4>\u77ED\u3044\u97F3\u7B26\u306B\u639B\u304B\u3089\u306A\u3044\u7406\u7531</h4>
13027
+ <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>
13028
+ <h4>\u4ED6\u306E\u8A2D\u5B9A\u3068\u306E\u517C\u306D\u5408\u3044</h4>
13029
+ <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>
13030
+ </div>
13031
+ `;
13032
+ var GENDER_INFO_HTML = `
13033
+ <div class="dtm-modal-body-content">
13034
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
13035
+ <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>
13036
+ <h4>\u30AA\u30AF\u30BF\u30FC\u30D6\u30B7\u30D5\u30C8\u3068\u306E\u9055\u3044</h4>
13037
+ <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>
13038
+ <h4>\u4ED6\u306E\u8A2D\u5B9A\u3068\u306E\u517C\u306D\u5408\u3044</h4>
13039
+ <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>
13040
+ </div>
13041
+ `;
13042
+ var BREATHINESS_INFO_HTML = `
13043
+ <div class="dtm-modal-body-content">
13044
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
13045
+ <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>
13046
+ <h4>\u4E0A\u3052\u3059\u304E\u308B\u3068\u3069\u3046\u306A\u308B\u304B</h4>
13047
+ <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>
13048
+ <h4>\u4ED6\u306E\u8A2D\u5B9A\u3068\u306E\u517C\u306D\u5408\u3044</h4>
13049
+ <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>
13050
+ </div>
13051
+ `;
13052
+ var LYRIC_REVERB_INFO_HTML = `
13053
+ <div class="dtm-modal-body-content">
13054
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
13055
+ <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>
13056
+ <h4>\u30DE\u30B9\u30BF\u306E\u300C\u30EA\u30D0\u30FC\u30D6\u300D\u3064\u307E\u307F\u3068\u306E\u95A2\u4FC2\uFF08\u91CD\u8981\uFF09</h4>
13057
+ <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>
13058
+ <h4>\u639B\u3051\u3059\u304E\u308B\u3068\u3069\u3046\u306A\u308B\u304B</h4>
13059
+ <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>
13060
+ </div>
13061
+ `;
13062
+ var LYRIC_DELAY_INFO_HTML = `
13063
+ <div class="dtm-modal-body-content">
13064
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
13065
+ <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>
13066
+ <h4>\u30DE\u30B9\u30BF\u306E\u300C\u30C7\u30A3\u30EC\u30A4\u300D\u3064\u307E\u307F\u3068\u306E\u95A2\u4FC2\uFF08\u91CD\u8981\uFF09</h4>
13067
+ <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>
13068
+ <h4>\u4F7F\u3044\u3069\u3053\u308D</h4>
13069
+ <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>
13070
+ </div>
13071
+ `;
13072
+ var MASTER_REVERB_INFO_HTML = `
13073
+ <div class="dtm-modal-body-content">
13074
+ <h4>\u30EA\u30D0\u30FC\u30D6\uFF08Mix\uFF09</h4>
13075
+ <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>
13076
+ <h4>Decay\uFF08\u6B8B\u97FF\u306E\u9577\u3055\uFF09</h4>
13077
+ <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>
13078
+ <h4>Pre Delay\uFF08\u539F\u97F3\u3068\u30EA\u30D0\u30FC\u30D6\u306E\u9593\u9694\uFF09</h4>
13079
+ <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>
13080
+ <h4>\u5404\u30C8\u30E9\u30C3\u30AF\u306E\u300C\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u300D\u3068\u306E\u95A2\u4FC2\uFF08\u91CD\u8981\uFF09</h4>
13081
+ <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>
13082
+ </div>
13083
+ `;
13084
+ var MASTER_DELAY_INFO_HTML = `
13085
+ <div class="dtm-modal-body-content">
13086
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
13087
+ <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>
13088
+ <h4>\u30EA\u30D0\u30FC\u30D6\u3068\u306E\u9055\u3044</h4>
13089
+ <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>
13090
+ <h4>\u5404\u30C8\u30E9\u30C3\u30AF\u306E\u300C\u30C7\u30A3\u30EC\u30A4\u9001\u308A\u300D\u3068\u306E\u95A2\u4FC2</h4>
13091
+ <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>
13092
+ </div>
13093
+ `;
13094
+ var TRACK_EQ_INFO_HTML = `
13095
+ <div class="dtm-modal-body-content">
13096
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
13097
+ <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>
13098
+ <h4>\u4F7F\u3044\u3069\u3053\u308D\uFF08\u5E2F\u57DF\u306E\u68F2\u307F\u5206\u3051\uFF09</h4>
13099
+ <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>
13100
+ <h4>\u4ED6\u306E\u8A2D\u5B9A\u3068\u306E\u517C\u306D\u5408\u3044\uFF08\u9806\u5E8F\u304C\u91CD\u8981\uFF09</h4>
13101
+ <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>
13102
+ </div>
13103
+ `;
13104
+ var TRACK_COMPRESSION_INFO_HTML = `
13105
+ <div class="dtm-modal-body-content">
13106
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
13107
+ <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>
13108
+ <h4>\u639B\u3051\u3059\u304E\u308B\u3068\u3069\u3046\u306A\u308B\u304B</h4>
13109
+ <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>
13110
+ <h4>\u4ED6\u306E\u8A2D\u5B9A\u3068\u306E\u517C\u306D\u5408\u3044</h4>
13111
+ <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>
13112
+ </div>
13113
+ `;
13114
+ var TRACK_WIDTH_INFO_HTML = `
13115
+ <div class="dtm-modal-body-content">
13116
+ <h4>\u4F55\u3092\u3059\u308B\u8A2D\u5B9A\u304B</h4>
13117
+ <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>
13118
+ <h4>\u300C\u5B9A\u4F4D\uFF08\u30D1\u30F3\uFF09\u300D\u3068\u306E\u9055\u3044</h4>
13119
+ <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>
13120
+ <h4>\u5E83\u3052\u3059\u304E\u308B\u3068\u3069\u3046\u306A\u308B\u304B</h4>
13121
+ <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>
13122
+ </div>
13123
+ `;
13124
+ var AUTO_MASTER_INFO_HTML = `
13125
+ <div class="dtm-modal-body-content">
13126
+ <h4>\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0\u3068\u306F</h4>
13127
+ <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>
13128
+ <ul>
13129
+ <li>\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6 Mix: 20%</li>
13130
+ <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>
13131
+ <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>
13132
+ <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>
13133
+ <li>\u5168\u30C8\u30E9\u30C3\u30AF\u306E\u30B9\u30C6\u30EC\u30AA\u5E45: 115%\uFF08\u308F\u305A\u304B\u306B\u5E83\u3052\u308B\uFF09</li>
13134
+ <li>\u6B4C\u8A5E\u306E\u3042\u308B\u30C8\u30E9\u30C3\u30AF: \u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8ON</li>
13135
+ </ul>
13136
+ <h4>\u30E1\u30A4\u30F3\u30DC\u30FC\u30AB\u30EB\u306E\u81EA\u52D5\u5224\u5B9A</h4>
13137
+ <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>
13138
+ <ul>
13139
+ <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>
13140
+ <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>
13141
+ </ul>
13142
+ <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>
12660
13143
  </div>
12661
13144
  `;
12662
13145
  var MIDI_INFO_HTML = `
@@ -12892,7 +13375,7 @@ var deriveCustomVocalKeyFromUrl = (url2) => {
12892
13375
  if (name && /^[0-9]/.test(name)) name = `_${name}`;
12893
13376
  return CUSTOM_VOCAL_KEY_RE.test(name) ? name : "";
12894
13377
  };
12895
- var clamp3 = (v, min, max) => Math.min(Math.max(v, min), max);
13378
+ var clamp4 = (v, min, max) => Math.min(Math.max(v, min), max);
12896
13379
  var normalizeInstrumentName = (name) => {
12897
13380
  if (!name) return "";
12898
13381
  const stripped = name.replace(/\s+/g, "").toLowerCase();
@@ -12932,6 +13415,17 @@ var mountDAW = (target, options = {}) => {
12932
13415
  refs.masterVolumeLabel.textContent = `${options.masterVolume ?? 50}%`;
12933
13416
  refs.reverbAmount.value = String(options.reverbAmount ?? 0);
12934
13417
  refs.reverbAmountLabel.textContent = `${options.reverbAmount ?? 0}%`;
13418
+ {
13419
+ const initDecay = options.reverbDecay ?? DEFAULT_REVERB_DECAY_SEC;
13420
+ const initPreDelay = options.reverbPreDelay ?? DEFAULT_REVERB_PREDELAY_MS;
13421
+ refs.reverbDecay.value = String(Math.round(initDecay * 10));
13422
+ refs.reverbDecayLabel.textContent = `${initDecay.toFixed(1)}s`;
13423
+ refs.reverbPreDelay.value = String(initPreDelay);
13424
+ refs.reverbPreDelayLabel.textContent = `${initPreDelay}ms`;
13425
+ }
13426
+ refs.delayAmount.value = String(options.delayAmount ?? 0);
13427
+ refs.delayAmountLabel.textContent = `${options.delayAmount ?? 0}%`;
13428
+ refs.delayDivision.value = options.delayDivision ?? "8";
12935
13429
  refs.drumVolume.value = String(options.drumVolume ?? 80);
12936
13430
  refs.drumVolumeLabel.textContent = `${options.drumVolume ?? 80}%`;
12937
13431
  const renderConfig = {
@@ -12948,7 +13442,16 @@ var mountDAW = (target, options = {}) => {
12948
13442
  let masterVolume = options.masterVolume ?? 50;
12949
13443
  options.singingVoices?.setVolume(masterVolume / 100);
12950
13444
  let reverbAmount = options.reverbAmount ?? 0;
13445
+ let reverbDecay = options.reverbDecay ?? DEFAULT_REVERB_DECAY_SEC;
13446
+ let reverbPreDelay = options.reverbPreDelay ?? DEFAULT_REVERB_PREDELAY_MS;
13447
+ let delayAmount = options.delayAmount ?? 0;
13448
+ let delayDivision = options.delayDivision ?? "8";
13449
+ let unsubscribeClip;
12951
13450
  options.onReverbChange?.(reverbAmount);
13451
+ options.onReverbDecayChange?.(reverbDecay);
13452
+ options.onReverbPreDelayChange?.(reverbPreDelay);
13453
+ options.onDelayChange?.(delayAmount);
13454
+ options.onDelayDivisionChange?.(delayDivision);
12952
13455
  let drumVolume = options.drumVolume ?? 80;
12953
13456
  let currentDrumPattern = refs.drumSelect.value;
12954
13457
  let currentDrumFont = options.drumFont ?? "FluidR3_GM_sf2_file:0";
@@ -13028,7 +13531,10 @@ var mountDAW = (target, options = {}) => {
13028
13531
  vocalPan: t.vocalPan,
13029
13532
  vocalOctave: t.vocalOctave,
13030
13533
  vocalVibrato: t.vocalVibrato,
13031
- vocalReverb: t.vocalReverb
13534
+ vocalReverb: t.vocalReverb,
13535
+ vocalDelay: t.vocalDelay,
13536
+ vocalGender: t.vocalGender,
13537
+ vocalBreathiness: t.vocalBreathiness
13032
13538
  };
13033
13539
  if (lyricsDebounceTimer) clearTimeout(lyricsDebounceTimer);
13034
13540
  lyricsDebounceTimer = setTimeout(() => {
@@ -13090,7 +13596,15 @@ var mountDAW = (target, options = {}) => {
13090
13596
  vocalOctave: 0,
13091
13597
  vocalVibrato: false,
13092
13598
  vocalReverb: 0,
13093
- trackInstrument: ""
13599
+ vocalDelay: 0,
13600
+ vocalGender: 50,
13601
+ vocalBreathiness: 50,
13602
+ trackInstrument: "",
13603
+ trackCompression: 0,
13604
+ trackWidth: 100,
13605
+ trackEqLow: 0,
13606
+ trackEqMid: 0,
13607
+ trackEqHigh: 0
13094
13608
  };
13095
13609
  });
13096
13610
  };
@@ -13111,6 +13625,9 @@ var mountDAW = (target, options = {}) => {
13111
13625
  octave: t.vocalOctave,
13112
13626
  vibrato: t.vocalVibrato,
13113
13627
  reverb: t.vocalReverb,
13628
+ delay: t.vocalDelay,
13629
+ gender: t.vocalGender,
13630
+ breathiness: t.vocalBreathiness,
13114
13631
  syllables
13115
13632
  });
13116
13633
  });
@@ -13231,7 +13748,7 @@ var mountDAW = (target, options = {}) => {
13231
13748
  const thumbW = Math.max(40, canvas.width / totalContentWidth * sbW);
13232
13749
  const ratio = currentOffsetX / maxOffsetX;
13233
13750
  refs.hScrollThumb.style.width = `${thumbW}px`;
13234
- refs.hScrollThumb.style.left = `${clamp3(ratio * (sbW - thumbW), 0, sbW - thumbW)}px`;
13751
+ refs.hScrollThumb.style.left = `${clamp4(ratio * (sbW - thumbW), 0, sbW - thumbW)}px`;
13235
13752
  }
13236
13753
  const totalHeight = renderConfig.keyCount * renderConfig.keyHeight;
13237
13754
  const sbH = refs.vScroll.clientHeight;
@@ -13301,9 +13818,9 @@ var mountDAW = (target, options = {}) => {
13301
13818
  if (maxOffsetX <= 0) return;
13302
13819
  const rect = refs.hScroll.getBoundingClientRect();
13303
13820
  const thumbW = Number.parseFloat(refs.hScrollThumb.style.width) || 40;
13304
- const x2 = clamp3(clientX - rect.left - thumbW / 2, 0, rect.width - thumbW);
13821
+ const x2 = clamp4(clientX - rect.left - thumbW / 2, 0, rect.width - thumbW);
13305
13822
  const ratio = x2 / (rect.width - thumbW);
13306
- currentOffsetX = clamp3(ratio * maxOffsetX, 0, maxOffsetX);
13823
+ currentOffsetX = clamp4(ratio * maxOffsetX, 0, maxOffsetX);
13307
13824
  setDrawOffset(currentOffsetX, currentOffsetY);
13308
13825
  redrawAll();
13309
13826
  };
@@ -13312,9 +13829,9 @@ var mountDAW = (target, options = {}) => {
13312
13829
  if (maxOffset <= 0) return;
13313
13830
  const rect = refs.vScroll.getBoundingClientRect();
13314
13831
  const thumbH = Number.parseFloat(refs.vScrollThumb.style.height) || 40;
13315
- const y = clamp3(clientY - rect.top - thumbH / 2, 0, rect.height - thumbH);
13832
+ const y = clamp4(clientY - rect.top - thumbH / 2, 0, rect.height - thumbH);
13316
13833
  const ratio = y / (rect.height - thumbH);
13317
- currentOffsetY = clamp3(ratio * maxOffset, 0, maxOffset);
13834
+ currentOffsetY = clamp4(ratio * maxOffset, 0, maxOffset);
13318
13835
  setDrawOffset(currentOffsetX, currentOffsetY);
13319
13836
  redrawAll();
13320
13837
  };
@@ -13361,8 +13878,8 @@ var mountDAW = (target, options = {}) => {
13361
13878
  if (dx !== 0 || dy !== 0) {
13362
13879
  const maxOffsetX = getMaxOffsetX();
13363
13880
  const maxOffsetY = getMaxOffsetY();
13364
- currentOffsetX = clamp3(currentOffsetX + dx, 0, maxOffsetX);
13365
- currentOffsetY = clamp3(currentOffsetY + dy, 0, maxOffsetY);
13881
+ currentOffsetX = clamp4(currentOffsetX + dx, 0, maxOffsetX);
13882
+ currentOffsetY = clamp4(currentOffsetY + dy, 0, maxOffsetY);
13366
13883
  setDrawOffset(currentOffsetX, currentOffsetY);
13367
13884
  onPointerMove(lastMoveEvent);
13368
13885
  }
@@ -13752,12 +14269,12 @@ var mountDAW = (target, options = {}) => {
13752
14269
  "wheel",
13753
14270
  (event) => {
13754
14271
  event.preventDefault();
13755
- currentOffsetY = clamp3(
14272
+ currentOffsetY = clamp4(
13756
14273
  currentOffsetY + event.deltaY,
13757
14274
  0,
13758
14275
  getMaxOffsetY()
13759
14276
  );
13760
- currentOffsetX = clamp3(
14277
+ currentOffsetX = clamp4(
13761
14278
  currentOffsetX + event.deltaX,
13762
14279
  0,
13763
14280
  getMaxOffsetX()
@@ -13796,7 +14313,7 @@ var mountDAW = (target, options = {}) => {
13796
14313
  const centerStep = (currentOffsetX + canvas.width / 2) / renderConfig.stepWidth;
13797
14314
  renderConfig.stepWidth = BASE_STEP_WIDTH * (zoomX * 2) / 100;
13798
14315
  refs.zoomXLabel.textContent = `${zoomX}%`;
13799
- currentOffsetX = clamp3(
14316
+ currentOffsetX = clamp4(
13800
14317
  centerStep * renderConfig.stepWidth - canvas.width / 2,
13801
14318
  0,
13802
14319
  getMaxOffsetX()
@@ -13809,7 +14326,7 @@ var mountDAW = (target, options = {}) => {
13809
14326
  const centerKey = (currentOffsetY + canvas.height / 2) / renderConfig.keyHeight;
13810
14327
  renderConfig.keyHeight = BASE_KEY_HEIGHT * zoomY / 100;
13811
14328
  refs.zoomYLabel.textContent = `${zoomY}%`;
13812
- currentOffsetY = clamp3(
14329
+ currentOffsetY = clamp4(
13813
14330
  centerKey * renderConfig.keyHeight - canvas.height / 2,
13814
14331
  0,
13815
14332
  getMaxOffsetY()
@@ -13859,7 +14376,7 @@ var mountDAW = (target, options = {}) => {
13859
14376
  const threshold = currentOffsetX / renderConfig.stepWidth + visibleSteps - 4;
13860
14377
  if (currentPlayStep > threshold) {
13861
14378
  const visibleBars = Math.round(visibleSteps / renderConfig.stepsPerBar);
13862
- currentOffsetX = clamp3(
14379
+ currentOffsetX = clamp4(
13863
14380
  currentOffsetX + visibleBars * renderConfig.stepsPerBar * renderConfig.stepWidth,
13864
14381
  0,
13865
14382
  getMaxOffsetX()
@@ -13915,6 +14432,9 @@ var mountDAW = (target, options = {}) => {
13915
14432
  pan: panToStereo(lt.pan ?? DEFAULT_PAN),
13916
14433
  vibrato: lt.vibrato,
13917
14434
  reverbSend: (lt.reverb ?? 0) / 100,
14435
+ delaySend: (lt.delay ?? 0) / 100,
14436
+ gender: (lt.gender ?? 50) / 100,
14437
+ breathiness: (lt.breathiness ?? 50) / 100,
13918
14438
  notes
13919
14439
  };
13920
14440
  }) : [];
@@ -13942,7 +14462,7 @@ var mountDAW = (target, options = {}) => {
13942
14462
  }
13943
14463
  if (playbackState !== "paused") {
13944
14464
  const canvas = getGridCanvas();
13945
- currentOffsetX = clamp3(
14465
+ currentOffsetX = clamp4(
13946
14466
  playStartStep * renderConfig.stepWidth - canvas.width * 0.5,
13947
14467
  0,
13948
14468
  getMaxOffsetX()
@@ -14020,7 +14540,38 @@ var mountDAW = (target, options = {}) => {
14020
14540
  <span class="dtm-label">\u30D9\u30ED\u30B7\u30C6\u30A3</span>
14021
14541
  <input type="range" class="dtm-range dtm-grow" data-dtm="track-vol" min="0" max="127" value="${active.volume}">
14022
14542
  <span class="dtm-label" data-dtm="track-vol-label">${active.volume}</span>
14023
- </div>`;
14543
+ </div>
14544
+ <details class="dtm-advanced" data-dtm="track-fx-advanced">
14545
+ <summary>\u8A73\u7D30\u8A2D\u5B9A\uFF08EQ\u30FB\u97F3\u5727\u30FB\u30B9\u30C6\u30EC\u30AA\u5E45\uFF09</summary>
14546
+ <div class="dtm-row">
14547
+ <span class="dtm-label">EQ\u4F4E\u57DF</span>
14548
+ <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">
14549
+ <span class="dtm-label" data-dtm="track-eq-low-label"></span>
14550
+ <button class="dtm-infobtn" data-dtm="track-eq-info" title="EQ\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14551
+ </div>
14552
+ <div class="dtm-row">
14553
+ <span class="dtm-label">EQ\u4E2D\u57DF</span>
14554
+ <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">
14555
+ <span class="dtm-label" data-dtm="track-eq-mid-label"></span>
14556
+ </div>
14557
+ <div class="dtm-row">
14558
+ <span class="dtm-label">EQ\u9AD8\u57DF</span>
14559
+ <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">
14560
+ <span class="dtm-label" data-dtm="track-eq-high-label"></span>
14561
+ </div>
14562
+ <div class="dtm-row">
14563
+ <span class="dtm-label">\u97F3\u5727\u5F37\u5316</span>
14564
+ <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">
14565
+ <span class="dtm-label" data-dtm="track-comp-label"></span>
14566
+ <button class="dtm-infobtn" data-dtm="track-comp-info" title="\u97F3\u5727\u5F37\u5316\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14567
+ </div>
14568
+ <div class="dtm-row">
14569
+ <span class="dtm-label">\u30B9\u30C6\u30EC\u30AA\u5E45</span>
14570
+ <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">
14571
+ <span class="dtm-label" data-dtm="track-width-label"></span>
14572
+ <button class="dtm-infobtn" data-dtm="track-width-info" title="\u30B9\u30C6\u30EC\u30AA\u5E45\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14573
+ </div>
14574
+ </details>`;
14024
14575
  const volInput = refs.trackBody.querySelector(
14025
14576
  '[data-dtm="track-vol"]'
14026
14577
  );
@@ -14037,6 +14588,93 @@ var mountDAW = (target, options = {}) => {
14037
14588
  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" : "";
14038
14589
  };
14039
14590
  syncVelocityDisabled();
14591
+ const trackEqLowInput = refs.trackBody.querySelector(
14592
+ '[data-dtm="track-eq-low"]'
14593
+ );
14594
+ const trackEqLowLabel = refs.trackBody.querySelector(
14595
+ '[data-dtm="track-eq-low-label"]'
14596
+ );
14597
+ const trackEqMidInput = refs.trackBody.querySelector(
14598
+ '[data-dtm="track-eq-mid"]'
14599
+ );
14600
+ const trackEqMidLabel = refs.trackBody.querySelector(
14601
+ '[data-dtm="track-eq-mid-label"]'
14602
+ );
14603
+ const trackEqHighInput = refs.trackBody.querySelector(
14604
+ '[data-dtm="track-eq-high"]'
14605
+ );
14606
+ const trackEqHighLabel = refs.trackBody.querySelector(
14607
+ '[data-dtm="track-eq-high-label"]'
14608
+ );
14609
+ const trackEqInfo = refs.trackBody.querySelector(
14610
+ '[data-dtm="track-eq-info"]'
14611
+ );
14612
+ const fmtDb = (db) => db > 0 ? `+${db}dB` : `${db}dB`;
14613
+ trackEqLowInput.value = String(active.trackEqLow);
14614
+ trackEqLowLabel.textContent = fmtDb(active.trackEqLow);
14615
+ trackEqMidInput.value = String(active.trackEqMid);
14616
+ trackEqMidLabel.textContent = fmtDb(active.trackEqMid);
14617
+ trackEqHighInput.value = String(active.trackEqHigh);
14618
+ trackEqHighLabel.textContent = fmtDb(active.trackEqHigh);
14619
+ trackEqLowInput.addEventListener("input", () => {
14620
+ active.trackEqLow = Number.parseInt(trackEqLowInput.value, 10);
14621
+ trackEqLowLabel.textContent = fmtDb(active.trackEqLow);
14622
+ options.onTrackEqLowChange?.(active.config.id, active.trackEqLow);
14623
+ });
14624
+ trackEqMidInput.addEventListener("input", () => {
14625
+ active.trackEqMid = Number.parseInt(trackEqMidInput.value, 10);
14626
+ trackEqMidLabel.textContent = fmtDb(active.trackEqMid);
14627
+ options.onTrackEqMidChange?.(active.config.id, active.trackEqMid);
14628
+ });
14629
+ trackEqHighInput.addEventListener("input", () => {
14630
+ active.trackEqHigh = Number.parseInt(trackEqHighInput.value, 10);
14631
+ trackEqHighLabel.textContent = fmtDb(active.trackEqHigh);
14632
+ options.onTrackEqHighChange?.(active.config.id, active.trackEqHigh);
14633
+ });
14634
+ trackEqInfo.addEventListener("click", () => {
14635
+ showModal("EQ\uFF08\u30A4\u30B3\u30E9\u30A4\u30B6\u30FC\uFF09\u306E\u89E3\u8AAC", TRACK_EQ_INFO_HTML);
14636
+ });
14637
+ const trackCompInput = refs.trackBody.querySelector(
14638
+ '[data-dtm="track-comp"]'
14639
+ );
14640
+ const trackCompLabel = refs.trackBody.querySelector(
14641
+ '[data-dtm="track-comp-label"]'
14642
+ );
14643
+ const trackWidthInput = refs.trackBody.querySelector(
14644
+ '[data-dtm="track-width"]'
14645
+ );
14646
+ const trackWidthLabel = refs.trackBody.querySelector(
14647
+ '[data-dtm="track-width-label"]'
14648
+ );
14649
+ const trackCompInfo = refs.trackBody.querySelector(
14650
+ '[data-dtm="track-comp-info"]'
14651
+ );
14652
+ const trackWidthInfo = refs.trackBody.querySelector(
14653
+ '[data-dtm="track-width-info"]'
14654
+ );
14655
+ trackCompInput.value = String(active.trackCompression);
14656
+ trackCompLabel.textContent = `${active.trackCompression}%`;
14657
+ trackWidthInput.value = String(active.trackWidth);
14658
+ trackWidthLabel.textContent = `${active.trackWidth}%`;
14659
+ trackCompInput.addEventListener("input", () => {
14660
+ active.trackCompression = Number.parseInt(trackCompInput.value, 10);
14661
+ trackCompLabel.textContent = `${active.trackCompression}%`;
14662
+ options.onTrackCompressionChange?.(
14663
+ active.config.id,
14664
+ active.trackCompression
14665
+ );
14666
+ });
14667
+ trackWidthInput.addEventListener("input", () => {
14668
+ active.trackWidth = Number.parseInt(trackWidthInput.value, 10);
14669
+ trackWidthLabel.textContent = `${active.trackWidth}%`;
14670
+ options.onTrackWidthChange?.(active.config.id, active.trackWidth);
14671
+ });
14672
+ trackCompInfo.addEventListener("click", () => {
14673
+ showModal("\u97F3\u5727\u5F37\u5316\u306E\u89E3\u8AAC", TRACK_COMPRESSION_INFO_HTML);
14674
+ });
14675
+ trackWidthInfo.addEventListener("click", () => {
14676
+ showModal("\u30B9\u30C6\u30EC\u30AA\u5E45\u306E\u89E3\u8AAC", TRACK_WIDTH_INFO_HTML);
14677
+ });
14040
14678
  const instRow = document.createElement("div");
14041
14679
  instRow.className = "dtm-row";
14042
14680
  instRow.innerHTML = `<span class="dtm-label">\u697D\u5668</span>`;
@@ -14100,17 +14738,6 @@ var mountDAW = (target, options = {}) => {
14100
14738
  <span class="dtm-label">\u266A UTAU</span>
14101
14739
  <select class="dtm-select" data-dtm="lyric-model" aria-label="\u6B4C\u5531\u30E2\u30C7\u30EB"></select>
14102
14740
  <img class="dtm-lyric-icon dtm-hidden" data-dtm="lyric-icon" width="20" height="20" alt="" draggable="false">
14103
- <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">
14104
- <option value="2">+2 oct</option>
14105
- <option value="1">+1 oct</option>
14106
- <option value="0">\xB10 oct</option>
14107
- <option value="-1">-1 oct</option>
14108
- <option value="-2">-2 oct</option>
14109
- </select>
14110
- <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">
14111
- <input type="checkbox" data-dtm="lyric-vibrato" aria-label="\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8">\u30D3\u30D6\u30E9\u30FC\u30C8
14112
- </label>
14113
- <button class="dtm-infobtn" data-dtm="lyric-vibrato-info" title="\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14114
14741
  <span class="dtm-label dtm-grow" data-dtm="lyric-count" style="text-align:right"></span>
14115
14742
  </div>
14116
14743
  <div class="dtm-row dtm-hidden" data-dtm="lyric-terms" style="font-size:10px;gap:4px;color:var(--dtm-warn)">
@@ -14136,16 +14763,54 @@ var mountDAW = (target, options = {}) => {
14136
14763
  <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">
14137
14764
  <span class="dtm-label" data-dtm="lyric-vol-label"></span>
14138
14765
  </div>
14139
- <div class="dtm-row">
14140
- <span class="dtm-label">\u5B9A\u4F4D</span>
14141
- <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">
14142
- <span class="dtm-label" data-dtm="lyric-pan-label"></span>
14143
- </div>
14144
- <div class="dtm-row">
14145
- <span class="dtm-label">\u30EA\u30D0\u30FC\u30D6\u9001\u308A</span>
14146
- <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">
14147
- <span class="dtm-label" data-dtm="lyric-reverb-label"></span>
14148
- </div>
14766
+ <details class="dtm-advanced" data-dtm="lyric-advanced">
14767
+ <summary>\u8A73\u7D30\u8A2D\u5B9A</summary>
14768
+ <div class="dtm-row">
14769
+ <span class="dtm-label">\u30AA\u30AF\u30BF\u30FC\u30D6</span>
14770
+ <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">
14771
+ <option value="2">+2 oct</option>
14772
+ <option value="1">+1 oct</option>
14773
+ <option value="0">\xB10 oct</option>
14774
+ <option value="-1">-1 oct</option>
14775
+ <option value="-2">-2 oct</option>
14776
+ </select>
14777
+ </div>
14778
+ <div class="dtm-row">
14779
+ <span class="dtm-label">\u5B9A\u4F4D</span>
14780
+ <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">
14781
+ <span class="dtm-label" data-dtm="lyric-pan-label"></span>
14782
+ </div>
14783
+ <div class="dtm-row">
14784
+ <span class="dtm-label">\u30EA\u30D0\u30FC\u30D6\u9001\u308A</span>
14785
+ <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">
14786
+ <span class="dtm-label" data-dtm="lyric-reverb-label"></span>
14787
+ <button class="dtm-infobtn" data-dtm="lyric-reverb-info" title="\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14788
+ </div>
14789
+ <div class="dtm-row">
14790
+ <span class="dtm-label">\u30C7\u30A3\u30EC\u30A4\u9001\u308A</span>
14791
+ <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">
14792
+ <span class="dtm-label" data-dtm="lyric-delay-label"></span>
14793
+ <button class="dtm-infobtn" data-dtm="lyric-delay-info" title="\u30C7\u30A3\u30EC\u30A4\u9001\u308A\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14794
+ </div>
14795
+ <div class="dtm-row">
14796
+ <span class="dtm-label">\u30B8\u30A7\u30F3\u30C0\u30FC</span>
14797
+ <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">
14798
+ <span class="dtm-label" data-dtm="lyric-gender-label"></span>
14799
+ <button class="dtm-infobtn" data-dtm="lyric-gender-info" title="\u30B8\u30A7\u30F3\u30C0\u30FC\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14800
+ </div>
14801
+ <div class="dtm-row">
14802
+ <span class="dtm-label">\u30D6\u30EC\u30B7\u30CD\u30B9</span>
14803
+ <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">
14804
+ <span class="dtm-label" data-dtm="lyric-breathiness-label"></span>
14805
+ <button class="dtm-infobtn" data-dtm="lyric-breathiness-info" title="\u30D6\u30EC\u30B7\u30CD\u30B9\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14806
+ </div>
14807
+ <div class="dtm-row">
14808
+ <span class="dtm-label" style="display:inline-flex;align-items:center;gap:2px">
14809
+ <input type="checkbox" data-dtm="lyric-vibrato" aria-label="\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8">\u30D3\u30D6\u30E9\u30FC\u30C8
14810
+ </span>
14811
+ <button class="dtm-infobtn" data-dtm="lyric-vibrato-info" title="\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
14812
+ </div>
14813
+ </details>
14149
14814
  <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>
14150
14815
  </div>`;
14151
14816
  refs.trackBody.appendChild(lyricDiv);
@@ -14185,6 +14850,48 @@ var mountDAW = (target, options = {}) => {
14185
14850
  const lyricReverbLabel = lyricDiv.querySelector(
14186
14851
  '[data-dtm="lyric-reverb-label"]'
14187
14852
  );
14853
+ const lyricReverbInfo = lyricDiv.querySelector(
14854
+ '[data-dtm="lyric-reverb-info"]'
14855
+ );
14856
+ lyricReverbInfo.addEventListener("click", () => {
14857
+ showModal("\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u306E\u89E3\u8AAC", LYRIC_REVERB_INFO_HTML);
14858
+ });
14859
+ const lyricDelay = lyricDiv.querySelector(
14860
+ '[data-dtm="lyric-delay"]'
14861
+ );
14862
+ const lyricDelayLabel = lyricDiv.querySelector(
14863
+ '[data-dtm="lyric-delay-label"]'
14864
+ );
14865
+ const lyricDelayInfo = lyricDiv.querySelector(
14866
+ '[data-dtm="lyric-delay-info"]'
14867
+ );
14868
+ lyricDelayInfo.addEventListener("click", () => {
14869
+ showModal("\u30C7\u30A3\u30EC\u30A4\u9001\u308A\u306E\u89E3\u8AAC", LYRIC_DELAY_INFO_HTML);
14870
+ });
14871
+ const lyricGender = lyricDiv.querySelector(
14872
+ '[data-dtm="lyric-gender"]'
14873
+ );
14874
+ const lyricGenderLabel = lyricDiv.querySelector(
14875
+ '[data-dtm="lyric-gender-label"]'
14876
+ );
14877
+ const lyricGenderInfo = lyricDiv.querySelector(
14878
+ '[data-dtm="lyric-gender-info"]'
14879
+ );
14880
+ lyricGenderInfo.addEventListener("click", () => {
14881
+ showModal("\u30B8\u30A7\u30F3\u30C0\u30FC\u306E\u89E3\u8AAC", GENDER_INFO_HTML);
14882
+ });
14883
+ const lyricBreathiness = lyricDiv.querySelector(
14884
+ '[data-dtm="lyric-breathiness"]'
14885
+ );
14886
+ const lyricBreathinessLabel = lyricDiv.querySelector(
14887
+ '[data-dtm="lyric-breathiness-label"]'
14888
+ );
14889
+ const lyricBreathinessInfo = lyricDiv.querySelector(
14890
+ '[data-dtm="lyric-breathiness-info"]'
14891
+ );
14892
+ lyricBreathinessInfo.addEventListener("click", () => {
14893
+ showModal("\u30D6\u30EC\u30B7\u30CD\u30B9\u306E\u89E3\u8AAC", BREATHINESS_INFO_HTML);
14894
+ });
14188
14895
  const lyricVibrato = lyricDiv.querySelector(
14189
14896
  '[data-dtm="lyric-vibrato"]'
14190
14897
  );
@@ -14257,6 +14964,12 @@ var mountDAW = (target, options = {}) => {
14257
14964
  lyricPanLabel.textContent = fmtPan(active.vocalPan);
14258
14965
  lyricReverb.value = String(active.vocalReverb);
14259
14966
  lyricReverbLabel.textContent = `${active.vocalReverb}%`;
14967
+ lyricDelay.value = String(active.vocalDelay);
14968
+ lyricDelayLabel.textContent = `${active.vocalDelay}%`;
14969
+ lyricGender.value = String(active.vocalGender);
14970
+ lyricGenderLabel.textContent = `${active.vocalGender}`;
14971
+ lyricBreathiness.value = String(active.vocalBreathiness);
14972
+ lyricBreathinessLabel.textContent = `${active.vocalBreathiness}`;
14260
14973
  lyricVibrato.checked = active.vocalVibrato;
14261
14974
  const updateLyricCount = () => {
14262
14975
  const n = normalizeLyrics(lyricInput.value).length;
@@ -14307,7 +15020,6 @@ var mountDAW = (target, options = {}) => {
14307
15020
  };
14308
15021
  const syncLyricVisibility = () => {
14309
15022
  lyricBody.style.display = active.lyricModel ? "" : "none";
14310
- lyricOctaveSel.style.display = active.lyricModel ? "" : "none";
14311
15023
  updateLyricCount();
14312
15024
  syncLyricTerms();
14313
15025
  syncLyricIcon();
@@ -14408,6 +15120,21 @@ var mountDAW = (target, options = {}) => {
14408
15120
  lyricReverbLabel.textContent = `${active.vocalReverb}%`;
14409
15121
  fireLyricsChange(active);
14410
15122
  });
15123
+ lyricDelay.addEventListener("input", () => {
15124
+ active.vocalDelay = Number.parseInt(lyricDelay.value, 10);
15125
+ lyricDelayLabel.textContent = `${active.vocalDelay}%`;
15126
+ fireLyricsChange(active);
15127
+ });
15128
+ lyricGender.addEventListener("input", () => {
15129
+ active.vocalGender = Number.parseInt(lyricGender.value, 10);
15130
+ lyricGenderLabel.textContent = `${active.vocalGender}`;
15131
+ fireLyricsChange(active);
15132
+ });
15133
+ lyricBreathiness.addEventListener("input", () => {
15134
+ active.vocalBreathiness = Number.parseInt(lyricBreathiness.value, 10);
15135
+ lyricBreathinessLabel.textContent = `${active.vocalBreathiness}`;
15136
+ fireLyricsChange(active);
15137
+ });
14411
15138
  }
14412
15139
  if (active.config.id === "chord" && showChord) {
14413
15140
  const div = document.createElement("div");
@@ -14491,18 +15218,44 @@ var mountDAW = (target, options = {}) => {
14491
15218
  const limitSteps = barLimitBars > 0 ? barLimitBars * renderConfig.stepsPerBar : Infinity;
14492
15219
  const clipNotes = (notes) => limitSteps === Infinity ? notes : notes.filter((n) => n.startStep < limitSteps);
14493
15220
  const trackInstrumentsForMeta = {};
15221
+ const trackCompressionForMeta = {};
15222
+ const trackWidthForMeta = {};
15223
+ const trackEqLowForMeta = {};
15224
+ const trackEqMidForMeta = {};
15225
+ const trackEqHighForMeta = {};
14494
15226
  trackStates.forEach((t, i2) => {
14495
15227
  if (t.trackInstrument) trackInstrumentsForMeta[i2] = t.trackInstrument;
15228
+ if (t.trackCompression !== 0)
15229
+ trackCompressionForMeta[i2] = t.trackCompression;
15230
+ if (t.trackWidth !== 100) trackWidthForMeta[i2] = t.trackWidth;
15231
+ if (t.trackEqLow !== 0) trackEqLowForMeta[i2] = t.trackEqLow;
15232
+ if (t.trackEqMid !== 0) trackEqMidForMeta[i2] = t.trackEqMid;
15233
+ if (t.trackEqHigh !== 0) trackEqHighForMeta[i2] = t.trackEqHigh;
14496
15234
  });
14497
15235
  const trackInstMeta = Object.keys(trackInstrumentsForMeta).length > 0 ? trackInstrumentsForMeta : void 0;
15236
+ const trackCompMeta = Object.keys(trackCompressionForMeta).length > 0 ? trackCompressionForMeta : void 0;
15237
+ const trackWidthMeta = Object.keys(trackWidthForMeta).length > 0 ? trackWidthForMeta : void 0;
15238
+ const trackEqLowMeta = Object.keys(trackEqLowForMeta).length > 0 ? trackEqLowForMeta : void 0;
15239
+ const trackEqMidMeta = Object.keys(trackEqMidForMeta).length > 0 ? trackEqMidForMeta : void 0;
15240
+ const trackEqHighMeta = Object.keys(trackEqHighForMeta).length > 0 ? trackEqHighForMeta : void 0;
14498
15241
  const metaLineFull = formatMmlMeta(
14499
15242
  {
14500
15243
  instrument: currentInstrument || void 0,
14501
15244
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
14502
15245
  volume: masterVolume,
14503
15246
  drumVolume,
15247
+ reverb: reverbAmount,
15248
+ reverbDecay: Math.round(reverbDecay * 10),
15249
+ reverbPreDelay,
15250
+ delay: delayAmount,
15251
+ delayDivision,
14504
15252
  mode,
14505
- trackInstruments: trackInstMeta
15253
+ trackInstruments: trackInstMeta,
15254
+ trackCompression: trackCompMeta,
15255
+ trackWidth: trackWidthMeta,
15256
+ trackEqLow: trackEqLowMeta,
15257
+ trackEqMid: trackEqMidMeta,
15258
+ trackEqHigh: trackEqHighMeta
14506
15259
  },
14507
15260
  " "
14508
15261
  );
@@ -14512,8 +15265,18 @@ var mountDAW = (target, options = {}) => {
14512
15265
  drum: currentDrumPattern !== "none" ? currentDrumPattern : void 0,
14513
15266
  volume: masterVolume,
14514
15267
  drumVolume,
15268
+ reverb: reverbAmount,
15269
+ reverbDecay: Math.round(reverbDecay * 10),
15270
+ reverbPreDelay,
15271
+ delay: delayAmount,
15272
+ delayDivision,
14515
15273
  mode,
14516
- trackInstruments: trackInstMeta
15274
+ trackInstruments: trackInstMeta,
15275
+ trackCompression: trackCompMeta,
15276
+ trackWidth: trackWidthMeta,
15277
+ trackEqLow: trackEqLowMeta,
15278
+ trackEqMid: trackEqMidMeta,
15279
+ trackEqHigh: trackEqHighMeta
14517
15280
  },
14518
15281
  ""
14519
15282
  );
@@ -14562,7 +15325,10 @@ var mountDAW = (target, options = {}) => {
14562
15325
  pan: t.vocalPan,
14563
15326
  oct: t.vocalOctave,
14564
15327
  vib: t.vocalVibrato,
14565
- rev: t.vocalReverb
15328
+ rev: t.vocalReverb,
15329
+ del: t.vocalDelay,
15330
+ gen: t.vocalGender,
15331
+ bre: t.vocalBreathiness
14566
15332
  })).filter(
14567
15333
  (x2) => x2.model.length > 0 && x2.text.length > 0 && x2.notes.length > 0
14568
15334
  ).map((x2) => {
@@ -14572,7 +15338,10 @@ var mountDAW = (target, options = {}) => {
14572
15338
  x2.pan === 64 ? "" : `p${x2.pan}`,
14573
15339
  x2.oct === 0 ? "" : `o${x2.oct}`,
14574
15340
  x2.vib ? "b1" : "",
14575
- x2.rev === 0 ? "" : `r${x2.rev}`
15341
+ x2.rev === 0 ? "" : `r${x2.rev}`,
15342
+ x2.del === 0 ? "" : `e${x2.del}`,
15343
+ x2.gen === 50 ? "" : `g${x2.gen}`,
15344
+ x2.bre === 50 ? "" : `h${x2.bre}`
14576
15345
  ].filter((s) => s.length > 0).join(" ");
14577
15346
  const head = params ? `${x2.model} ${params}` : x2.model;
14578
15347
  return `@@${x2.i} ${head} ${x2.text}`;
@@ -14641,7 +15410,7 @@ var mountDAW = (target, options = {}) => {
14641
15410
  const canvas = getGridCanvas();
14642
15411
  const yIndex = renderConfig.keyCount - 1 - (pitch - renderConfig.pitchRangeStart);
14643
15412
  const logicalY = yIndex * renderConfig.keyHeight;
14644
- currentOffsetY = clamp3(
15413
+ currentOffsetY = clamp4(
14645
15414
  logicalY - (canvas.height - renderConfig.keyHeight) / 2,
14646
15415
  0,
14647
15416
  getMaxOffsetY()
@@ -14712,6 +15481,35 @@ var mountDAW = (target, options = {}) => {
14712
15481
  refs.drumVolume.value = String(meta.drumVolume);
14713
15482
  refs.drumVolumeLabel.textContent = `${drumVolume}%`;
14714
15483
  }
15484
+ if (meta.reverb !== void 0) {
15485
+ reverbAmount = meta.reverb;
15486
+ refs.reverbAmount.value = String(meta.reverb);
15487
+ refs.reverbAmountLabel.textContent = `${meta.reverb}%`;
15488
+ options.onReverbChange?.(meta.reverb);
15489
+ }
15490
+ if (meta.reverbDecay !== void 0) {
15491
+ reverbDecay = meta.reverbDecay / 10;
15492
+ refs.reverbDecay.value = String(meta.reverbDecay);
15493
+ refs.reverbDecayLabel.textContent = `${reverbDecay.toFixed(1)}s`;
15494
+ options.onReverbDecayChange?.(reverbDecay);
15495
+ }
15496
+ if (meta.reverbPreDelay !== void 0) {
15497
+ reverbPreDelay = meta.reverbPreDelay;
15498
+ refs.reverbPreDelay.value = String(meta.reverbPreDelay);
15499
+ refs.reverbPreDelayLabel.textContent = `${meta.reverbPreDelay}ms`;
15500
+ options.onReverbPreDelayChange?.(reverbPreDelay);
15501
+ }
15502
+ if (meta.delay !== void 0) {
15503
+ delayAmount = meta.delay;
15504
+ refs.delayAmount.value = String(meta.delay);
15505
+ refs.delayAmountLabel.textContent = `${meta.delay}%`;
15506
+ options.onDelayChange?.(meta.delay);
15507
+ }
15508
+ if (meta.delayDivision && ["4", "8", "8d", "16"].includes(meta.delayDivision)) {
15509
+ delayDivision = meta.delayDivision;
15510
+ refs.delayDivision.value = delayDivision;
15511
+ options.onDelayDivisionChange?.(delayDivision);
15512
+ }
14715
15513
  }
14716
15514
  trackStates.forEach((t, i2) => {
14717
15515
  if (applyActiveOnly && i2 !== activeTrackIndex) return;
@@ -14721,6 +15519,34 @@ var mountDAW = (target, options = {}) => {
14721
15519
  options.onTrackInstrumentChange?.(i2, name);
14722
15520
  }
14723
15521
  });
15522
+ trackStates.forEach((t, i2) => {
15523
+ if (applyActiveOnly && i2 !== activeTrackIndex) return;
15524
+ const comp = meta.trackCompression?.[i2] ?? 0;
15525
+ if (t.trackCompression !== comp) {
15526
+ t.trackCompression = comp;
15527
+ options.onTrackCompressionChange?.(t.config.id, comp);
15528
+ }
15529
+ const width = meta.trackWidth?.[i2] ?? 100;
15530
+ if (t.trackWidth !== width) {
15531
+ t.trackWidth = width;
15532
+ options.onTrackWidthChange?.(t.config.id, width);
15533
+ }
15534
+ const eqLow = meta.trackEqLow?.[i2] ?? 0;
15535
+ if (t.trackEqLow !== eqLow) {
15536
+ t.trackEqLow = eqLow;
15537
+ options.onTrackEqLowChange?.(t.config.id, eqLow);
15538
+ }
15539
+ const eqMid = meta.trackEqMid?.[i2] ?? 0;
15540
+ if (t.trackEqMid !== eqMid) {
15541
+ t.trackEqMid = eqMid;
15542
+ options.onTrackEqMidChange?.(t.config.id, eqMid);
15543
+ }
15544
+ const eqHigh = meta.trackEqHigh?.[i2] ?? 0;
15545
+ if (t.trackEqHigh !== eqHigh) {
15546
+ t.trackEqHigh = eqHigh;
15547
+ options.onTrackEqHighChange?.(t.config.id, eqHigh);
15548
+ }
15549
+ });
14724
15550
  trackStates.forEach((t, i2) => {
14725
15551
  if (applyActiveOnly && i2 !== activeTrackIndex) return;
14726
15552
  const v = trackVelocity.get(i2);
@@ -14739,6 +15565,9 @@ var mountDAW = (target, options = {}) => {
14739
15565
  active.vocalOctave = 0;
14740
15566
  active.vocalVibrato = false;
14741
15567
  active.vocalReverb = 0;
15568
+ active.vocalDelay = 0;
15569
+ active.vocalGender = 50;
15570
+ active.vocalBreathiness = 50;
14742
15571
  } else {
14743
15572
  for (const t of trackStates) {
14744
15573
  t.lyrics = "";
@@ -14749,6 +15578,9 @@ var mountDAW = (target, options = {}) => {
14749
15578
  t.vocalOctave = 0;
14750
15579
  t.vocalVibrato = false;
14751
15580
  t.vocalReverb = 0;
15581
+ t.vocalDelay = 0;
15582
+ t.vocalGender = 50;
15583
+ t.vocalBreathiness = 50;
14752
15584
  }
14753
15585
  }
14754
15586
  lyrics?.forEach((lt) => {
@@ -14763,6 +15595,9 @@ var mountDAW = (target, options = {}) => {
14763
15595
  t.vocalOctave = lt.octave ?? 0;
14764
15596
  t.vocalVibrato = lt.vibrato ?? false;
14765
15597
  t.vocalReverb = lt.reverb ?? 0;
15598
+ t.vocalDelay = lt.delay ?? 0;
15599
+ t.vocalGender = lt.gender ?? 50;
15600
+ t.vocalBreathiness = lt.breathiness ?? 50;
14766
15601
  });
14767
15602
  for (const p of placements) {
14768
15603
  if (applyActiveOnly && p.trackIndex !== activeTrackIndex) continue;
@@ -14843,6 +15678,9 @@ var mountDAW = (target, options = {}) => {
14843
15678
  active.vocalOctave = 0;
14844
15679
  active.vocalVibrato = false;
14845
15680
  active.vocalReverb = 0;
15681
+ active.vocalDelay = 0;
15682
+ active.vocalGender = 50;
15683
+ active.vocalBreathiness = 50;
14846
15684
  } else {
14847
15685
  clearAll();
14848
15686
  for (const t of trackStates) t.core.setLoadMode(true);
@@ -14855,6 +15693,9 @@ var mountDAW = (target, options = {}) => {
14855
15693
  t.vocalOctave = 0;
14856
15694
  t.vocalVibrato = false;
14857
15695
  t.vocalReverb = 0;
15696
+ t.vocalDelay = 0;
15697
+ t.vocalGender = 50;
15698
+ t.vocalBreathiness = 50;
14858
15699
  }
14859
15700
  }
14860
15701
  const { placements, bpm: parsedBpm } = isAdvanced ? extractMidiPlacementsByTrack(
@@ -14903,6 +15744,7 @@ var mountDAW = (target, options = {}) => {
14903
15744
  bpm = value;
14904
15745
  refs.bpmInput.value = String(value);
14905
15746
  for (const t of trackStates) t.core.setTempo(value);
15747
+ options.onBpmChange?.(value);
14906
15748
  };
14907
15749
  let lastUndoTime = 0;
14908
15750
  const undo = () => {
@@ -14989,6 +15831,12 @@ var mountDAW = (target, options = {}) => {
14989
15831
  isSolo = refs.soloCheckbox.checked;
14990
15832
  redrawAll();
14991
15833
  });
15834
+ unsubscribeClip = options.clipMeter?.onClipChange((clipping) => {
15835
+ refs.clipBadge.classList.toggle("dtm-hidden", !clipping);
15836
+ });
15837
+ refs.clipBadge.addEventListener("click", () => {
15838
+ options.clipMeter?.reset();
15839
+ });
14992
15840
  refs.toolPen.addEventListener("click", () => setToolMode("pen"));
14993
15841
  refs.toolSelect.addEventListener("click", () => setToolMode("select"));
14994
15842
  refs.toolEraser.addEventListener("click", () => setToolMode("eraser"));
@@ -15069,6 +15917,81 @@ var mountDAW = (target, options = {}) => {
15069
15917
  refs.reverbAmountLabel.textContent = `${reverbAmount}%`;
15070
15918
  options.onReverbChange?.(reverbAmount);
15071
15919
  });
15920
+ refs.reverbAmountInfoBtn.addEventListener("click", () => {
15921
+ showModal("\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\u306E\u89E3\u8AAC", MASTER_REVERB_INFO_HTML);
15922
+ });
15923
+ refs.reverbDecay.addEventListener("input", () => {
15924
+ const raw = Number.parseInt(refs.reverbDecay.value, 10) || 22;
15925
+ reverbDecay = Math.max(
15926
+ MIN_REVERB_DECAY_SEC,
15927
+ Math.min(MAX_REVERB_DECAY_SEC, raw / 10)
15928
+ );
15929
+ refs.reverbDecayLabel.textContent = `${reverbDecay.toFixed(1)}s`;
15930
+ options.onReverbDecayChange?.(reverbDecay);
15931
+ });
15932
+ refs.reverbPreDelay.addEventListener("input", () => {
15933
+ reverbPreDelay = Number.parseInt(refs.reverbPreDelay.value, 10) || 0;
15934
+ refs.reverbPreDelayLabel.textContent = `${reverbPreDelay}ms`;
15935
+ options.onReverbPreDelayChange?.(reverbPreDelay);
15936
+ });
15937
+ refs.delayAmount.addEventListener("input", () => {
15938
+ delayAmount = Number.parseInt(refs.delayAmount.value, 10) || 0;
15939
+ refs.delayAmountLabel.textContent = `${delayAmount}%`;
15940
+ options.onDelayChange?.(delayAmount);
15941
+ });
15942
+ refs.delayDivision.addEventListener("change", () => {
15943
+ delayDivision = refs.delayDivision.value;
15944
+ options.onDelayDivisionChange?.(delayDivision);
15945
+ });
15946
+ refs.delayAmountInfoBtn.addEventListener("click", () => {
15947
+ showModal("\u30DE\u30B9\u30BF\u30C7\u30A3\u30EC\u30A4\u306E\u89E3\u8AAC", MASTER_DELAY_INFO_HTML);
15948
+ });
15949
+ refs.autoMasterInfoBtn.addEventListener("click", () => {
15950
+ showModal("\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0\u89E3\u8AAC", AUTO_MASTER_INFO_HTML);
15951
+ });
15952
+ refs.autoMasterBtn.addEventListener("click", () => {
15953
+ reverbAmount = 20;
15954
+ refs.reverbAmount.value = "20";
15955
+ refs.reverbAmountLabel.textContent = "20%";
15956
+ options.onReverbChange?.(20);
15957
+ const bpmT = clamp4((bpm - 60) / (180 - 60), 0, 1);
15958
+ reverbDecay = clamp4(
15959
+ 3 - bpmT * (3 - 0.9),
15960
+ MIN_REVERB_DECAY_SEC,
15961
+ MAX_REVERB_DECAY_SEC
15962
+ );
15963
+ refs.reverbDecay.value = String(Math.round(reverbDecay * 10));
15964
+ refs.reverbDecayLabel.textContent = `${reverbDecay.toFixed(1)}s`;
15965
+ options.onReverbDecayChange?.(reverbDecay);
15966
+ reverbPreDelay = 20;
15967
+ refs.reverbPreDelay.value = "20";
15968
+ refs.reverbPreDelayLabel.textContent = "20ms";
15969
+ options.onReverbPreDelayChange?.(20);
15970
+ let mainVocalId = null;
15971
+ let mainVocalScore = -1;
15972
+ for (const t of trackStates) {
15973
+ if (!t.lyricModel) continue;
15974
+ const score = t.core.getNotes().reduce((sum, n) => sum + n.durationSteps, 0);
15975
+ if (score > mainVocalScore) {
15976
+ mainVocalScore = score;
15977
+ mainVocalId = t.config.id;
15978
+ }
15979
+ }
15980
+ for (const t of trackStates) {
15981
+ const isMainVocal = !!t.lyricModel && t.config.id === mainVocalId;
15982
+ const comp = !t.lyricModel ? 35 : isMainVocal ? 30 : 25;
15983
+ t.trackCompression = comp;
15984
+ t.trackWidth = 115;
15985
+ options.onTrackCompressionChange?.(t.config.id, comp);
15986
+ options.onTrackWidthChange?.(t.config.id, 115);
15987
+ if (t.lyricModel) {
15988
+ t.vocalVibrato = true;
15989
+ t.vocalReverb = isMainVocal ? 15 : 30;
15990
+ fireLyricsChange(t);
15991
+ }
15992
+ }
15993
+ updateTrackPanel();
15994
+ });
15072
15995
  refs.drumSelect.addEventListener("change", () => {
15073
15996
  currentDrumPattern = refs.drumSelect.value;
15074
15997
  options.onDrumChange?.(currentDrumPattern);
@@ -15945,7 +16868,7 @@ var mountDAW = (target, options = {}) => {
15945
16868
  currentPlayStep = step;
15946
16869
  playbackState = "paused";
15947
16870
  const canvas = getGridCanvas();
15948
- currentOffsetX = clamp3(
16871
+ currentOffsetX = clamp4(
15949
16872
  step * renderConfig.stepWidth - canvas.width * 0.5,
15950
16873
  0,
15951
16874
  getMaxOffsetX()
@@ -15991,11 +16914,11 @@ var mountDAW = (target, options = {}) => {
15991
16914
  getViewState,
15992
16915
  setViewState: (state) => {
15993
16916
  if (typeof state.zoomX === "number") {
15994
- zoomX = clamp3(state.zoomX, 25, 200);
16917
+ zoomX = clamp4(state.zoomX, 25, 200);
15995
16918
  applyZoomX();
15996
16919
  }
15997
16920
  if (typeof state.zoomY === "number") {
15998
- zoomY = clamp3(state.zoomY, 50, 200);
16921
+ zoomY = clamp4(state.zoomY, 50, 200);
15999
16922
  applyZoomY();
16000
16923
  }
16001
16924
  if (typeof state.decomposeChord === "boolean") {
@@ -16017,28 +16940,52 @@ var mountDAW = (target, options = {}) => {
16017
16940
  forcePauseAt,
16018
16941
  setLoading,
16019
16942
  setMasterVolume: (volume) => {
16020
- masterVolume = clamp3(volume, 0, 100);
16943
+ masterVolume = clamp4(volume, 0, 100);
16021
16944
  refs.masterVolume.value = String(masterVolume);
16022
16945
  refs.masterVolumeLabel.textContent = `${masterVolume}%`;
16023
16946
  options.singingVoices?.setVolume(masterVolume / 100);
16024
16947
  },
16025
16948
  setVolume: (volume) => {
16026
- masterVolume = clamp3(volume, 0, 100);
16949
+ masterVolume = clamp4(volume, 0, 100);
16027
16950
  refs.masterVolume.value = String(masterVolume);
16028
16951
  refs.masterVolumeLabel.textContent = `${masterVolume}%`;
16029
16952
  options.singingVoices?.setVolume(masterVolume / 100);
16030
16953
  },
16031
16954
  setDrumVolume: (volume) => {
16032
- drumVolume = clamp3(volume, 0, 100);
16955
+ drumVolume = clamp4(volume, 0, 100);
16033
16956
  refs.drumVolume.value = String(drumVolume);
16034
16957
  refs.drumVolumeLabel.textContent = `${drumVolume}%`;
16035
16958
  },
16036
16959
  setReverbAmount: (amount) => {
16037
- reverbAmount = clamp3(amount, 0, 100);
16960
+ reverbAmount = clamp4(amount, 0, 100);
16038
16961
  refs.reverbAmount.value = String(reverbAmount);
16039
16962
  refs.reverbAmountLabel.textContent = `${reverbAmount}%`;
16040
16963
  options.onReverbChange?.(reverbAmount);
16041
16964
  },
16965
+ setReverbDecay: (seconds) => {
16966
+ reverbDecay = Math.max(
16967
+ MIN_REVERB_DECAY_SEC,
16968
+ Math.min(MAX_REVERB_DECAY_SEC, seconds)
16969
+ );
16970
+ refs.reverbDecay.value = String(Math.round(reverbDecay * 10));
16971
+ refs.reverbDecayLabel.textContent = `${reverbDecay.toFixed(1)}s`;
16972
+ options.onReverbDecayChange?.(reverbDecay);
16973
+ },
16974
+ setReverbPreDelay: (ms) => {
16975
+ reverbPreDelay = Math.max(
16976
+ MIN_REVERB_PREDELAY_MS,
16977
+ Math.min(MAX_REVERB_PREDELAY_MS, ms)
16978
+ );
16979
+ refs.reverbPreDelay.value = String(reverbPreDelay);
16980
+ refs.reverbPreDelayLabel.textContent = `${reverbPreDelay}ms`;
16981
+ options.onReverbPreDelayChange?.(reverbPreDelay);
16982
+ },
16983
+ setDelayAmount: (amount) => {
16984
+ delayAmount = clamp4(amount, 0, 100);
16985
+ refs.delayAmount.value = String(delayAmount);
16986
+ refs.delayAmountLabel.textContent = `${delayAmount}%`;
16987
+ options.onDelayChange?.(delayAmount);
16988
+ },
16042
16989
  applyPatch: (trackId, added, removed) => {
16043
16990
  const track = trackStates.find((t) => t.config.id === trackId);
16044
16991
  if (!track) return;
@@ -16080,6 +17027,9 @@ var mountDAW = (target, options = {}) => {
16080
17027
  t.vocalOctave = data.vocalOctave;
16081
17028
  t.vocalVibrato = data.vocalVibrato ?? false;
16082
17029
  t.vocalReverb = data.vocalReverb ?? 0;
17030
+ t.vocalDelay = data.vocalDelay ?? 0;
17031
+ t.vocalGender = data.vocalGender ?? 50;
17032
+ t.vocalBreathiness = data.vocalBreathiness ?? 50;
16083
17033
  },
16084
17034
  applyTrackInstrument: (trackIndex, instrumentName) => {
16085
17035
  const t = trackStates[trackIndex];
@@ -16105,6 +17055,7 @@ var mountDAW = (target, options = {}) => {
16105
17055
  destroy: () => {
16106
17056
  sequencer.stop();
16107
17057
  options.singingVoices?.stopStream();
17058
+ unsubscribeClip?.();
16108
17059
  resizeObserver.disconnect();
16109
17060
  document.removeEventListener("pointermove", onPointerMove);
16110
17061
  document.removeEventListener("pointerup", onPointerUp);
@@ -16197,6 +17148,9 @@ var playSingingMML = async (mml, options = {}) => {
16197
17148
  pan: panToStereo(lt.pan ?? DEFAULT_PAN),
16198
17149
  vibrato: lt.vibrato,
16199
17150
  reverbSend: (lt.reverb ?? 0) / 100,
17151
+ delaySend: (lt.delay ?? 0) / 100,
17152
+ gender: (lt.gender ?? 50) / 100,
17153
+ breathiness: (lt.breathiness ?? 50) / 100,
16200
17154
  notes
16201
17155
  };
16202
17156
  });
@@ -16804,24 +17758,191 @@ var isSupported = midiJsonParser.isSupported;
16804
17758
  var parseArrayBuffer = midiJsonParser.parseArrayBuffer;
16805
17759
  URL.revokeObjectURL(url);
16806
17760
 
16807
- // src/reverb.ts
16808
- var IMPULSE_DURATION_SEC = 2.2;
16809
- var IMPULSE_DECAY = 2.5;
16810
- var createReverbImpulse = (ctx) => {
16811
- const rate = ctx.sampleRate;
16812
- const length = Math.max(1, Math.floor(rate * IMPULSE_DURATION_SEC));
16813
- const impulse = ctx.createBuffer(2, length, rate);
16814
- for (let ch = 0; ch < impulse.numberOfChannels; ch++) {
16815
- const data = impulse.getChannelData(ch);
16816
- for (let i2 = 0; i2 < length; i2++) {
16817
- const envelope = (1 - i2 / length) ** IMPULSE_DECAY;
16818
- data[i2] = (Math.random() * 2 - 1) * envelope;
17761
+ // src/channel-strip.ts
17762
+ var clamp5 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
17763
+ var compressionParams = (amount) => {
17764
+ const t = clamp5(amount, 0, 100) / 100;
17765
+ return {
17766
+ threshold: 0 + (-24 - 0) * t,
17767
+ ratio: 1 + (12 - 1) * t,
17768
+ knee: 0 + (6 - 0) * t,
17769
+ attack: 0.02 + (3e-3 - 0.02) * t,
17770
+ release: 0.25 + (0.15 - 0.25) * t
17771
+ };
17772
+ };
17773
+ var EQ_LOW_FREQ = 200;
17774
+ var EQ_MID_FREQ = 1e3;
17775
+ var EQ_HIGH_FREQ = 5e3;
17776
+ var EQ_MID_Q = 1;
17777
+ var EQ_MAX_DB = 12;
17778
+ var createChannelStrip = (ctx, destination, options = {}) => {
17779
+ const input = ctx.createGain();
17780
+ const eqLow = ctx.createBiquadFilter();
17781
+ eqLow.type = "lowshelf";
17782
+ eqLow.frequency.value = EQ_LOW_FREQ;
17783
+ eqLow.gain.value = clamp5(options.eqLow ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
17784
+ const eqMid = ctx.createBiquadFilter();
17785
+ eqMid.type = "peaking";
17786
+ eqMid.frequency.value = EQ_MID_FREQ;
17787
+ eqMid.Q.value = EQ_MID_Q;
17788
+ eqMid.gain.value = clamp5(options.eqMid ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
17789
+ const eqHigh = ctx.createBiquadFilter();
17790
+ eqHigh.type = "highshelf";
17791
+ eqHigh.frequency.value = EQ_HIGH_FREQ;
17792
+ eqHigh.gain.value = clamp5(options.eqHigh ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
17793
+ input.connect(eqLow);
17794
+ eqLow.connect(eqMid);
17795
+ eqMid.connect(eqHigh);
17796
+ const compressor = ctx.createDynamicsCompressor();
17797
+ const applyCompression = (amount) => {
17798
+ const p = compressionParams(amount);
17799
+ const now = ctx.currentTime;
17800
+ compressor.threshold.setValueAtTime(p.threshold, now);
17801
+ compressor.ratio.setValueAtTime(p.ratio, now);
17802
+ compressor.knee.setValueAtTime(p.knee, now);
17803
+ compressor.attack.setValueAtTime(p.attack, now);
17804
+ compressor.release.setValueAtTime(p.release, now);
17805
+ };
17806
+ applyCompression(options.compression ?? 0);
17807
+ const splitter = ctx.createChannelSplitter(2);
17808
+ const mid = ctx.createGain();
17809
+ mid.gain.value = 0.5;
17810
+ splitter.connect(mid, 0);
17811
+ splitter.connect(mid, 1);
17812
+ const sideSum = ctx.createGain();
17813
+ sideSum.gain.value = 1;
17814
+ const sideL = ctx.createGain();
17815
+ sideL.gain.value = 0.5;
17816
+ const sideR = ctx.createGain();
17817
+ sideR.gain.value = -0.5;
17818
+ splitter.connect(sideL, 0);
17819
+ splitter.connect(sideR, 1);
17820
+ sideL.connect(sideSum);
17821
+ sideR.connect(sideSum);
17822
+ const widthGain = ctx.createGain();
17823
+ sideSum.connect(widthGain);
17824
+ const widthInv = ctx.createGain();
17825
+ widthInv.gain.value = -1;
17826
+ widthGain.connect(widthInv);
17827
+ const outL = ctx.createGain();
17828
+ mid.connect(outL);
17829
+ widthGain.connect(outL);
17830
+ const outR = ctx.createGain();
17831
+ mid.connect(outR);
17832
+ widthInv.connect(outR);
17833
+ const merger = ctx.createChannelMerger(2);
17834
+ outL.connect(merger, 0, 0);
17835
+ outR.connect(merger, 0, 1);
17836
+ const setWidth = (width) => {
17837
+ widthGain.gain.setTargetAtTime(
17838
+ clamp5(width, 0, 200) / 100,
17839
+ ctx.currentTime,
17840
+ 0.02
17841
+ );
17842
+ };
17843
+ setWidth(options.width ?? 100);
17844
+ eqHigh.connect(compressor);
17845
+ compressor.connect(splitter);
17846
+ merger.connect(destination);
17847
+ return {
17848
+ input,
17849
+ setEqLow: (db) => {
17850
+ eqLow.gain.setTargetAtTime(
17851
+ clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
17852
+ ctx.currentTime,
17853
+ 0.02
17854
+ );
17855
+ },
17856
+ setEqMid: (db) => {
17857
+ eqMid.gain.setTargetAtTime(
17858
+ clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
17859
+ ctx.currentTime,
17860
+ 0.02
17861
+ );
17862
+ },
17863
+ setEqHigh: (db) => {
17864
+ eqHigh.gain.setTargetAtTime(
17865
+ clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
17866
+ ctx.currentTime,
17867
+ 0.02
17868
+ );
17869
+ },
17870
+ setCompression: applyCompression,
17871
+ setWidth,
17872
+ dispose: () => {
17873
+ input.disconnect();
17874
+ eqLow.disconnect();
17875
+ eqMid.disconnect();
17876
+ eqHigh.disconnect();
17877
+ compressor.disconnect();
17878
+ splitter.disconnect();
17879
+ mid.disconnect();
17880
+ sideSum.disconnect();
17881
+ sideL.disconnect();
17882
+ sideR.disconnect();
17883
+ widthGain.disconnect();
17884
+ widthInv.disconnect();
17885
+ outL.disconnect();
17886
+ outR.disconnect();
17887
+ merger.disconnect();
16819
17888
  }
16820
- }
16821
- return impulse;
17889
+ };
17890
+ };
17891
+
17892
+ // src/clip-meter.ts
17893
+ var createClipMeter = (ctx, source, options = {}) => {
17894
+ const threshold = options.threshold ?? 0.98;
17895
+ const holdMs = options.holdMs ?? 800;
17896
+ const analyser = ctx.createAnalyser();
17897
+ analyser.fftSize = 512;
17898
+ source.connect(analyser);
17899
+ const buf = new Float32Array(analyser.fftSize);
17900
+ let clipping = false;
17901
+ let clipUntil = 0;
17902
+ let peakLevel = 0;
17903
+ const listeners = /* @__PURE__ */ new Set();
17904
+ let rafId = null;
17905
+ const tick = () => {
17906
+ analyser.getFloatTimeDomainData(buf);
17907
+ let peak = 0;
17908
+ let clipped = false;
17909
+ for (let i2 = 0; i2 < buf.length; i2++) {
17910
+ const v = Math.abs(buf[i2]);
17911
+ if (v > peak) peak = v;
17912
+ if (v >= threshold) clipped = true;
17913
+ }
17914
+ peakLevel = peak;
17915
+ const now = performance.now();
17916
+ if (clipped) clipUntil = now + holdMs;
17917
+ const nextClipping = now < clipUntil;
17918
+ if (nextClipping !== clipping) {
17919
+ clipping = nextClipping;
17920
+ for (const cb of listeners) cb(clipping);
17921
+ }
17922
+ rafId = requestAnimationFrame(tick);
17923
+ };
17924
+ rafId = requestAnimationFrame(tick);
17925
+ return {
17926
+ onClipChange: (cb) => {
17927
+ listeners.add(cb);
17928
+ return () => listeners.delete(cb);
17929
+ },
17930
+ getPeakLevel: () => peakLevel,
17931
+ reset: () => {
17932
+ clipUntil = 0;
17933
+ if (clipping) {
17934
+ clipping = false;
17935
+ for (const cb of listeners) cb(false);
17936
+ }
17937
+ },
17938
+ dispose: () => {
17939
+ if (rafId !== null) cancelAnimationFrame(rafId);
17940
+ rafId = null;
17941
+ analyser.disconnect();
17942
+ listeners.clear();
17943
+ }
17944
+ };
16822
17945
  };
16823
- var REVERB_MAX_WET = 0.6;
16824
- var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100 * REVERB_MAX_WET;
16825
17946
 
16826
17947
  // src/sf/SoundFont_drum.ts
16827
17948
  var touch = (map, key, ctor) => {
@@ -16975,18 +18096,20 @@ var createDtmStudio = async (options = {}) => {
16975
18096
  const audioCtx = options.audioContext ?? new AudioContext({ sampleRate: 44100 });
16976
18097
  const masterGain = audioCtx.createGain();
16977
18098
  masterGain.gain.value = options.masterVolume ?? 1;
16978
- masterGain.connect(audioCtx.destination);
16979
18099
  const drumGain = audioCtx.createGain();
16980
18100
  drumGain.gain.value = options.drumVolume ?? 1;
16981
18101
  drumGain.connect(masterGain);
18102
+ let reverbDecaySec = options.reverbDecay ?? DEFAULT_REVERB_DECAY_SEC;
18103
+ const reverbPreDelay = audioCtx.createDelay(MAX_REVERB_PREDELAY_MS / 1e3);
18104
+ reverbPreDelay.delayTime.value = (options.reverbPreDelay ?? DEFAULT_REVERB_PREDELAY_MS) / 1e3;
16982
18105
  const reverbConvolver = audioCtx.createConvolver();
16983
- reverbConvolver.buffer = createReverbImpulse(audioCtx);
18106
+ reverbConvolver.buffer = createReverbImpulse(audioCtx, reverbDecaySec);
16984
18107
  reverbConvolver.normalize = true;
16985
18108
  const reverbWetGain = audioCtx.createGain();
16986
18109
  reverbWetGain.gain.value = reverbAmountToGain(options.reverbAmount ?? 0);
16987
- masterGain.connect(reverbConvolver);
18110
+ masterGain.connect(reverbPreDelay);
18111
+ reverbPreDelay.connect(reverbConvolver);
16988
18112
  reverbConvolver.connect(reverbWetGain);
16989
- reverbWetGain.connect(audioCtx.destination);
16990
18113
  const setReverbAmount = (amount) => {
16991
18114
  reverbWetGain.gain.setTargetAtTime(
16992
18115
  reverbAmountToGain(amount),
@@ -16994,6 +18117,46 @@ var createDtmStudio = async (options = {}) => {
16994
18117
  0.02
16995
18118
  );
16996
18119
  };
18120
+ const setReverbDecay = (seconds) => {
18121
+ reverbDecaySec = Math.max(
18122
+ MIN_REVERB_DECAY_SEC,
18123
+ Math.min(MAX_REVERB_DECAY_SEC, seconds)
18124
+ );
18125
+ reverbConvolver.buffer = createReverbImpulse(audioCtx, reverbDecaySec);
18126
+ };
18127
+ const setReverbPreDelay = (ms) => {
18128
+ reverbPreDelay.delayTime.setTargetAtTime(
18129
+ Math.max(MIN_REVERB_PREDELAY_MS, Math.min(MAX_REVERB_PREDELAY_MS, ms)) / 1e3,
18130
+ audioCtx.currentTime,
18131
+ 0.02
18132
+ );
18133
+ };
18134
+ const finalMix = audioCtx.createGain();
18135
+ masterGain.connect(finalMix);
18136
+ reverbWetGain.connect(finalMix);
18137
+ const delayBus = createDelayBus(audioCtx, finalMix, {
18138
+ amount: options.delayAmount ?? 0,
18139
+ division: options.delayDivision ?? "8"
18140
+ });
18141
+ const setDelayAmount = (amount) => delayBus.setAmount(amount);
18142
+ const safetyLimiter = audioCtx.createDynamicsCompressor();
18143
+ safetyLimiter.threshold.value = -1;
18144
+ safetyLimiter.knee.value = 0;
18145
+ safetyLimiter.ratio.value = 20;
18146
+ safetyLimiter.attack.value = 1e-3;
18147
+ safetyLimiter.release.value = 0.1;
18148
+ finalMix.connect(safetyLimiter);
18149
+ safetyLimiter.connect(options.destination ?? audioCtx.destination);
18150
+ const clipMeter = createClipMeter(audioCtx, finalMix);
18151
+ const channelStrips = /* @__PURE__ */ new Map();
18152
+ const getChannelStrip = (trackId) => {
18153
+ let strip = channelStrips.get(trackId);
18154
+ if (!strip) {
18155
+ strip = createChannelStrip(audioCtx, masterGain);
18156
+ channelStrips.set(trackId, strip);
18157
+ }
18158
+ return strip;
18159
+ };
16997
18160
  const resumeAudio = () => {
16998
18161
  if (audioCtx.state === "closed") return Promise.resolve();
16999
18162
  return audioCtx.resume();
@@ -17027,7 +18190,13 @@ var createDtmStudio = async (options = {}) => {
17027
18190
  worldlineScriptUrl: options.worldlineScriptUrl,
17028
18191
  // ボーカルトラック個別のリバーブセンド(`r`トークン)先。マスタリバーブの
17029
18192
  // Convolver 入力へ直接センドする(masterGain 経由のドライ段は素通り)。
17030
- reverbBus: reverbConvolver
18193
+ reverbBus: reverbConvolver,
18194
+ // ボーカルトラック個別のディレイセンド(`e`トークン)先。
18195
+ delayBus: delayBus.input,
18196
+ // トラック単位チャンネルストリップ(コンプレッサー/ステレオワイド)の入口。
18197
+ // 楽器と同じ getChannelStrip キャッシュを共有するので、演奏トラックと歌詞トラックが
18198
+ // 同じ trackId を指していれば同じ処理が掛かる。
18199
+ getTrackDestination: (trackId) => getChannelStrip(trackId).input
17031
18200
  });
17032
18201
  const listReady = new Promise((resolve) => {
17033
18202
  sfList.init();
@@ -17257,7 +18426,7 @@ var createDtmStudio = async (options = {}) => {
17257
18426
  if (!sfInst) return;
17258
18427
  sfInst.play({
17259
18428
  ctx: audioCtx,
17260
- destination: masterGain,
18429
+ destination: getChannelStrip(e.trackId).input,
17261
18430
  pitch: e.pitch,
17262
18431
  volume: e.volume,
17263
18432
  when: e.when,
@@ -17304,6 +18473,21 @@ var createDtmStudio = async (options = {}) => {
17304
18473
  },
17305
18474
  reverbAmount: options.reverbAmount,
17306
18475
  onReverbChange: setReverbAmount,
18476
+ reverbDecay: options.reverbDecay,
18477
+ onReverbDecayChange: setReverbDecay,
18478
+ reverbPreDelay: options.reverbPreDelay,
18479
+ onReverbPreDelayChange: setReverbPreDelay,
18480
+ delayAmount: options.delayAmount,
18481
+ onDelayChange: setDelayAmount,
18482
+ delayDivision: options.delayDivision,
18483
+ onDelayDivisionChange: (division) => delayBus.setDivision(division),
18484
+ onBpmChange: (bpm) => delayBus.setBpm(bpm),
18485
+ onTrackCompressionChange: (trackId, amount) => getChannelStrip(trackId).setCompression(amount),
18486
+ onTrackWidthChange: (trackId, width) => getChannelStrip(trackId).setWidth(width),
18487
+ onTrackEqLowChange: (trackId, db) => getChannelStrip(trackId).setEqLow(db),
18488
+ onTrackEqMidChange: (trackId, db) => getChannelStrip(trackId).setEqMid(db),
18489
+ onTrackEqHighChange: (trackId, db) => getChannelStrip(trackId).setEqHigh(db),
18490
+ clipMeter,
17307
18491
  ...dawOverrides,
17308
18492
  // dawOverrides で上書きされないよう、スプレッドの後に配置して合成する
17309
18493
  onDrumChange: (name) => {
@@ -17330,17 +18514,22 @@ var createDtmStudio = async (options = {}) => {
17330
18514
  mountedEditors.push(daw);
17331
18515
  const wantPresetUI = presetUI ?? features.presetUI;
17332
18516
  const rollEl = target.querySelector('[data-dtm="roll"]');
18517
+ const presetSlot = target.querySelector(
18518
+ '[data-dtm="preset-select-slot"]'
18519
+ );
17333
18520
  if (wantPresetUI) {
17334
18521
  editorPresetSelects.get(target)?.destroy();
17335
- presetSelect = mountPresetSelect(target, {
18522
+ presetSelect = mountPresetSelect(presetSlot ?? target, {
17336
18523
  getDaw: () => daw,
17337
18524
  getTrackIds: () => trackIds,
17338
18525
  value: initialPreset,
17339
18526
  loadingTarget: rollEl ?? target,
17340
- position: "prepend",
17341
- // 楽器変更時、このエディタの発音解決が使うプリセットも追従させる。
18527
+ position: presetSlot ? "append" : "prepend",
18528
+ // 楽器変更時、このエディタの発音解決が使うプリセットも追従させ、
18529
+ // 外部の onInstrumentChange(呼び出し側の永続化フック等)へも通知する。
18530
+ // #inst= 付きMML読込時の通知経路(handleInstrumentChange)と揃える。
17342
18531
  onChange: (key) => {
17343
- editorPreset = key;
18532
+ handleInstrumentChange(key);
17344
18533
  }
17345
18534
  });
17346
18535
  editorPresetSelects.set(target, presetSelect);
@@ -17523,7 +18712,7 @@ var createDtmStudio = async (options = {}) => {
17523
18712
  if (!sfInst) return;
17524
18713
  sfInst.play({
17525
18714
  ctx: audioCtx,
17526
- destination: masterGain,
18715
+ destination: getChannelStrip(e.trackId).input,
17527
18716
  pitch: e.pitch,
17528
18717
  volume: e.volume,
17529
18718
  when: e.when,
@@ -17609,7 +18798,7 @@ var createDtmStudio = async (options = {}) => {
17609
18798
  if (!sfInst) return;
17610
18799
  sfInst.play({
17611
18800
  ctx: audioCtx,
17612
- destination: masterGain,
18801
+ destination: getChannelStrip(e.trackId).input,
17613
18802
  pitch: e.pitch,
17614
18803
  volume: e.volume,
17615
18804
  when: e.when,
@@ -17688,7 +18877,7 @@ var createDtmStudio = async (options = {}) => {
17688
18877
  if (!sfInst) return;
17689
18878
  sfInst.play({
17690
18879
  ctx: audioCtx,
17691
- destination: masterGain,
18880
+ destination: getChannelStrip(e.trackId).input,
17692
18881
  pitch: e.pitch,
17693
18882
  volume: e.volume,
17694
18883
  when: e.when,
@@ -17727,7 +18916,7 @@ var createDtmStudio = async (options = {}) => {
17727
18916
  if (!sfInst) return;
17728
18917
  sfInst.play({
17729
18918
  ctx: audioCtx,
17730
- destination: masterGain,
18919
+ destination: getChannelStrip(e.trackId).input,
17731
18920
  pitch: e.pitch,
17732
18921
  volume: e.volume,
17733
18922
  when: e.when,
@@ -17783,7 +18972,7 @@ var createDtmStudio = async (options = {}) => {
17783
18972
  if (!sfInst) return;
17784
18973
  sfInst.play({
17785
18974
  ctx: audioCtx,
17786
- destination: masterGain,
18975
+ destination: getChannelStrip("chord").input,
17787
18976
  pitch: e.pitch,
17788
18977
  volume: e.volume,
17789
18978
  when: e.when,
@@ -17833,6 +19022,10 @@ var createDtmStudio = async (options = {}) => {
17833
19022
  mountedPlayers.length = 0;
17834
19023
  mountedChordPlayers.length = 0;
17835
19024
  mountedEditors.length = 0;
19025
+ clipMeter.dispose();
19026
+ delayBus.dispose();
19027
+ for (const strip of channelStrips.values()) strip.dispose();
19028
+ channelStrips.clear();
17836
19029
  void audioCtx.close();
17837
19030
  };
17838
19031
  const setMasterVolume = (volume) => {
@@ -17872,6 +19065,9 @@ var createDtmStudio = async (options = {}) => {
17872
19065
  setMasterVolume,
17873
19066
  setVolume: setMasterVolume,
17874
19067
  setReverbAmount,
19068
+ setReverbDecay,
19069
+ setReverbPreDelay,
19070
+ setDelayAmount,
17875
19071
  dispose
17876
19072
  };
17877
19073
  };