@onjmin/dtm 0.1.1 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -285,7 +285,10 @@ var buildUI = (target, options) => {
285
285
  <div class="dtm-tracks" data-dtm="track-tabs"></div>
286
286
 
287
287
  <div class="dtm-roll-wrap">
288
- <div class="dtm-roll" data-dtm="roll"><div data-dtm="wrapper" style="position:absolute;inset:0;"></div></div>
288
+ <div class="dtm-roll" data-dtm="roll">
289
+ <div data-dtm="wrapper" style="position:absolute;inset:0;"></div>
290
+ <div class="dtm-overlay" data-dtm="overlay" hidden><div class="dtm-spinner"></div></div>
291
+ </div>
289
292
  <div class="dtm-vscroll" data-dtm="vscroll"><div class="dtm-vscroll-thumb" data-dtm="vscroll-thumb"></div></div>
290
293
  </div>
291
294
  <div class="dtm-hscroll" data-dtm="hscroll"><div class="dtm-hscroll-thumb" data-dtm="hscroll-thumb"></div></div>
@@ -418,8 +421,6 @@ var buildUI = (target, options) => {
418
421
  </div>
419
422
  </div>
420
423
  </details>
421
-
422
- <div class="dtm-overlay" data-dtm="overlay" hidden><div class="dtm-spinner"></div></div>
423
424
  </div>`;
424
425
  const root = q(target, '[data-dtm="root"]');
425
426
  const sel = (name) => q(root, `[data-dtm="${name}"]`);
@@ -906,6 +907,15 @@ var Worldline = class _Worldline {
906
907
  }
907
908
  };
908
909
 
910
+ // src/types.ts
911
+ var DEFAULT_VOCAL_VOLUME = 200;
912
+ var DEFAULT_BPM = 120;
913
+ var DEFAULT_GATE = 100;
914
+ var DEFAULT_PAN = 64;
915
+ var DEFAULT_VELOCITY = 100;
916
+ var DEFAULT_PLAYBACK_VELOCITY = 127;
917
+ var DEFAULT_STEPS_PER_BAR = 192;
918
+
909
919
  // src/lyrics.ts
910
920
  var kanaTable = {
911
921
  \u3042: ["", "a"],
@@ -1076,9 +1086,10 @@ var parseLyrics = (mml) => {
1076
1086
  const trackId = Number.parseInt(m[1], 10);
1077
1087
  const tokens = m[2].trim().split(/\s+/);
1078
1088
  const modelToken = tokens.shift() ?? "";
1079
- let volume = 300;
1089
+ let volume = DEFAULT_VOCAL_VOLUME;
1080
1090
  let gate = 100;
1081
1091
  let pan = 64;
1092
+ let octave = 0;
1082
1093
  const colon = modelToken.indexOf(":");
1083
1094
  const model = (colon === -1 ? modelToken : modelToken.slice(0, colon)).toLowerCase();
1084
1095
  if (colon !== -1) {
@@ -1090,12 +1101,15 @@ var parseLyrics = (mml) => {
1090
1101
  const v = /^v(\d+)$/.exec(tokens[0]);
1091
1102
  const q2 = /^q(\d+)$/.exec(tokens[0]);
1092
1103
  const p = /^p(\d+)$/.exec(tokens[0]);
1104
+ const o = /^o(-?\d+)$/.exec(tokens[0]);
1093
1105
  if (v) {
1094
1106
  volume = clamp(Number.parseInt(v[1], 10), 0, MAX_VOCAL_VOLUME);
1095
1107
  } else if (q2) {
1096
1108
  gate = clamp(Number.parseInt(q2[1], 10), 0, 100);
1097
1109
  } else if (p) {
1098
1110
  pan = clamp(Number.parseInt(p[1], 10), 0, 127);
1111
+ } else if (o) {
1112
+ octave = clamp(Number.parseInt(o[1], 10), -2, 2);
1099
1113
  } else {
1100
1114
  break;
1101
1115
  }
@@ -1112,6 +1126,7 @@ var parseLyrics = (mml) => {
1112
1126
  volume,
1113
1127
  gate,
1114
1128
  pan,
1129
+ octave,
1115
1130
  syllables,
1116
1131
  metaText: metaTokens.join(" "),
1117
1132
  ...lineBreaks.length > 0 ? { lineBreaks } : {}
@@ -1145,9 +1160,9 @@ var createLyricsConductor = (lyrics) => {
1145
1160
  return {
1146
1161
  model: track.model,
1147
1162
  syllable,
1148
- volume: vocalVolumeToGain(track.volume ?? 300),
1149
- gate: (track.gate ?? 100) / 100,
1150
- pan: panToStereo(track.pan ?? 64)
1163
+ volume: vocalVolumeToGain(track.volume ?? DEFAULT_VOCAL_VOLUME),
1164
+ gate: (track.gate ?? DEFAULT_GATE) / 100,
1165
+ pan: panToStereo(track.pan ?? DEFAULT_PAN)
1151
1166
  };
1152
1167
  };
1153
1168
  const reset = () => pointers.clear();
@@ -1255,7 +1270,8 @@ var KOE_VOICEBANKS = {
1255
1270
  tsukuyomi: "\u3064\u304F\u3088\u307F\u3061\u3083\u3093.koe",
1256
1271
  rino: "\u6625\u97F3\u30EA\u30CEver0.3.koe",
1257
1272
  roze: "\u675F\u97F3\u30ED\u30BCver0.\uFF151(\u591A\u97F3\u968E).koe",
1258
- ruko: "\u6B32\u97F3\u30EB\u30B3\u2640\u6B4C\u9023\u7D9A\u97F3\u666E1.00.koe",
1273
+ ruko_male: "\u6B32\u97F3\u30EB\u30B3\u2642\u9023\u7D9A\u97F3Ver.1.03.koe",
1274
+ ruko_female: "\u6B32\u97F3\u30EB\u30B3\u2640\u6B4C\u9023\u7D9A\u97F3\u666E1.00.koe",
1259
1275
  teto: "\u91CD\u97F3\u30C6\u30C8\u5358\u72EC\u97F3.koe",
1260
1276
  shiyo: "\u9769\u547D\u30B7\u30E8.koe"
1261
1277
  };
@@ -1263,10 +1279,30 @@ var KOE_VOICEBANK_LABELS = {
1263
1279
  tsukuyomi: "\u3064\u304F\u3088\u307F\u3061\u3083\u3093",
1264
1280
  rino: "\u6625\u97F3\u30EA\u30CE",
1265
1281
  roze: "\u675F\u97F3\u30ED\u30BC",
1266
- ruko: "\u6B32\u97F3\u30EB\u30B3",
1282
+ ruko_male: "\u6B32\u97F3\u30EB\u30B3\u2642",
1283
+ ruko_female: "\u6B32\u97F3\u30EB\u30B3\u2640",
1267
1284
  teto: "\u91CD\u97F3\u30C6\u30C8",
1268
1285
  shiyo: "\u9769\u547D\u30B7\u30E8"
1269
1286
  };
1287
+ var VOICE_IMAGE_KEY = {
1288
+ klatt: "puyuyu",
1289
+ tsukuyomi: "tsukuyomi",
1290
+ rino: "rino",
1291
+ roze: "roze",
1292
+ ruko_male: "ruko",
1293
+ ruko_female: "ruko",
1294
+ teto: "teto",
1295
+ shiyo: "shiyo"
1296
+ };
1297
+ var KOE_VOICEBANK_TERMS = {
1298
+ tsukuyomi: "https://tyc.rei-yumesaki.net/material/utau/terms/",
1299
+ rino: "https://hatenakun1.github.io/halunelino/",
1300
+ roze: "https://tabaneroze.ninja-web.net/terms-of-use.html",
1301
+ ruko_male: "https://long-sleeper.net/index.php?id=22",
1302
+ ruko_female: "https://long-sleeper.net/index.php?id=22",
1303
+ teto: "https://kasaneteto.jp/guidelines/voice.html",
1304
+ shiyo: "https://kakumeisiyo.my.canva.site/dagkuyjwycs"
1305
+ };
1270
1306
  var koeUrl = (name, base = KOE_BASE_URL) => `${base}/${encodeURIComponent(name)}`;
1271
1307
  var DEFAULT_WORLDLINE_SCRIPT = "https://onjmin.github.io/koe/demo/world/worldline.js";
1272
1308
  var KOE_SAMPLE_RATE = 48e3;
@@ -1414,9 +1450,10 @@ var createWorkerBackend = async (workerUrl, options) => {
1414
1450
  }
1415
1451
  }
1416
1452
  };
1417
- worker.onerror = (e) => onFail?.(
1418
- new Error(`voice worker error: ${e.message || e}`)
1419
- );
1453
+ worker.onerror = (e) => {
1454
+ const ev = e;
1455
+ onFail?.(new Error(ev.message || ev.error || `Event: ${ev.type}`));
1456
+ };
1420
1457
  await new Promise((resolve, reject) => {
1421
1458
  onReady = resolve;
1422
1459
  onFail = reject;
@@ -1592,22 +1629,18 @@ var createSingingVoices = (ctx, destination, options = {}) => {
1592
1629
  if (inflight) return inflight;
1593
1630
  const koe = catalog[m];
1594
1631
  if (!koe) return Promise.resolve(null);
1595
- const p = (async () => {
1596
- let source = koe;
1597
- if (typeof source === "string") {
1598
- const res = await fetch(source);
1599
- if (!res.ok) {
1600
- throw new Error(`fetch failed: ${res.statusText}`);
1601
- }
1602
- source = await res.blob();
1603
- }
1604
- return createKoeVoice(ctx, destination, {
1605
- koe: source,
1632
+ const p = (async () => (
1633
+ // URL文字列はそのまま渡す。koe側が VoiceBank.load 内で HTTP Range により
1634
+ // マニフェストだけ先読みし、音素PCMは歌う直前にオンデマンド取得する
1635
+ // (= 初回に .koe 全体をDLしない。モバイル初回ロードの待ちを解消)。
1636
+ // Blob/File が直接渡されたケース(ローカル読み込み)はそのまま BlobVoiceSource。
1637
+ createKoeVoice(ctx, destination, {
1638
+ koe,
1606
1639
  worldlineScriptUrl: options.worldlineScriptUrl,
1607
1640
  lightweight: options.lightweight,
1608
1641
  voiceWorkerUrl: options.voiceWorkerUrl
1609
- });
1610
- })().then((v) => {
1642
+ })
1643
+ ))().then((v) => {
1611
1644
  loaded.set(m, v);
1612
1645
  return v;
1613
1646
  }).catch((err) => {
@@ -2131,7 +2164,7 @@ var exportMIDI = (options) => {
2131
2164
  (n.startStep + (n.durationSteps || 1)) * tickPerStep
2132
2165
  );
2133
2166
  const vel = Math.round(
2134
- (n.velocity ?? 100) * (track.volume || 100) / 100
2167
+ (n.velocity ?? DEFAULT_VELOCITY) * (track.volume ?? 100) / 100
2135
2168
  );
2136
2169
  events.push({ t: startTick, m: [144 | ch & 15, n.pitch, vel] });
2137
2170
  events.push({ t: endTick, m: [144 | ch & 15, n.pitch, 0] });
@@ -2686,7 +2719,7 @@ var MMLCore = class _MMLCore {
2686
2719
  startStep: step,
2687
2720
  durationSteps: options.noteLengthSteps,
2688
2721
  pitch,
2689
- velocity: options.velocity ?? 100
2722
+ velocity: options.velocity ?? DEFAULT_VELOCITY
2690
2723
  };
2691
2724
  this.notes.push(newNote);
2692
2725
  }
@@ -2861,19 +2894,15 @@ var MMLCore = class _MMLCore {
2861
2894
  notesByStep.set(n.startStep, list);
2862
2895
  }
2863
2896
  const sortedSteps = Array.from(notesByStep.keys()).sort((a, b) => a - b);
2897
+ const MIN_STEP = config.stepsPerBar / 64;
2864
2898
  const fillRests = (until) => {
2865
- while (currentCursor < until) {
2899
+ while (until - currentCursor >= MIN_STEP) {
2866
2900
  const gap = until - currentCursor;
2867
- if (gap <= 2) {
2868
- currentCursor = until;
2869
- break;
2870
- }
2871
2901
  const { dur, steps } = this.findBestFitDuration(gap);
2872
2902
  segments.push(`r${dur}`);
2873
2903
  currentCursor += steps;
2874
2904
  }
2875
2905
  };
2876
- const MIN_STEP = config.stepsPerBar / 64;
2877
2906
  for (let i = 0; i < sortedSteps.length; i++) {
2878
2907
  const startStep = sortedSteps[i];
2879
2908
  const notes = notesByStep.get(startStep);
@@ -2882,7 +2911,6 @@ var MMLCore = class _MMLCore {
2882
2911
  const nextStart = sortedSteps[i + 1] ?? endStep;
2883
2912
  const physicsLimit = nextStart - currentCursor;
2884
2913
  if (physicsLimit < MIN_STEP) {
2885
- currentCursor = startStep;
2886
2914
  continue;
2887
2915
  }
2888
2916
  const idealDuration = notes[0].durationSteps;
@@ -3007,7 +3035,7 @@ var formatMmlMeta = (meta) => {
3007
3035
  return parts.join(" ");
3008
3036
  };
3009
3037
  var parseMML = (mml, options = {}) => {
3010
- const stepsPerBar = options.stepsPerBar ?? 192;
3038
+ const stepsPerBar = options.stepsPerBar ?? DEFAULT_STEPS_PER_BAR;
3011
3039
  const collectTokens = options.collectTokens ?? false;
3012
3040
  const collectLyrics = options.collectLyrics ?? false;
3013
3041
  const clampTrackCount = options.clampTrackCount;
@@ -3084,14 +3112,14 @@ var parseMML = (mml, options = {}) => {
3084
3112
  numStr += body[j];
3085
3113
  j++;
3086
3114
  }
3087
- octave = clamp2(Number.parseInt(numStr, 10) || 4, 0, 8);
3115
+ octave = numStr ? clamp2(Number.parseInt(numStr, 10), 0, 8) : 4;
3088
3116
  pushTok("octave", currentStep, 0, tokStart);
3089
3117
  } else if (ch === ">") {
3090
- octave++;
3118
+ octave = Math.min(8, octave + 1);
3091
3119
  j++;
3092
3120
  pushTok("shift", currentStep, 0, tokStart);
3093
3121
  } else if (ch === "<") {
3094
- octave--;
3122
+ octave = Math.max(0, octave - 1);
3095
3123
  j++;
3096
3124
  pushTok("shift", currentStep, 0, tokStart);
3097
3125
  } else if (ch === "l") {
@@ -3138,10 +3166,10 @@ var parseMML = (mml, options = {}) => {
3138
3166
  }
3139
3167
  chordNotes.push((octave + 1) * 12 + pitch);
3140
3168
  } else if (c === ">") {
3141
- octave++;
3169
+ octave = Math.min(8, octave + 1);
3142
3170
  j++;
3143
3171
  } else if (c === "<") {
3144
- octave--;
3172
+ octave = Math.max(0, octave - 1);
3145
3173
  j++;
3146
3174
  } else if (c === "o") {
3147
3175
  j++;
@@ -3150,7 +3178,7 @@ var parseMML = (mml, options = {}) => {
3150
3178
  numStr += body[j];
3151
3179
  j++;
3152
3180
  }
3153
- octave = clamp2(Number.parseInt(numStr, 10) || 4, 0, 8);
3181
+ octave = numStr ? clamp2(Number.parseInt(numStr, 10), 0, 8) : 4;
3154
3182
  } else {
3155
3183
  j++;
3156
3184
  }
@@ -3225,7 +3253,7 @@ var createSequencer = (options) => {
3225
3253
  for (const note of track.notes) {
3226
3254
  const relativeStart = note.startStep - fromStep;
3227
3255
  if (relativeStart < 0) continue;
3228
- const velocity = note.velocity ?? 127;
3256
+ const velocity = note.velocity ?? DEFAULT_PLAYBACK_VELOCITY;
3229
3257
  timeline.push({
3230
3258
  trackId: track.id,
3231
3259
  pitch: note.pitch,
@@ -3844,6 +3872,13 @@ var DAW_CSS = `
3844
3872
 
3845
3873
  .dtm-hidden { display: none !important; }
3846
3874
  .dtm-grow { flex: 1 1 auto; }
3875
+ .dtm-lyric-icon {
3876
+ flex: 0 0 auto;
3877
+ width: 20px;
3878
+ height: 20px;
3879
+ border-radius: 50%;
3880
+ object-fit: cover;
3881
+ }
3847
3882
 
3848
3883
  /* \u2500\u2500\u2500 \u5E83\u5E45\u62E1\u5F35 \u2500\u2500\u2500 */
3849
3884
  @media (min-width: 768px) {
@@ -3886,13 +3921,26 @@ var DAW_CSS = `
3886
3921
  .dtm-player-play:active { transform: translate(2px, 2px); box-shadow: none; }
3887
3922
  .dtm-player-play--stop { background: var(--dtm-danger); }
3888
3923
  .dtm-player-play:disabled { opacity: 0.4; cursor: default; }
3889
- .dtm-player-tempo,
3890
- .dtm-player-time {
3924
+ .dtm-player-beat-row {
3925
+ display: flex;
3926
+ align-items: center;
3927
+ gap: 4px;
3928
+ }
3929
+ .dtm-player-beat-dot {
3930
+ width: 8px;
3931
+ height: 8px;
3932
+ border-radius: 50%;
3933
+ background: var(--dtm-border2);
3934
+ transition: background 0.06s;
3935
+ }
3936
+ .dtm-player-beat-dot--on { background: var(--dtm-primary); }
3937
+ .dtm-player-bar {
3891
3938
  font-family: 'k8x12', monospace;
3892
- font-size: 12px;
3893
- color: var(--dtm-muted);
3939
+ font-size: 11px;
3940
+ color: var(--dtm-text);
3941
+ min-width: 2em;
3942
+ margin-left: 4px;
3894
3943
  }
3895
- .dtm-player-time { color: var(--dtm-text); min-width: 3em; }
3896
3944
  .dtm-player-dots {
3897
3945
  margin-left: auto;
3898
3946
  display: flex;
@@ -3900,6 +3948,40 @@ var DAW_CSS = `
3900
3948
  gap: 6px;
3901
3949
  }
3902
3950
  .dtm-player-dot { width: 8px; height: 8px; display: inline-block; }
3951
+ .dtm-player-mml-header {
3952
+ margin-left: auto;
3953
+ display: flex;
3954
+ align-items: center;
3955
+ gap: 4px;
3956
+ }
3957
+ .dtm-player-mml-link {
3958
+ font-family: 'k8x12', monospace;
3959
+ font-size: 10px;
3960
+ color: var(--dtm-muted);
3961
+ text-decoration: none;
3962
+ white-space: nowrap;
3963
+ }
3964
+ .dtm-player-mml-link:hover { color: var(--dtm-primary); }
3965
+ .dtm-player-emoji {
3966
+ display: inline-flex;
3967
+ align-items: center;
3968
+ justify-content: center;
3969
+ width: 20px;
3970
+ height: 20px;
3971
+ border-radius: 50%;
3972
+ font-size: 18px;
3973
+ line-height: 1;
3974
+ user-select: none;
3975
+ }
3976
+ @keyframes dtm-emoji-jump {
3977
+ 0% { transform: translateY(0); }
3978
+ 35% { transform: translateY(-5px); }
3979
+ 65% { transform: translateY(-5px); }
3980
+ 100% { transform: translateY(0); }
3981
+ }
3982
+ .dtm-player-emoji--jump {
3983
+ animation: dtm-emoji-jump 0.18s ease-out forwards;
3984
+ }
3903
3985
  .dtm-player-chip {
3904
3986
  font-family: 'k8x12', monospace;
3905
3987
  font-size: 9px;
@@ -3908,6 +3990,12 @@ var DAW_CSS = `
3908
3990
  padding: 2px 6px;
3909
3991
  white-space: nowrap;
3910
3992
  }
3993
+ .dtm-player-body {
3994
+ position: relative; /* \u30ED\u30FC\u30C7\u30A3\u30F3\u30B0\u30AA\u30FC\u30D0\u30FC\u30EC\u30A4\u306E\u57FA\u6E96\u3002\u30EC\u30FC\u30F3\u7FA4\u3060\u3051\u3092\u8986\u3046 */
3995
+ display: flex;
3996
+ flex-direction: column;
3997
+ gap: var(--dtm-gap);
3998
+ }
3911
3999
  .dtm-player-lane-row {
3912
4000
  display: flex;
3913
4001
  align-items: stretch;
@@ -3922,6 +4010,12 @@ var DAW_CSS = `
3922
4010
  gap: 2px;
3923
4011
  padding-top: 4px;
3924
4012
  }
4013
+ .dtm-player-lane-label--btn {
4014
+ cursor: pointer;
4015
+ user-select: none;
4016
+ }
4017
+ .dtm-player-lane-label--btn:hover { opacity: 0.7; }
4018
+ .dtm-player-lane-label--muted { opacity: 0.3; }
3925
4019
  .dtm-player-lane-no {
3926
4020
  font-family: 'k8x12', monospace;
3927
4021
  font-size: 9px;
@@ -4004,6 +4098,38 @@ var showLoadingOverlay = (container) => {
4004
4098
  };
4005
4099
  };
4006
4100
 
4101
+ // assets/puyuyu.png
4102
+ var puyuyu_default = "data:image/png;base64,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";
4103
+
4104
+ // assets/rino.png
4105
+ var rino_default = "data:image/png;base64,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";
4106
+
4107
+ // assets/roze.png
4108
+ var roze_default = "data:image/png;base64,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";
4109
+
4110
+ // assets/ruko.png
4111
+ var ruko_default = "data:image/png;base64,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";
4112
+
4113
+ // assets/shiyo.png
4114
+ var shiyo_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYAAAAGACAYAAACkx7W/AAAQAElEQVR4AezVv48d1RUH8OsUQaKhpHERiYIOCxoURwoFSEh0FMiuI2GsdNvwb2y7wRJuTeUG6NyAYnYjIcVAQ2GJAokSuXARS9GLn9eG7Hrfvh8zZ+beez6Iu+v3Zubecz5n7O8fiv/WCSwe3xC5Hm/f7v+Hh4eLltci+L9omwnenMh3f7n3BC04YpWAAFgl43sCBAh0LiAAOh+w9gisFHAhvYAASP8KACBAIKuAAMg6eX0TIJBeQACkfQU0ToBAdgEBkP0N0D8BAmkFBEDa0WucAIGsAs/6FgDPJPwmQIBAMgEBkGzg2iVAgMAzAQHwTMJvAlkE9EngqYAAeArhFwECBLIJCIBsE9cvAQIEngoIgKcQeX7plAABAscCAuDYwU8CBAikExAA6UauYQIEsgqc7lsAnBbxmQABAkkEBECSQWuTAAECpwUEwGkRnwn0KqAvAqcEBMApEB8JECCQRUAAZJm0PgkQIHBKQACcAun3o84IECBwUkAAnPTwiQABAmkEBECaUWuUAIGsAqv67iEAFo+bi1yPtw/9P7L2xeHhYeh68803S8vr6OioRK5om+j5hr75x5uHvv+39i8tItdxC+3+7CEA2tVXOQECBGYUEAAz4juawCQCDiGwQkAArIDxNQECBHoXEAC9T1h/BAgQWCEgAFbA9PO1TggQIHC2gAA428W3BAgQ6F5AAHQ/Yg0SIJBVYF3fAmCdkOsECBDoVEAAdDpYbREgQGCdgABYJ+Q6gVYF1E1gjYAAWAPkMgECBHoVEAC9TlZfBAgQWCMgANYAtXtZ5QQIEDhfQACc7+MqAQIEuhUQAN2OVmMECGQV2LRvAbCplPsIECDQmYAA6Gyg2iFAgMCmAgJgUyn3EWhFQJ0ENhQQABtCuY0AAQK9CQiA3iaqHwIECGwoIAA2hGrnNpUSIEBgMwEBsJmTuwgQINCdgADobqQaIkAgq8C2fQuAbcVGvn+xWJTINXK5k293dHRUItfrL35cItej798qkSt6IJHv5nLv6Pqj97+1f2kRuaLrFwDRwvYnQIBApQICoNLBKIvA1gIeILClgADYEsztBAgQ6EVAAPQySX0QIEBgSwEBsCVYvberjAABAtsJCIDtvNxNgACBbgQEQDej1AgBAlkFdu1bAOwq5zkCBAg0LiAAGh+g8gkQILCrgADYVc5zBGoRUAeBHQUEwI5wHiNAgEDrAgKg9QmqnwABAjsKCIAd4ep5TCUECBDYTUAA7ObmKQIECDQvIACaH6EGCBDIKjC0bwEwVNDzBAgQaFRAADQ6OGUTIEBgqIAAGCroeQJzCTiXwEABATAQ0OMECBBoVUAAtDo5dRMgQGCggAAYCDjf404mQIDAMAEBMMzP0wQIEGhWQAA0OzqFEyCQVWCsvgXAGsn/fPfXErmOjo5K5Prpm49K5Hr0/Vslcr3+4sclcq0Zf/WXI22We0fOdrl3NPDVvXslckXXf2v/0iJyCYDoCdqfAAEClQoIgEoHoywCKwVcIDCSgAAYCdI2BAgQaE1AALQ2MfUSIEBgJAEBMBLkdNs4iQABAuMICIBxHO1CgACB5gQEQHMjUzABAlkFxu5bAIwtaj8CBAg0IiAAGhmUMgkQIDC2gAAYW9R+BKIE7EtgZAEBMDKo7QgQINCKgABoZVLqJECAwMgCAmBk0Ljt7EyAAIFxBQTAuJ52I0CAQDMCAqCZUSmUAIGsAlF9C4AoWfsSIECgcgEBUPmAlEeAAIEoAQEQJWtfAmMJ2IdAkIAACIK1LQECBGoXEAC1T0h9BAgQCBIQAEGw421rJwIECMQICIAYV7sSIECgegEBUP2IFEiAQFaB6L6bD4Bb+5dK5PrjK/slcv30zUclcr3/9kslckW/oK3vf/vOgxK5WvdZPPy2RK5on8i/W8u9o+tvPgCigexPgACBXgUEQK+T1Vf7AjogECwgAIKBbU+AAIFaBQRArZNRFwECBIIFBEAw8O7be5IAAQKxAgIg1tfuBAgQqFZAAFQ7GoURIJBVYKq+BcBU0s4hQIBAZQICoLKBKIcAAQJTCQiAqaSdQ2BTAfcRmEhAAEwE7RgCBAjUJiAAapuIeggQIDCRgACYCHrzY9xJgACBaQQEwDTOTiFAgEB1AgKgupEoiACBrAJT9y0AphZ3HgECBCoREACVDEIZBAgQmFpAAEwt7jwCqwR8T2BiAQEwMbjjCBAgUIuAAKhlEuogQIDAxAICYGLw1ce5QoAAgWkFBMC03k4jQIBANQICoJpRKIQAgawCc/UdHgC39i8tIteVa5+WyDXXYFo594+v7JeW1+07D0rkuvLBFyVyRda+3Dt6ttHv+eN/e0rkeuG1r0rkev/tl0rkCg+A6AHbnwABAgR2ExAAu7l5isB4AnYiMJOAAJgJ3rEECBCYW0AAzD0B5xMgQGAmAQEwE/zvx/oTAQIE5hEQAPO4O5UAAQKzCwiA2UegAAIEsgrM3bcAmHsCzidAgMBMAgJgJnjHEiBAYG4BATD3BJyfV0DnBGYWEAAzD8DxBAgQmEtAAMwl71wCBAjMLCAAZhuAgwkQIDCvgACY19/pBAgQmE1AAMxG72ACBLIK1NK3AKhlEuogQIDAxAICYGJwxxEgQKAWAQFQyyTUkUdApwQqERAAlQxCGQQIEJhaQABMLe48AgQIVCIgACYfhAMJECBQh4AAqGMOqiBAgMDkAgJgcnIHEiCQVaC2vgXAmol89snfSuR6/+2XSuS6fedBiVyP7u+VyBVpv9z7ygdflMi15vWq/nLkbKfY+8q1T0vkqn6AawoUAGuAXCZAgECvAgKg18nqqz4BFRGoTEAAVDYQ5RAgQGAqAQEwlbRzCBAgUJmAAJhsIA4iQIBAXQICoK55qIYAAQKTCQiAyagdRIBAVoFa+xYAtU5GXQQIEAgWEADBwLYnQIBArQICoNbJqKsfAZ0QqFRAAFQ6GGURIEAgWkAARAvbnwABApUKCIDwwTiAAAECdQoIgDrnoioCBAiECwiAcGIHECCQVaD2vgVA7RNSHwECBIIEBEAQrG0JECBQu4AAqH1C6mtXQOUEKhcQAJUPSHkECBCIEhAAUbL2JUCAQOUCAiBsQDYmQIBA3QICoO75qI4AAQJhAgIgjNbGBAhkFWilbwGwZlJX9+6VyPXCa1+VyBVZ+3Lv23celMi1ZjzVX75w8WKJXMsZRK7Id3OKvR/d3yuRq/oXcE2BAmANkMsECBDoVUAA9DpZfc0n4GQCjQgIgEYGpUwCBAiMLSAAxha1HwECBBoREACjD8qGBAgQaENAALQxJ1USIEBgdAEBMDqpDQkQyCrQWt8CoLWJqZcAAQIjCQiAkSBtQ4AAgdYEBEBrE1NvvQIqI9CYgABobGDKJUCAwFgCAmAsSfsQIECgMQEBMNrAbESAAIG2BARAW/NSLQECBEYTEACjUdqIAIGsAq32LQBanZy6CRAgMFBAAAwE9DgBAgRaFRAArU5O3fUIqIRAowICoNHBKZsAAQJDBQTAUEHPEyBAoFEBATB4cDYgQIBAmwICoM25qZoAAQKDBQTAYEIbECCQVaD1vpsPgEf390rkih7wv959t7S8rnzwRWl5me/5799isSiRK9q/9f1feO2rErmaD4DWB6x+AgQIzCUgAOaSd277Ajog0LiAAGh8gMonQIDArgICYFc5zxEgQKBxAQGw8wA9SIAAgbYFBEDb81M9AQIEdhYQADvTeZAAgawCvfQtAHqZpD4IECCwpYAA2BLM7QQIEOhFQAD0Mkl9TCfgJAKdCAiATgapDQIECGwrIAC2FXM/AQIEOhEQAFsP0gMECBDoQ0AA9DFHXRAgQGBrAQGwNZkHCBDIKtBb3wKgt4nqhwABAhsKCIANodxGgACB3gQEQG8T1U+cgJ0JdCYgADobqHYIECCwqYAA2FTKfQQIEOhMQABsPFA3EiBAoC8BAdDXPHVDgACBjQUEwMZUbiRAIKtAr30LgDWT/ffRUYlcv77xRml5Xbh4sbS87v34Y4lcLc92Wfuavx6DL9/av1Qi1+ACO99AAHQ+YO0RIEBglYAAWCXjewLPBPwm0KmAAOh0sNoiQIDAOgEBsE7IdQIECHQqIADWDtYNBAgQ6FNAAPQ5V10RIEBgrYAAWEvkBgIEsgr03rcA6H3C+iNAgMAKAQGwAsbXBAgQ6F1AAPQ+Yf3tLuBJAp0LCIDOB6w9AgQIrBIQAKtkfE+AAIHOBQTAygG7QIAAgb4FBEDf89UdAQIEVgoIgJU0LhAgkFUgS98CIMuk9UmAAIFTAgLgFIiPBAgQyCIgALJMWp+bC7iTQBIBAZBk0NokQIDAaQEBcFrEZwIECCQREADPDdoXBAgQyCEgAHLMWZcECBB4TkAAPEfiCwIEsgpk6zs8AK7u3bsQuW7feVAi1y8//FAi18vvvFNaXouffy4tr+i/8C3Pdln73Rs3SuSK9o/8t2G5d3T90fuHB0B0A/YnQIAAgd0EBMBubp7qUUBPBJIJCIBkA9cuAQIEngkIgGcSfhMgQCCZgAD4beD+QIAAgVwCAiDXvHVLgACB3wQEwG8U/kCAQFaBrH0LgKyT1zcBAukFBED6VwAAAQJZBQRA1snr+3cBfyKQVEAAJB28tgkQICAAvAMECBBIKiAAStLJa5sAgfQCAiD9KwCAAIGsAgIg6+T1TYBAyU4gALK/AfonQCCtgABIO3qNEyCQXUAAZH8DMvevdwLJBQRA8hdA+wQI5BUQAHlnr3MCBJILJA6A5JPXPgEC6QUEQPpXAAABAlkFBEDWyeubQGIBrR8LNB8AV/fuXYhcn339dYlcD+/fLy2v49eo3Z+XXn21tLyi3512J3tc+eN/G0rkOj4l7ufi4bclcjUfAHH0diZAgEDfAgKg7/nq7iwB3xEg8ERAADxh8IMAAQL5BARAvpnrmAABAk8EEgbAk779IECAQHoBAZD+FQBAgEBWAQGQdfL6JpBQQMsnBQTASQ+fCBAgkEZAAKQZtUYJECBwUkAAnPTwqWcBvREgcEJAAJzg8IEAAQJ5BARAnlnrlAABAicEEgXAib59IECAQHoBAZD+FQBAgEBWAQGQdfL6JpBIQKtnCwiAs118S4AAge4FBED3I9YgAQIEzhYQAGe7+LYnAb0QIHCmgAA4k8WXBAgQ6F9AAPQ/Yx0SIEDgTIEEAXBm374kQIBAegEBkP4VAECAQFYBAZB18vomkEBAi+cLNB8Ah4eHi8h1/fr1Erlu3L1bItf54x9+9e6XX5bI9ejzz0vLK9JmuXfku7Pc+y/Xrl2IXH/68z9K5Hr8b0OJXMP/Bs27Q/MBMC+f0wkQINCugABod3YqXyfgOgEC5woIgHN5XCRAgEC/AgKg39nqjAABAucKdBwA5/btIgECBNILCID0rwAAAgSyCgiArJPXN4GOBbS2mYAA2MzJXQQIEOhOQAB0N1INESBAYDMBAbCZk7taElArAQIbCQiAjZjcPEdnaQAABGhJREFURIAAgf4EBEB/M9URAQIENhLoMAA26ttNBAgQSC8gANK/AgAIEMgqIACyTl7fBDoU0NJ2AgJgOy93EyBAoBsBAdDNKDVCgACB7QQEwHZe7q5ZQG0ECGwlIAC24nIzAQIE+hEQAP3MUicECBDYSqCjANiqbzcTIEAgvYAASP8KACBAIKuAAMg6eX0T6EhAK7sJNB8ABwcHJXLtxrr5U9evXy+R68bdu6XltblknXdG20d3fXh4uIhc0fVH7794+G2JXNH1Nx8A0UD2J0CAQK8CAqDXyWbqS68ECOwkIAB2YvMQAQIE2hcQAO3PUAcECBDYSaCDANipbw8RIEAgvYAASP8KACBAIKuAAMg6eX0T6EBAC8MEBMAwP08TIECgWQEB0OzoFE6AAIFhAgJgmJ+n5xRwNgECgwQEwCA+DxMgQKBdAQHQ7uxUToAAgUECDQfAoL49TIAAgfQCAiD9KwCAAIGsAgIg6+T1TaBhAaWPIyAAxnG0CwECBJoTEADNjUzBBAgQGEdAAIzjaJcpBZxFgMAoAgJgFEabECBAoD0BAdDezFRMgACBUQQaDIBR+rYJAQIE0gsIgPSvAAACBLIKCICsk9c3gQYFlDyuQPMB8OHlyyVyvXnxYolcBwcHJXLdvHmzRK5xX8fnd7t3+3aJXB8dHpbI9XxH434TOdvl3uNWO/1ukX93l3uXX18ukevR/b0SuZoPgOlfKScSIECgDwEB0Mccc3ShSwIERhUQAKNy2owAAQLtCAiAdmalUgIECIwq0FAAjNq3zQgQIJBeQACkfwUAECCQVUAAZJ28vgk0JKDUGAEBEONqVwIECFQvIACqH5ECCRAgECMgAGJc7TqmgL0IEAgREAAhrDYlQIBA/QICoP4ZqZAAAQIhAg0EQEjfNiVAgEB6AQGQ/hUAQIBAVgEBkHXy+ibQgIASYwUEQKyv3QkQIFCtgACodjQKI0CAQKyAAIj1tfsQAc8SIBAqIABCeW1OgACBegUEQL2zURkBAgRCBSoOgNC+bU6AAIH0AgIg/SsAgACBrAICIOvk9U2gYgGlTSMQHgD//OSTReS6/N57JXJFj+HDy5dL5Iqu/+bNmyVyRdf/919+KZEr0ma5d7TPwcFBiVz//e67ErmifVrfPzwAWgdSPwECBHoVEAC9TrblvtROgMAkAgJgEmaHECBAoD4BAVDfTFREgACBSQQqDIBJ+nYIAQIE0gsIgPSvAAACBLIKCICsk9c3gQoFlDStgACY1ttpBAgQqEZAAFQzCoUQIEBgWgEBMK23084TcI0AgUkFBMCk3A4jQIBAPQICoJ5ZqIQAAQKTClQUAJP27TACBAikFxAA6V8BAAQIZBUQAFknr28CFQkoZR4BATCPu1MJECAwu4AAmH0ECiBAgMA8AgJgHnen/r+APxMgMIuAAJiF3aEECBCYX0AAzD8DFRAgQGAWgQoCYJa+HUqAAIH0AgIg/SsAgACBrAICIOvk9U2gAgElzCvwPwAAAP//kMHGRAAAAAZJREFUAwASfg7S5EMvVgAAAABJRU5ErkJggg==";
4115
+
4116
+ // assets/teto.png
4117
+ var teto_default = "data:image/png;base64,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";
4118
+
4119
+ // assets/tsukuyomi.png
4120
+ var tsukuyomi_default = "data:image/png;base64,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";
4121
+
4122
+ // src/voice-images.ts
4123
+ var VOICE_IMAGES = {
4124
+ puyuyu: puyuyu_default,
4125
+ rino: rino_default,
4126
+ roze: roze_default,
4127
+ ruko: ruko_default,
4128
+ shiyo: shiyo_default,
4129
+ teto: teto_default,
4130
+ tsukuyomi: tsukuyomi_default
4131
+ };
4132
+
4007
4133
  // src/daw.ts
4008
4134
  var BASE_STEP_WIDTH = 0.5;
4009
4135
  var BASE_KEY_HEIGHT = 15;
@@ -4170,7 +4296,7 @@ var mountDAW = (target, options = {}) => {
4170
4296
  tracks: trackConfigs,
4171
4297
  drumPatternNames: Object.keys(drumPatterns),
4172
4298
  defaultDrumPattern: drumPatterns.dance ? "dance" : Object.keys(drumPatterns)[0] ?? "none",
4173
- defaultBpm: options.defaultBpm ?? 120,
4299
+ defaultBpm: options.defaultBpm ?? DEFAULT_BPM,
4174
4300
  showMidi,
4175
4301
  showChord
4176
4302
  });
@@ -4185,7 +4311,7 @@ var mountDAW = (target, options = {}) => {
4185
4311
  const leftPaddingSteps = renderConfig.stepsPerBar * 16;
4186
4312
  let zoomX = 100;
4187
4313
  let zoomY = 100;
4188
- let bpm = options.defaultBpm ?? 120;
4314
+ let bpm = options.defaultBpm ?? DEFAULT_BPM;
4189
4315
  let masterVolume = 50;
4190
4316
  let drumVolume = 80;
4191
4317
  let currentDrumPattern = refs.drumSelect.value;
@@ -4230,9 +4356,10 @@ var mountDAW = (target, options = {}) => {
4230
4356
  lyrics: "",
4231
4357
  lyricModel: "",
4232
4358
  // 既定は「なし」(歌わない)
4233
- vocalVolume: 300,
4359
+ vocalVolume: DEFAULT_VOCAL_VOLUME,
4234
4360
  vocalGate: 100,
4235
- vocalPan: 64
4361
+ vocalPan: 64,
4362
+ vocalOctave: 0
4236
4363
  }));
