@onjmin/dtm 2.1.4 → 2.1.6

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
@@ -44,6 +44,7 @@ __export(index_exports, {
44
44
  EDO31_NAMES: () => EDO31_NAMES,
45
45
  FADE_IN_MARK: () => FADE_IN_MARK,
46
46
  FADE_OUT_MARK: () => FADE_OUT_MARK,
47
+ GLOBAL_STORAGE_KEYS: () => GLOBAL_STORAGE_KEYS,
47
48
  GM_INSTRUMENT_NAMES: () => GM_INSTRUMENT_NAMES,
48
49
  INSTRUMENT_PRESETS: () => INSTRUMENT_PRESETS,
49
50
  KEY_COUNT: () => KEY_COUNT,
@@ -52,6 +53,7 @@ __export(index_exports, {
52
53
  KOE_VOICEBANK_LABELS: () => KOE_VOICEBANK_LABELS,
53
54
  KOE_VOICEBANK_TERMS: () => KOE_VOICEBANK_TERMS,
54
55
  LinkedList: () => LinkedList,
56
+ MACRO_STORAGE_KEYS: () => MACRO_STORAGE_KEYS,
55
57
  MAJOR_KEY_IDS: () => MAJOR_KEY_IDS,
56
58
  MAX_VOCAL_VOLUME: () => MAX_VOCAL_VOLUME,
57
59
  MICRO_STEP: () => MICRO_STEP,
@@ -154,6 +156,11 @@ __export(index_exports, {
154
156
  playNote: () => playNote,
155
157
  playPlacements: () => playPlacements,
156
158
  playSingingMML: () => playSingingMML,
159
+ readGlobalBool: () => readGlobalBool,
160
+ readGlobalNumber: () => readGlobalNumber,
161
+ readGlobalSetting: () => readGlobalSetting,
162
+ readMacroSections: () => readMacroSections,
163
+ readMacroSetting: () => readMacroSetting,
157
164
  resolveComposeKey: () => resolveComposeKey,
158
165
  resolveDrumPattern: () => resolveDrumPattern,
159
166
  resolveLoopPoint: () => resolveLoopPoint,
@@ -176,7 +183,10 @@ __export(index_exports, {
176
183
  unitsToMidi: () => unitsToMidi,
177
184
  unitsToMidiDetune: () => unitsToMidiDetune,
178
185
  unitsToPitchV1: () => unitsToPitchV1,
179
- vocalVolumeToGain: () => vocalVolumeToGain
186
+ vocalVolumeToGain: () => vocalVolumeToGain,
187
+ writeGlobalSetting: () => writeGlobalSetting,
188
+ writeMacroSections: () => writeMacroSections,
189
+ writeMacroSetting: () => writeMacroSetting
180
190
  });
181
191
  module.exports = __toCommonJS(index_exports);
182
192
 
@@ -4910,6 +4920,7 @@ var createSequencer = (options) => {
4910
4920
  let drumCursor = 0;
4911
4921
  let lastDrumStep = Number.NEGATIVE_INFINITY;
4912
4922
  let lastPlayStep = 0;
4923
+ let maxTimelineEndSec = 0;
4913
4924
  const secondsPerStep = () => 60 / options.getBpm() / STEPS_PER_BEAT;
4914
4925
  const getWrappedPlayStep = (time, sps) => {
4915
4926
  if (!isLooping || loopDurationSec <= 0 || time < loopEndSec) {
@@ -4936,6 +4947,7 @@ var createSequencer = (options) => {
4936
4947
  }
4937
4948
  const startLimit = isLooping ? Math.min(fromStep, loopStartStep) : fromStep;
4938
4949
  let maxEndStep = 0;
4950
+ maxTimelineEndSec = 0;
4939
4951
  for (const track of options.getTracks()) {
4940
4952
  trackVolumeMap.set(track.id, track.volume);
4941
4953
  for (const note of track.notes) {
@@ -4944,6 +4956,7 @@ var createSequencer = (options) => {
4944
4956
  const when = relativeStart * sps;
4945
4957
  const duration = note.durationSteps * sps;
4946
4958
  maxEndStep = Math.max(maxEndStep, note.startStep + note.durationSteps);
4959
+ maxTimelineEndSec = Math.max(maxTimelineEndSec, when + duration);
4947
4960
  timeline.push({
4948
4961
  trackId: track.id,
4949
4962
  pitch: note.pitchUnits,
@@ -5069,10 +5082,7 @@ var createSequencer = (options) => {
5069
5082
  lastPlayStep = currentStep2;
5070
5083
  }
5071
5084
  if (!isLooping) {
5072
- const last = timeline[timeline.length - 1];
5073
- const lastWhen = last?.when ?? 0;
5074
- const lastDuration = last?.duration ?? 0;
5075
- if (nowIndex >= timeline.length && time > lastWhen + lastDuration + 0.1) {
5085
+ if (nowIndex >= timeline.length && time > maxTimelineEndSec + 0.1) {
5076
5086
  stop();
5077
5087
  options.onEnd(false);
5078
5088
  }
@@ -8404,6 +8414,21 @@ var DAW_CSS = `
8404
8414
  .dtm-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
8405
8415
  .dtm-track-body { display: flex; flex-direction: column; gap: 10px; }
8406
8416
 
8417
+ /* \u2500\u2500\u2500 \u81EA\u52D5\u4F5C\u66F2\u30B3\u30F3\u30C6\u30CA\uFF08\u30DE\u30AF\u30ED\u30D1\u30CD\u30EB\u5185\u306E\u5C02\u7528\u30AB\u30FC\u30C9\u98A8\u30B0\u30EB\u30FC\u30D7\u67A0\uFF09 \u2500\u2500\u2500 */
8418
+ .dtm-compose-container {
8419
+ background: var(--dtm-deep);
8420
+ border: 2px solid var(--c-black);
8421
+ box-shadow:
8422
+ inset 0 0 0 1px var(--dtm-border2),
8423
+ 0 0 0 1px var(--dtm-success),
8424
+ 3px 3px 0 var(--c-black);
8425
+ padding: 10px 12px 12px;
8426
+ display: flex;
8427
+ flex-direction: column;
8428
+ gap: 10px;
8429
+ margin-bottom: 6px;
8430
+ }
8431
+
8407
8432
  /* \u2500\u2500\u2500 \u30A2\u30AF\u30C6\u30A3\u30D6\u30C8\u30E9\u30C3\u30AF\u8272\uFF08\u500B\u5225\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A\u30D1\u30CD\u30EB\u306E\u5DE6\u7AEF\u30E9\u30A4\u30F3\uFF09 \u2500\u2500\u2500 */
8408
8433
  .dtm-panel--track > summary::before,
8409
8434
  .dtm-panel--track[open] > summary::before {
@@ -13198,11 +13223,25 @@ var densityFeatures = (notes, opts) => {
13198
13223
  shortNoteRatio: short / notes.length
13199
13224
  };
13200
13225
  };
13201
- var tensionFeatures = (barTension) => {
13202
- if (barTension.length < 16) return { rise: 0, resolve: 0 };
13226
+ var tensionFeatures = (barTension, opts) => {
13227
+ if (barTension.length < 8) return { rise: 0, resolve: 0 };
13203
13228
  const mean2 = (xs) => xs.reduce((a2, b2) => a2 + b2, 0) / Math.max(1, xs.length);
13204
- const a = mean2(barTension.slice(0, 4));
13205
- const b = mean2(barTension.slice(8, 12));
13229
+ let a;
13230
+ let b;
13231
+ if (opts?.verseBars && opts?.chorusBars && opts.verseBars.length > 0 && opts.chorusBars.length > 0) {
13232
+ a = mean2(opts.verseBars.map((i2) => barTension[i2] ?? 0));
13233
+ b = mean2(opts.chorusBars.map((i2) => barTension[i2] ?? 0));
13234
+ } else if (barTension.length >= 16 && barTension.length <= 24) {
13235
+ a = mean2(barTension.slice(0, 4));
13236
+ b = mean2(barTension.slice(8, 12));
13237
+ } else {
13238
+ const q1Start = Math.floor(barTension.length * 0.15);
13239
+ const q1End = Math.max(q1Start + 2, Math.floor(barTension.length * 0.35));
13240
+ const q3Start = Math.floor(barTension.length * 0.55);
13241
+ const q3End = Math.max(q3Start + 2, Math.floor(barTension.length * 0.75));
13242
+ a = mean2(barTension.slice(q1Start, q1End));
13243
+ b = mean2(barTension.slice(q3Start, q3End));
13244
+ }
13206
13245
  const all = mean2(barTension);
13207
13246
  const last = barTension[barTension.length - 1];
13208
13247
  const rise = Math.max(0, Math.min(1, (b - a) / 0.5));
@@ -13249,6 +13288,8 @@ var SECTION_LABELS = {
13249
13288
  verse: "A\u30E1\u30ED",
13250
13289
  prechorus: "B\u30E1\u30ED",
13251
13290
  chorus: "\u30B5\u30D3",
13291
+ bridge: "C\u30E1\u30ED",
13292
+ drop_chorus: "\u843D\u3061\u30B5\u30D3",
13252
13293
  interlude: "\u9593\u594F",
13253
13294
  outro: "\u30A2\u30A6\u30C8\u30ED"
13254
13295
  };
@@ -13257,6 +13298,8 @@ var SECTION_ORDER = [
13257
13298
  "verse",
13258
13299
  "prechorus",
13259
13300
  "chorus",
13301
+ "bridge",
13302
+ "drop_chorus",
13260
13303
  "interlude",
13261
13304
  "outro"
13262
13305
  ];
@@ -13308,6 +13351,35 @@ var SECTION_SPECS = {
13308
13351
  // 主音へ全終止
13309
13352
  progression: "b"
13310
13353
  },
13354
+ // Cメロ。Verse/Pre chorusとは違うメロディで、ラストのサビ前に緊張感を持たせる。
13355
+ // 2番のサビの後に来ることが多い(参考: ONLIVE Studio blog)。
13356
+ // 専用のコード進行 "c" を持ち、AメロともBメロとも雰囲気が違う。
13357
+ bridge: {
13358
+ bars: 4,
13359
+ melody: true,
13360
+ registerShift: 2,
13361
+ density: 0.9,
13362
+ drumLevel: 1,
13363
+ landing: 1,
13364
+ // 解決しない音で止めてラスサビへ渡す
13365
+ progression: "c"
13366
+ },
13367
+ // 落ちサビ。サビのメロディを伴奏控えめに歌う。J-POPの王道パターン。
13368
+ // density と drumLevel だけ下げ、メロディはサビと同じものを使う。
13369
+ drop_chorus: {
13370
+ bars: 4,
13371
+ melody: true,
13372
+ registerShift: 4,
13373
+ // サビと同じ高さ
13374
+ density: 0.6,
13375
+ // 薄い(ここが「落ち」の実体)
13376
+ drumLevel: 0,
13377
+ // ドラム控えめ
13378
+ landing: 0,
13379
+ // 主音へ全終止
13380
+ progression: "b"
13381
+ // サビと同じ進行
13382
+ },
13311
13383
  interlude: {
13312
13384
  bars: 4,
13313
13385
  melody: false,
@@ -13327,18 +13399,120 @@ var SECTION_SPECS = {
13327
13399
  progression: "a"
13328
13400
  }
13329
13401
  };
13330
- var buildSectionPlan = (kinds) => {
13331
- const wanted = kinds.length > 0 ? kinds : DEFAULT_SECTIONS;
13332
- const ordered = SECTION_ORDER.filter((k) => wanted.includes(k));
13402
+ var STRUCTURE_TEMPLATES = [
13403
+ // 1コーラス(現行デフォルト、短い曲・初心者向け)
13404
+ {
13405
+ name: "1chorus",
13406
+ label: "1\u30B3\u30FC\u30E9\u30B9",
13407
+ plan: ["intro", "verse", "prechorus", "chorus"]
13408
+ },
13409
+ // JPOP王道(マリーゴールド型)
13410
+ // イントロ→Aメロ→Aメロ→Bメロ→サビ→Aメロ→Bメロ→サビ→Cメロ→間奏→サビ→アウトロ
13411
+ {
13412
+ name: "jpop_standard",
13413
+ label: "JPOP\u738B\u9053",
13414
+ plan: [
13415
+ "intro",
13416
+ "verse",
13417
+ "verse",
13418
+ "prechorus",
13419
+ "chorus",
13420
+ "verse",
13421
+ "prechorus",
13422
+ "chorus",
13423
+ "bridge",
13424
+ "interlude",
13425
+ "chorus",
13426
+ "outro"
13427
+ ]
13428
+ },
13429
+ // 落ちサビ入り(J-POP王道の変形。ラスサビ前に落ちサビ)
13430
+ {
13431
+ name: "jpop_drop",
13432
+ label: "\u843D\u3061\u30B5\u30D3\u5165\u308A",
13433
+ plan: [
13434
+ "intro",
13435
+ "verse",
13436
+ "verse",
13437
+ "prechorus",
13438
+ "chorus",
13439
+ "verse",
13440
+ "prechorus",
13441
+ "chorus",
13442
+ "bridge",
13443
+ "drop_chorus",
13444
+ "chorus",
13445
+ "outro"
13446
+ ]
13447
+ },
13448
+ // ボカロ王道(短め高速、Aメロ繰り返しなし)
13449
+ {
13450
+ name: "vocaloid",
13451
+ label: "\u30DC\u30AB\u30ED\u738B\u9053",
13452
+ plan: [
13453
+ "intro",
13454
+ "verse",
13455
+ "prechorus",
13456
+ "chorus",
13457
+ "verse",
13458
+ "prechorus",
13459
+ "chorus",
13460
+ "bridge",
13461
+ "chorus",
13462
+ "outro"
13463
+ ]
13464
+ },
13465
+ // Verse-Chorus形式(Bメロなし、洋楽的)
13466
+ {
13467
+ name: "verse_chorus",
13468
+ label: "Verse-Chorus",
13469
+ plan: [
13470
+ "intro",
13471
+ "verse",
13472
+ "chorus",
13473
+ "verse",
13474
+ "chorus",
13475
+ "bridge",
13476
+ "chorus",
13477
+ "outro"
13478
+ ]
13479
+ }
13480
+ ];
13481
+ var buildSectionPlan = (kinds, templateName) => {
13482
+ let ordered;
13483
+ if (templateName) {
13484
+ const tmpl = STRUCTURE_TEMPLATES.find((t) => t.name === templateName);
13485
+ ordered = tmpl ? tmpl.plan : DEFAULT_SECTIONS;
13486
+ } else {
13487
+ const wanted = kinds.length > 0 ? kinds : DEFAULT_SECTIONS;
13488
+ ordered = SECTION_ORDER.filter((k) => wanted.includes(k));
13489
+ }
13333
13490
  const plan = [];
13334
13491
  let bar = 0;
13492
+ const seen = /* @__PURE__ */ new Map();
13335
13493
  for (const kind of ordered) {
13336
13494
  const spec = SECTION_SPECS[kind];
13337
- plan.push({ kind, startBar: bar, bars: spec.bars, spec, keyShift: 0 });
13495
+ const count = seen.get(kind) ?? 0;
13496
+ plan.push({
13497
+ kind,
13498
+ startBar: bar,
13499
+ bars: spec.bars,
13500
+ spec,
13501
+ keyShift: 0,
13502
+ restatement: count > 0
13503
+ });
13504
+ seen.set(kind, count + 1);
13338
13505
  bar += spec.bars;
13339
13506
  }
13340
13507
  return plan;
13341
13508
  };
13509
+ var sectionAt = (plan, bar) => {
13510
+ for (const section of plan) {
13511
+ if (bar >= section.startBar && bar < section.startBar + section.bars)
13512
+ return section;
13513
+ }
13514
+ return plan[plan.length - 1];
13515
+ };
13342
13516
 
13343
13517
  // src/compose.ts
13344
13518
  var STEP_SEMITONES = 2;
@@ -13624,6 +13798,16 @@ var SECTION_A_PROGRESSIONS = [
13624
13798
  ["Am", "Dm7", "Bb", "E7"]
13625
13799
  // bVI からドミナントへ
13626
13800
  ];
13801
+ var SECTION_C_PROGRESSIONS = [
13802
+ ["Am", "Em", "F", "C"],
13803
+ ["Dm7", "Em7", "FM7", "G7"],
13804
+ ["Am7", "Dm7", "G7", "CM7"],
13805
+ ["F", "G", "Am", "G"],
13806
+ ["Dm7", "G7", "Em7", "Am"],
13807
+ ["FM7", "Em7", "Am7", "Dm7"],
13808
+ ["Am", "G", "FM7", "E7"],
13809
+ ["C", "Am", "FM7", "G7"]
13810
+ ];
13627
13811
  var SECTION_B_PROGRESSIONS = [
13628
13812
  ["F", "G", "Em7", "Am"],
13629
13813
  ["Dm7", "G7", "CM7", "A7"],
@@ -13850,10 +14034,9 @@ var HALF_BAR_FIGURES = [
13850
14034
  [EIGHTH, EIGHTH, EIGHTH, EIGHTH],
13851
14035
  // タタタタ
13852
14036
  [EIGHTH, EIGHTH, EIGHTH, EIGHTH],
13853
- [EIGHTH, EIGHTH, EIGHTH, EIGHTH],
13854
- [EIGHTH, EIGHTH, EIGHTH, EIGHTH],
13855
14037
  [QUARTER, EIGHTH, EIGHTH],
13856
14038
  // ター・タタ
14039
+ [QUARTER, EIGHTH, EIGHTH],
13857
14040
  [EIGHTH, EIGHTH, QUARTER],
13858
14041
  // タタ・ター
13859
14042
  [DOT_QUARTER, EIGHTH],
@@ -13861,8 +14044,24 @@ var HALF_BAR_FIGURES = [
13861
14044
  [DOT_QUARTER, EIGHTH],
13862
14045
  [EIGHTH, QUARTER, EIGHTH],
13863
14046
  // 頭抜き
14047
+ // 4分音符主体・ロングトーン(J-POPの歌い上げるバラード〜ミドル向け。音数過多を是正)
14048
+ [QUARTER, QUARTER],
14049
+ // ター・ター
14050
+ [QUARTER, QUARTER],
14051
+ [HALF],
14052
+ // ターーー
13864
14053
  [HALF],
13865
- // ロングトーン
14054
+ [-QUARTER, QUARTER],
14055
+ // 休・ター
14056
+ [QUARTER, -QUARTER],
14057
+ // ター・休
14058
+ // 弱起(アウフタクト)パターン:末尾から次へ飛び込む言い回し
14059
+ [-DOT_QUARTER, EIGHTH],
14060
+ // 3拍半休みからの裏拍アウフタクト
14061
+ [-QUARTER, EIGHTH, EIGHTH],
14062
+ // 2拍目裏からのアウフタクト(タタ)
14063
+ [QUARTER, -EIGHTH, EIGHTH],
14064
+ // ター・休タ(8分裏弱起)
13866
14065
  // 付点8分+16分(タッカ)。歌モノで非常に多用される跳ね・推進力の型。
13867
14066
  [DOT_EIGHTH, SIXTEENTH, EIGHTH, EIGHTH],
13868
14067
  [EIGHTH, EIGHTH, DOT_EIGHTH, SIXTEENTH],
@@ -14531,7 +14730,10 @@ var draw = (options, resolvedKey, rnd) => {
14531
14730
  };
14532
14731
  const quarterSteps = scaleStep(QUARTER);
14533
14732
  const strongStep = Math.max(1, Math.round(stepsPerBar / 2));
14534
- const sectionPlan = buildSectionPlan(options.sections ?? DEFAULT_SECTIONS);
14733
+ const sectionPlan = buildSectionPlan(
14734
+ options.sections ?? DEFAULT_SECTIONS,
14735
+ options.template
14736
+ );
14535
14737
  const totalBars = sectionPlan.reduce((sum, s) => sum + s.bars, 0);
14536
14738
  if (rnd() < 0.35) {
14537
14739
  const modType = pick(
@@ -14568,12 +14770,18 @@ var draw = (options, resolvedKey, rnd) => {
14568
14770
  (p) => p.join("|") !== progA.join("|")
14569
14771
  );
14570
14772
  const progB = pick(progBPool, rnd);
14773
+ const progC = pick(
14774
+ SECTION_C_PROGRESSIONS.filter(
14775
+ (p) => p.join("|") !== progA.join("|") && p.join("|") !== progB.join("|")
14776
+ ),
14777
+ rnd
14778
+ );
14571
14779
  const tonic = progA[0].startsWith("Am") ? "Am" : "C";
14572
14780
  const progHalf = pick(SECTION_A2_DERIVATIONS, rnd)(progA);
14573
14781
  const progFull = pick(SECTION_A3_DERIVATIONS, rnd)(progA, tonic);
14574
14782
  const progression = [];
14575
14783
  for (const section of sectionPlan) {
14576
- const base = section.spec.progression === "b" ? progB : progA;
14784
+ const base = section.spec.progression === "c" ? progC : section.spec.progression === "b" ? progB : progA;
14577
14785
  for (let i2 = 0; i2 < section.bars; i2 += 4) {
14578
14786
  const isLastPhrase = i2 + 4 >= section.bars;
14579
14787
  if (!isLastPhrase) {
@@ -14591,7 +14799,7 @@ var draw = (options, resolvedKey, rnd) => {
14591
14799
  const chordPattern = pick(CHORD_PATTERNS, rnd);
14592
14800
  const rootShift = resolvedKey.rootShift;
14593
14801
  const bpm = pick(BPM_CHOICES, rnd);
14594
- const sourceOf = (kind) => kind === "chorus" || kind === "interlude" ? "b" : kind === "prechorus" ? "a2" : "a";
14802
+ const sourceOf = (kind) => kind === "chorus" || kind === "interlude" || kind === "drop_chorus" ? "b" : kind === "prechorus" ? "a2" : kind === "bridge" ? "c" : "a";
14595
14803
  const units2 = [];
14596
14804
  for (const section of sectionPlan) {
14597
14805
  const unitCount = Math.max(1, Math.round(section.bars / 2));
@@ -14628,6 +14836,19 @@ var draw = (options, resolvedKey, rnd) => {
14628
14836
  const barInUnit = (bar) => bar % 2;
14629
14837
  const unitOf = (bar) => Math.floor(bar / 2);
14630
14838
  const restatementOf = (bar) => {
14839
+ const curSec = sectionAt(sectionPlan, bar);
14840
+ if (!curSec.spec.melody) return null;
14841
+ if (curSec.restatement) {
14842
+ const firstSec = sectionPlan.find(
14843
+ (s) => s.kind === curSec.kind && !s.restatement
14844
+ );
14845
+ if (firstSec) {
14846
+ const offset = bar - curSec.startBar;
14847
+ if (offset < firstSec.bars) {
14848
+ return firstSec.startBar + offset;
14849
+ }
14850
+ }
14851
+ }
14631
14852
  const u = unitOf(bar);
14632
14853
  if (units2[u].source === "silent") return null;
14633
14854
  for (let v = 0; v < u; v++) {
@@ -14657,10 +14878,9 @@ var draw = (options, resolvedKey, rnd) => {
14657
14878
  ),
14658
14879
  // 人間の曲の跳躍率は p25〜p75 で 0.40〜0.55。上限が低いとその帯へ届かない。
14659
14880
  leapAffinity: 0.08 + rnd() * 0.3,
14660
- // 4曲に1曲は変化音を使わない曲にする(調の外の音は曲の性格そのものなので、
14661
- // 全曲に掛けると「どの曲も同じ味付け」になる)。
14662
- chromaticAffinity: rnd() < 0.25 ? 0 : 0.15 + rnd() * 0.45,
14663
- barHeadWeight: rnd() < 0.65 ? 3 : 2,
14881
+ // 界隈曲らしさ:調の外の音(クロマチック)や微小な逸脱を積極的に許容する。
14882
+ chromaticAffinity: rnd() < 0.1 ? 0 : 0.3 + rnd() * 0.6,
14883
+ barHeadWeight: rnd() < 0.5 ? 3 : 2,
14664
14884
  bassStyle: pick(
14665
14885
  [
14666
14886
  "quarter",
@@ -14686,7 +14906,7 @@ var draw = (options, resolvedKey, rnd) => {
14686
14906
  subInterval: pick([3, 4, 8, 9], rnd)
14687
14907
  };
14688
14908
  const motifPool = groovyCells(MOTIF_CELLS, style.groove, rnd);
14689
- const targetNotesPerBar = 5.5 + rnd() * 2.5;
14909
+ const targetNotesPerBar = 4 + rnd() * 4;
14690
14910
  const targetRestRatio = 0.02 + rnd() * 0.1;
14691
14911
  const cellNotes = (c) => c.value.filter((v) => v > 0).length;
14692
14912
  const cellRest = (c) => {
@@ -14698,27 +14918,49 @@ var draw = (options, resolvedKey, rnd) => {
14698
14918
  }
14699
14919
  return total === 0 ? 0 : rest / total;
14700
14920
  };
14701
- const cellDistance = (c) => Math.abs(cellNotes(c) - targetNotesPerBar) + Math.abs(cellRest(c) - targetRestRatio) * 8;
14702
- const pickCell = (pool) => {
14921
+ const cellDistance = (c, densityMul = 1) => Math.abs(cellNotes(c) - targetNotesPerBar * densityMul) + Math.abs(cellRest(c) - targetRestRatio / Math.max(0.5, densityMul)) * 8;
14922
+ const pickCell = (pool, densityMul = 1) => {
14703
14923
  let best = pick(pool, rnd);
14704
14924
  for (let i2 = 0; i2 < 2; i2++) {
14705
14925
  const c = pick(pool, rnd);
14706
- if (cellDistance(c) < cellDistance(best)) best = c;
14926
+ if (cellDistance(c, densityMul) < cellDistance(best, densityMul))
14927
+ best = c;
14707
14928
  }
14708
14929
  return best;
14709
14930
  };
14710
- const motifCell = pickCell(motifPool);
14711
- const motifCell2 = rnd() < 0.45 ? motifCell : pickCell(motifPool.filter((c) => c !== motifCell));
14931
+ const motifCell = pickCell(motifPool, 0.85);
14932
+ const motifCell2 = rnd() < 0.45 ? motifCell : pickCell(
14933
+ motifPool.filter((c) => c !== motifCell),
14934
+ 0.85
14935
+ );
14712
14936
  const motifB = pickCell(
14713
- motifPool.filter((c) => c !== motifCell && c !== motifCell2)
14937
+ motifPool.filter((c) => c !== motifCell && c !== motifCell2),
14938
+ 1.2
14939
+ );
14940
+ const motifB2 = pickCell(
14941
+ motifPool.filter((c) => c !== motifB),
14942
+ 1.2
14714
14943
  );
14715
- const motifB2 = pickCell(motifPool.filter((c) => c !== motifB));
14716
14944
  const motifA2 = pickCell(
14717
14945
  motifPool.filter(
14718
14946
  (c) => c !== motifCell && c !== motifCell2 && c !== motifB
14719
- )
14947
+ ),
14948
+ 1.1
14949
+ );
14950
+ const motifA22 = rnd() < 0.45 ? motifA2 : pickCell(
14951
+ motifPool.filter((c) => c !== motifA2),
14952
+ 1.1
14953
+ );
14954
+ const motifC = pickCell(
14955
+ motifPool.filter(
14956
+ (c) => c !== motifCell && c !== motifCell2 && c !== motifB && c !== motifA2
14957
+ ),
14958
+ 0.9
14959
+ );
14960
+ const motifC2 = rnd() < 0.45 ? motifC : pickCell(
14961
+ motifPool.filter((c) => c !== motifC),
14962
+ 0.9
14720
14963
  );
14721
- const motifA22 = rnd() < 0.45 ? motifA2 : pickCell(motifPool.filter((c) => c !== motifA2));
14722
14964
  const motifVar = pickCell(
14723
14965
  motifPool.filter((c) => c !== motifCell && c !== motifCell2)
14724
14966
  );
@@ -14771,12 +15013,13 @@ var draw = (options, resolvedKey, rnd) => {
14771
15013
  }
14772
15014
  const isB = units2[u].source === "b";
14773
15015
  const isA2 = units2[u].source === "a2";
15016
+ const isC = units2[u].source === "c";
14774
15017
  const isPrechorusEnd = units2[u].section.kind === "prechorus" && bar === units2[u].section.startBar + units2[u].section.bars - 1;
14775
15018
  if (isPrechorusEnd) {
14776
15019
  barRhythms.push(scaleCell(buildUpCell.value));
14777
15020
  continue;
14778
15021
  }
14779
- const pair = () => isB ? [motifB, motifB2] : isA2 ? [motifA2, motifA22] : [motifCell, motifCell2];
15022
+ const pair = () => isB ? [motifB, motifB2] : isA2 ? [motifA2, motifA22] : isC ? [motifC, motifC2] : [motifCell, motifCell2];
14780
15023
  let cell;
14781
15024
  if (units2[u].source === "answer") {
14782
15025
  const isPeriodEnd = units2[u].landing !== null;
@@ -14807,6 +15050,8 @@ var draw = (options, resolvedKey, rnd) => {
14807
15050
  const melody = [];
14808
15051
  const submelody = [];
14809
15052
  const bass = [];
15053
+ const harmony = [];
15054
+ const pad = [];
14810
15055
  const melodyDurations = [];
14811
15056
  const barTension = new Array(totalBars).fill(0);
14812
15057
  let restSteps = 0;
@@ -14853,13 +15098,22 @@ var draw = (options, resolvedKey, rnd) => {
14853
15098
  (bar + style.arcPhase) / style.arcPeriod * Math.PI * 2
14854
15099
  );
14855
15100
  const arcCenter = MELODY_CENTER + arc * style.arcAmp;
14856
- const headTarget = prevSemi + (arcCenter - prevSemi) * 0.5;
14857
- const headSemi = nearestChordTone(
14858
- headTarget,
14859
- tones,
14860
- headWeight,
14861
- isSectionB
14862
- ).semi;
15101
+ const inUnit = barInUnit(bar);
15102
+ let headSemi;
15103
+ if (inUnit === 1 && bar > 0) {
15104
+ headSemi = nearestChordTone(prevSemi, tones, headWeight, isSectionB).semi;
15105
+ if (Math.abs(headSemi - prevSemi) > 4) {
15106
+ headSemi = nearestChordTone(prevSemi, tones, 1, isSectionB).semi;
15107
+ }
15108
+ } else {
15109
+ const headTarget = prevSemi + (arcCenter - prevSemi) * 0.5;
15110
+ headSemi = nearestChordTone(
15111
+ headTarget,
15112
+ tones,
15113
+ headWeight,
15114
+ isSectionB
15115
+ ).semi;
15116
+ }
14863
15117
  const contourOffset = barInUnit(bar) === 0 ? 0 : barRhythms[bar - 1].filter((v) => v > 0).length;
14864
15118
  const prevRole = bar > 0 ? barRoles[bar - 1] : null;
14865
15119
  const repeatShift = role === "motif" && (prevRole === "motif" || prevRole === "sequence" || prevRole === "climax") ? pick([-2, -1, 1, 2], rnd) : 0;
@@ -15039,6 +15293,48 @@ var draw = (options, resolvedKey, rnd) => {
15039
15293
  } else {
15040
15294
  pushSub(scaleStep(QUARTER), scaleStep(DOT_HALF), subSemi);
15041
15295
  }
15296
+ const barSec = sectionAt(sectionPlan, bar);
15297
+ if (!silent && (barSec.kind === "chorus" || barSec.kind === "drop_chorus")) {
15298
+ for (let i2 = 0; i2 < slots.length; i2++) {
15299
+ const hSemi = pitches[i2] + 3;
15300
+ const hTone = nearestChordTone(hSemi, tones, 2);
15301
+ const hClamped = clampSemi(hTone.semi, MELODY_LOW, MELODY_HIGH);
15302
+ const k = barKeyShift[bar];
15303
+ const fifthShift = k === 0 ? 0 : SEMITONE_TO_FIFTH_SHIFT[(k % 12 + 12) % 12];
15304
+ harmony.push({
15305
+ startStep: barStart + slots[i2].at,
15306
+ pitchUnits: spelledToUnits(
15307
+ hClamped + k,
15308
+ hTone.fifth + fifthShift,
15309
+ edo
15310
+ ),
15311
+ durationSteps: slots[i2].value,
15312
+ velocity: slots[i2].at === 0 ? 82 : 76
15313
+ });
15314
+ }
15315
+ }
15316
+ if (barSec.kind === "prechorus" || barSec.kind === "chorus" || barSec.kind === "bridge" || barSec.kind === "drop_chorus") {
15317
+ const padTone = nearestChordTone(
15318
+ MELODY_HIGH + 2,
15319
+ // メロディの上の音域
15320
+ tones,
15321
+ 2
15322
+ );
15323
+ const padSemi = clampSemi(padTone.semi, MELODY_HIGH - 4, MELODY_HIGH + 8);
15324
+ const k = barKeyShift[bar];
15325
+ const fifthShift = k === 0 ? 0 : SEMITONE_TO_FIFTH_SHIFT[(k % 12 + 12) % 12];
15326
+ pad.push({
15327
+ startStep: barStart,
15328
+ pitchUnits: spelledToUnits(
15329
+ padSemi + k,
15330
+ padTone.fifth + fifthShift,
15331
+ edo
15332
+ ),
15333
+ durationSteps: stepsPerBar,
15334
+ // 小節全体を伸ばす
15335
+ velocity: barSec.kind === "drop_chorus" ? 50 : 62
15336
+ });
15337
+ }
15042
15338
  const rootTone = tones[0];
15043
15339
  const fifthTone = tones.find((t) => t.weight === 3 && t !== rootTone);
15044
15340
  const thirdTone = tones.find((t) => t.weight === 2);
@@ -15145,6 +15441,69 @@ var draw = (options, resolvedKey, rnd) => {
15145
15441
  }
15146
15442
  for (const n of barBass) bass.push(n);
15147
15443
  }
15444
+ const eighthSteps = scaleStep(EIGHTH);
15445
+ for (let i2 = 0; i2 < melody.length - 1; i2++) {
15446
+ const cur = melody[i2];
15447
+ const nxt = melody[i2 + 1];
15448
+ const curEnd = cur.startStep + cur.durationSteps;
15449
+ const atBarBoundary = curEnd % stepsPerBar === 0 && nxt.startStep === curEnd;
15450
+ if (!atBarBoundary) continue;
15451
+ if (cur.durationSteps > eighthSteps) continue;
15452
+ const barIdx = Math.floor(curEnd / stepsPerBar);
15453
+ if (barIdx >= totalBars) continue;
15454
+ if (i2 + 1 >= melody.length - 1) continue;
15455
+ const curSec = sectionAt(sectionPlan, barIdx - 1);
15456
+ const nextSec = sectionAt(sectionPlan, barIdx);
15457
+ if (!nextSec.spec.melody || curSec !== nextSec) continue;
15458
+ const curSemi = Math.round(cur.pitchUnits / UNITS_PER_SEMITONE);
15459
+ const nxtSemi = Math.round(nxt.pitchUnits / UNITS_PER_SEMITONE);
15460
+ if (Math.abs(nxtSemi - curSemi) > 3) continue;
15461
+ const nextRole = barRoles[barIdx];
15462
+ let tieProb = 0.4;
15463
+ if (nextRole === "motif") tieProb = 0.05;
15464
+ else if (nextRole === "sequence") tieProb = 0.75;
15465
+ else if (nextRole === "climax") tieProb = 0.9;
15466
+ else if (nextRole === "cadence") tieProb = 0.1;
15467
+ if (rnd() < tieProb) {
15468
+ cur.durationSteps += nxt.durationSteps;
15469
+ melody.splice(i2 + 1, 1);
15470
+ if (i2 < melodyDurations.length - 1) {
15471
+ melodyDurations[i2] += melodyDurations[i2 + 1];
15472
+ melodyDurations.splice(i2 + 1, 1);
15473
+ }
15474
+ const hCurIdx = harmony.findIndex((h) => h.startStep === cur.startStep);
15475
+ const hNxtIdx = harmony.findIndex((h) => h.startStep === nxt.startStep);
15476
+ if (hCurIdx >= 0 && hNxtIdx >= 0) {
15477
+ harmony[hCurIdx].durationSteps += harmony[hNxtIdx].durationSteps;
15478
+ harmony.splice(hNxtIdx, 1);
15479
+ }
15480
+ i2--;
15481
+ }
15482
+ }
15483
+ for (let i2 = 0; i2 < melody.length; i2++) {
15484
+ const barIdx = Math.floor(melody[i2].startStep / stepsPerBar);
15485
+ if (barIdx >= totalBars) continue;
15486
+ const role = barRoles[barIdx];
15487
+ let splitProb = 0.15;
15488
+ if (role === "motif" || role === "sequence") splitProb = 0;
15489
+ else if (role === "climax") splitProb = 0.6;
15490
+ else if (role === "cadence") splitProb = 0;
15491
+ if (rnd() < splitProb && melody[i2].durationSteps === quarterSteps) {
15492
+ const half = scaleStep(EIGHTH);
15493
+ const newNote = {
15494
+ ...melody[i2],
15495
+ startStep: melody[i2].startStep + half,
15496
+ durationSteps: half
15497
+ };
15498
+ melody[i2].durationSteps = half;
15499
+ melody.splice(i2 + 1, 0, newNote);
15500
+ if (i2 < melodyDurations.length) {
15501
+ melodyDurations[i2] = half;
15502
+ melodyDurations.splice(i2 + 1, 0, half);
15503
+ }
15504
+ i2++;
15505
+ }
15506
+ }
15148
15507
  const range = (notes) => {
15149
15508
  if (notes.length === 0) return 0;
15150
15509
  const us = notes.map((n) => n.pitchUnits);
@@ -15152,7 +15511,7 @@ var draw = (options, resolvedKey, rnd) => {
15152
15511
  };
15153
15512
  const shiftUnits = semitonesToUnits(rootShift, edo);
15154
15513
  if (shiftUnits !== 0)
15155
- for (const list of [melody, submelody, bass])
15514
+ for (const list of [melody, submelody, bass, harmony, pad])
15156
15515
  for (const n of list) n.pitchUnits = n.pitchUnits + shiftUnits;
15157
15516
  return {
15158
15517
  chordProgression,
@@ -15167,6 +15526,8 @@ var draw = (options, resolvedKey, rnd) => {
15167
15526
  melody,
15168
15527
  submelody,
15169
15528
  bass,
15529
+ harmony,
15530
+ pad,
15170
15531
  melodyDurations,
15171
15532
  restSteps,
15172
15533
  totalSteps: Math.max(1, sungBars) * stepsPerBar,
@@ -15202,7 +15563,16 @@ var evaluate = (d, recent) => {
15202
15563
  fromMelody(toMetricNotes(d.submelody)),
15203
15564
  opts
15204
15565
  );
15205
- const tension = tensionFeatures(d.barTension);
15566
+ const verseBars = [];
15567
+ const chorusBars = [];
15568
+ for (const s of d.sections) {
15569
+ for (let b = s.startBar; b < s.startBar + s.bars; b++) {
15570
+ if (s.kind === "verse") verseBars.push(b);
15571
+ else if (s.kind === "chorus" || s.kind === "drop_chorus")
15572
+ chorusBars.push(b);
15573
+ }
15574
+ }
15575
+ const tension = tensionFeatures(d.barTension, { verseBars, chorusBars });
15206
15576
  const density = densityFeatures(melodyFrom, opts);
15207
15577
  const fingerprint = featureVector({
15208
15578
  entropy,
@@ -15218,6 +15588,7 @@ var evaluate = (d, recent) => {
15218
15588
  );
15219
15589
  const at = (b, v) => band(v, b[0], b[1], b[2], b[3]);
15220
15590
  const atc = (key, v) => centeredBand(v, CORPUS_BANDS[key], CORPUS_MEDIANS[key]);
15591
+ const peakBand = d.bars > 24 ? [0, 1, Math.round(d.bars / 16), Math.round(d.bars / 8) + 2] : HAND_BANDS.climaxPeaks;
15221
15592
  const scoreBreakdown = {
15222
15593
  entropy: atc("entropy", entropy),
15223
15594
  valueKinds: atc("valueKinds", valueKinds),
@@ -15235,7 +15606,7 @@ var evaluate = (d, recent) => {
15235
15606
  sim8: atc("sim8", structure.sim8),
15236
15607
  phraseBreath: atc("phraseBreath", structure.phraseBreath),
15237
15608
  climaxPosition: atc("climaxPosition", structure.climaxPosition),
15238
- climaxPeaks: at(HAND_BANDS.climaxPeaks, structure.climaxPeaks),
15609
+ climaxPeaks: at(peakBand, structure.climaxPeaks),
15239
15610
  complementarity: at(HAND_BANDS.complementarity, structure.complementarity),
15240
15611
  subDensity: at(HAND_BANDS.subDensity, d.submelody.length / d.bars),
15241
15612
  tensionRise: tension.rise,
@@ -15289,9 +15660,10 @@ var composeSong = (options) => {
15289
15660
  bestScore = stats.score;
15290
15661
  bestIsValid = ok;
15291
15662
  best = {
15292
- // ドラムは勝った候補にだけ後から付ける(メロディに依存しないので
15663
+ // ドラム・楽器は勝った候補にだけ後から付ける(メロディに依存しないので
15293
15664
  // 候補ごとに引いても採点は動かず、40本ぶん無駄になる)。
15294
15665
  drum: "",
15666
+ instrument: "",
15295
15667
  chordProgression: d.chordProgression,
15296
15668
  chordPattern: d.chordPattern,
15297
15669
  rootShift: d.rootShift,
@@ -15304,6 +15676,8 @@ var composeSong = (options) => {
15304
15676
  melody: d.melody,
15305
15677
  submelody: d.submelody,
15306
15678
  bass: d.bass,
15679
+ harmony: d.harmony,
15680
+ pad: d.pad,
15307
15681
  stats: { ...stats, attempts: attempt, rejected }
15308
15682
  };
15309
15683
  }
@@ -15311,6 +15685,7 @@ var composeSong = (options) => {
15311
15685
  result.stats.attempts = count;
15312
15686
  result.stats.rejected = rejected;
15313
15687
  result.drum = pickBuiltinDrum(result, rnd);
15688
+ result.instrument = pickBuiltinInstrument(result, rnd);
15314
15689
  return result;
15315
15690
  };
15316
15691
  var pickBuiltinDrum = (song, rnd) => {
@@ -15319,6 +15694,35 @@ var pickBuiltinDrum = (song, rnd) => {
15319
15694
  const pool = song.bpm >= 150 ? ["4beat", "dance", "16beat", "disco"] : short >= 0.6 ? ["16beat", "dance", "4beat", "disco"] : song.bpm <= 110 && short < 0.4 ? ["bossa", "8beat"] : ["8beat", "4beat", "16beat"];
15320
15695
  return pick(pool, rnd);
15321
15696
  };
15697
+ var pickBuiltinInstrument = (song, rnd) => {
15698
+ const eighth = BASE_STEPS_PER_BAR / 8;
15699
+ const short = song.melody.filter((n) => n.durationSteps <= eighth).length / Math.max(1, song.melody.length);
15700
+ if (rnd() < 0.06) {
15701
+ return pick(["japanese_wa", "arabic_exotic"], rnd);
15702
+ }
15703
+ let pool;
15704
+ if (song.drum === "dance" || song.drum === "disco") {
15705
+ pool = song.bpm >= 150 ? ["cyber_punk", "synth_pop", "retro_game"] : ["synth_pop", "retro_game", "rock", "piano"];
15706
+ } else if (song.drum === "bossa") {
15707
+ pool = ["jazz_night", "acoustic", "piano"];
15708
+ } else if (song.bpm <= 105 && short < 0.4) {
15709
+ pool = ["ambient_cloud", "acoustic", "orchestra", "piano", "fantasy_rpg"];
15710
+ } else if (song.bpm >= 145) {
15711
+ pool = ["rock", "synth_pop", "cyber_punk", "retro_game", "piano"];
15712
+ } else if (song.stats.leapRatio >= 0.45) {
15713
+ pool = ["fantasy_rpg", "orchestra", "acoustic", "piano"];
15714
+ } else {
15715
+ pool = [
15716
+ "piano",
15717
+ "acoustic",
15718
+ "synth_pop",
15719
+ "rock",
15720
+ "jazz_night",
15721
+ "fantasy_rpg"
15722
+ ];
15723
+ }
15724
+ return pick(pool, rnd);
15725
+ };
15322
15726
  var LYRIC_WORDS = [
15323
15727
  "\u3042\u3055",
15324
15728
  "\u3072\u304B\u308A",
@@ -15720,74 +16124,89 @@ var buildUI = (target, options) => {
15720
16124
  <details class="dtm-panel" data-dtm-acc="macro">
15721
16125
  <summary>\u30DE\u30AF\u30ED</summary>
15722
16126
  <div class="dtm-panel-body">
15723
- <div class="dtm-row ${showCompose ? "" : "dtm-hidden"}" data-dtm="compose-row">
15724
- <button class="dtm-btn dtm-btn--success" data-dtm="macro-compose" title="\u30B3\u30FC\u30C9\u9032\u884C\u30FB\u30E1\u30ED\u30C7\u30A3\u30FB\u30B5\u30D6\u30E1\u30ED\u30FB\u30D9\u30FC\u30B9\u30FB\u4F34\u594F\u30FB\u30C9\u30E9\u30E0\u3092\u81EA\u52D5\u3067\u4F5C\u308A\u307E\u3059">\u4F5C\u66F2</button>
15725
- <button class="dtm-btn dtm-btn--success" data-dtm="macro-compose-vocal" title="\u4F5C\u66F2\u3057\u305F\u3046\u3048\u3067\u3001\u30E1\u30ED\u30C7\u30A3\u306B\u6B4C\u8A5E\u3092\u4ED8\u3051\u3066\u6B4C\u308F\u305B\u307E\u3059">\u6B4C\u5165\u308A\u4F5C\u66F2</button>
15726
- <button class="dtm-infobtn" data-dtm="macro-compose-info" title="\u4F5C\u66F2\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
15727
- <span class="dtm-grow"></span>
15728
- </div>
15729
- <div class="dtm-row ${showCompose ? "" : "dtm-hidden"}" data-dtm="compose-sections-row">
15730
- <span class="dtm-label">\u4F5C\u308B\u90E8\u5206</span>
15731
- <div class="dtm-checks" data-dtm="compose-sections">
15732
- <label class="dtm-check"><input type="checkbox" value="intro" checked>\u30A4\u30F3\u30C8\u30ED</label>
15733
- <label class="dtm-check"><input type="checkbox" value="verse" checked>A\u30E1\u30ED</label>
15734
- <label class="dtm-check"><input type="checkbox" value="prechorus" checked>B\u30E1\u30ED</label>
15735
- <label class="dtm-check"><input type="checkbox" value="chorus" checked>\u30B5\u30D3</label>
15736
- <label class="dtm-check"><input type="checkbox" value="interlude">\u9593\u594F</label>
15737
- <label class="dtm-check"><input type="checkbox" value="outro">\u30A2\u30A6\u30C8\u30ED</label>
16127
+ <div class="dtm-compose-container ${showCompose ? "" : "dtm-hidden"}" data-dtm="compose-container">
16128
+ <div class="dtm-row" data-dtm="compose-row">
16129
+ <button class="dtm-btn dtm-btn--success" data-dtm="macro-compose" title="\u30B3\u30FC\u30C9\u9032\u884C\u30FB\u30E1\u30ED\u30C7\u30A3\u30FB\u30B5\u30D6\u30E1\u30ED\u30FB\u30D9\u30FC\u30B9\u30FB\u4F34\u594F\u30FB\u30C9\u30E9\u30E0\u3092\u81EA\u52D5\u3067\u4F5C\u308A\u307E\u3059">\u4F5C\u66F2</button>
16130
+ <button class="dtm-btn dtm-btn--success" data-dtm="macro-compose-vocal" title="\u4F5C\u66F2\u3057\u305F\u3046\u3048\u3067\u3001\u30E1\u30ED\u30C7\u30A3\u306B\u6B4C\u8A5E\u3092\u4ED8\u3051\u3066\u6B4C\u308F\u305B\u307E\u3059">\u6B4C\u5165\u308A\u4F5C\u66F2</button>
16131
+ <button class="dtm-infobtn" data-dtm="macro-compose-info" title="\u4F5C\u66F2\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
16132
+ <span class="dtm-grow"></span>
16133
+ </div>
16134
+ <div class="dtm-row" data-dtm="compose-template-row">
16135
+ <span class="dtm-label">\u69CB\u6210</span>
16136
+ <select class="dtm-select" data-dtm="compose-template" title="J-POP\u738B\u9053\u306A\u3069\u306E\u30D7\u30EA\u30BB\u30C3\u30C8\u69CB\u6210\u3092\u9078\u3073\u307E\u3059">
16137
+ <option value="custom">\u81EA\u7531\u9078\u629E\uFF08\u4E0B\u8A18\u30C1\u30A7\u30C3\u30AF\uFF09</option>
16138
+ <option value="1chorus">1\u30B3\u30FC\u30E9\u30B9\uFF08\u77ED\u3081\u30FB\u521D\u5FC3\u8005\u5411\u3051\uFF09</option>
16139
+ <option value="jpop_standard">JPOP\u738B\u9053\uFF08\u30DE\u30EA\u30FC\u30B4\u30FC\u30EB\u30C9\u578B 2\u756A/C\u30E1\u30ED/\u30E9\u30B9\u30B5\u30D3\uFF09</option>
16140
+ <option value="jpop_drop">\u843D\u3061\u30B5\u30D3\u5165\u308A\uFF08JPOP\u738B\u9053 + \u30E9\u30B9\u30B5\u30D3\u524D\u843D\u3061\u30B5\u30D3\uFF09</option>
16141
+ <option value="vocaloid">\u30DC\u30AB\u30ED\u738B\u9053\uFF08\u75BE\u8D70\u30FB2\u756A/C\u30E1\u30ED/\u30E9\u30B9\u30B5\u30D3\uFF09</option>
16142
+ <option value="verse_chorus">Verse-Chorus\uFF08B\u30E1\u30ED\u306A\u3057\u30FB\u6D0B\u697D\u98A8\uFF09</option>
16143
+ </select>
16144
+ </div>
16145
+ <div class="dtm-row" data-dtm="compose-sections-row">
16146
+ <span class="dtm-label">\u4F5C\u308B\u90E8\u5206</span>
16147
+ <div class="dtm-checks" data-dtm="compose-sections">
16148
+ <label class="dtm-check"><input type="checkbox" value="intro" checked>\u30A4\u30F3\u30C8\u30ED</label>
16149
+ <label class="dtm-check"><input type="checkbox" value="verse" checked>A\u30E1\u30ED</label>
16150
+ <label class="dtm-check"><input type="checkbox" value="prechorus" checked>B\u30E1\u30ED</label>
16151
+ <label class="dtm-check"><input type="checkbox" value="chorus" checked>\u30B5\u30D3</label>
16152
+ <label class="dtm-check"><input type="checkbox" value="bridge">C\u30E1\u30ED</label>
16153
+ <label class="dtm-check"><input type="checkbox" value="drop_chorus">\u843D\u3061\u30B5\u30D3</label>
16154
+ <label class="dtm-check"><input type="checkbox" value="interlude">\u9593\u594F</label>
16155
+ <label class="dtm-check"><input type="checkbox" value="outro">\u30A2\u30A6\u30C8\u30ED</label>
16156
+ </div>
16157
+ <span class="dtm-grow"></span>
16158
+ <span class="dtm-hint" data-dtm="compose-sections-len"></span>
16159
+ </div>
16160
+ <div class="dtm-row" data-dtm="compose-key-row">
16161
+ <span class="dtm-label">\u30D9\u30FC\u30B9\u8ABF</span>
16162
+ <select class="dtm-select" data-dtm="compose-key" title="\u81EA\u52D5\u4F5C\u66F2\u306E\u30D9\u30FC\u30B9\u3068\u306A\u308B\u8ABF\u3084\u96F0\u56F2\u6C17\u3092\u9078\u3073\u307E\u3059">
16163
+ <option value="any" title="\u516824\u8ABF\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u6C7A\u5B9A\u3057\u307E\u3059">\u5E0C\u671B\u306A\u3057</option>
16164
+ <optgroup label="\u57FA\u672C">
16165
+ <option value="major" title="12\u306E\u9577\u8ABF\u306E\u4E2D\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u62BD\u9078\u3057\u307E\u3059">\u9577\u8ABF</option>
16166
+ <option value="minor" title="12\u306E\u77ED\u8ABF\u306E\u4E2D\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u62BD\u9078\u3057\u307E\u3059">\u77ED\u8ABF</option>
16167
+ </optgroup>
16168
+ <optgroup label="\u96F0\u56F2\u6C17\u304B\u3089\u9078\u3076\uFF08\u62BD\u9078\uFF09">
16169
+ <option value="mood_happy" title="\u30CF\u9577\u8ABF\u30FB\u30A4\u9577\u8ABF\u30FB\u5909\u30ED\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u7121\u57A2\u306B\u559C\u3070\u3057\u3044\u3001\u7267\u6B4C\u7684\u3001\u967D\u6C17\uFF09">\u559C\u3070\u3057\u3044\u30FB\u967D\u6C17\u306A\u66F2</option>
16170
+ <option value="mood_triumphant" title="\u30CB\u9577\u8ABF\u30FB\u5909\u30C8\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u610F\u6C17\u63DA\u3005\u3001\u52DD\u5229\u306E\u558A\u58F0\u3001\u56F0\u96E3\u6253\u7834\u3001\u5B89\u5835\uFF09">\u52DD\u5229\u30FB\u529B\u5F37\u3044\u66F2</option>
16171
+ <option value="mood_fierce" title="\u30DB\u9577\u8ABF\u30FB\u30D8\u9577\u8ABF\u30FB\u30ED\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u3051\u3093\u304B\u3063\u65E9\u3044\u3001\u6012\u308A\u72C2\u3063\u305F\u8352\u3005\u3057\u3055\u3001\u3069\u304E\u3064\u304F\u731B\u70C8\uFF09">\u6FC0\u3057\u3044\u30FB\u8352\u3005\u3057\u3044\u66F2</option>
16172
+ <option value="mood_solemn" title="\u30C8\u9577\u8ABF\u30FB\u30CB\u77ED\u8ABF\u30FB\u30A4\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u53B3\u7C9B\u3001\u5D07\u9AD8\u3001\u5E7B\u60F3\u3001\u656C\u8654\u3001\u601D\u7D22\u7684\uFF09">\u53B3\u7C9B\u30FB\u5E7B\u60F3\u7684\u306A\u66F2</option>
16173
+ <option value="mood_plaintive" title="\u30CF\u77ED\u8ABF\u30FB\u30DB\u77ED\u8ABF\u30FB\u30D8\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u7D14\u7C8B\u306B\u60B2\u3057\u3052\u3001\u604B\u308F\u305A\u3089\u3044\u3001\u843D\u3061\u7740\u304D\u306E\u306A\u3044\u3001\u7269\u60B2\u3057\u3044\u54C0\u6101\uFF09">\u7269\u60B2\u3057\u3044\u30FB\u54C0\u6101\u306E\u66F2</option>
16174
+ <option value="mood_melancholy" title="\u5909\u30CB\u9577\u8ABF\u30FB\u30ED\u77ED\u8ABF\u30FB\u5B30\u30CF\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u60B2\u3057\u307F\u3001\u6182\u9B31\u3001\u5B64\u72EC\u3001\u5FCD\u8010\u3001\u843D\u80C6\u3001\u60B2\u6D99\uFF09">\u6182\u9B31\u30FB\u5B64\u72EC\u306A\u66F2</option>
16175
+ <option value="mood_anxious" title="\u30C8\u77ED\u8ABF\u30FB\u5909\u30DB\u77ED\u8ABF\u30FB\u5B30\u30D8\u77ED\u8ABF\u30FB\u5909\u30A4\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u4E0D\u6E80\u3001\u4E0D\u5B89\u3001\u6DF1\u3044\u82E6\u60A9\u3001\u9670\u6C17\u306A\u61A4\u308A\uFF09">\u4E0D\u5B89\u30FB\u82E6\u60A9\u306A\u66F2</option>
16176
+ <option value="mood_dark" title="\u5909\u30DB\u9577\u8ABF\u30FB\u5909\u30A4\u9577\u8ABF\u30FB\u5909\u30ED\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u53B3\u3057\u3044\u611B\u3001\u6B7B\u3001\u6C38\u9060\u3001\u88C1\u304D\u3001\u6697\u95C7\u3001\u6050\u308D\u3057\u3044\u5632\u308A\uFF09">\u6697\u95C7\u30FB\u91CD\u539A\u306A\u66F2</option>
16177
+ </optgroup>
16178
+ <optgroup label="\u9577\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
16179
+ <option value="key_C" title="\u7121\u57A2\u306B\u559C\u3070\u3057\u3044\u3001\u7D14\u7C8B\u3001\u7D20\u6734\u3001\u51FA\u767A">\u30CF\u9577\u8ABF (C)</option>
16180
+ <option value="key_Db" title="\u60B2\u3057\u307F\u3001\u6182\u9B31\u306A\u3001\u7518\u7F8E\u306A\u611F\u50B7">\u5909\u30CB\u9577\u8ABF (D\u266D)</option>
16181
+ <option value="key_D" title="\u610F\u6C17\u63DA\u3005\u3068\u3057\u305F\u3001\u52DD\u5229\u306E\u3001\u558A\u58F0">\u30CB\u9577\u8ABF (D)</option>
16182
+ <option value="key_Eb" title="\u53B3\u3057\u3044\u3001\u304D\u3064\u3044\u3001\u305D\u308C\u3067\u3044\u3066\u611B\u306B\u6E80\u3061\u305F">\u5909\u30DB\u9577\u8ABF (E\u266D)</option>
16183
+ <option value="key_E" title="\u3051\u3093\u304B\u3063\u65E9\u3044\u3001\u8352\u3005\u3057\u3044\u3001\u8F1D\u304B\u3057\u3044\u60C5\u71B1">\u30DB\u9577\u8ABF (E)</option>
16184
+ <option value="key_F" title="\u6012\u308A\u72C2\u3063\u305F\u3001\u6C17\u6027\u306E\u8352\u3044\u3001\u4E00\u6642\u7684\u306A\u60B2\u5606">\u30D8\u9577\u8ABF (F)</option>
16185
+ <option value="key_Gb" title="\u56F0\u96E3\u306E\u6253\u7834\u3001\u5B89\u5835\u306E\u305F\u3081\u606F\u3001\u51F1\u65CB">\u5909\u30C8\u9577\u8ABF (G\u266D)</option>
16186
+ <option value="key_G" title="\u53B3\u7C9B\u306A\u3001\u5D07\u9AD8\u306A\u3001\u5E7B\u60F3\u3001\u8AA0\u5B9F">\u30C8\u9577\u8ABF (G)</option>
16187
+ <option value="key_Ab" title="\u6B7B\u3001\u6C38\u9060\u3001\u88C1\u304D\u3001\u6DF1\u9060\u306A\u7791\u60F3">\u5909\u30A4\u9577\u8ABF (A\u266D)</option>
16188
+ <option value="key_A" title="\u3046\u308C\u3057\u3044\u3001\u7267\u6B4C\u7684\u306A\u3001\u611B\u306E\u544A\u767D">\u30A4\u9577\u8ABF (A)</option>
16189
+ <option value="key_Bb" title="\u559C\u3070\u3057\u3044\u3001\u98A8\u5909\u308F\u308A\u306A\u3001\u967D\u6C17\u306A\u3001\u8EFD\u5FEB">\u5909\u30ED\u9577\u8ABF (B\u266D)</option>
16190
+ <option value="key_B" title="\u3069\u304E\u3064\u3044\u3001\u5F37\u70C8\u306A\u3001\u8352\u3063\u307D\u3044\u3001\u731B\u70C8">\u30ED\u9577\u8ABF (B)</option>
16191
+ </optgroup>
16192
+ <optgroup label="\u77ED\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
16193
+ <option value="key_Am" title="\u67D4\u3089\u304B\u306A\u3001\u7269\u60B2\u3057\u3044\u3001\u656C\u8654\u306A\u3001\u7D20\u6734\u306A\u54C0\u6101">\u30A4\u77ED\u8ABF (Am)</option>
16194
+ <option value="key_Bbm" title="\u6050\u308D\u3057\u3044\u3001\u6697\u95C7\u3001\u5632\u308B\u3088\u3046\u306A\u3001\u4E0D\u6C17\u5473">\u5909\u30ED\u77ED\u8ABF (B\u266Dm)</option>
16195
+ <option value="key_Bm" title="\u5B64\u72EC\u306A\u3001\u6182\u9B31\u306A\u3001\u5FCD\u8010\u3001\u9759\u304B\u306A\u8AE6\u5FF5">\u30ED\u77ED\u8ABF (Bm)</option>
16196
+ <option value="key_Cm" title="\u7D14\u7C8B\u306B\u60B2\u3057\u3052\u306A\u3001\u604B\u308F\u305A\u3089\u3044\u306E\u3001\u60B2\u5287\u7684">\u30CF\u77ED\u8ABF (Cm)</option>
16197
+ <option value="key_Csm" title="\u843D\u80C6\u3001\u6CE3\u304D\u53EB\u3093\u3060\u3001\u60B2\u6D99\u306E\u3001\u6DF1\u3044\u5606\u304D">\u5B30\u30CF\u77ED\u8ABF (C\u266Fm)</option>
16198
+ <option value="key_Dm" title="\u53B3\u7C9B\u306A\u3001\u656C\u8654\u306A\u3001\u601D\u7D22\u7684\u306A\u3001\u91CD\u539A\u306A\u7948\u308A">\u30CB\u77ED\u8ABF (Dm)</option>
16199
+ <option value="key_Ebm" title="\u6DF1\u3044\u82E6\u60A9\u3001\u5B9F\u5B58\u7684\u306A\u4E0D\u5B89\u3001\u6226\u6144">\u5909\u30DB\u77ED\u8ABF (E\u266Dm)</option>
16200
+ <option value="key_Em" title="\u5F31\u3005\u3057\u3044\u3001\u306A\u307E\u3081\u304B\u3057\u3044\u3001\u843D\u3061\u7740\u304D\u306E\u306A\u3044">\u30DB\u77ED\u8ABF (Em)</option>
16201
+ <option value="key_Fm" title="\u307C\u3093\u3084\u308A\u3057\u305F\u3001\u7269\u60B2\u3057\u3044\u3001\u3057\u3081\u3084\u304B\u306A\u3001\u846C\u9001">\u30D8\u77ED\u8ABF (Fm)</option>
16202
+ <option value="key_Fsm" title="\u9670\u6C17\u306A\u3001\u6FC0\u3057\u3044\u61A4\u308A\u3001\u6697\u3044\u60C5\u5FF5">\u5B30\u30D8\u77ED\u8ABF (F\u266Fm)</option>
16203
+ <option value="key_Gm" title="\u4E0D\u6E80\u3001\u4E0D\u5B89\u3001\u3084\u308B\u305B\u306A\u3055\u3001\u60B2\u75DB\u306A\u53EB\u3073">\u30C8\u77ED\u8ABF (Gm)</option>
16204
+ <option value="key_Abm" title="\u4E0D\u670D\u306A\u3001\u5606\u304D\u306E\u3001\u6CE3\u304D\u53EB\u3093\u3060">\u5909\u30A4\u77ED\u8ABF (A\u266Dm)</option>
16205
+ </optgroup>
16206
+ </select>
16207
+ <span class="dtm-grow"></span>
16208
+ <span class="dtm-hint" data-dtm="compose-key-hint"></span>
15738
16209
  </div>
15739
- <span class="dtm-grow"></span>
15740
- <span class="dtm-hint" data-dtm="compose-sections-len"></span>
15741
- </div>
15742
- <div class="dtm-row ${showCompose ? "" : "dtm-hidden"}" data-dtm="compose-key-row">
15743
- <span class="dtm-label">\u30D9\u30FC\u30B9\u8ABF</span>
15744
- <select class="dtm-select" data-dtm="compose-key" title="\u81EA\u52D5\u4F5C\u66F2\u306E\u30D9\u30FC\u30B9\u3068\u306A\u308B\u8ABF\u3084\u96F0\u56F2\u6C17\u3092\u9078\u3073\u307E\u3059">
15745
- <option value="any" title="\u516824\u8ABF\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u6C7A\u5B9A\u3057\u307E\u3059">\u5E0C\u671B\u306A\u3057</option>
15746
- <optgroup label="\u57FA\u672C">
15747
- <option value="major" title="12\u306E\u9577\u8ABF\u306E\u4E2D\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u62BD\u9078\u3057\u307E\u3059">\u9577\u8ABF</option>
15748
- <option value="minor" title="12\u306E\u77ED\u8ABF\u306E\u4E2D\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u62BD\u9078\u3057\u307E\u3059">\u77ED\u8ABF</option>
15749
- </optgroup>
15750
- <optgroup label="\u96F0\u56F2\u6C17\u304B\u3089\u9078\u3076\uFF08\u62BD\u9078\uFF09">
15751
- <option value="mood_happy" title="\u30CF\u9577\u8ABF\u30FB\u30A4\u9577\u8ABF\u30FB\u5909\u30ED\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u7121\u57A2\u306B\u559C\u3070\u3057\u3044\u3001\u7267\u6B4C\u7684\u3001\u967D\u6C17\uFF09">\u559C\u3070\u3057\u3044\u30FB\u967D\u6C17\u306A\u66F2</option>
15752
- <option value="mood_triumphant" title="\u30CB\u9577\u8ABF\u30FB\u5909\u30C8\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u610F\u6C17\u63DA\u3005\u3001\u52DD\u5229\u306E\u558A\u58F0\u3001\u56F0\u96E3\u6253\u7834\u3001\u5B89\u5835\uFF09">\u52DD\u5229\u30FB\u529B\u5F37\u3044\u66F2</option>
15753
- <option value="mood_fierce" title="\u30DB\u9577\u8ABF\u30FB\u30D8\u9577\u8ABF\u30FB\u30ED\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u3051\u3093\u304B\u3063\u65E9\u3044\u3001\u6012\u308A\u72C2\u3063\u305F\u8352\u3005\u3057\u3055\u3001\u3069\u304E\u3064\u304F\u731B\u70C8\uFF09">\u6FC0\u3057\u3044\u30FB\u8352\u3005\u3057\u3044\u66F2</option>
15754
- <option value="mood_solemn" title="\u30C8\u9577\u8ABF\u30FB\u30CB\u77ED\u8ABF\u30FB\u30A4\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u53B3\u7C9B\u3001\u5D07\u9AD8\u3001\u5E7B\u60F3\u3001\u656C\u8654\u3001\u601D\u7D22\u7684\uFF09">\u53B3\u7C9B\u30FB\u5E7B\u60F3\u7684\u306A\u66F2</option>
15755
- <option value="mood_plaintive" title="\u30CF\u77ED\u8ABF\u30FB\u30DB\u77ED\u8ABF\u30FB\u30D8\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u7D14\u7C8B\u306B\u60B2\u3057\u3052\u3001\u604B\u308F\u305A\u3089\u3044\u3001\u843D\u3061\u7740\u304D\u306E\u306A\u3044\u3001\u7269\u60B2\u3057\u3044\u54C0\u6101\uFF09">\u7269\u60B2\u3057\u3044\u30FB\u54C0\u6101\u306E\u66F2</option>
15756
- <option value="mood_melancholy" title="\u5909\u30CB\u9577\u8ABF\u30FB\u30ED\u77ED\u8ABF\u30FB\u5B30\u30CF\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u60B2\u3057\u307F\u3001\u6182\u9B31\u3001\u5B64\u72EC\u3001\u5FCD\u8010\u3001\u843D\u80C6\u3001\u60B2\u6D99\uFF09">\u6182\u9B31\u30FB\u5B64\u72EC\u306A\u66F2</option>
15757
- <option value="mood_anxious" title="\u30C8\u77ED\u8ABF\u30FB\u5909\u30DB\u77ED\u8ABF\u30FB\u5B30\u30D8\u77ED\u8ABF\u30FB\u5909\u30A4\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u4E0D\u6E80\u3001\u4E0D\u5B89\u3001\u6DF1\u3044\u82E6\u60A9\u3001\u9670\u6C17\u306A\u61A4\u308A\uFF09">\u4E0D\u5B89\u30FB\u82E6\u60A9\u306A\u66F2</option>
15758
- <option value="mood_dark" title="\u5909\u30DB\u9577\u8ABF\u30FB\u5909\u30A4\u9577\u8ABF\u30FB\u5909\u30ED\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u53B3\u3057\u3044\u611B\u3001\u6B7B\u3001\u6C38\u9060\u3001\u88C1\u304D\u3001\u6697\u95C7\u3001\u6050\u308D\u3057\u3044\u5632\u308A\uFF09">\u6697\u95C7\u30FB\u91CD\u539A\u306A\u66F2</option>
15759
- </optgroup>
15760
- <optgroup label="\u9577\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
15761
- <option value="key_C" title="\u7121\u57A2\u306B\u559C\u3070\u3057\u3044\u3001\u7D14\u7C8B\u3001\u7D20\u6734\u3001\u51FA\u767A">\u30CF\u9577\u8ABF (C)</option>
15762
- <option value="key_Db" title="\u60B2\u3057\u307F\u3001\u6182\u9B31\u306A\u3001\u7518\u7F8E\u306A\u611F\u50B7">\u5909\u30CB\u9577\u8ABF (D\u266D)</option>
15763
- <option value="key_D" title="\u610F\u6C17\u63DA\u3005\u3068\u3057\u305F\u3001\u52DD\u5229\u306E\u3001\u558A\u58F0">\u30CB\u9577\u8ABF (D)</option>
15764
- <option value="key_Eb" title="\u53B3\u3057\u3044\u3001\u304D\u3064\u3044\u3001\u305D\u308C\u3067\u3044\u3066\u611B\u306B\u6E80\u3061\u305F">\u5909\u30DB\u9577\u8ABF (E\u266D)</option>
15765
- <option value="key_E" title="\u3051\u3093\u304B\u3063\u65E9\u3044\u3001\u8352\u3005\u3057\u3044\u3001\u8F1D\u304B\u3057\u3044\u60C5\u71B1">\u30DB\u9577\u8ABF (E)</option>
15766
- <option value="key_F" title="\u6012\u308A\u72C2\u3063\u305F\u3001\u6C17\u6027\u306E\u8352\u3044\u3001\u4E00\u6642\u7684\u306A\u60B2\u5606">\u30D8\u9577\u8ABF (F)</option>
15767
- <option value="key_Gb" title="\u56F0\u96E3\u306E\u6253\u7834\u3001\u5B89\u5835\u306E\u305F\u3081\u606F\u3001\u51F1\u65CB">\u5909\u30C8\u9577\u8ABF (G\u266D)</option>
15768
- <option value="key_G" title="\u53B3\u7C9B\u306A\u3001\u5D07\u9AD8\u306A\u3001\u5E7B\u60F3\u3001\u8AA0\u5B9F">\u30C8\u9577\u8ABF (G)</option>
15769
- <option value="key_Ab" title="\u6B7B\u3001\u6C38\u9060\u3001\u88C1\u304D\u3001\u6DF1\u9060\u306A\u7791\u60F3">\u5909\u30A4\u9577\u8ABF (A\u266D)</option>
15770
- <option value="key_A" title="\u3046\u308C\u3057\u3044\u3001\u7267\u6B4C\u7684\u306A\u3001\u611B\u306E\u544A\u767D">\u30A4\u9577\u8ABF (A)</option>
15771
- <option value="key_Bb" title="\u559C\u3070\u3057\u3044\u3001\u98A8\u5909\u308F\u308A\u306A\u3001\u967D\u6C17\u306A\u3001\u8EFD\u5FEB">\u5909\u30ED\u9577\u8ABF (B\u266D)</option>
15772
- <option value="key_B" title="\u3069\u304E\u3064\u3044\u3001\u5F37\u70C8\u306A\u3001\u8352\u3063\u307D\u3044\u3001\u731B\u70C8">\u30ED\u9577\u8ABF (B)</option>
15773
- </optgroup>
15774
- <optgroup label="\u77ED\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
15775
- <option value="key_Am" title="\u67D4\u3089\u304B\u306A\u3001\u7269\u60B2\u3057\u3044\u3001\u656C\u8654\u306A\u3001\u7D20\u6734\u306A\u54C0\u6101">\u30A4\u77ED\u8ABF (Am)</option>
15776
- <option value="key_Bbm" title="\u6050\u308D\u3057\u3044\u3001\u6697\u95C7\u3001\u5632\u308B\u3088\u3046\u306A\u3001\u4E0D\u6C17\u5473">\u5909\u30ED\u77ED\u8ABF (B\u266Dm)</option>
15777
- <option value="key_Bm" title="\u5B64\u72EC\u306A\u3001\u6182\u9B31\u306A\u3001\u5FCD\u8010\u3001\u9759\u304B\u306A\u8AE6\u5FF5">\u30ED\u77ED\u8ABF (Bm)</option>
15778
- <option value="key_Cm" title="\u7D14\u7C8B\u306B\u60B2\u3057\u3052\u306A\u3001\u604B\u308F\u305A\u3089\u3044\u306E\u3001\u60B2\u5287\u7684">\u30CF\u77ED\u8ABF (Cm)</option>
15779
- <option value="key_Csm" title="\u843D\u80C6\u3001\u6CE3\u304D\u53EB\u3093\u3060\u3001\u60B2\u6D99\u306E\u3001\u6DF1\u3044\u5606\u304D">\u5B30\u30CF\u77ED\u8ABF (C\u266Fm)</option>
15780
- <option value="key_Dm" title="\u53B3\u7C9B\u306A\u3001\u656C\u8654\u306A\u3001\u601D\u7D22\u7684\u306A\u3001\u91CD\u539A\u306A\u7948\u308A">\u30CB\u77ED\u8ABF (Dm)</option>
15781
- <option value="key_Ebm" title="\u6DF1\u3044\u82E6\u60A9\u3001\u5B9F\u5B58\u7684\u306A\u4E0D\u5B89\u3001\u6226\u6144">\u5909\u30DB\u77ED\u8ABF (E\u266Dm)</option>
15782
- <option value="key_Em" title="\u5F31\u3005\u3057\u3044\u3001\u306A\u307E\u3081\u304B\u3057\u3044\u3001\u843D\u3061\u7740\u304D\u306E\u306A\u3044">\u30DB\u77ED\u8ABF (Em)</option>
15783
- <option value="key_Fm" title="\u307C\u3093\u3084\u308A\u3057\u305F\u3001\u7269\u60B2\u3057\u3044\u3001\u3057\u3081\u3084\u304B\u306A\u3001\u846C\u9001">\u30D8\u77ED\u8ABF (Fm)</option>
15784
- <option value="key_Fsm" title="\u9670\u6C17\u306A\u3001\u6FC0\u3057\u3044\u61A4\u308A\u3001\u6697\u3044\u60C5\u5FF5">\u5B30\u30D8\u77ED\u8ABF (F\u266Fm)</option>
15785
- <option value="key_Gm" title="\u4E0D\u6E80\u3001\u4E0D\u5B89\u3001\u3084\u308B\u305B\u306A\u3055\u3001\u60B2\u75DB\u306A\u53EB\u3073">\u30C8\u77ED\u8ABF (Gm)</option>
15786
- <option value="key_Abm" title="\u4E0D\u670D\u306A\u3001\u5606\u304D\u306E\u3001\u6CE3\u304D\u53EB\u3093\u3060">\u5909\u30A4\u77ED\u8ABF (A\u266Dm)</option>
15787
- </optgroup>
15788
- </select>
15789
- <span class="dtm-grow"></span>
15790
- <span class="dtm-hint" data-dtm="compose-key-hint"></span>
15791
16210
  </div>
15792
16211
  <div class="dtm-row">
15793
16212
  <span class="dtm-label">\u5168\u4F53\u30B7\u30D5\u30C8</span>
@@ -15985,6 +16404,7 @@ var buildUI = (target, options) => {
15985
16404
  transposeApplyBtn: sel("transpose-apply"),
15986
16405
  transposeInfoBtn: sel("transpose-info"),
15987
16406
  macroCompose: sel("macro-compose"),
16407
+ composeTemplate: sel("compose-template"),
15988
16408
  composeSections: sel("compose-sections"),
15989
16409
  composeSectionsLen: sel("compose-sections-len"),
15990
16410
  composeKey: sel("compose-key"),
@@ -16127,6 +16547,45 @@ var INSTRUMENT_PRESETS = {
16127
16547
  }
16128
16548
  };
16129
16549
 
16550
+ // src/macro-state.ts
16551
+ var MACRO_STORAGE_KEYS = {
16552
+ template: "dtm-macro:template",
16553
+ sections: "dtm-macro:sections",
16554
+ key: "dtm-macro:key",
16555
+ shift: "dtm-macro:shift",
16556
+ transpose: "dtm-macro:transpose"
16557
+ };
16558
+ var readMacroSetting = (key) => {
16559
+ try {
16560
+ if (typeof localStorage === "undefined" || !localStorage) return null;
16561
+ return localStorage.getItem(MACRO_STORAGE_KEYS[key]);
16562
+ } catch (_) {
16563
+ return null;
16564
+ }
16565
+ };
16566
+ var writeMacroSetting = (key, value) => {
16567
+ try {
16568
+ if (typeof localStorage === "undefined" || !localStorage) return;
16569
+ localStorage.setItem(MACRO_STORAGE_KEYS[key], value);
16570
+ } catch (_) {
16571
+ }
16572
+ };
16573
+ var readMacroSections = () => {
16574
+ const raw = readMacroSetting("sections");
16575
+ if (!raw) return null;
16576
+ try {
16577
+ const parsed = JSON.parse(raw);
16578
+ if (Array.isArray(parsed) && parsed.every((x2) => typeof x2 === "string")) {
16579
+ return parsed;
16580
+ }
16581
+ } catch (_) {
16582
+ }
16583
+ return null;
16584
+ };
16585
+ var writeMacroSections = (sections) => {
16586
+ writeMacroSetting("sections", JSON.stringify(sections));
16587
+ };
16588
+
16130
16589
  // src/macros.ts
16131
16590
  var SCALES = [
16132
16591
  [0, 2, 4, 5, 7, 9, 11],
@@ -18162,7 +18621,7 @@ var COMPOSE_INFO_HTML = `
18162
18621
  <tr><td>\u30A2\u30A6\u30C8\u30ED</td><td>\u6709\u308A</td><td>\u4F4E\u3081</td><td>\u8584\u3044</td><td>\u4E3B\u97F3\u3078\u7740\u5730</td></tr>
18163
18622
  </table>
18164
18623
  <p>\u30A4\u30F3\u30C8\u30ED\u3068\u9593\u594F\u306F\u30E1\u30ED\u30C7\u30A3\u3092\u66F8\u304D\u307E\u305B\u3093\uFF08\u4F34\u594F\u30FB\u30D9\u30FC\u30B9\u30FB\u30C9\u30E9\u30E0\u30FB\u30B5\u30D6\u30E1\u30ED\u3060\u3051\u304C\u9CF4\u308A\u307E\u3059\uFF09\u3002\u3053\u3053\u306B\u6B4C\u30E1\u30ED\u3092\u7F6E\u304F\u3068\u3001\u3069\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u3082\u540C\u3058\u9854\u306B\u306A\u3063\u3066\u300C\u305A\u3063\u3068\u6B4C\u3063\u3066\u3044\u308B\u66F2\u300D\u306B\u306A\u3063\u3066\u3057\u307E\u3046\u305F\u3081\u3067\u3059\u3002\u30BB\u30AF\u30B7\u30E7\u30F3\u306E\u982D\u3067\u306F\u30C9\u30E9\u30E0\u304C\u30AF\u30E9\u30C3\u30B7\u30E5\u3092\u53E9\u304D\u3001\u7D42\u308F\u308A\u306E1\u3064\u624B\u524D\u3067\u30D5\u30A3\u30EB\u304C\u5165\u308A\u307E\u3059\u3002</p>
18165
- <p>\u30CE\u30FC\u30C8\u3092\u914D\u7F6E\u3059\u308B\u3060\u3051\u3067\u3001\u697D\u5668\u30FB\u97F3\u91CF\u30FB\u30DF\u30C3\u30AF\u30B9\u306F\u5909\u3048\u307E\u305B\u3093\u3002\u97F3\u306E\u30D0\u30E9\u30F3\u30B9\u3092\u6574\u3048\u305F\u3044\u3068\u304D\u306F\u300C\u5168\u4F53\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A\u300D\u306E<strong>\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0</strong>\u3092\u7D9A\u3051\u3066\u62BC\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30B7\u30F3\u30D7\u30EB\u30E2\u30FC\u30C9\u306F\u30C8\u30E9\u30C3\u30AF\u305D\u306E\u3082\u306E\u304C\u5F79\u5272\uFF08\u30E1\u30ED\u30C7\u30A3\uFF0F\u30B5\u30D6\u30E1\u30ED\uFF0F\u30D9\u30FC\u30B9\uFF0F\u4F34\u594F\uFF09\u306A\u306E\u3067\u3001\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0\u306F\u305D\u306E\u5F79\u5272\u3069\u304A\u308A\u306B\u697D\u5668\u3068\u97F3\u91CF\u3092\u5F53\u3066\u307E\u3059\u3002</p>
18624
+ <p>\u30CE\u30FC\u30C8\u306E\u914D\u7F6E\u306B\u52A0\u3048\u3066\u3001\u30C6\u30F3\u30DD\u30FB\u30C9\u30E9\u30E0\u30FB\u697D\u5668\u30FB<strong>\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0</strong>\uFF08\u5404\u30C8\u30E9\u30C3\u30AF\u306E\u97F3\u91CF\u30D0\u30E9\u30F3\u30B9\u30FB\u5B9A\u4F4D\u30FBEQ\u30FB\u30B3\u30F3\u30D7\u30EC\u30C3\u30B5\u30FC\u30FB\u30EA\u30D0\u30FC\u30D6\u30FB\u30DE\u30B9\u30BF\u8A2D\u5B9A\uFF09\u3082\u81EA\u52D5\u3067\u4E00\u62EC\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002\u3067\u304D\u305F\u5F8C\u306B\u5FAE\u8ABF\u6574\u3057\u305F\u3044\u3068\u304D\u306F\u3001\u5404\u30C8\u30E9\u30C3\u30AF\u306E\u8A2D\u5B9A\u3084\u300C\u5168\u4F53\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A\u300D\u304B\u3089\u81EA\u7531\u306B\u5909\u66F4\u3067\u304D\u307E\u3059\u3002</p>
18166
18625
  <h4>\u66F2\u306E\u7D44\u307F\u7ACB\u3066\u65B9</h4>
18167
18626
  <p>\u5358\u7D14\u306B\u30E9\u30F3\u30C0\u30E0\u306A\u97F3\u3092\u4E26\u3079\u3066\u3044\u308B\u308F\u3051\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u6B21\u306E\u9806\u756A\u3067\u7D44\u307F\u7ACB\u3066\u3066\u3044\u307E\u3059\u3002</p>
18168
18627
  <ol>
@@ -18470,12 +18929,15 @@ var pickComposeVocal = (exclude) => {
18470
18929
  var ADVANCED_COMPOSE_LAYOUT = [
18471
18930
  { index: 0, part: "melody", octave: 0, volume: 104 },
18472
18931
  { index: 1, part: "melody", octave: 1, volume: 62 },
18473
- { index: 2, part: "submelody", octave: 0, volume: 88 },
18474
- { index: 3, part: "bass", octave: 0, volume: 92 },
18475
- { index: 4, part: "bass", octave: -1, volume: 58 },
18476
- { index: 5, part: "block", octave: 0, volume: 62 },
18477
- { index: 6, part: "arpeggio", octave: 0, volume: 54 },
18478
- { index: 7, part: "offbeat", octave: 0, volume: 50 }
18932
+ { index: 2, part: "harmony", octave: 0, volume: 82 },
18933
+ { index: 3, part: "submelody", octave: 0, volume: 86 },
18934
+ { index: 4, part: "bass", octave: 0, volume: 92 },
18935
+ { index: 5, part: "bass", octave: -1, volume: 58 },
18936
+ { index: 6, part: "pad", octave: 0, volume: 64 },
18937
+ { index: 7, part: "block", octave: 0, volume: 62 },
18938
+ { index: 8, part: "arpeggio", octave: 0, volume: 54 },
18939
+ { index: 9, part: "offbeat", octave: 0, volume: 50 },
18940
+ { index: 10, part: "uwamono", octave: 1, volume: 56 }
18479
18941
  ];
18480
18942
  var LYRIC_MODEL_CATEGORIES = [
18481
18943
  {
@@ -18765,6 +19227,7 @@ var mountDAW = (target, options = {}) => {
18765
19227
  options.onDrumFontChange?.(font);
18766
19228
  };
18767
19229
  let currentInstrument = "";
19230
+ let autoComposeInstrument = null;
18768
19231
  let activeTrackId = options.initialActiveTrack ?? trackConfigs[0].id;
18769
19232
  let trackFxAdvancedOpen = readPanelOpen("track-fx-advanced") ?? false;
18770
19233
  let lyricAdvancedOpen = readPanelOpen("lyric-advanced") ?? false;
@@ -19802,8 +20265,8 @@ var mountDAW = (target, options = {}) => {
19802
20265
  redrawAll();
19803
20266
  },
19804
20267
  onEnd: (interrupted) => {
19805
- options.onScheduleFade?.(null);
19806
20268
  if (interrupted) {
20269
+ options.onScheduleFade?.(null);
19807
20270
  playbackState = "paused";
19808
20271
  pausedPlayStep = currentPlayStep;
19809
20272
  } else {
@@ -21758,7 +22221,7 @@ var mountDAW = (target, options = {}) => {
21758
22221
  refs.autoMasterInfoBtn.addEventListener("click", () => {
21759
22222
  showModal("\u304A\u307E\u304B\u305B\u30DE\u30B9\u30BF\u30EA\u30F3\u30B0\u89E3\u8AAC", AUTO_MASTER_INFO_HTML);
21760
22223
  });
21761
- refs.autoMasterBtn.addEventListener("click", () => {
22224
+ const applyAutoMastering = () => {
21762
22225
  if (observedPlayMs >= PEAK_SAMPLE_MIN_PLAY_MS && observedPeakMax > 0) {
21763
22226
  const targetPeak = 0.5;
21764
22227
  const scale = targetPeak / observedPeakMax;
@@ -21911,6 +22374,9 @@ var mountDAW = (target, options = {}) => {
21911
22374
  }
21912
22375
  }
21913
22376
  updateTrackPanel();
22377
+ };
22378
+ refs.autoMasterBtn.addEventListener("click", () => {
22379
+ applyAutoMastering();
21914
22380
  });
21915
22381
  refs.drumSelect.addEventListener("change", () => {
21916
22382
  currentDrumPattern = refs.drumSelect.value;
@@ -21928,12 +22394,9 @@ var mountDAW = (target, options = {}) => {
21928
22394
  refs.macroComposeInfo.addEventListener("click", () => {
21929
22395
  showModal("\u4F5C\u66F2\u306E\u89E3\u8AAC", COMPOSE_INFO_HTML);
21930
22396
  });
21931
- const bassTracksForCompose = () => {
21932
- if (!isAdvanced) {
21933
- const t = trackStates.find((x2) => x2.config.id === "bass");
21934
- return t ? [t] : [];
21935
- }
21936
- return ADVANCED_COMPOSE_LAYOUT.filter((l) => l.part === "bass").map((l) => trackStates[l.index]).filter((t) => !!t);
22397
+ const selectedComposeTemplate = () => {
22398
+ const val = refs.composeTemplate?.value;
22399
+ return val && val !== "custom" ? val : void 0;
21937
22400
  };
21938
22401
  const selectedComposeSections = () => {
21939
22402
  const boxes = [
@@ -21945,11 +22408,97 @@ var mountDAW = (target, options = {}) => {
21945
22408
  return picked.length > 0 ? picked : DEFAULT_SECTIONS;
21946
22409
  };
21947
22410
  const updateComposeSectionsLen = () => {
21948
- const plan = buildSectionPlan(selectedComposeSections());
22411
+ const tmpl = selectedComposeTemplate();
22412
+ const plan = buildSectionPlan(selectedComposeSections(), tmpl);
21949
22413
  const bars = plan.reduce((sum, x2) => sum + x2.bars, 0);
21950
22414
  refs.composeSectionsLen.textContent = `${bars}\u5C0F\u7BC0`;
21951
22415
  };
21952
- refs.composeSections.addEventListener("change", updateComposeSectionsLen);
22416
+ const savedTemplate = readMacroSetting("template");
22417
+ if (savedTemplate && refs.composeTemplate) {
22418
+ const hasOption = Array.from(refs.composeTemplate.options).some(
22419
+ (opt) => opt.value === savedTemplate
22420
+ );
22421
+ if (hasOption) {
22422
+ refs.composeTemplate.value = savedTemplate;
22423
+ }
22424
+ }
22425
+ const savedSections = readMacroSections();
22426
+ if (savedSections && refs.composeSections) {
22427
+ const boxes = refs.composeSections.querySelectorAll(
22428
+ 'input[type="checkbox"]'
22429
+ );
22430
+ const validValues = new Set(savedSections);
22431
+ for (const box of boxes) {
22432
+ box.checked = validValues.has(box.value);
22433
+ }
22434
+ } else if (savedTemplate && savedTemplate !== "custom" && refs.composeSections) {
22435
+ const tmpl = STRUCTURE_TEMPLATES.find((t) => t.name === savedTemplate);
22436
+ if (tmpl) {
22437
+ const planSet = new Set(tmpl.plan);
22438
+ const boxes = refs.composeSections.querySelectorAll(
22439
+ 'input[type="checkbox"]'
22440
+ );
22441
+ for (const box of boxes) {
22442
+ box.checked = planSet.has(box.value);
22443
+ }
22444
+ }
22445
+ }
22446
+ const savedKey = readMacroSetting("key");
22447
+ if (savedKey && refs.composeKey) {
22448
+ const hasOption = Array.from(refs.composeKey.options).some(
22449
+ (opt) => opt.value === savedKey
22450
+ );
22451
+ if (hasOption) {
22452
+ refs.composeKey.value = savedKey;
22453
+ }
22454
+ }
22455
+ const savedShift = readMacroSetting("shift");
22456
+ if (savedShift && refs.shiftSelect) {
22457
+ const hasOption = Array.from(refs.shiftSelect.options).some(
22458
+ (opt) => opt.value === savedShift
22459
+ );
22460
+ if (hasOption) {
22461
+ refs.shiftSelect.value = savedShift;
22462
+ }
22463
+ }
22464
+ const savedTranspose = readMacroSetting("transpose");
22465
+ if (savedTranspose && refs.transposeSelect) {
22466
+ const hasOption = Array.from(refs.transposeSelect.options).some(
22467
+ (opt) => opt.value === savedTranspose
22468
+ );
22469
+ if (hasOption) {
22470
+ refs.transposeSelect.value = savedTranspose;
22471
+ }
22472
+ }
22473
+ refs.composeSections.addEventListener("change", () => {
22474
+ if (refs.composeTemplate) {
22475
+ refs.composeTemplate.value = "custom";
22476
+ writeMacroSetting("template", "custom");
22477
+ }
22478
+ writeMacroSections(selectedComposeSections());
22479
+ updateComposeSectionsLen();
22480
+ });
22481
+ const composeTemplate = refs.composeTemplate;
22482
+ if (composeTemplate) {
22483
+ composeTemplate.addEventListener("change", () => {
22484
+ writeMacroSetting("template", composeTemplate.value);
22485
+ const tmplName = selectedComposeTemplate();
22486
+ if (tmplName) {
22487
+ const tmpl = STRUCTURE_TEMPLATES.find((t) => t.name === tmplName);
22488
+ if (tmpl) {
22489
+ const planSet = new Set(tmpl.plan);
22490
+ const boxes = refs.composeSections.querySelectorAll(
22491
+ 'input[type="checkbox"]'
22492
+ );
22493
+ for (const box of boxes) {
22494
+ box.checked = planSet.has(box.value);
22495
+ }
22496
+ }
22497
+ }
22498
+ writeMacroSections(selectedComposeSections());
22499
+ updateComposeSectionsLen();
22500
+ });
22501
+ }
21953
22502
  updateComposeSectionsLen();
21954
22503
  const updateComposeKeyHint = () => {
21955
22504
  if (!refs.composeKey || !refs.composeKeyHint) return;
@@ -21957,17 +22506,33 @@ var mountDAW = (target, options = {}) => {
21957
22506
  refs.composeKeyHint.textContent = desc;
21958
22507
  refs.composeKeyHint.title = desc;
21959
22508
  };
21960
- if (refs.composeKey) {
21961
- refs.composeKey.addEventListener("change", updateComposeKeyHint);
22509
+ const composeKey = refs.composeKey;
22510
+ if (composeKey) {
22511
+ composeKey.addEventListener("change", () => {
22512
+ writeMacroSetting("key", composeKey.value);
22513
+ updateComposeKeyHint();
22514
+ });
21962
22515
  updateComposeKeyHint();
21963
22516
  }
22517
+ if (refs.shiftSelect) {
22518
+ refs.shiftSelect.addEventListener("change", () => {
22519
+ writeMacroSetting("shift", refs.shiftSelect.value);
22520
+ });
22521
+ }
22522
+ if (refs.transposeSelect) {
22523
+ refs.transposeSelect.addEventListener("change", () => {
22524
+ writeMacroSetting("transpose", refs.transposeSelect.value);
22525
+ });
22526
+ }
21964
22527
  const runCompose = (withVocal) => {
21965
22528
  stop();
21966
22529
  overlayDuring(() => {
22530
+ const tmpl = selectedComposeTemplate();
21967
22531
  const song = composeSong({
21968
22532
  stepsPerBar: renderConfig.stepsPerBar,
21969
22533
  edo: renderConfig.edo,
21970
22534
  sections: selectedComposeSections(),
22535
+ template: tmpl,
21971
22536
  baseKey: refs.composeKey?.value ?? "any",
21972
22537
  // 直近に作った曲の特徴を渡すと、それらから離れた候補に加点される。
21973
22538
  // 「作曲」を続けて押したときに似た曲が並ぶのを防ぐ。
@@ -22006,7 +22571,19 @@ var mountDAW = (target, options = {}) => {
22006
22571
  for (const layer of ADVANCED_COMPOSE_LAYOUT) {
22007
22572
  const track = trackStates[layer.index];
22008
22573
  if (!track) continue;
22009
- const notes = layer.part === "melody" ? song.melody : layer.part === "submelody" ? song.submelody : layer.part === "bass" ? song.bass : buildChordPlacements({
22574
+ const notes = layer.part === "melody" ? song.melody : layer.part === "harmony" ? song.harmony : layer.part === "submelody" ? song.submelody : layer.part === "bass" ? song.bass : layer.part === "pad" ? song.pad : layer.part === "uwamono" ? buildChordPlacements({
22575
+ edo: renderConfig.edo,
22576
+ chordStr: song.chordProgression,
22577
+ patternType: "arpeggio",
22578
+ rootShift: song.rootShift,
22579
+ bpm: song.bpm,
22580
+ stepsPerBar: renderConfig.stepsPerBar
22581
+ }).map((p) => ({
22582
+ startStep: p.startStep,
22583
+ pitchUnits: p.pitchUnits,
22584
+ durationSteps: p.durationSteps,
22585
+ velocity: Math.max(30, p.velocity - 14)
22586
+ })) : buildChordPlacements({
22010
22587
  edo: renderConfig.edo,
22011
22588
  chordStr: song.chordProgression,
22012
22589
  patternType: layer.part,
@@ -22045,9 +22622,11 @@ var mountDAW = (target, options = {}) => {
22045
22622
  refs.drumSelect.value = song.drum;
22046
22623
  options.onDrumChange?.(song.drum);
22047
22624
  applyDrumPatternFont(song.drum);
22048
- for (const t of bassTracksForCompose()) {
22049
- t.trackCompression = AUTO_ROLE_MIX.bass.compression;
22050
- options.onTrackCompressionChange?.(t.config.id, t.trackCompression);
22625
+ const shouldAutoInstrument = !currentInstrument || currentInstrument === "auto" || currentInstrument === autoComposeInstrument;
22626
+ if (shouldAutoInstrument && song.instrument) {
22627
+ currentInstrument = song.instrument;
22628
+ autoComposeInstrument = song.instrument;
22629
+ options.onInstrumentChange?.(song.instrument);
22051
22630
  }
22052
22631
  if (withVocal) {
22053
22632
  const melodyTrack = isAdvanced ? trackStates[0] : trackStates.find((t) => t.config.id === "melody");
@@ -22063,6 +22642,7 @@ var mountDAW = (target, options = {}) => {
22063
22642
  fireLyricsChange(melodyTrack);
22064
22643
  }
22065
22644
  }
22645
+ applyAutoMastering();
22066
22646
  if (refs.composeKeyHint) {
22067
22647
  refs.composeKeyHint.textContent = `${song.keyLabel} \u3067\u4F5C\u6210`;
22068
22648
  refs.composeKeyHint.title = `${song.keyLabel}${song.moodLabel ? `\uFF08${song.moodLabel}\uFF09` : ""}`;
@@ -23234,6 +23814,49 @@ var mountDAW = (target, options = {}) => {
23234
23814
  };
23235
23815
  };
23236
23816
 
23817
+ // src/global-state.ts
23818
+ var GLOBAL_STORAGE_KEYS = {
23819
+ edo: "dtm-global:edo",
23820
+ loop: "dtm-global:loop",
23821
+ masterVolume: "dtm-global:master-volume",
23822
+ masterComp: "dtm-global:master-comp",
23823
+ reverbAmount: "dtm-global:reverb-amount",
23824
+ reverbDecay: "dtm-global:reverb-decay",
23825
+ reverbPreDelay: "dtm-global:reverb-predelay",
23826
+ delayAmount: "dtm-global:delay-amount",
23827
+ delayDivision: "dtm-global:delay-division",
23828
+ fadeIn: "dtm-global:fade-in",
23829
+ fadeOut: "dtm-global:fade-out"
23830
+ };
23831
+ var readGlobalSetting = (key) => {
23832
+ try {
23833
+ if (typeof localStorage === "undefined" || !localStorage) return null;
23834
+ return localStorage.getItem(GLOBAL_STORAGE_KEYS[key]);
23835
+ } catch (_) {
23836
+ return null;
23837
+ }
23838
+ };
23839
+ var writeGlobalSetting = (key, value) => {
23840
+ try {
23841
+ if (typeof localStorage === "undefined" || !localStorage) return;
23842
+ localStorage.setItem(GLOBAL_STORAGE_KEYS[key], value);
23843
+ } catch (_) {
23844
+ }
23845
+ };
23846
+ var readGlobalNumber = (key, min, max) => {
23847
+ const raw = readGlobalSetting(key);
23848
+ if (raw === null) return null;
23849
+ const n = Number.parseFloat(raw);
23850
+ if (Number.isNaN(n)) return null;
23851
+ return Math.max(min, Math.min(max, n));
23852
+ };
23853
+ var readGlobalBool = (key) => {
23854
+ const raw = readGlobalSetting(key);
23855
+ if (raw === "1" || raw === "true") return true;
23856
+ if (raw === "0" || raw === "false") return false;
23857
+ return null;
23858
+ };
23859
+
23237
23860
  // src/headless-singing-player.ts
23238
23861
  var STEPS_PER_BEAT4 = 48;
23239
23862
  var STEPS_PER_BAR3 = 192;
@@ -24318,6 +24941,8 @@ var createDtmStudio = async (options = {}) => {
24318
24941
  if (fadeOutStartAt !== void 0 && fadeOutEndAt !== void 0) {
24319
24942
  fadeGain.gain.setValueAtTime(1, fadeOutStartAt);
24320
24943
  fadeGain.gain.linearRampToValueAtTime(0, fadeOutEndAt);
24944
+ fadeGain.gain.setValueAtTime(0, fadeOutEndAt + 2);
24945
+ fadeGain.gain.setValueAtTime(1, fadeOutEndAt + 2);
24321
24946
  }
24322
24947
  };
24323
24948
  const clipMeter = createClipMeter(audioCtx, glueMakeup);
@@ -24589,8 +25214,15 @@ var createDtmStudio = async (options = {}) => {
24589
25214
  await listReady;
24590
25215
  nameToKey = await buildNameToKeyMapping();
24591
25216
  await Promise.all([drumReady, loadPreset(defaultPreset)]);
25217
+ const restoreFadeGainIfMuted = () => {
25218
+ if (fadeGain.gain.value < 1) {
25219
+ fadeGain.gain.cancelScheduledValues(audioCtx.currentTime);
25220
+ fadeGain.gain.setValueAtTime(1, audioCtx.currentTime);
25221
+ }
25222
+ };
24592
25223
  const playDrum = (e) => {
24593
25224
  if (!sfDrum.font) return;
25225
+ if (e.when === 0) restoreFadeGainIfMuted();
24594
25226
  if (KICK_PITCHES.has(e.pitch)) duckBassForKick(e.when);
24595
25227
  sfDrum.play({
24596
25228
  ctx: audioCtx,
@@ -24634,6 +25266,7 @@ var createDtmStudio = async (options = {}) => {
24634
25266
  }
24635
25267
  }
24636
25268
  const playNote4 = (e) => {
25269
+ if (e.when === 0) restoreFadeGainIfMuted();
24637
25270
  const { trackIdx, role } = resolveTrackIdxAndRole(e.trackId);
24638
25271
  const overrideKey = trackIdx >= 0 ? trackInstOverrides.get(trackIdx) : void 0;
24639
25272
  let sfInst;
@@ -24659,12 +25292,26 @@ var createDtmStudio = async (options = {}) => {
24659
25292
  duration: e.duration
24660
25293
  });
24661
25294
  };
25295
+ let daw;
24662
25296
  let presetSelect = null;
24663
25297
  const handleInstrumentChange = (key) => {
24664
25298
  editorPreset = key;
24665
25299
  if (presetSelect) {
24666
25300
  presetSelect.setValue(key);
24667
25301
  }
25302
+ if (INSTRUMENT_PRESETS[key]) {
25303
+ const wasPlaying = daw?.getPlaybackState() === "playing";
25304
+ if (wasPlaying) daw.pause();
25305
+ daw?.setLoading?.(true);
25306
+ void loadPreset(
25307
+ key,
25308
+ trackIds,
25309
+ isAdvancedMode ? "advanced" : "simple"
25310
+ ).finally(() => {
25311
+ daw?.setLoading?.(false);
25312
+ if (wasPlaying) daw?.play();
25313
+ });
25314
+ }
24668
25315
  onInstrumentChange?.(key);
24669
25316
  };
24670
25317
  const handleTrackInstrumentChange = async (trackIndex, instrumentName) => {
@@ -24678,7 +25325,6 @@ var createDtmStudio = async (options = {}) => {
24678
25325
  trackInstOverrides.set(trackIndex, key);
24679
25326
  await loadInstrument(key);
24680
25327
  };
24681
- let daw;
24682
25328
  const base = {
24683
25329
  getAudioTime: () => audioCtx.currentTime,
24684
25330
  onResumeAudio: async () => {
@@ -25429,6 +26075,7 @@ var createDtmStudio = async (options = {}) => {
25429
26075
  EDO31_NAMES,
25430
26076
  FADE_IN_MARK,
25431
26077
  FADE_OUT_MARK,
26078
+ GLOBAL_STORAGE_KEYS,
25432
26079
  GM_INSTRUMENT_NAMES,
25433
26080
  INSTRUMENT_PRESETS,
25434
26081
  KEY_COUNT,
@@ -25437,6 +26084,7 @@ var createDtmStudio = async (options = {}) => {
25437
26084
  KOE_VOICEBANK_LABELS,
25438
26085
  KOE_VOICEBANK_TERMS,
25439
26086
  LinkedList,
26087
+ MACRO_STORAGE_KEYS,
25440
26088
  MAJOR_KEY_IDS,
25441
26089
  MAX_VOCAL_VOLUME,
25442
26090
  MICRO_STEP,
@@ -25539,6 +26187,11 @@ var createDtmStudio = async (options = {}) => {
25539
26187
  playNote,
25540
26188
  playPlacements,
25541
26189
  playSingingMML,
26190
+ readGlobalBool,
26191
+ readGlobalNumber,
26192
+ readGlobalSetting,
26193
+ readMacroSections,
26194
+ readMacroSetting,
25542
26195
  resolveComposeKey,
25543
26196
  resolveDrumPattern,
25544
26197
  resolveLoopPoint,
@@ -25561,5 +26214,8 @@ var createDtmStudio = async (options = {}) => {
25561
26214
  unitsToMidi,
25562
26215
  unitsToMidiDetune,
25563
26216
  unitsToPitchV1,
25564
- vocalVolumeToGain
26217
+ vocalVolumeToGain,
26218
+ writeGlobalSetting,
26219
+ writeMacroSections,
26220
+ writeMacroSetting
25565
26221
  });