@onjmin/dtm 2.1.4 → 2.1.5

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
@@ -52,6 +52,7 @@ __export(index_exports, {
52
52
  KOE_VOICEBANK_LABELS: () => KOE_VOICEBANK_LABELS,
53
53
  KOE_VOICEBANK_TERMS: () => KOE_VOICEBANK_TERMS,
54
54
  LinkedList: () => LinkedList,
55
+ MACRO_STORAGE_KEYS: () => MACRO_STORAGE_KEYS,
55
56
  MAJOR_KEY_IDS: () => MAJOR_KEY_IDS,
56
57
  MAX_VOCAL_VOLUME: () => MAX_VOCAL_VOLUME,
57
58
  MICRO_STEP: () => MICRO_STEP,
@@ -154,6 +155,8 @@ __export(index_exports, {
154
155
  playNote: () => playNote,
155
156
  playPlacements: () => playPlacements,
156
157
  playSingingMML: () => playSingingMML,
158
+ readMacroSections: () => readMacroSections,
159
+ readMacroSetting: () => readMacroSetting,
157
160
  resolveComposeKey: () => resolveComposeKey,
158
161
  resolveDrumPattern: () => resolveDrumPattern,
159
162
  resolveLoopPoint: () => resolveLoopPoint,
@@ -176,7 +179,9 @@ __export(index_exports, {
176
179
  unitsToMidi: () => unitsToMidi,
177
180
  unitsToMidiDetune: () => unitsToMidiDetune,
178
181
  unitsToPitchV1: () => unitsToPitchV1,
179
- vocalVolumeToGain: () => vocalVolumeToGain
182
+ vocalVolumeToGain: () => vocalVolumeToGain,
183
+ writeMacroSections: () => writeMacroSections,
184
+ writeMacroSetting: () => writeMacroSetting
180
185
  });
181
186
  module.exports = __toCommonJS(index_exports);
182
187
 
@@ -4910,6 +4915,7 @@ var createSequencer = (options) => {
4910
4915
  let drumCursor = 0;
4911
4916
  let lastDrumStep = Number.NEGATIVE_INFINITY;
4912
4917
  let lastPlayStep = 0;
4918
+ let maxTimelineEndSec = 0;
4913
4919
  const secondsPerStep = () => 60 / options.getBpm() / STEPS_PER_BEAT;
4914
4920
  const getWrappedPlayStep = (time, sps) => {
4915
4921
  if (!isLooping || loopDurationSec <= 0 || time < loopEndSec) {
@@ -4936,6 +4942,7 @@ var createSequencer = (options) => {
4936
4942
  }
4937
4943
  const startLimit = isLooping ? Math.min(fromStep, loopStartStep) : fromStep;
4938
4944
  let maxEndStep = 0;
4945
+ maxTimelineEndSec = 0;
4939
4946
  for (const track of options.getTracks()) {
4940
4947
  trackVolumeMap.set(track.id, track.volume);
4941
4948
  for (const note of track.notes) {
@@ -4944,6 +4951,7 @@ var createSequencer = (options) => {
4944
4951
  const when = relativeStart * sps;
4945
4952
  const duration = note.durationSteps * sps;
4946
4953
  maxEndStep = Math.max(maxEndStep, note.startStep + note.durationSteps);
4954
+ maxTimelineEndSec = Math.max(maxTimelineEndSec, when + duration);
4947
4955
  timeline.push({
4948
4956
  trackId: track.id,
4949
4957
  pitch: note.pitchUnits,
@@ -5069,10 +5077,7 @@ var createSequencer = (options) => {
5069
5077
  lastPlayStep = currentStep2;
5070
5078
  }
5071
5079
  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) {
5080
+ if (nowIndex >= timeline.length && time > maxTimelineEndSec + 0.1) {
5076
5081
  stop();
5077
5082
  options.onEnd(false);
5078
5083
  }
@@ -8404,6 +8409,21 @@ var DAW_CSS = `
8404
8409
  .dtm-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
8405
8410
  .dtm-track-body { display: flex; flex-direction: column; gap: 10px; }
8406
8411
 
8412
+ /* \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 */
8413
+ .dtm-compose-container {
8414
+ background: var(--dtm-deep);
8415
+ border: 2px solid var(--c-black);
8416
+ box-shadow:
8417
+ inset 0 0 0 1px var(--dtm-border2),
8418
+ 0 0 0 1px var(--dtm-success),
8419
+ 3px 3px 0 var(--c-black);
8420
+ padding: 10px 12px 12px;
8421
+ display: flex;
8422
+ flex-direction: column;
8423
+ gap: 10px;
8424
+ margin-bottom: 6px;
8425
+ }
8426
+
8407
8427
  /* \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
8428
  .dtm-panel--track > summary::before,
8409
8429
  .dtm-panel--track[open] > summary::before {
@@ -13198,11 +13218,25 @@ var densityFeatures = (notes, opts) => {
13198
13218
  shortNoteRatio: short / notes.length
13199
13219
  };
13200
13220
  };
13201
- var tensionFeatures = (barTension) => {
13202
- if (barTension.length < 16) return { rise: 0, resolve: 0 };
13221
+ var tensionFeatures = (barTension, opts) => {
13222
+ if (barTension.length < 8) return { rise: 0, resolve: 0 };
13203
13223
  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));
13224
+ let a;
13225
+ let b;
13226
+ if (opts?.verseBars && opts?.chorusBars && opts.verseBars.length > 0 && opts.chorusBars.length > 0) {
13227
+ a = mean2(opts.verseBars.map((i2) => barTension[i2] ?? 0));
13228
+ b = mean2(opts.chorusBars.map((i2) => barTension[i2] ?? 0));
13229
+ } else if (barTension.length >= 16 && barTension.length <= 24) {
13230
+ a = mean2(barTension.slice(0, 4));
13231
+ b = mean2(barTension.slice(8, 12));
13232
+ } else {
13233
+ const q1Start = Math.floor(barTension.length * 0.15);
13234
+ const q1End = Math.max(q1Start + 2, Math.floor(barTension.length * 0.35));
13235
+ const q3Start = Math.floor(barTension.length * 0.55);
13236
+ const q3End = Math.max(q3Start + 2, Math.floor(barTension.length * 0.75));
13237
+ a = mean2(barTension.slice(q1Start, q1End));
13238
+ b = mean2(barTension.slice(q3Start, q3End));
13239
+ }
13206
13240
  const all = mean2(barTension);
13207
13241
  const last = barTension[barTension.length - 1];
13208
13242
  const rise = Math.max(0, Math.min(1, (b - a) / 0.5));
@@ -13249,6 +13283,8 @@ var SECTION_LABELS = {
13249
13283
  verse: "A\u30E1\u30ED",
13250
13284
  prechorus: "B\u30E1\u30ED",
13251
13285
  chorus: "\u30B5\u30D3",
13286
+ bridge: "C\u30E1\u30ED",
13287
+ drop_chorus: "\u843D\u3061\u30B5\u30D3",
13252
13288
  interlude: "\u9593\u594F",
13253
13289
  outro: "\u30A2\u30A6\u30C8\u30ED"
13254
13290
  };
@@ -13257,6 +13293,8 @@ var SECTION_ORDER = [
13257
13293
  "verse",
13258
13294
  "prechorus",
13259
13295
  "chorus",
13296
+ "bridge",
13297
+ "drop_chorus",
13260
13298
  "interlude",
13261
13299
  "outro"
13262
13300
  ];
@@ -13308,6 +13346,35 @@ var SECTION_SPECS = {
13308
13346
  // 主音へ全終止
13309
13347
  progression: "b"
13310
13348
  },
13349
+ // Cメロ。Verse/Pre chorusとは違うメロディで、ラストのサビ前に緊張感を持たせる。
13350
+ // 2番のサビの後に来ることが多い(参考: ONLIVE Studio blog)。
13351
+ // 専用のコード進行 "c" を持ち、AメロともBメロとも雰囲気が違う。
13352
+ bridge: {
13353
+ bars: 4,
13354
+ melody: true,
13355
+ registerShift: 2,
13356
+ density: 0.9,
13357
+ drumLevel: 1,
13358
+ landing: 1,
13359
+ // 解決しない音で止めてラスサビへ渡す
13360
+ progression: "c"
13361
+ },
13362
+ // 落ちサビ。サビのメロディを伴奏控えめに歌う。J-POPの王道パターン。
13363
+ // density と drumLevel だけ下げ、メロディはサビと同じものを使う。
13364
+ drop_chorus: {
13365
+ bars: 4,
13366
+ melody: true,
13367
+ registerShift: 4,
13368
+ // サビと同じ高さ
13369
+ density: 0.6,
13370
+ // 薄い(ここが「落ち」の実体)
13371
+ drumLevel: 0,
13372
+ // ドラム控えめ
13373
+ landing: 0,
13374
+ // 主音へ全終止
13375
+ progression: "b"
13376
+ // サビと同じ進行
13377
+ },
13311
13378
  interlude: {
13312
13379
  bars: 4,
13313
13380
  melody: false,
@@ -13327,18 +13394,120 @@ var SECTION_SPECS = {
13327
13394
  progression: "a"
13328
13395
  }
13329
13396
  };
13330
- var buildSectionPlan = (kinds) => {
13331
- const wanted = kinds.length > 0 ? kinds : DEFAULT_SECTIONS;
13332
- const ordered = SECTION_ORDER.filter((k) => wanted.includes(k));
13397
+ var STRUCTURE_TEMPLATES = [
13398
+ // 1コーラス(現行デフォルト、短い曲・初心者向け)
13399
+ {
13400
+ name: "1chorus",
13401
+ label: "1\u30B3\u30FC\u30E9\u30B9",
13402
+ plan: ["intro", "verse", "prechorus", "chorus"]
13403
+ },
13404
+ // JPOP王道(マリーゴールド型)
13405
+ // イントロ→Aメロ→Aメロ→Bメロ→サビ→Aメロ→Bメロ→サビ→Cメロ→間奏→サビ→アウトロ
13406
+ {
13407
+ name: "jpop_standard",
13408
+ label: "JPOP\u738B\u9053",
13409
+ plan: [
13410
+ "intro",
13411
+ "verse",
13412
+ "verse",
13413
+ "prechorus",
13414
+ "chorus",
13415
+ "verse",
13416
+ "prechorus",
13417
+ "chorus",
13418
+ "bridge",
13419
+ "interlude",
13420
+ "chorus",
13421
+ "outro"
13422
+ ]
13423
+ },
13424
+ // 落ちサビ入り(J-POP王道の変形。ラスサビ前に落ちサビ)
13425
+ {
13426
+ name: "jpop_drop",
13427
+ label: "\u843D\u3061\u30B5\u30D3\u5165\u308A",
13428
+ plan: [
13429
+ "intro",
13430
+ "verse",
13431
+ "verse",
13432
+ "prechorus",
13433
+ "chorus",
13434
+ "verse",
13435
+ "prechorus",
13436
+ "chorus",
13437
+ "bridge",
13438
+ "drop_chorus",
13439
+ "chorus",
13440
+ "outro"
13441
+ ]
13442
+ },
13443
+ // ボカロ王道(短め高速、Aメロ繰り返しなし)
13444
+ {
13445
+ name: "vocaloid",
13446
+ label: "\u30DC\u30AB\u30ED\u738B\u9053",
13447
+ plan: [
13448
+ "intro",
13449
+ "verse",
13450
+ "prechorus",
13451
+ "chorus",
13452
+ "verse",
13453
+ "prechorus",
13454
+ "chorus",
13455
+ "bridge",
13456
+ "chorus",
13457
+ "outro"
13458
+ ]
13459
+ },
13460
+ // Verse-Chorus形式(Bメロなし、洋楽的)
13461
+ {
13462
+ name: "verse_chorus",
13463
+ label: "Verse-Chorus",
13464
+ plan: [
13465
+ "intro",
13466
+ "verse",
13467
+ "chorus",
13468
+ "verse",
13469
+ "chorus",
13470
+ "bridge",
13471
+ "chorus",
13472
+ "outro"
13473
+ ]
13474
+ }
13475
+ ];
13476
+ var buildSectionPlan = (kinds, templateName) => {
13477
+ let ordered;
13478
+ if (templateName) {
13479
+ const tmpl = STRUCTURE_TEMPLATES.find((t) => t.name === templateName);
13480
+ ordered = tmpl ? tmpl.plan : DEFAULT_SECTIONS;
13481
+ } else {
13482
+ const wanted = kinds.length > 0 ? kinds : DEFAULT_SECTIONS;
13483
+ ordered = SECTION_ORDER.filter((k) => wanted.includes(k));
13484
+ }
13333
13485
  const plan = [];
13334
13486
  let bar = 0;
13487
+ const seen = /* @__PURE__ */ new Map();
13335
13488
  for (const kind of ordered) {
13336
13489
  const spec = SECTION_SPECS[kind];
13337
- plan.push({ kind, startBar: bar, bars: spec.bars, spec, keyShift: 0 });
13490
+ const count = seen.get(kind) ?? 0;
13491
+ plan.push({
13492
+ kind,
13493
+ startBar: bar,
13494
+ bars: spec.bars,
13495
+ spec,
13496
+ keyShift: 0,
13497
+ restatement: count > 0
13498
+ });
13499
+ seen.set(kind, count + 1);
13338
13500
  bar += spec.bars;
13339
13501
  }
13340
13502
  return plan;
13341
13503
  };
13504
+ var sectionAt = (plan, bar) => {
13505
+ for (const section of plan) {
13506
+ if (bar >= section.startBar && bar < section.startBar + section.bars)
13507
+ return section;
13508
+ }
13509
+ return plan[plan.length - 1];
13510
+ };
13342
13511
 
13343
13512
  // src/compose.ts
13344
13513
  var STEP_SEMITONES = 2;
@@ -13624,6 +13793,16 @@ var SECTION_A_PROGRESSIONS = [
13624
13793
  ["Am", "Dm7", "Bb", "E7"]
13625
13794
  // bVI からドミナントへ
13626
13795
  ];
13796
+ var SECTION_C_PROGRESSIONS = [
13797
+ ["Am", "Em", "F", "C"],
13798
+ ["Dm7", "Em7", "FM7", "G7"],
13799
+ ["Am7", "Dm7", "G7", "CM7"],
13800
+ ["F", "G", "Am", "G"],
13801
+ ["Dm7", "G7", "Em7", "Am"],
13802
+ ["FM7", "Em7", "Am7", "Dm7"],
13803
+ ["Am", "G", "FM7", "E7"],
13804
+ ["C", "Am", "FM7", "G7"]
13805
+ ];
13627
13806
  var SECTION_B_PROGRESSIONS = [
13628
13807
  ["F", "G", "Em7", "Am"],
13629
13808
  ["Dm7", "G7", "CM7", "A7"],
@@ -13850,10 +14029,9 @@ var HALF_BAR_FIGURES = [
13850
14029
  [EIGHTH, EIGHTH, EIGHTH, EIGHTH],
13851
14030
  // タタタタ
13852
14031
  [EIGHTH, EIGHTH, EIGHTH, EIGHTH],
13853
- [EIGHTH, EIGHTH, EIGHTH, EIGHTH],
13854
- [EIGHTH, EIGHTH, EIGHTH, EIGHTH],
13855
14032
  [QUARTER, EIGHTH, EIGHTH],
13856
14033
  // ター・タタ
14034
+ [QUARTER, EIGHTH, EIGHTH],
13857
14035
  [EIGHTH, EIGHTH, QUARTER],
13858
14036
  // タタ・ター
13859
14037
  [DOT_QUARTER, EIGHTH],
@@ -13861,8 +14039,24 @@ var HALF_BAR_FIGURES = [
13861
14039
  [DOT_QUARTER, EIGHTH],
13862
14040
  [EIGHTH, QUARTER, EIGHTH],
13863
14041
  // 頭抜き
14042
+ // 4分音符主体・ロングトーン(J-POPの歌い上げるバラード〜ミドル向け。音数過多を是正)
14043
+ [QUARTER, QUARTER],
14044
+ // ター・ター
14045
+ [QUARTER, QUARTER],
13864
14046
  [HALF],
13865
- // ロングトーン
14047
+ // ターーー
14048
+ [HALF],
14049
+ [-QUARTER, QUARTER],
14050
+ // 休・ター
14051
+ [QUARTER, -QUARTER],
14052
+ // ター・休
14053
+ // 弱起(アウフタクト)パターン:末尾から次へ飛び込む言い回し
14054
+ [-DOT_QUARTER, EIGHTH],
14055
+ // 3拍半休みからの裏拍アウフタクト
14056
+ [-QUARTER, EIGHTH, EIGHTH],
14057
+ // 2拍目裏からのアウフタクト(タタ)
14058
+ [QUARTER, -EIGHTH, EIGHTH],
14059
+ // ター・休タ(8分裏弱起)
13866
14060
  // 付点8分+16分(タッカ)。歌モノで非常に多用される跳ね・推進力の型。
13867
14061
  [DOT_EIGHTH, SIXTEENTH, EIGHTH, EIGHTH],
13868
14062
  [EIGHTH, EIGHTH, DOT_EIGHTH, SIXTEENTH],
@@ -14531,7 +14725,10 @@ var draw = (options, resolvedKey, rnd) => {
14531
14725
  };
14532
14726
  const quarterSteps = scaleStep(QUARTER);
14533
14727
  const strongStep = Math.max(1, Math.round(stepsPerBar / 2));
14534
- const sectionPlan = buildSectionPlan(options.sections ?? DEFAULT_SECTIONS);
14728
+ const sectionPlan = buildSectionPlan(
14729
+ options.sections ?? DEFAULT_SECTIONS,
14730
+ options.template
14731
+ );
14535
14732
  const totalBars = sectionPlan.reduce((sum, s) => sum + s.bars, 0);
14536
14733
  if (rnd() < 0.35) {
14537
14734
  const modType = pick(
@@ -14568,12 +14765,18 @@ var draw = (options, resolvedKey, rnd) => {
14568
14765
  (p) => p.join("|") !== progA.join("|")
14569
14766
  );
14570
14767
  const progB = pick(progBPool, rnd);
14768
+ const progC = pick(
14769
+ SECTION_C_PROGRESSIONS.filter(
14770
+ (p) => p.join("|") !== progA.join("|") && p.join("|") !== progB.join("|")
14771
+ ),
14772
+ rnd
14773
+ );
14571
14774
  const tonic = progA[0].startsWith("Am") ? "Am" : "C";
14572
14775
  const progHalf = pick(SECTION_A2_DERIVATIONS, rnd)(progA);
14573
14776
  const progFull = pick(SECTION_A3_DERIVATIONS, rnd)(progA, tonic);
14574
14777
  const progression = [];
14575
14778
  for (const section of sectionPlan) {
14576
- const base = section.spec.progression === "b" ? progB : progA;
14779
+ const base = section.spec.progression === "c" ? progC : section.spec.progression === "b" ? progB : progA;
14577
14780
  for (let i2 = 0; i2 < section.bars; i2 += 4) {
14578
14781
  const isLastPhrase = i2 + 4 >= section.bars;
14579
14782
  if (!isLastPhrase) {
@@ -14591,7 +14794,7 @@ var draw = (options, resolvedKey, rnd) => {
14591
14794
  const chordPattern = pick(CHORD_PATTERNS, rnd);
14592
14795
  const rootShift = resolvedKey.rootShift;
14593
14796
  const bpm = pick(BPM_CHOICES, rnd);
14594
- const sourceOf = (kind) => kind === "chorus" || kind === "interlude" ? "b" : kind === "prechorus" ? "a2" : "a";
14797
+ const sourceOf = (kind) => kind === "chorus" || kind === "interlude" || kind === "drop_chorus" ? "b" : kind === "prechorus" ? "a2" : kind === "bridge" ? "c" : "a";
14595
14798
  const units2 = [];
14596
14799
  for (const section of sectionPlan) {
14597
14800
  const unitCount = Math.max(1, Math.round(section.bars / 2));
@@ -14628,6 +14831,19 @@ var draw = (options, resolvedKey, rnd) => {
14628
14831
  const barInUnit = (bar) => bar % 2;
14629
14832
  const unitOf = (bar) => Math.floor(bar / 2);
14630
14833
  const restatementOf = (bar) => {
14834
+ const curSec = sectionAt(sectionPlan, bar);
14835
+ if (!curSec.spec.melody) return null;
14836
+ if (curSec.restatement) {
14837
+ const firstSec = sectionPlan.find(
14838
+ (s) => s.kind === curSec.kind && !s.restatement
14839
+ );
14840
+ if (firstSec) {
14841
+ const offset = bar - curSec.startBar;
14842
+ if (offset < firstSec.bars) {
14843
+ return firstSec.startBar + offset;
14844
+ }
14845
+ }
14846
+ }
14631
14847
  const u = unitOf(bar);
14632
14848
  if (units2[u].source === "silent") return null;
14633
14849
  for (let v = 0; v < u; v++) {
@@ -14657,10 +14873,9 @@ var draw = (options, resolvedKey, rnd) => {
14657
14873
  ),
14658
14874
  // 人間の曲の跳躍率は p25〜p75 で 0.40〜0.55。上限が低いとその帯へ届かない。
14659
14875
  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,
14876
+ // 界隈曲らしさ:調の外の音(クロマチック)や微小な逸脱を積極的に許容する。
14877
+ chromaticAffinity: rnd() < 0.1 ? 0 : 0.3 + rnd() * 0.6,
14878
+ barHeadWeight: rnd() < 0.5 ? 3 : 2,
14664
14879
  bassStyle: pick(
14665
14880
  [
14666
14881
  "quarter",
@@ -14686,7 +14901,7 @@ var draw = (options, resolvedKey, rnd) => {
14686
14901
  subInterval: pick([3, 4, 8, 9], rnd)
14687
14902
  };
14688
14903
  const motifPool = groovyCells(MOTIF_CELLS, style.groove, rnd);
14689
- const targetNotesPerBar = 5.5 + rnd() * 2.5;
14904
+ const targetNotesPerBar = 4 + rnd() * 4;
14690
14905
  const targetRestRatio = 0.02 + rnd() * 0.1;
14691
14906
  const cellNotes = (c) => c.value.filter((v) => v > 0).length;
14692
14907
  const cellRest = (c) => {
@@ -14698,27 +14913,49 @@ var draw = (options, resolvedKey, rnd) => {
14698
14913
  }
14699
14914
  return total === 0 ? 0 : rest / total;
14700
14915
  };
14701
- const cellDistance = (c) => Math.abs(cellNotes(c) - targetNotesPerBar) + Math.abs(cellRest(c) - targetRestRatio) * 8;
14702
- const pickCell = (pool) => {
14916
+ const cellDistance = (c, densityMul = 1) => Math.abs(cellNotes(c) - targetNotesPerBar * densityMul) + Math.abs(cellRest(c) - targetRestRatio / Math.max(0.5, densityMul)) * 8;
14917
+ const pickCell = (pool, densityMul = 1) => {
14703
14918
  let best = pick(pool, rnd);
14704
14919
  for (let i2 = 0; i2 < 2; i2++) {
14705
14920
  const c = pick(pool, rnd);
14706
- if (cellDistance(c) < cellDistance(best)) best = c;
14921
+ if (cellDistance(c, densityMul) < cellDistance(best, densityMul))
14922
+ best = c;
14707
14923
  }
14708
14924
  return best;
14709
14925
  };
14710
- const motifCell = pickCell(motifPool);
14711
- const motifCell2 = rnd() < 0.45 ? motifCell : pickCell(motifPool.filter((c) => c !== motifCell));
14926
+ const motifCell = pickCell(motifPool, 0.85);
14927
+ const motifCell2 = rnd() < 0.45 ? motifCell : pickCell(
14928
+ motifPool.filter((c) => c !== motifCell),
14929
+ 0.85
14930
+ );
14712
14931
  const motifB = pickCell(
14713
- motifPool.filter((c) => c !== motifCell && c !== motifCell2)
14932
+ motifPool.filter((c) => c !== motifCell && c !== motifCell2),
14933
+ 1.2
14934
+ );
14935
+ const motifB2 = pickCell(
14936
+ motifPool.filter((c) => c !== motifB),
14937
+ 1.2
14714
14938
  );
14715
- const motifB2 = pickCell(motifPool.filter((c) => c !== motifB));
14716
14939
  const motifA2 = pickCell(
14717
14940
  motifPool.filter(
14718
14941
  (c) => c !== motifCell && c !== motifCell2 && c !== motifB
14719
- )
14942
+ ),
14943
+ 1.1
14944
+ );
14945
+ const motifA22 = rnd() < 0.45 ? motifA2 : pickCell(
14946
+ motifPool.filter((c) => c !== motifA2),
14947
+ 1.1
14948
+ );
14949
+ const motifC = pickCell(
14950
+ motifPool.filter(
14951
+ (c) => c !== motifCell && c !== motifCell2 && c !== motifB && c !== motifA2
14952
+ ),
14953
+ 0.9
14954
+ );
14955
+ const motifC2 = rnd() < 0.45 ? motifC : pickCell(
14956
+ motifPool.filter((c) => c !== motifC),
14957
+ 0.9
14720
14958
  );
14721
- const motifA22 = rnd() < 0.45 ? motifA2 : pickCell(motifPool.filter((c) => c !== motifA2));
14722
14959
  const motifVar = pickCell(
14723
14960
  motifPool.filter((c) => c !== motifCell && c !== motifCell2)
14724
14961
  );
@@ -14771,12 +15008,13 @@ var draw = (options, resolvedKey, rnd) => {
14771
15008
  }
14772
15009
  const isB = units2[u].source === "b";
14773
15010
  const isA2 = units2[u].source === "a2";
15011
+ const isC = units2[u].source === "c";
14774
15012
  const isPrechorusEnd = units2[u].section.kind === "prechorus" && bar === units2[u].section.startBar + units2[u].section.bars - 1;
14775
15013
  if (isPrechorusEnd) {
14776
15014
  barRhythms.push(scaleCell(buildUpCell.value));
14777
15015
  continue;
14778
15016
  }
14779
- const pair = () => isB ? [motifB, motifB2] : isA2 ? [motifA2, motifA22] : [motifCell, motifCell2];
15017
+ const pair = () => isB ? [motifB, motifB2] : isA2 ? [motifA2, motifA22] : isC ? [motifC, motifC2] : [motifCell, motifCell2];
14780
15018
  let cell;
14781
15019
  if (units2[u].source === "answer") {
14782
15020
  const isPeriodEnd = units2[u].landing !== null;
@@ -14807,6 +15045,8 @@ var draw = (options, resolvedKey, rnd) => {
14807
15045
  const melody = [];
14808
15046
  const submelody = [];
14809
15047
  const bass = [];
15048
+ const harmony = [];
15049
+ const pad = [];
14810
15050
  const melodyDurations = [];
14811
15051
  const barTension = new Array(totalBars).fill(0);
14812
15052
  let restSteps = 0;
@@ -14853,13 +15093,22 @@ var draw = (options, resolvedKey, rnd) => {
14853
15093
  (bar + style.arcPhase) / style.arcPeriod * Math.PI * 2
14854
15094
  );
14855
15095
  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;
15096
+ const inUnit = barInUnit(bar);
15097
+ let headSemi;
15098
+ if (inUnit === 1 && bar > 0) {
15099
+ headSemi = nearestChordTone(prevSemi, tones, headWeight, isSectionB).semi;
15100
+ if (Math.abs(headSemi - prevSemi) > 4) {
15101
+ headSemi = nearestChordTone(prevSemi, tones, 1, isSectionB).semi;
15102
+ }
15103
+ } else {
15104
+ const headTarget = prevSemi + (arcCenter - prevSemi) * 0.5;
15105
+ headSemi = nearestChordTone(
15106
+ headTarget,
15107
+ tones,
15108
+ headWeight,
15109
+ isSectionB
15110
+ ).semi;
15111
+ }
14863
15112
  const contourOffset = barInUnit(bar) === 0 ? 0 : barRhythms[bar - 1].filter((v) => v > 0).length;
14864
15113
  const prevRole = bar > 0 ? barRoles[bar - 1] : null;
14865
15114
  const repeatShift = role === "motif" && (prevRole === "motif" || prevRole === "sequence" || prevRole === "climax") ? pick([-2, -1, 1, 2], rnd) : 0;
@@ -15039,6 +15288,48 @@ var draw = (options, resolvedKey, rnd) => {
15039
15288
  } else {
15040
15289
  pushSub(scaleStep(QUARTER), scaleStep(DOT_HALF), subSemi);
15041
15290
  }
15291
+ const barSec = sectionAt(sectionPlan, bar);
15292
+ if (!silent && (barSec.kind === "chorus" || barSec.kind === "drop_chorus")) {
15293
+ for (let i2 = 0; i2 < slots.length; i2++) {
15294
+ const hSemi = pitches[i2] + 3;
15295
+ const hTone = nearestChordTone(hSemi, tones, 2);
15296
+ const hClamped = clampSemi(hTone.semi, MELODY_LOW, MELODY_HIGH);
15297
+ const k = barKeyShift[bar];
15298
+ const fifthShift = k === 0 ? 0 : SEMITONE_TO_FIFTH_SHIFT[(k % 12 + 12) % 12];
15299
+ harmony.push({
15300
+ startStep: barStart + slots[i2].at,
15301
+ pitchUnits: spelledToUnits(
15302
+ hClamped + k,
15303
+ hTone.fifth + fifthShift,
15304
+ edo
15305
+ ),
15306
+ durationSteps: slots[i2].value,
15307
+ velocity: slots[i2].at === 0 ? 82 : 76
15308
+ });
15309
+ }
15310
+ }
15311
+ if (barSec.kind === "prechorus" || barSec.kind === "chorus" || barSec.kind === "bridge" || barSec.kind === "drop_chorus") {
15312
+ const padTone = nearestChordTone(
15313
+ MELODY_HIGH + 2,
15314
+ // メロディの上の音域
15315
+ tones,
15316
+ 2
15317
+ );
15318
+ const padSemi = clampSemi(padTone.semi, MELODY_HIGH - 4, MELODY_HIGH + 8);
15319
+ const k = barKeyShift[bar];
15320
+ const fifthShift = k === 0 ? 0 : SEMITONE_TO_FIFTH_SHIFT[(k % 12 + 12) % 12];
15321
+ pad.push({
15322
+ startStep: barStart,
15323
+ pitchUnits: spelledToUnits(
15324
+ padSemi + k,
15325
+ padTone.fifth + fifthShift,
15326
+ edo
15327
+ ),
15328
+ durationSteps: stepsPerBar,
15329
+ // 小節全体を伸ばす
15330
+ velocity: barSec.kind === "drop_chorus" ? 50 : 62
15331
+ });
15332
+ }
15042
15333
  const rootTone = tones[0];
15043
15334
  const fifthTone = tones.find((t) => t.weight === 3 && t !== rootTone);
15044
15335
  const thirdTone = tones.find((t) => t.weight === 2);
@@ -15145,6 +15436,69 @@ var draw = (options, resolvedKey, rnd) => {
15145
15436
  }
15146
15437
  for (const n of barBass) bass.push(n);
15147
15438
  }
15439
+ const eighthSteps = scaleStep(EIGHTH);
15440
+ for (let i2 = 0; i2 < melody.length - 1; i2++) {
15441
+ const cur = melody[i2];
15442
+ const nxt = melody[i2 + 1];
15443
+ const curEnd = cur.startStep + cur.durationSteps;
15444
+ const atBarBoundary = curEnd % stepsPerBar === 0 && nxt.startStep === curEnd;
15445
+ if (!atBarBoundary) continue;
15446
+ if (cur.durationSteps > eighthSteps) continue;
15447
+ const barIdx = Math.floor(curEnd / stepsPerBar);
15448
+ if (barIdx >= totalBars) continue;
15449
+ if (i2 + 1 >= melody.length - 1) continue;
15450
+ const curSec = sectionAt(sectionPlan, barIdx - 1);
15451
+ const nextSec = sectionAt(sectionPlan, barIdx);
15452
+ if (!nextSec.spec.melody || curSec !== nextSec) continue;
15453
+ const curSemi = Math.round(cur.pitchUnits / UNITS_PER_SEMITONE);
15454
+ const nxtSemi = Math.round(nxt.pitchUnits / UNITS_PER_SEMITONE);
15455
+ if (Math.abs(nxtSemi - curSemi) > 3) continue;
15456
+ const nextRole = barRoles[barIdx];
15457
+ let tieProb = 0.4;
15458
+ if (nextRole === "motif") tieProb = 0.05;
15459
+ else if (nextRole === "sequence") tieProb = 0.75;
15460
+ else if (nextRole === "climax") tieProb = 0.9;
15461
+ else if (nextRole === "cadence") tieProb = 0.1;
15462
+ if (rnd() < tieProb) {
15463
+ cur.durationSteps += nxt.durationSteps;
15464
+ melody.splice(i2 + 1, 1);
15465
+ if (i2 < melodyDurations.length - 1) {
15466
+ melodyDurations[i2] += melodyDurations[i2 + 1];
15467
+ melodyDurations.splice(i2 + 1, 1);
15468
+ }
15469
+ const hCurIdx = harmony.findIndex((h) => h.startStep === cur.startStep);
15470
+ const hNxtIdx = harmony.findIndex((h) => h.startStep === nxt.startStep);
15471
+ if (hCurIdx >= 0 && hNxtIdx >= 0) {
15472
+ harmony[hCurIdx].durationSteps += harmony[hNxtIdx].durationSteps;
15473
+ harmony.splice(hNxtIdx, 1);
15474
+ }
15475
+ i2--;
15476
+ }
15477
+ }
15478
+ for (let i2 = 0; i2 < melody.length; i2++) {
15479
+ const barIdx = Math.floor(melody[i2].startStep / stepsPerBar);
15480
+ if (barIdx >= totalBars) continue;
15481
+ const role = barRoles[barIdx];
15482
+ let splitProb = 0.15;
15483
+ if (role === "motif" || role === "sequence") splitProb = 0;
15484
+ else if (role === "climax") splitProb = 0.6;
15485
+ else if (role === "cadence") splitProb = 0;
15486
+ if (rnd() < splitProb && melody[i2].durationSteps === quarterSteps) {
15487
+ const half = scaleStep(EIGHTH);
15488
+ const newNote = {
15489
+ ...melody[i2],
15490
+ startStep: melody[i2].startStep + half,
15491
+ durationSteps: half
15492
+ };
15493
+ melody[i2].durationSteps = half;
15494
+ melody.splice(i2 + 1, 0, newNote);
15495
+ if (i2 < melodyDurations.length) {
15496
+ melodyDurations[i2] = half;
15497
+ melodyDurations.splice(i2 + 1, 0, half);
15498
+ }
15499
+ i2++;
15500
+ }
15501
+ }
15148
15502
  const range = (notes) => {
15149
15503
  if (notes.length === 0) return 0;
15150
15504
  const us = notes.map((n) => n.pitchUnits);
@@ -15152,7 +15506,7 @@ var draw = (options, resolvedKey, rnd) => {
15152
15506
  };
15153
15507
  const shiftUnits = semitonesToUnits(rootShift, edo);
15154
15508
  if (shiftUnits !== 0)
15155
- for (const list of [melody, submelody, bass])
15509
+ for (const list of [melody, submelody, bass, harmony, pad])
15156
15510
  for (const n of list) n.pitchUnits = n.pitchUnits + shiftUnits;
15157
15511
  return {
15158
15512
  chordProgression,
@@ -15167,6 +15521,8 @@ var draw = (options, resolvedKey, rnd) => {
15167
15521
  melody,
15168
15522
  submelody,
15169
15523
  bass,
15524
+ harmony,
15525
+ pad,
15170
15526
  melodyDurations,
15171
15527
  restSteps,
15172
15528
  totalSteps: Math.max(1, sungBars) * stepsPerBar,
@@ -15202,7 +15558,16 @@ var evaluate = (d, recent) => {
15202
15558
  fromMelody(toMetricNotes(d.submelody)),
15203
15559
  opts
15204
15560
  );
15205
- const tension = tensionFeatures(d.barTension);
15561
+ const verseBars = [];
15562
+ const chorusBars = [];
15563
+ for (const s of d.sections) {
15564
+ for (let b = s.startBar; b < s.startBar + s.bars; b++) {
15565
+ if (s.kind === "verse") verseBars.push(b);
15566
+ else if (s.kind === "chorus" || s.kind === "drop_chorus")
15567
+ chorusBars.push(b);
15568
+ }
15569
+ }
15570
+ const tension = tensionFeatures(d.barTension, { verseBars, chorusBars });
15206
15571
  const density = densityFeatures(melodyFrom, opts);
15207
15572
  const fingerprint = featureVector({
15208
15573
  entropy,
@@ -15218,6 +15583,7 @@ var evaluate = (d, recent) => {
15218
15583
  );
15219
15584
  const at = (b, v) => band(v, b[0], b[1], b[2], b[3]);
15220
15585
  const atc = (key, v) => centeredBand(v, CORPUS_BANDS[key], CORPUS_MEDIANS[key]);
15586
+ const peakBand = d.bars > 24 ? [0, 1, Math.round(d.bars / 16), Math.round(d.bars / 8) + 2] : HAND_BANDS.climaxPeaks;
15221
15587
  const scoreBreakdown = {
15222
15588
  entropy: atc("entropy", entropy),
15223
15589
  valueKinds: atc("valueKinds", valueKinds),
@@ -15235,7 +15601,7 @@ var evaluate = (d, recent) => {
15235
15601
  sim8: atc("sim8", structure.sim8),
15236
15602
  phraseBreath: atc("phraseBreath", structure.phraseBreath),
15237
15603
  climaxPosition: atc("climaxPosition", structure.climaxPosition),
15238
- climaxPeaks: at(HAND_BANDS.climaxPeaks, structure.climaxPeaks),
15604
+ climaxPeaks: at(peakBand, structure.climaxPeaks),
15239
15605
  complementarity: at(HAND_BANDS.complementarity, structure.complementarity),
15240
15606
  subDensity: at(HAND_BANDS.subDensity, d.submelody.length / d.bars),
15241
15607
  tensionRise: tension.rise,
@@ -15289,9 +15655,10 @@ var composeSong = (options) => {
15289
15655
  bestScore = stats.score;
15290
15656
  bestIsValid = ok;
15291
15657
  best = {
15292
- // ドラムは勝った候補にだけ後から付ける(メロディに依存しないので
15658
+ // ドラム・楽器は勝った候補にだけ後から付ける(メロディに依存しないので
15293
15659
  // 候補ごとに引いても採点は動かず、40本ぶん無駄になる)。
15294
15660
  drum: "",
15661
+ instrument: "",
15295
15662
  chordProgression: d.chordProgression,
15296
15663
  chordPattern: d.chordPattern,
15297
15664
  rootShift: d.rootShift,
@@ -15304,6 +15671,8 @@ var composeSong = (options) => {
15304
15671
  melody: d.melody,
15305
15672
  submelody: d.submelody,
15306
15673
  bass: d.bass,
15674
+ harmony: d.harmony,
15675
+ pad: d.pad,
15307
15676
  stats: { ...stats, attempts: attempt, rejected }
15308
15677
  };
15309
15678
  }
@@ -15311,6 +15680,7 @@ var composeSong = (options) => {
15311
15680
  result.stats.attempts = count;
15312
15681
  result.stats.rejected = rejected;
15313
15682
  result.drum = pickBuiltinDrum(result, rnd);
15683
+ result.instrument = pickBuiltinInstrument(result, rnd);
15314
15684
  return result;
15315
15685
  };
15316
15686
  var pickBuiltinDrum = (song, rnd) => {
@@ -15319,6 +15689,35 @@ var pickBuiltinDrum = (song, rnd) => {
15319
15689
  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
15690
  return pick(pool, rnd);
15321
15691
  };
15692
+ var pickBuiltinInstrument = (song, rnd) => {
15693
+ const eighth = BASE_STEPS_PER_BAR / 8;
15694
+ const short = song.melody.filter((n) => n.durationSteps <= eighth).length / Math.max(1, song.melody.length);
15695
+ if (rnd() < 0.06) {
15696
+ return pick(["japanese_wa", "arabic_exotic"], rnd);
15697
+ }
15698
+ let pool;
15699
+ if (song.drum === "dance" || song.drum === "disco") {
15700
+ pool = song.bpm >= 150 ? ["cyber_punk", "synth_pop", "retro_game"] : ["synth_pop", "retro_game", "rock", "piano"];
15701
+ } else if (song.drum === "bossa") {
15702
+ pool = ["jazz_night", "acoustic", "piano"];
15703
+ } else if (song.bpm <= 105 && short < 0.4) {
15704
+ pool = ["ambient_cloud", "acoustic", "orchestra", "piano", "fantasy_rpg"];
15705
+ } else if (song.bpm >= 145) {
15706
+ pool = ["rock", "synth_pop", "cyber_punk", "retro_game", "piano"];
15707
+ } else if (song.stats.leapRatio >= 0.45) {
15708
+ pool = ["fantasy_rpg", "orchestra", "acoustic", "piano"];
15709
+ } else {
15710
+ pool = [
15711
+ "piano",
15712
+ "acoustic",
15713
+ "synth_pop",
15714
+ "rock",
15715
+ "jazz_night",
15716
+ "fantasy_rpg"
15717
+ ];
15718
+ }
15719
+ return pick(pool, rnd);
15720
+ };
15322
15721
  var LYRIC_WORDS = [
15323
15722
  "\u3042\u3055",
15324
15723
  "\u3072\u304B\u308A",
@@ -15720,74 +16119,89 @@ var buildUI = (target, options) => {
15720
16119
  <details class="dtm-panel" data-dtm-acc="macro">
15721
16120
  <summary>\u30DE\u30AF\u30ED</summary>
15722
16121
  <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>
16122
+ <div class="dtm-compose-container ${showCompose ? "" : "dtm-hidden"}" data-dtm="compose-container">
16123
+ <div class="dtm-row" data-dtm="compose-row">
16124
+ <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>
16125
+ <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>
16126
+ <button class="dtm-infobtn" data-dtm="macro-compose-info" title="\u4F5C\u66F2\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
16127
+ <span class="dtm-grow"></span>
16128
+ </div>
16129
+ <div class="dtm-row" data-dtm="compose-template-row">
16130
+ <span class="dtm-label">\u69CB\u6210</span>
16131
+ <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">
16132
+ <option value="custom">\u81EA\u7531\u9078\u629E\uFF08\u4E0B\u8A18\u30C1\u30A7\u30C3\u30AF\uFF09</option>
16133
+ <option value="1chorus">1\u30B3\u30FC\u30E9\u30B9\uFF08\u77ED\u3081\u30FB\u521D\u5FC3\u8005\u5411\u3051\uFF09</option>
16134
+ <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>
16135
+ <option value="jpop_drop">\u843D\u3061\u30B5\u30D3\u5165\u308A\uFF08JPOP\u738B\u9053 + \u30E9\u30B9\u30B5\u30D3\u524D\u843D\u3061\u30B5\u30D3\uFF09</option>
16136
+ <option value="vocaloid">\u30DC\u30AB\u30ED\u738B\u9053\uFF08\u75BE\u8D70\u30FB2\u756A/C\u30E1\u30ED/\u30E9\u30B9\u30B5\u30D3\uFF09</option>
16137
+ <option value="verse_chorus">Verse-Chorus\uFF08B\u30E1\u30ED\u306A\u3057\u30FB\u6D0B\u697D\u98A8\uFF09</option>
16138
+ </select>
16139
+ </div>
16140
+ <div class="dtm-row" data-dtm="compose-sections-row">
16141
+ <span class="dtm-label">\u4F5C\u308B\u90E8\u5206</span>
16142
+ <div class="dtm-checks" data-dtm="compose-sections">
16143
+ <label class="dtm-check"><input type="checkbox" value="intro" checked>\u30A4\u30F3\u30C8\u30ED</label>
16144
+ <label class="dtm-check"><input type="checkbox" value="verse" checked>A\u30E1\u30ED</label>
16145
+ <label class="dtm-check"><input type="checkbox" value="prechorus" checked>B\u30E1\u30ED</label>
16146
+ <label class="dtm-check"><input type="checkbox" value="chorus" checked>\u30B5\u30D3</label>
16147
+ <label class="dtm-check"><input type="checkbox" value="bridge">C\u30E1\u30ED</label>
16148
+ <label class="dtm-check"><input type="checkbox" value="drop_chorus">\u843D\u3061\u30B5\u30D3</label>
16149
+ <label class="dtm-check"><input type="checkbox" value="interlude">\u9593\u594F</label>
16150
+ <label class="dtm-check"><input type="checkbox" value="outro">\u30A2\u30A6\u30C8\u30ED</label>
16151
+ </div>
16152
+ <span class="dtm-grow"></span>
16153
+ <span class="dtm-hint" data-dtm="compose-sections-len"></span>
16154
+ </div>
16155
+ <div class="dtm-row" data-dtm="compose-key-row">
16156
+ <span class="dtm-label">\u30D9\u30FC\u30B9\u8ABF</span>
16157
+ <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">
16158
+ <option value="any" title="\u516824\u8ABF\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u6C7A\u5B9A\u3057\u307E\u3059">\u5E0C\u671B\u306A\u3057</option>
16159
+ <optgroup label="\u57FA\u672C">
16160
+ <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>
16161
+ <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>
16162
+ </optgroup>
16163
+ <optgroup label="\u96F0\u56F2\u6C17\u304B\u3089\u9078\u3076\uFF08\u62BD\u9078\uFF09">
16164
+ <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>
16165
+ <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>
16166
+ <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>
16167
+ <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>
16168
+ <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>
16169
+ <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>
16170
+ <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>
16171
+ <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>
16172
+ </optgroup>
16173
+ <optgroup label="\u9577\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
16174
+ <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>
16175
+ <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>
16176
+ <option value="key_D" title="\u610F\u6C17\u63DA\u3005\u3068\u3057\u305F\u3001\u52DD\u5229\u306E\u3001\u558A\u58F0">\u30CB\u9577\u8ABF (D)</option>
16177
+ <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>
16178
+ <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>
16179
+ <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>
16180
+ <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>
16181
+ <option value="key_G" title="\u53B3\u7C9B\u306A\u3001\u5D07\u9AD8\u306A\u3001\u5E7B\u60F3\u3001\u8AA0\u5B9F">\u30C8\u9577\u8ABF (G)</option>
16182
+ <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>
16183
+ <option value="key_A" title="\u3046\u308C\u3057\u3044\u3001\u7267\u6B4C\u7684\u306A\u3001\u611B\u306E\u544A\u767D">\u30A4\u9577\u8ABF (A)</option>
16184
+ <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>
16185
+ <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>
16186
+ </optgroup>
16187
+ <optgroup label="\u77ED\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
16188
+ <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>
16189
+ <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>
16190
+ <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>
16191
+ <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>
16192
+ <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>
16193
+ <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>
16194
+ <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>
16195
+ <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>
16196
+ <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>
16197
+ <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>
16198
+ <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>
16199
+ <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>
16200
+ </optgroup>
16201
+ </select>
16202
+ <span class="dtm-grow"></span>
16203
+ <span class="dtm-hint" data-dtm="compose-key-hint"></span>
15738
16204
  </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
16205
  </div>
15792
16206
  <div class="dtm-row">
15793
16207
  <span class="dtm-label">\u5168\u4F53\u30B7\u30D5\u30C8</span>
@@ -15985,6 +16399,7 @@ var buildUI = (target, options) => {
15985
16399
  transposeApplyBtn: sel("transpose-apply"),
15986
16400
  transposeInfoBtn: sel("transpose-info"),
15987
16401
  macroCompose: sel("macro-compose"),
16402
+ composeTemplate: sel("compose-template"),
15988
16403
  composeSections: sel("compose-sections"),
15989
16404
  composeSectionsLen: sel("compose-sections-len"),
15990
16405
  composeKey: sel("compose-key"),
@@ -16127,6 +16542,45 @@ var INSTRUMENT_PRESETS = {
16127
16542
  }
16128
16543
  };
16129
16544
 
16545
+ // src/macro-state.ts
16546
+ var MACRO_STORAGE_KEYS = {
16547
+ template: "dtm-macro:template",
16548
+ sections: "dtm-macro:sections",
16549
+ key: "dtm-macro:key",
16550
+ shift: "dtm-macro:shift",
16551
+ transpose: "dtm-macro:transpose"
16552
+ };
16553
+ var readMacroSetting = (key) => {
16554
+ try {
16555
+ if (typeof localStorage === "undefined" || !localStorage) return null;
16556
+ return localStorage.getItem(MACRO_STORAGE_KEYS[key]);
16557
+ } catch (_) {
16558
+ return null;
16559
+ }
16560
+ };
16561
+ var writeMacroSetting = (key, value) => {
16562
+ try {
16563
+ if (typeof localStorage === "undefined" || !localStorage) return;
16564
+ localStorage.setItem(MACRO_STORAGE_KEYS[key], value);
16565
+ } catch (_) {
16566
+ }
16567
+ };
16568
+ var readMacroSections = () => {
16569
+ const raw = readMacroSetting("sections");
16570
+ if (!raw) return null;
16571
+ try {
16572
+ const parsed = JSON.parse(raw);
16573
+ if (Array.isArray(parsed) && parsed.every((x2) => typeof x2 === "string")) {
16574
+ return parsed;
16575
+ }
16576
+ } catch (_) {
16577
+ }
16578
+ return null;
16579
+ };
16580
+ var writeMacroSections = (sections) => {
16581
+ writeMacroSetting("sections", JSON.stringify(sections));
16582
+ };
16583
+
16130
16584
  // src/macros.ts
16131
16585
  var SCALES = [
16132
16586
  [0, 2, 4, 5, 7, 9, 11],
@@ -18470,12 +18924,15 @@ var pickComposeVocal = (exclude) => {
18470
18924
  var ADVANCED_COMPOSE_LAYOUT = [
18471
18925
  { index: 0, part: "melody", octave: 0, volume: 104 },
18472
18926
  { 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 }
18927
+ { index: 2, part: "harmony", octave: 0, volume: 82 },
18928
+ { index: 3, part: "submelody", octave: 0, volume: 86 },
18929
+ { index: 4, part: "bass", octave: 0, volume: 92 },
18930
+ { index: 5, part: "bass", octave: -1, volume: 58 },
18931
+ { index: 6, part: "pad", octave: 0, volume: 64 },
18932
+ { index: 7, part: "block", octave: 0, volume: 62 },
18933
+ { index: 8, part: "arpeggio", octave: 0, volume: 54 },
18934
+ { index: 9, part: "offbeat", octave: 0, volume: 50 },
18935
+ { index: 10, part: "uwamono", octave: 1, volume: 56 }
18479
18936
  ];
18480
18937
  var LYRIC_MODEL_CATEGORIES = [
18481
18938
  {
@@ -18765,6 +19222,7 @@ var mountDAW = (target, options = {}) => {
18765
19222
  options.onDrumFontChange?.(font);
18766
19223
  };
18767
19224
  let currentInstrument = "";
19225
+ let autoComposeInstrument = null;
18768
19226
  let activeTrackId = options.initialActiveTrack ?? trackConfigs[0].id;
18769
19227
  let trackFxAdvancedOpen = readPanelOpen("track-fx-advanced") ?? false;
18770
19228
  let lyricAdvancedOpen = readPanelOpen("lyric-advanced") ?? false;
@@ -19802,8 +20260,8 @@ var mountDAW = (target, options = {}) => {
19802
20260
  redrawAll();
19803
20261
  },
19804
20262
  onEnd: (interrupted) => {
19805
- options.onScheduleFade?.(null);
19806
20263
  if (interrupted) {
20264
+ options.onScheduleFade?.(null);
19807
20265
  playbackState = "paused";
19808
20266
  pausedPlayStep = currentPlayStep;
19809
20267
  } else {
@@ -21935,6 +22393,10 @@ var mountDAW = (target, options = {}) => {
21935
22393
  }
21936
22394
  return ADVANCED_COMPOSE_LAYOUT.filter((l) => l.part === "bass").map((l) => trackStates[l.index]).filter((t) => !!t);
21937
22395
  };
22396
+ const selectedComposeTemplate = () => {
22397
+ const val = refs.composeTemplate?.value;
22398
+ return val && val !== "custom" ? val : void 0;
22399
+ };
21938
22400
  const selectedComposeSections = () => {
21939
22401
  const boxes = [
21940
22402
  ...refs.composeSections.querySelectorAll(
@@ -21945,11 +22407,97 @@ var mountDAW = (target, options = {}) => {
21945
22407
  return picked.length > 0 ? picked : DEFAULT_SECTIONS;
21946
22408
  };
21947
22409
  const updateComposeSectionsLen = () => {
21948
- const plan = buildSectionPlan(selectedComposeSections());
22410
+ const tmpl = selectedComposeTemplate();
22411
+ const plan = buildSectionPlan(selectedComposeSections(), tmpl);
21949
22412
  const bars = plan.reduce((sum, x2) => sum + x2.bars, 0);
21950
22413
  refs.composeSectionsLen.textContent = `${bars}\u5C0F\u7BC0`;
21951
22414
  };
21952
- refs.composeSections.addEventListener("change", updateComposeSectionsLen);
22415
+ const savedTemplate = readMacroSetting("template");
22416
+ if (savedTemplate && refs.composeTemplate) {
22417
+ const hasOption = Array.from(refs.composeTemplate.options).some(
22418
+ (opt) => opt.value === savedTemplate
22419
+ );
22420
+ if (hasOption) {
22421
+ refs.composeTemplate.value = savedTemplate;
22422
+ }
22423
+ }
22424
+ const savedSections = readMacroSections();
22425
+ if (savedSections && refs.composeSections) {
22426
+ const boxes = refs.composeSections.querySelectorAll(
22427
+ 'input[type="checkbox"]'
22428
+ );
22429
+ const validValues = new Set(savedSections);
22430
+ for (const box of boxes) {
22431
+ box.checked = validValues.has(box.value);
22432
+ }
22433
+ } else if (savedTemplate && savedTemplate !== "custom" && refs.composeSections) {
22434
+ const tmpl = STRUCTURE_TEMPLATES.find((t) => t.name === savedTemplate);
22435
+ if (tmpl) {
22436
+ const planSet = new Set(tmpl.plan);
22437
+ const boxes = refs.composeSections.querySelectorAll(
22438
+ 'input[type="checkbox"]'
22439
+ );
22440
+ for (const box of boxes) {
22441
+ box.checked = planSet.has(box.value);
22442
+ }
22443
+ }
22444
+ }
22445
+ const savedKey = readMacroSetting("key");
22446
+ if (savedKey && refs.composeKey) {
22447
+ const hasOption = Array.from(refs.composeKey.options).some(
22448
+ (opt) => opt.value === savedKey
22449
+ );
22450
+ if (hasOption) {
22451
+ refs.composeKey.value = savedKey;
22452
+ }
22453
+ }
22454
+ const savedShift = readMacroSetting("shift");
22455
+ if (savedShift && refs.shiftSelect) {
22456
+ const hasOption = Array.from(refs.shiftSelect.options).some(
22457
+ (opt) => opt.value === savedShift
22458
+ );
22459
+ if (hasOption) {
22460
+ refs.shiftSelect.value = savedShift;
22461
+ }
22462
+ }
22463
+ const savedTranspose = readMacroSetting("transpose");
22464
+ if (savedTranspose && refs.transposeSelect) {
22465
+ const hasOption = Array.from(refs.transposeSelect.options).some(
22466
+ (opt) => opt.value === savedTranspose
22467
+ );
22468
+ if (hasOption) {
22469
+ refs.transposeSelect.value = savedTranspose;
22470
+ }
22471
+ }
22472
+ refs.composeSections.addEventListener("change", () => {
22473
+ if (refs.composeTemplate) {
22474
+ refs.composeTemplate.value = "custom";
22475
+ writeMacroSetting("template", "custom");
22476
+ }
22477
+ writeMacroSections(selectedComposeSections());
22478
+ updateComposeSectionsLen();
22479
+ });
22480
+ const composeTemplate = refs.composeTemplate;
22481
+ if (composeTemplate) {
22482
+ composeTemplate.addEventListener("change", () => {
22483
+ writeMacroSetting("template", composeTemplate.value);
22484
+ const tmplName = selectedComposeTemplate();
22485
+ if (tmplName) {
22486
+ const tmpl = STRUCTURE_TEMPLATES.find((t) => t.name === tmplName);
22487
+ if (tmpl) {
22488
+ const planSet = new Set(tmpl.plan);
22489
+ const boxes = refs.composeSections.querySelectorAll(
22490
+ 'input[type="checkbox"]'
22491
+ );
22492
+ for (const box of boxes) {
22493
+ box.checked = planSet.has(box.value);
22494
+ }
22495
+ }
22496
+ }
22497
+ writeMacroSections(selectedComposeSections());
22498
+ updateComposeSectionsLen();
22499
+ });
22500
+ }
21953
22501
  updateComposeSectionsLen();
21954
22502
  const updateComposeKeyHint = () => {
21955
22503
  if (!refs.composeKey || !refs.composeKeyHint) return;
@@ -21957,17 +22505,33 @@ var mountDAW = (target, options = {}) => {
21957
22505
  refs.composeKeyHint.textContent = desc;
21958
22506
  refs.composeKeyHint.title = desc;
21959
22507
  };
21960
- if (refs.composeKey) {
21961
- refs.composeKey.addEventListener("change", updateComposeKeyHint);
22508
+ const composeKey = refs.composeKey;
22509
+ if (composeKey) {
22510
+ composeKey.addEventListener("change", () => {
22511
+ writeMacroSetting("key", composeKey.value);
22512
+ updateComposeKeyHint();
22513
+ });
21962
22514
  updateComposeKeyHint();
21963
22515
  }
22516
+ if (refs.shiftSelect) {
22517
+ refs.shiftSelect.addEventListener("change", () => {
22518
+ writeMacroSetting("shift", refs.shiftSelect.value);
22519
+ });
22520
+ }
22521
+ if (refs.transposeSelect) {
22522
+ refs.transposeSelect.addEventListener("change", () => {
22523
+ writeMacroSetting("transpose", refs.transposeSelect.value);
22524
+ });
22525
+ }
21964
22526
  const runCompose = (withVocal) => {
21965
22527
  stop();
21966
22528
  overlayDuring(() => {
22529
+ const tmpl = selectedComposeTemplate();
21967
22530
  const song = composeSong({
21968
22531
  stepsPerBar: renderConfig.stepsPerBar,
21969
22532
  edo: renderConfig.edo,
21970
22533
  sections: selectedComposeSections(),
22534
+ template: tmpl,
21971
22535
  baseKey: refs.composeKey?.value ?? "any",
21972
22536
  // 直近に作った曲の特徴を渡すと、それらから離れた候補に加点される。
21973
22537
  // 「作曲」を続けて押したときに似た曲が並ぶのを防ぐ。
@@ -22006,7 +22570,19 @@ var mountDAW = (target, options = {}) => {
22006
22570
  for (const layer of ADVANCED_COMPOSE_LAYOUT) {
22007
22571
  const track = trackStates[layer.index];
22008
22572
  if (!track) continue;
22009
- const notes = layer.part === "melody" ? song.melody : layer.part === "submelody" ? song.submelody : layer.part === "bass" ? song.bass : buildChordPlacements({
22573
+ 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({
22574
+ edo: renderConfig.edo,
22575
+ chordStr: song.chordProgression,
22576
+ patternType: "arpeggio",
22577
+ rootShift: song.rootShift,
22578
+ bpm: song.bpm,
22579
+ stepsPerBar: renderConfig.stepsPerBar
22580
+ }).map((p) => ({
22581
+ startStep: p.startStep,
22582
+ pitchUnits: p.pitchUnits,
22583
+ durationSteps: p.durationSteps,
22584
+ velocity: Math.max(30, p.velocity - 14)
22585
+ })) : buildChordPlacements({
22010
22586
  edo: renderConfig.edo,
22011
22587
  chordStr: song.chordProgression,
22012
22588
  patternType: layer.part,
@@ -22045,6 +22621,12 @@ var mountDAW = (target, options = {}) => {
22045
22621
  refs.drumSelect.value = song.drum;
22046
22622
  options.onDrumChange?.(song.drum);
22047
22623
  applyDrumPatternFont(song.drum);
22624
+ const shouldAutoInstrument = !currentInstrument || currentInstrument === "auto" || currentInstrument === autoComposeInstrument;
22625
+ if (shouldAutoInstrument && song.instrument) {
22626
+ currentInstrument = song.instrument;
22627
+ autoComposeInstrument = song.instrument;
22628
+ options.onInstrumentChange?.(song.instrument);
22629
+ }
22048
22630
  for (const t of bassTracksForCompose()) {
22049
22631
  t.trackCompression = AUTO_ROLE_MIX.bass.compression;
22050
22632
  options.onTrackCompressionChange?.(t.config.id, t.trackCompression);
@@ -24318,6 +24900,8 @@ var createDtmStudio = async (options = {}) => {
24318
24900
  if (fadeOutStartAt !== void 0 && fadeOutEndAt !== void 0) {
24319
24901
  fadeGain.gain.setValueAtTime(1, fadeOutStartAt);
24320
24902
  fadeGain.gain.linearRampToValueAtTime(0, fadeOutEndAt);
24903
+ fadeGain.gain.setValueAtTime(0, fadeOutEndAt + 2);
24904
+ fadeGain.gain.setValueAtTime(1, fadeOutEndAt + 2);
24321
24905
  }
24322
24906
  };
24323
24907
  const clipMeter = createClipMeter(audioCtx, glueMakeup);
@@ -24545,13 +25129,17 @@ var createDtmStudio = async (options = {}) => {
24545
25129
  }
24546
25130
  const select = doc.createElement("select");
24547
25131
  select.className = "dtm-select dtm-grow";
25132
+ const autoOpt = doc.createElement("option");
25133
+ autoOpt.value = "auto";
25134
+ autoOpt.textContent = "\u2728 \u81EA\u52D5\uFF08\u66F2\u8ABF\u9023\u52D5\uFF09";
25135
+ select.appendChild(autoOpt);
24548
25136
  for (const [key, p] of Object.entries(INSTRUMENT_PRESETS)) {
24549
25137
  const o = doc.createElement("option");
24550
25138
  o.value = key;
24551
25139
  o.textContent = p.displayName;
24552
25140
  select.appendChild(o);
24553
25141
  }
24554
- select.value = opts.value && INSTRUMENT_PRESETS[opts.value] ? opts.value : defaultPreset;
25142
+ select.value = opts.value === "auto" || opts.value && INSTRUMENT_PRESETS[opts.value] ? opts.value : defaultPreset;
24555
25143
  wrapper.appendChild(select);
24556
25144
  let busy = false;
24557
25145
  const onChange = async () => {
@@ -24560,6 +25148,10 @@ var createDtmStudio = async (options = {}) => {
24560
25148
  busy = true;
24561
25149
  const key = select.value;
24562
25150
  opts.onChange?.(key);
25151
+ if (key === "auto") {
25152
+ busy = false;
25153
+ return;
25154
+ }
24563
25155
  const trackIds = opts.getTrackIds?.() ?? [...TRACK_ROLES];
24564
25156
  const isAdvanced = trackIds.includes("t0");
24565
25157
  const mode = isAdvanced ? "advanced" : "simple";
@@ -24577,7 +25169,7 @@ var createDtmStudio = async (options = {}) => {
24577
25169
  element: wrapper,
24578
25170
  select,
24579
25171
  setValue: (k) => {
24580
- if (INSTRUMENT_PRESETS[k]) select.value = k;
25172
+ if (k === "auto" || INSTRUMENT_PRESETS[k]) select.value = k;
24581
25173
  },
24582
25174
  getValue: () => select.value,
24583
25175
  destroy: () => {
@@ -24589,8 +25181,15 @@ var createDtmStudio = async (options = {}) => {
24589
25181
  await listReady;
24590
25182
  nameToKey = await buildNameToKeyMapping();
24591
25183
  await Promise.all([drumReady, loadPreset(defaultPreset)]);
25184
+ const restoreFadeGainIfMuted = () => {
25185
+ if (fadeGain.gain.value < 1) {
25186
+ fadeGain.gain.cancelScheduledValues(audioCtx.currentTime);
25187
+ fadeGain.gain.setValueAtTime(1, audioCtx.currentTime);
25188
+ }
25189
+ };
24592
25190
  const playDrum = (e) => {
24593
25191
  if (!sfDrum.font) return;
25192
+ if (e.when === 0) restoreFadeGainIfMuted();
24594
25193
  if (KICK_PITCHES.has(e.pitch)) duckBassForKick(e.when);
24595
25194
  sfDrum.play({
24596
25195
  ctx: audioCtx,
@@ -24634,6 +25233,7 @@ var createDtmStudio = async (options = {}) => {
24634
25233
  }
24635
25234
  }
24636
25235
  const playNote4 = (e) => {
25236
+ if (e.when === 0) restoreFadeGainIfMuted();
24637
25237
  const { trackIdx, role } = resolveTrackIdxAndRole(e.trackId);
24638
25238
  const overrideKey = trackIdx >= 0 ? trackInstOverrides.get(trackIdx) : void 0;
24639
25239
  let sfInst;
@@ -24659,12 +25259,33 @@ var createDtmStudio = async (options = {}) => {
24659
25259
  duration: e.duration
24660
25260
  });
24661
25261
  };
25262
+ let daw;
24662
25263
  let presetSelect = null;
24663
25264
  const handleInstrumentChange = (key) => {
25265
+ if (key === "auto") {
25266
+ if (presetSelect) {
25267
+ presetSelect.setValue("auto");
25268
+ }
25269
+ onInstrumentChange?.("auto");
25270
+ return;
25271
+ }
24664
25272
  editorPreset = key;
24665
25273
  if (presetSelect) {
24666
25274
  presetSelect.setValue(key);
24667
25275
  }
25276
+ if (INSTRUMENT_PRESETS[key]) {
25277
+ const wasPlaying = daw?.getPlaybackState() === "playing";
25278
+ if (wasPlaying) daw.pause();
25279
+ daw?.setLoading?.(true);
25280
+ void loadPreset(
25281
+ key,
25282
+ trackIds,
25283
+ isAdvancedMode ? "advanced" : "simple"
25284
+ ).finally(() => {
25285
+ daw?.setLoading?.(false);
25286
+ if (wasPlaying) daw?.play();
25287
+ });
25288
+ }
24668
25289
  onInstrumentChange?.(key);
24669
25290
  };
24670
25291
  const handleTrackInstrumentChange = async (trackIndex, instrumentName) => {
@@ -24678,7 +25299,6 @@ var createDtmStudio = async (options = {}) => {
24678
25299
  trackInstOverrides.set(trackIndex, key);
24679
25300
  await loadInstrument(key);
24680
25301
  };
24681
- let daw;
24682
25302
  const base = {
24683
25303
  getAudioTime: () => audioCtx.currentTime,
24684
25304
  onResumeAudio: async () => {
@@ -25437,6 +26057,7 @@ var createDtmStudio = async (options = {}) => {
25437
26057
  KOE_VOICEBANK_LABELS,
25438
26058
  KOE_VOICEBANK_TERMS,
25439
26059
  LinkedList,
26060
+ MACRO_STORAGE_KEYS,
25440
26061
  MAJOR_KEY_IDS,
25441
26062
  MAX_VOCAL_VOLUME,
25442
26063
  MICRO_STEP,
@@ -25539,6 +26160,8 @@ var createDtmStudio = async (options = {}) => {
25539
26160
  playNote,
25540
26161
  playPlacements,
25541
26162
  playSingingMML,
26163
+ readMacroSections,
26164
+ readMacroSetting,
25542
26165
  resolveComposeKey,
25543
26166
  resolveDrumPattern,
25544
26167
  resolveLoopPoint,
@@ -25561,5 +26184,7 @@ var createDtmStudio = async (options = {}) => {
25561
26184
  unitsToMidi,
25562
26185
  unitsToMidiDetune,
25563
26186
  unitsToPitchV1,
25564
- vocalVolumeToGain
26187
+ vocalVolumeToGain,
26188
+ writeMacroSections,
26189
+ writeMacroSetting
25565
26190
  });