4237
4364
  };
4238
4365
  const buildLyricsMap = () => {
@@ -4249,6 +4376,7 @@ var mountDAW = (target, options = {}) => {
4249
4376
  volume: t.vocalVolume,
4250
4377
  gate: t.vocalGate,
4251
4378
  pan: t.vocalPan,
4379
+ octave: t.vocalOctave,
4252
4380
  syllables
4253
4381
  });
4254
4382
  });
@@ -4767,6 +4895,13 @@ var mountDAW = (target, options = {}) => {
4767
4895
  setDrawOffset(currentOffsetX, currentOffsetY);
4768
4896
  redrawAll();
4769
4897
  };
4898
+ const getViewState = () => ({
4899
+ zoomX,
4900
+ zoomY,
4901
+ decomposeChord: refs.decomposeChordToggle.checked,
4902
+ ignoreChordHeavy: refs.ignoreChordHeavyToggle.checked
4903
+ });
4904
+ const notifyViewState = () => options.onViewStateChange?.(getViewState());
4770
4905
  const dispatchNote = (trackId, pitch, trackVol, velocity, when, duration) => {
4771
4906
  const volume = trackVol / 100 * (velocity / 127) * (masterVolume / 100);
4772
4907
  options.onPlayNote?.({ trackId, pitch, velocity, volume, when, duration });
@@ -4826,7 +4961,8 @@ var mountDAW = (target, options = {}) => {
4826
4961
  const sorted = [...trackState?.core.getNotes() ?? []].sort(
4827
4962
  (a, b) => a.startStep - b.startStep
4828
4963
  );
4829
- const gate = (lt.gate ?? 100) / 100;
4964
+ const gate = (lt.gate ?? DEFAULT_GATE) / 100;
4965
+ const semis = (lt.octave ?? 0) * 12;
4830
4966
  const count = Math.min(sorted.length, lt.syllables.length);
4831
4967
  const notes = [];
4832
4968
  for (let i = 0; i < count; i++) {
@@ -4834,22 +4970,22 @@ var mountDAW = (target, options = {}) => {
4834
4970
  if (n.startStep < fromStep) continue;
4835
4971
  notes.push({
4836
4972
  syllable: lt.syllables[i],
4837
- pitch: n.pitch,
4973
+ pitch: n.pitch + semis,
4838
4974
  startSec: (n.startStep - fromStep) * secondsPerStep,
4839
4975
  durationSec: n.durationSteps * secondsPerStep * gate
4840
4976
  });
4841
4977
  }
4842
4978
  return {
4843
4979
  model: lt.model,
4844
- volume: vocalVolumeToGain(lt.volume ?? 300) * (masterVolume / 100),
4845
- pan: panToStereo(lt.pan ?? 64),
4980
+ volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME) * (masterVolume / 100),
4981
+ pan: panToStereo(lt.pan ?? DEFAULT_PAN),
4846
4982
  notes
4847
4983
  };
4848
4984
  }) : [];
4849
4985
  const voices = options.singingVoices;
4850
4986
  const streaming = !!voices && streamTracks.some((t) => t.notes.length > 0);
4851
4987
  if (streaming && voices) {
4852
- const overlay = showLoadingOverlay(target);
4988
+ const overlay = showLoadingOverlay(refs.rollContainer);
4853
4989
  try {
4854
4990
  await voices.loadModels(streamTracks.map((t) => t.model));
4855
4991
  await voices.warm(streamTracks);
@@ -4942,12 +5078,25 @@ var mountDAW = (target, options = {}) => {
4942
5078
  <div class="dtm-row">
4943
5079
  <span class="dtm-label">\u266A \u6B4C\u8A5E</span>
4944
5080
  <select class="dtm-select" data-dtm="lyric-model" aria-label="\u6B4C\u5531\u30E2\u30C7\u30EB"></select>
5081
+ <img class="dtm-lyric-icon dtm-hidden" data-dtm="lyric-icon" width="20" height="20" alt="" draggable="false">
5082
+ <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">
5083
+ <option value="2">+2 oct</option>
5084
+ <option value="1">+1 oct</option>
5085
+ <option value="0">\xB10 oct</option>
5086
+ <option value="-1">-1 oct</option>
5087
+ <option value="-2">-2 oct</option>
5088
+ </select>
4945
5089
  <span class="dtm-label dtm-grow" data-dtm="lyric-count" style="text-align:right"></span>
4946
5090
  </div>
5091
+ <div class="dtm-row dtm-hidden" data-dtm="lyric-terms" style="font-size:10px;gap:4px;color:var(--dtm-warn)">
5092
+ <span>\u4F7F\u7528\u6642\u306B\u306F</span>
5093
+ <a data-dtm="lyric-terms-link" target="_blank" rel="noopener" style="color:var(--dtm-primary);text-decoration:underline"></a>
5094
+ <span>\u306E\u5229\u7528\u898F\u7D04\u306B\u5F93\u3063\u3066\u304F\u3060\u3055\u3044</span>
5095
+ </div>
4947
5096
  <div class="dtm-row" data-dtm="lyric-body" style="flex-direction:column;align-items:stretch">
4948
5097
  <div class="dtm-row">
4949
5098
  <span class="dtm-label">\u58F0\u91CF</span>
4950
- <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\u5B9A300\uFF09">
5099
+ <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">
4951
5100
  <span class="dtm-label" data-dtm="lyric-vol-label"></span>
4952
5101
  </div>
4953
5102
  <div class="dtm-row">
@@ -4961,6 +5110,12 @@ var mountDAW = (target, options = {}) => {
4961
5110
  const lyricModelSel = lyricDiv.querySelector(
4962
5111
  '[data-dtm="lyric-model"]'
4963
5112
  );
5113
+ const lyricOctaveSel = lyricDiv.querySelector(
5114
+ '[data-dtm="lyric-octave"]'
5115
+ );
5116
+ const lyricIcon = lyricDiv.querySelector(
5117
+ '[data-dtm="lyric-icon"]'
5118
+ );
4964
5119
  const lyricBody = lyricDiv.querySelector(
4965
5120
  '[data-dtm="lyric-body"]'
4966
5121
  );
@@ -4982,6 +5137,12 @@ var mountDAW = (target, options = {}) => {
4982
5137
  const lyricPanLabel = lyricDiv.querySelector(
4983
5138
  '[data-dtm="lyric-pan-label"]'
4984
5139
  );
5140
+ const lyricTerms = lyricDiv.querySelector(
5141
+ '[data-dtm="lyric-terms"]'
5142
+ );
5143
+ const lyricTermsLink = lyricDiv.querySelector(
5144
+ '[data-dtm="lyric-terms-link"]'
5145
+ );
4985
5146
  const fmtPan = (pan) => pan === 64 ? "C" : pan < 64 ? `L${64 - pan}` : `R${pan - 64}`;
4986
5147
  const addOpt = (value, label) => {
4987
5148
  const o = document.createElement("option");
@@ -4995,6 +5156,7 @@ var mountDAW = (target, options = {}) => {
4995
5156
  addOpt(active.lyricModel, lyricModelLabel(active.lyricModel));
4996
5157
  }
4997
5158
  lyricModelSel.value = active.lyricModel;
5159
+ lyricOctaveSel.value = String(active.vocalOctave);
4998
5160
  lyricInput.value = active.lyrics;
4999
5161
  lyricVol.value = String(active.vocalVolume);
5000
5162
  lyricVolLabel.textContent = String(active.vocalVolume);
@@ -5004,15 +5166,43 @@ var mountDAW = (target, options = {}) => {
5004
5166
  const n = normalizeLyrics(lyricInput.value).length;
5005
5167
  lyricCount.textContent = active.lyricModel && n > 0 ? `${n}\u97F3\u7BC0` : "";
5006
5168
  };
5169
+ const syncLyricTerms = () => {
5170
+ const url = active.lyricModel ? KOE_VOICEBANK_TERMS[active.lyricModel] : void 0;
5171
+ if (url) {
5172
+ const label = lyricModelLabel(active.lyricModel);
5173
+ lyricTermsLink.textContent = `${label}UTAU\u97F3\u6E90`;
5174
+ lyricTermsLink.href = url;
5175
+ lyricTerms.classList.remove("dtm-hidden");
5176
+ } else {
5177
+ lyricTerms.classList.add("dtm-hidden");
5178
+ }
5179
+ };
5180
+ const syncLyricIcon = () => {
5181
+ const imgKey = active.lyricModel ? VOICE_IMAGE_KEY[active.lyricModel.toLowerCase()] : void 0;
5182
+ const src = imgKey ? VOICE_IMAGES[imgKey] : void 0;
5183
+ if (src) {
5184
+ lyricIcon.src = src;
5185
+ lyricIcon.classList.remove("dtm-hidden");
5186
+ } else {
5187
+ lyricIcon.removeAttribute("src");
5188
+ lyricIcon.classList.add("dtm-hidden");
5189
+ }
5190
+ };
5007
5191
  const syncLyricVisibility = () => {
5008
5192
  lyricBody.style.display = active.lyricModel ? "" : "none";
5193
+ lyricOctaveSel.style.display = active.lyricModel ? "" : "none";
5009
5194
  updateLyricCount();
5195
+ syncLyricTerms();
5196
+ syncLyricIcon();
5010
5197
  };
5011
5198
  syncLyricVisibility();
5012
5199
  lyricModelSel.addEventListener("change", () => {
5013
5200
  active.lyricModel = lyricModelSel.value;
5014
5201
  syncLyricVisibility();
5015
5202
  });
5203
+ lyricOctaveSel.addEventListener("change", () => {
5204
+ active.vocalOctave = Number.parseInt(lyricOctaveSel.value, 10);
5205
+ });
5016
5206
  lyricInput.addEventListener("input", () => {
5017
5207
  active.lyrics = lyricInput.value;
5018
5208
  updateLyricCount();
@@ -5161,12 +5351,14 @@ var mountDAW = (target, options = {}) => {
5161
5351
  model: t.lyricModel.trim(),
5162
5352
  vol: t.vocalVolume,
5163
5353
  gate: t.vocalGate,
5164
- pan: t.vocalPan
5354
+ pan: t.vocalPan,
5355
+ oct: t.vocalOctave
5165
5356
  })).filter((x) => x.model.length > 0 && x.text.length > 0).map((x) => {
5166
5357
  const params = [
5167
- x.vol === 300 ? "" : `v${x.vol}`,
5358
+ x.vol === DEFAULT_VOCAL_VOLUME ? "" : `v${x.vol}`,
5168
5359
  x.gate === 100 ? "" : `q${x.gate}`,
5169
- x.pan === 64 ? "" : `p${x.pan}`
5360
+ x.pan === 64 ? "" : `p${x.pan}`,
5361
+ x.oct === 0 ? "" : `o${x.oct}`
5170
5362
  ].filter((s) => s.length > 0).join(" ");
5171
5363
  const head = params ? `${x.model} ${params}` : x.model;
5172
5364
  return `@@${x.i} ${head} ${x.text}`;
@@ -5247,6 +5439,7 @@ var mountDAW = (target, options = {}) => {
5247
5439
  if (meta.drum && drumPatterns[meta.drum]) {
5248
5440
  currentDrumPattern = meta.drum;
5249
5441
  refs.drumSelect.value = meta.drum;
5442
+ options.onDrumChange?.(meta.drum);
5250
5443
  }
5251
5444
  if (meta.volume !== void 0) {
5252
5445
  masterVolume = meta.volume;
@@ -5256,9 +5449,10 @@ var mountDAW = (target, options = {}) => {
5256
5449
  for (const t of trackStates) {
5257
5450
  t.lyrics = "";
5258
5451
  t.lyricModel = "";
5259
- t.vocalVolume = 300;
5452
+ t.vocalVolume = DEFAULT_VOCAL_VOLUME;
5260
5453
  t.vocalGate = 100;
5261
5454
  t.vocalPan = 64;
5455
+ t.vocalOctave = 0;
5262
5456
  }
5263
5457
  lyrics?.forEach((lt, idx) => {
5264
5458
  const t = trackStates[idx];
@@ -5268,6 +5462,7 @@ var mountDAW = (target, options = {}) => {
5268
5462
  t.vocalVolume = lt.volume;
5269
5463
  t.vocalGate = lt.gate;
5270
5464
  t.vocalPan = lt.pan;
5465
+ t.vocalOctave = lt.octave ?? 0;
5271
5466
  });
5272
5467
  for (const p of placements) {
5273
5468
  const t = trackStates[p.trackIndex];
@@ -5419,25 +5614,32 @@ var mountDAW = (target, options = {}) => {
5419
5614
  refs.zoomXIn.addEventListener("click", () => {
5420
5615
  zoomX = Math.min(200, zoomX + 25);
5421
5616
  applyZoomX();
5617
+ notifyViewState();
5422
5618
  });
5423
5619
  refs.zoomXOut.addEventListener("click", () => {
5424
5620
  zoomX = Math.max(25, zoomX - 25);
5425
5621
  applyZoomX();
5622
+ notifyViewState();
5426
5623
  });
5427
5624
  refs.zoomYIn.addEventListener("click", () => {
5428
5625
  zoomY = Math.min(200, zoomY + 25);
5429
5626
  applyZoomY();
5627
+ notifyViewState();
5430
5628
  });
5431
5629
  refs.zoomYOut.addEventListener("click", () => {
5432
5630
  zoomY = Math.max(50, zoomY - 25);
5433
5631
  applyZoomY();
5632
+ notifyViewState();
5434
5633
  });
5634
+ refs.decomposeChordToggle.addEventListener("change", notifyViewState);
5635
+ refs.ignoreChordHeavyToggle.addEventListener("change", notifyViewState);
5435
5636
  refs.masterVolume.addEventListener("input", () => {
5436
5637
  masterVolume = Number.parseInt(refs.masterVolume.value, 10) || 0;
5437
5638
  refs.masterVolumeLabel.textContent = `${masterVolume}%`;
5438
5639
  });
5439
5640
  refs.drumSelect.addEventListener("change", () => {
5440
5641
  currentDrumPattern = refs.drumSelect.value;
5642
+ options.onDrumChange?.(currentDrumPattern);
5441
5643
  });
5442
5644
  refs.drumVolume.addEventListener("input", () => {
5443
5645
  drumVolume = Number.parseInt(refs.drumVolume.value, 10) || 0;
@@ -5630,6 +5832,30 @@ var mountDAW = (target, options = {}) => {
5630
5832
  setInstrument: (name) => {
5631
5833
  currentInstrument = name;
5632
5834
  },
5835
+ getDrum: () => currentDrumPattern,
5836
+ setDrum: (name) => {
5837
+ if (name !== "none" && !drumPatterns[name]) return;
5838
+ currentDrumPattern = name;
5839
+ refs.drumSelect.value = name;
5840
+ options.onDrumChange?.(name);
5841
+ },
5842
+ getViewState,
5843
+ setViewState: (state) => {
5844
+ if (typeof state.zoomX === "number") {
5845
+ zoomX = clamp3(state.zoomX, 25, 200);
5846
+ applyZoomX();
5847
+ }
5848
+ if (typeof state.zoomY === "number") {
5849
+ zoomY = clamp3(state.zoomY, 50, 200);
5850
+ applyZoomY();
5851
+ }
5852
+ if (typeof state.decomposeChord === "boolean") {
5853
+ refs.decomposeChordToggle.checked = state.decomposeChord;
5854
+ }
5855
+ if (typeof state.ignoreChordHeavy === "boolean") {
5856
+ refs.ignoreChordHeavyToggle.checked = state.ignoreChordHeavy;
5857
+ }
5858
+ },
5633
5859
  loadMML,
5634
5860
  loadMIDI,
5635
5861
  exportMIDI: exportMIDI2,
@@ -5755,11 +5981,6 @@ var STEPS_PER_BEAT3 = 48;
5755
5981
  var STEPS_PER_BAR = 192;
5756
5982
  var DEFAULT_TRACK_COLORS = ["#00e436", "#29adff", "#ff77a8", "#ffec27"];
5757
5983
  var activePlayer = null;
5758
- var formatTime = (seconds) => {
5759
- const m = Math.floor(seconds / 60);
5760
- const s = Math.floor(seconds % 60);
5761
- return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
5762
- };
5763
5984
  var freqFromPitch = (pitch) => 440 * 2 ** ((pitch - 69) / 12);
5764
5985
  var mountMmlPlayer = (target, mml, options = {}) => {
5765
5986
  injectStyles(target.ownerDocument ?? document);
@@ -5774,7 +5995,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
5774
5995
  collectLyrics: true
5775
5996
  });
5776
5997
  const lyricTracks = lyrics ?? /* @__PURE__ */ new Map();
5777
- const bpm = parsedBpm ?? options.defaultBpm ?? 120;
5998
+ const bpm = parsedBpm ?? options.defaultBpm ?? DEFAULT_BPM;
5778
5999
  const drumPatternDict = options.drumPatterns ?? DRUM_PATTERNS;
5779
6000
  const drumPattern = meta.drum ? drumPatternDict[meta.drum] ?? null : null;
5780
6001
  const trackVolume = meta.volume ?? options.volume ?? 100;
@@ -5889,32 +6110,118 @@ var mountMmlPlayer = (target, mml, options = {}) => {
5889
6110
  playBtn.className = "dtm-player-play";
5890
6111
  playBtn.innerHTML = icon("play", 12);
5891
6112
  playBtn.disabled = trackIndices.length === 0;
5892
- const tempoEl = doc.createElement("span");
5893
- tempoEl.className = "dtm-player-tempo";
5894
- tempoEl.textContent = `\u2669=${bpm}`;
5895
- const timeEl = doc.createElement("span");
5896
- timeEl.className = "dtm-player-time";
5897
- timeEl.textContent = "00:00";
6113
+ const mmlHeader = doc.createElement("div");
6114
+ mmlHeader.className = "dtm-player-mml-header";
6115
+ const emojiEls = [];
6116
+ const emojiByTrack = /* @__PURE__ */ new Map();
6117
+ for (const index of trackIndices) {
6118
+ const em = doc.createElement("span");
6119
+ em.className = "dtm-player-emoji";
6120
+ em.style.backgroundColor = colorOf(index);
6121
+ em.textContent = "\u{1F97A}";
6122
+ mmlHeader.appendChild(em);
6123
+ emojiEls.push(em);
6124
+ emojiByTrack.set(index, em);
6125
+ }
6126
+ const promotedToImage = /* @__PURE__ */ new Set();
6127
+ for (const [index, lt] of lyricTracks) {
6128
+ const em = emojiByTrack.get(index);
6129
+ if (!em) continue;
6130
+ const imgKey = VOICE_IMAGE_KEY[lt.model.toLowerCase()];
6131
+ const src = imgKey ? VOICE_IMAGES[imgKey] : void 0;
6132
+ if (!src) continue;
6133
+ const img = doc.createElement("img");
6134
+ img.src = src;
6135
+ img.width = 20;
6136
+ img.height = 20;
6137
+ img.style.borderRadius = "50%";
6138
+ img.style.objectFit = "cover";
6139
+ img.draggable = false;
6140
+ promotedToImage.add(em);
6141
+ em.textContent = "";
6142
+ em.appendChild(img);
6143
+ }
6144
+ const JUMP_DEDUPE_MS = 50;
6145
+ const lastJumpAt = /* @__PURE__ */ new WeakMap();
6146
+ const jumpEmoji = (em) => {
6147
+ const now = performance.now();
6148
+ const prev = lastJumpAt.get(em);
6149
+ if (prev !== void 0 && now - prev < JUMP_DEDUPE_MS) return;
6150
+ lastJumpAt.set(em, now);
6151
+ em.classList.remove("dtm-player-emoji--jump");
6152
+ void em.offsetWidth;
6153
+ em.classList.add("dtm-player-emoji--jump");
6154
+ };
6155
+ const jumpTimers = [];
6156
+ const jumpEmojiAt = (em, when) => {
6157
+ if (when <= 0) {
6158
+ jumpEmoji(em);
6159
+ return;
6160
+ }
6161
+ jumpTimers.push(setTimeout(() => jumpEmoji(em), when * 1e3));
6162
+ };
6163
+ const clearJumpTimers = () => {
6164
+ for (const t of jumpTimers) clearTimeout(t);
6165
+ jumpTimers.length = 0;
6166
+ };
6167
+ const blinkTimers = [];
6168
+ const scheduleBlink = (em) => {
6169
+ const delay = 2e3 + Math.random() * 5e3;
6170
+ const t = setTimeout(() => {
6171
+ if (promotedToImage.has(em)) return;
6172
+ em.textContent = "\u{1F60C}";
6173
+ const t2 = setTimeout(
6174
+ () => {
6175
+ if (promotedToImage.has(em)) return;
6176
+ em.textContent = "\u{1F97A}";
6177
+ scheduleBlink(em);
6178
+ },
6179
+ 200 + Math.random() * 150
6180
+ );
6181
+ blinkTimers.push(t2);
6182
+ }, delay);
6183
+ blinkTimers.push(t);
6184
+ };
6185
+ for (const em of emojiEls) scheduleBlink(em);
5898
6186
  const dots = doc.createElement("div");
5899
6187
  dots.className = "dtm-player-dots";
6188
+ dots.style.display = "none";
5900
6189
  for (const index of trackIndices) {
5901
6190
  const dot = doc.createElement("span");
5902
6191
  dot.className = "dtm-player-dot";
5903
6192
  dot.style.backgroundColor = colorOf(index);
5904
6193
  dots.appendChild(dot);
5905
6194
  }
5906
- head.append(playBtn, tempoEl, timeEl);
5907
- const addChip = (label) => {
6195
+ const beatRow = doc.createElement("div");
6196
+ beatRow.className = "dtm-player-beat-row";
6197
+ const beatDots = [];
6198
+ for (let i = 0; i < 4; i++) {
6199
+ const d = doc.createElement("span");
6200
+ d.className = "dtm-player-beat-dot";
6201
+ beatRow.appendChild(d);
6202
+ beatDots.push(d);
6203
+ }
6204
+ const barEl = doc.createElement("span");
6205
+ barEl.className = "dtm-player-bar";
6206
+ barEl.textContent = "-";
6207
+ beatRow.appendChild(barEl);
6208
+ const chips = [];
6209
+ const makeChip = (label) => {
5908
6210
  const chip = doc.createElement("span");
5909
6211
  chip.className = "dtm-player-chip";
5910
6212
  chip.textContent = label;
5911
- head.appendChild(chip);
6213
+ chips.push(chip);
6214
+ return chip;
5912
6215
  };
5913
- if (meta.instrument) addChip(`\u266A ${meta.instrument}`);
5914
- if (meta.drum) addChip(`\u{1F941} ${meta.drum}${drumPattern ? "" : " (?)"}`);
5915
- if (meta.volume !== void 0) addChip(`\u{1F50A} ${meta.volume}%`);
5916
- head.appendChild(dots);
6216
+ if (meta.instrument) makeChip(`\u266A ${meta.instrument}`);
6217
+ if (meta.drum) makeChip(`\u{1F941} ${meta.drum}${drumPattern ? "" : " (?)"}`);
6218
+ if (meta.volume !== void 0) makeChip(`\u{1F50A} ${meta.volume}%`);
6219
+ head.append(playBtn, beatRow, ...chips, dots, mmlHeader);
5917
6220
  root.appendChild(head);
6221
+ const body = doc.createElement("div");
6222
+ body.className = "dtm-player-body";
6223
+ root.appendChild(body);
6224
+ const mutedTracks = /* @__PURE__ */ new Set();
5918
6225
  const laneViews = [];
5919
6226
  for (const index of trackIndices) {
5920
6227
  const lyricTrack = lyricTracks.get(index);
@@ -5922,7 +6229,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
5922
6229
  const row = doc.createElement("div");
5923
6230
  row.className = "dtm-player-lane-row";
5924
6231
  const label = doc.createElement("div");
5925
- label.className = "dtm-player-lane-label";
6232
+ label.className = "dtm-player-lane-label dtm-player-lane-label--btn";
5926
6233
  const swatch = doc.createElement("span");
5927
6234
  swatch.className = "dtm-player-dot";
5928
6235
  swatch.style.backgroundColor = colorOf(index);
@@ -5930,13 +6237,22 @@ var mountMmlPlayer = (target, mml, options = {}) => {
5930
6237
  no.className = "dtm-player-lane-no";
5931
6238
  no.textContent = `@${index}`;
5932
6239
  label.append(swatch, no);
6240
+ label.addEventListener("click", () => {
6241
+ if (mutedTracks.has(index)) {
6242
+ mutedTracks.delete(index);
6243
+ label.classList.remove("dtm-player-lane-label--muted");
6244
+ } else {
6245
+ mutedTracks.add(index);
6246
+ label.classList.add("dtm-player-lane-label--muted");
6247
+ }
6248
+ });
5933
6249
  const lane = doc.createElement("div");
5934
6250
  lane.className = "dtm-player-lane";
5935
6251
  lane.style.setProperty("--tk", colorOf(index));
5936
6252
  const laneTokens = [];
5937
6253
  if (isLyricLane) {
5938
6254
  const notes = placements.filter((p) => p.trackIndex === index).sort((a, b) => a.startStep - b.startStep);
5939
- const gateScale = (lyricTrack.gate ?? 100) / 100;
6255
+ const gateScale = (lyricTrack.gate ?? DEFAULT_GATE) / 100;
5940
6256
  const breaks = new Set(lyricTrack.lineBreaks ?? []);
5941
6257
  if (lyricTrack.metaText) {
5942
6258
  const metaEl = doc.createElement("span");
@@ -5983,9 +6299,46 @@ var mountMmlPlayer = (target, mml, options = {}) => {
5983
6299
  }
5984
6300
  }
5985
6301
  row.append(label, lane);
5986
- root.appendChild(row);
6302
+ body.appendChild(row);
5987
6303
  laneViews.push({ lane, tokens: laneTokens });
5988
6304
  }
6305
+ const termsModels = [
6306
+ ...new Set([...lyricTracks.values()].map((lt) => lt.model))
6307
+ ].filter((model) => KOE_VOICEBANK_TERMS[model]);
6308
+ if (termsModels.length > 0) {
6309
+ const termsDiv = doc.createElement("div");
6310
+ termsDiv.className = "dtm-player-terms";
6311
+ termsDiv.style.fontSize = "10px";
6312
+ termsDiv.style.color = "var(--dtm-warn)";
6313
+ termsDiv.style.display = "flex";
6314
+ termsDiv.style.flexDirection = "column";
6315
+ termsDiv.style.gap = "4px";
6316
+ termsDiv.style.marginTop = "4px";
6317
+ termsDiv.style.padding = "0 4px";
6318
+ for (const model of termsModels) {
6319
+ const termsRow = doc.createElement("div");
6320
+ termsRow.style.display = "flex";
6321
+ termsRow.style.alignItems = "center";
6322
+ termsRow.style.gap = "4px";
6323
+ termsRow.style.flexWrap = "wrap";
6324
+ const label = KOE_VOICEBANK_LABELS[model] ?? model;
6325
+ const url = KOE_VOICEBANK_TERMS[model];
6326
+ const span1 = doc.createElement("span");
6327
+ span1.textContent = "\u4F7F\u7528\u6642\u306B\u306F";
6328
+ const a = doc.createElement("a");
6329
+ a.textContent = `${label}UTAU\u97F3\u6E90`;
6330
+ a.href = url;
6331
+ a.target = "_blank";
6332
+ a.rel = "noopener";
6333
+ a.style.color = "var(--dtm-primary)";
6334
+ a.style.textDecoration = "underline";
6335
+ const span2 = doc.createElement("span");
6336
+ span2.textContent = "\u306E\u5229\u7528\u898F\u7D04\u306B\u5F93\u3063\u3066\u304F\u3060\u3055\u3044";
6337
+ termsRow.append(span1, a, span2);
6338
+ termsDiv.appendChild(termsRow);
6339
+ }
6340
+ root.appendChild(termsDiv);
6341
+ }
5989
6342
  target.appendChild(root);
5990
6343
  const autoScroll = (lane, el) => {
5991
6344
  if (el.offsetWidth === 0 || lane.clientWidth === 0) return;
@@ -5995,7 +6348,10 @@ var mountMmlPlayer = (target, mml, options = {}) => {
5995
6348
  lane.scrollLeft = Math.max(0, Math.min(next, maxScroll));
5996
6349
  };
5997
6350
  const renderPlayhead = (step) => {
5998
- timeEl.textContent = formatTime(Math.max(0, step) * secondsPerStep);
6351
+ const beatIndex = Math.floor(step / STEPS_PER_BEAT3) % 4;
6352
+ for (let i = 0; i < 4; i++)
6353
+ beatDots[i].classList.toggle("dtm-player-beat-dot--on", i === beatIndex);
6354
+ barEl.textContent = String(Math.floor(step / STEPS_PER_BAR) + 1);
5999
6355
  for (const view of laneViews) {
6000
6356
  let active = null;
6001
6357
  for (const t of view.tokens) {
@@ -6007,7 +6363,8 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6007
6363
  }
6008
6364
  };
6009
6365
  const resetPlayhead = () => {
6010
- timeEl.textContent = "00:00";
6366
+ for (const d of beatDots) d.classList.remove("dtm-player-beat-dot--on");
6367
+ barEl.textContent = "-";
6011
6368
  for (const view of laneViews) {
6012
6369
  for (const t of view.tokens) t.el.classList.remove("is-active");
6013
6370
  view.lane.scrollLeft = 0;
@@ -6021,11 +6378,17 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6021
6378
  getSoloTrackId: () => null,
6022
6379
  getAudioTime,
6023
6380
  onPlayNote: (e) => {
6024
- if (lyricTracks.has(Number(e.trackId))) return;
6381
+ const trackIdx = Number(e.trackId);
6382
+ if (mutedTracks.has(trackIdx)) return;
6383
+ const em = emojiByTrack.get(trackIdx);
6384
+ if (em) jumpEmojiAt(em, e.when);
6385
+ if (lyricTracks.has(trackIdx)) return;
6025
6386
  options.onPlayNote?.(e);
6026
6387
  if (useSynth) synthPlay(e);
6027
6388
  },
6028
6389
  onPlayDrum: (e) => {
6390
+ const em = emojiEls[0];
6391
+ if (em) jumpEmojiAt(em, e.when);
6029
6392
  const velocity = e.velocity * (trackVolume / 100);
6030
6393
  options.onPlayDrum?.({ ...e, velocity });
6031
6394
  if (useSynth) drumSynth({ ...e, velocity });
@@ -6044,6 +6407,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6044
6407
  };
6045
6408
  const finish = () => {
6046
6409
  setPlayingUI(false);
6410
+ clearJumpTimers();
6047
6411
  resetPlayhead();
6048
6412
  if (activePlayer === instance) activePlayer = null;
6049
6413
  };
@@ -6052,22 +6416,23 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6052
6416
  const sorted = [...seqTrack?.notes ?? []].sort(
6053
6417
  (a, b) => a.startStep - b.startStep
6054
6418
  );
6055
- const gate = (lt.gate ?? 100) / 100;
6419
+ const gate = (lt.gate ?? DEFAULT_GATE) / 100;
6420
+ const semis = (lt.octave ?? 0) * 12;
6056
6421
  const count = Math.min(sorted.length, lt.syllables.length);
6057
6422
  const notes = [];
6058
6423
  for (let i = 0; i < count; i++) {
6059
6424
  const n = sorted[i];
6060
6425
  notes.push({
6061
6426
  syllable: lt.syllables[i],
6062
- pitch: n.pitch,
6427
+ pitch: n.pitch + semis,
6063
6428
  startSec: n.startStep * secondsPerStep,
6064
6429
  durationSec: n.durationSteps * secondsPerStep * gate
6065
6430
  });
6066
6431
  }
6067
6432
  return {
6068
6433
  model: lt.model,
6069
- volume: vocalVolumeToGain(lt.volume ?? 300) * (trackVolume / 100),
6070
- pan: panToStereo(lt.pan ?? 64),
6434
+ volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME) * (trackVolume / 100),
6435
+ pan: panToStereo(lt.pan ?? DEFAULT_PAN),
6071
6436
  notes
6072
6437
  };
6073
6438
  });
@@ -6076,7 +6441,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6076
6441
  const tracks = streaming ? buildStreamTracks() : [];
6077
6442
  if (streaming) {
6078
6443
  const v = ensureVoices();
6079
- const overlay = showLoadingOverlay(root);
6444
+ const overlay = showLoadingOverlay(body);
6080
6445
  try {
6081
6446
  await v.loadModels(tracks.map((t) => t.model));
6082
6447
  await v.warm(tracks);
@@ -6121,6 +6486,8 @@ var mountMmlPlayer = (target, mml, options = {}) => {
6121
6486
  void audioCtx.close();
6122
6487
  audioCtx = null;
6123
6488
  }
6489
+ for (const t of blinkTimers) clearTimeout(t);
6490
+ clearJumpTimers();
6124
6491
  root.remove();
6125
6492
  };
6126
6493
  const instance = {
@@ -6416,6 +6783,13 @@ var createPianoRoll = (options, handlers) => {
6416
6783
  };
6417
6784
  export {
6418
6785
  DAW_CSS,
6786
+ DEFAULT_BPM,
6787
+ DEFAULT_GATE,
6788
+ DEFAULT_PAN,
6789
+ DEFAULT_PLAYBACK_VELOCITY,
6790
+ DEFAULT_STEPS_PER_BAR,
6791
+ DEFAULT_VELOCITY,
6792
+ DEFAULT_VOCAL_VOLUME,
6419
6793
  DRUM_FONT,
6420
6794
  DRUM_KEYS,
6421
6795
  DRUM_PATTERNS,
@@ -6423,6 +6797,7 @@ export {
6423
6797
  KOE_BASE_URL,
6424
6798
  KOE_VOICEBANKS,
6425
6799
  KOE_VOICEBANK_LABELS,
6800
+ KOE_VOICEBANK_TERMS,
6426
6801
  LinkedList,
6427
6802
  MAX_VOCAL_VOLUME,
6428
6803
  MMLCore,
@@ -6430,6 +6805,7 @@ export {
6430
6805
  PREWARM_NOTES,
6431
6806
  TRACKS_ADVANCED,
6432
6807
  TRACKS_SIMPLE,
6808
+ VOICE_IMAGE_KEY,
6433
6809
  analyzeMidiTracks,
6434
6810
  applyHarmonicFilter,
6435
6811
  applyMonophonic,