@onjmin/dtm 2.1.10 → 2.1.11
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.d.mts +83 -7
- package/dist/index.d.ts +83 -7
- package/dist/index.js +1062 -763
- package/dist/index.mjs +1062 -763
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1763,11 +1763,19 @@ var createChannelStrip = (ctx, destination, options = {}) => {
|
|
|
1763
1763
|
const duckGain = ctx.createGain();
|
|
1764
1764
|
duckGain.gain.value = 1;
|
|
1765
1765
|
const DUCK_ATTACK_SEC = 8e-3;
|
|
1766
|
+
let duckReleaseEndAt = 0;
|
|
1766
1767
|
const duck = (atTime, depth = 0.3, releaseSec = 0.18) => {
|
|
1767
1768
|
const t = Math.max(atTime, ctx.currentTime);
|
|
1768
1769
|
const floor = clamp(1 - depth, 0, 1);
|
|
1769
|
-
duckGain.gain
|
|
1770
|
-
|
|
1770
|
+
const param = duckGain.gain;
|
|
1771
|
+
if (t >= duckReleaseEndAt) {
|
|
1772
|
+
param.setValueAtTime(1, t);
|
|
1773
|
+
} else if (typeof param.cancelAndHoldAtTime === "function") {
|
|
1774
|
+
param.cancelAndHoldAtTime(t);
|
|
1775
|
+
}
|
|
1776
|
+
param.linearRampToValueAtTime(floor, t + DUCK_ATTACK_SEC);
|
|
1777
|
+
param.linearRampToValueAtTime(1, t + DUCK_ATTACK_SEC + releaseSec);
|
|
1778
|
+
duckReleaseEndAt = t + DUCK_ATTACK_SEC + releaseSec;
|
|
1771
1779
|
};
|
|
1772
1780
|
eqHigh.connect(compressor);
|
|
1773
1781
|
compressor.connect(duckGain);
|
|
@@ -2064,12 +2072,15 @@ var buildChordPlacements = (options) => {
|
|
|
2064
2072
|
}
|
|
2065
2073
|
}
|
|
2066
2074
|
} else if (patternType === "alternating") {
|
|
2075
|
+
const quarterSteps = Math.floor(stepsPerBar / 4);
|
|
2067
2076
|
notes.forEach((noteOffset, i2) => {
|
|
2068
|
-
const stepOffset = i2 *
|
|
2077
|
+
const stepOffset = i2 * quarterSteps;
|
|
2078
|
+
const isLast = i2 === notes.length - 1;
|
|
2079
|
+
const durationSteps = isLast ? Math.max(12, noteLength - stepOffset) : Math.max(12, quarterSteps);
|
|
2069
2080
|
placements.push({
|
|
2070
2081
|
startStep: chord.whenStep + stepOffset,
|
|
2071
2082
|
pitchUnits: toUnits(noteOffset),
|
|
2072
|
-
durationSteps
|
|
2083
|
+
durationSteps,
|
|
2073
2084
|
velocity: 100
|
|
2074
2085
|
});
|
|
2075
2086
|
});
|
|
@@ -4697,6 +4708,45 @@ var createReverbImpulse = (ctx, decaySec = DEFAULT_REVERB_DECAY_SEC) => {
|
|
|
4697
4708
|
};
|
|
4698
4709
|
var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100;
|
|
4699
4710
|
|
|
4711
|
+
// src/safety-limiter.ts
|
|
4712
|
+
var THRESHOLD_DB = -1;
|
|
4713
|
+
var KNEE_DB = 0;
|
|
4714
|
+
var RATIO = 20;
|
|
4715
|
+
var ATTACK_SEC = 1e-3;
|
|
4716
|
+
var RELEASE_SEC = 0.1;
|
|
4717
|
+
var CURVE_SAMPLES2 = 2048;
|
|
4718
|
+
var SOFT_KNEE = 0.85;
|
|
4719
|
+
var CEILING = 0.98;
|
|
4720
|
+
var SHAPER_RANGE = 4;
|
|
4721
|
+
var OVERSAMPLE2 = "none";
|
|
4722
|
+
var createSoftClipCurve = () => {
|
|
4723
|
+
const curve = new Float32Array(CURVE_SAMPLES2);
|
|
4724
|
+
const span = CEILING - SOFT_KNEE;
|
|
4725
|
+
for (let i2 = 0; i2 < CURVE_SAMPLES2; i2++) {
|
|
4726
|
+
const x2 = (i2 / (CURVE_SAMPLES2 - 1) * 2 - 1) * SHAPER_RANGE;
|
|
4727
|
+
const a = Math.abs(x2);
|
|
4728
|
+
curve[i2] = Math.sign(x2) * (a <= SOFT_KNEE ? a : SOFT_KNEE + span * Math.tanh((a - SOFT_KNEE) / span));
|
|
4729
|
+
}
|
|
4730
|
+
return curve;
|
|
4731
|
+
};
|
|
4732
|
+
var createSafetyLimiter = (ctx, destination) => {
|
|
4733
|
+
const limiter = ctx.createDynamicsCompressor();
|
|
4734
|
+
limiter.threshold.value = THRESHOLD_DB;
|
|
4735
|
+
limiter.knee.value = KNEE_DB;
|
|
4736
|
+
limiter.ratio.value = RATIO;
|
|
4737
|
+
limiter.attack.value = ATTACK_SEC;
|
|
4738
|
+
limiter.release.value = RELEASE_SEC;
|
|
4739
|
+
const preScale = ctx.createGain();
|
|
4740
|
+
preScale.gain.value = 1 / SHAPER_RANGE;
|
|
4741
|
+
const softClip = ctx.createWaveShaper();
|
|
4742
|
+
softClip.curve = createSoftClipCurve();
|
|
4743
|
+
softClip.oversample = OVERSAMPLE2;
|
|
4744
|
+
limiter.connect(preScale);
|
|
4745
|
+
preScale.connect(softClip);
|
|
4746
|
+
softClip.connect(destination);
|
|
4747
|
+
return limiter;
|
|
4748
|
+
};
|
|
4749
|
+
|
|
4700
4750
|
// src/sequencer.ts
|
|
4701
4751
|
var STEPS_PER_BEAT = 48;
|
|
4702
4752
|
var PLAN_TIME = 0.5;
|
|
@@ -7160,6 +7210,9 @@ var SONG_DRUM_PATTERNS = {
|
|
|
7160
7210
|
|
|
7161
7211
|
// src/synth.ts
|
|
7162
7212
|
var freqFromPitch = (pitchUnits) => unitsToHz(pitchUnits);
|
|
7213
|
+
var MIN_ATTACK_SEC = 15e-4;
|
|
7214
|
+
var MIN_RELEASE_SEC = 4e-3;
|
|
7215
|
+
var TAIL_RATIO = 1e-3;
|
|
7163
7216
|
var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
7164
7217
|
const wave = tone.wave ?? "square";
|
|
7165
7218
|
const attack = tone.attack ?? 0;
|
|
@@ -7178,25 +7231,25 @@ var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
|
7178
7231
|
osc.frequency.value = freqFromPitch(e.pitchUnits);
|
|
7179
7232
|
const t0 = ctx.currentTime + e.when;
|
|
7180
7233
|
const peak = Math.max(1e-4, 0.06 * e.volume * 1.5 * gainScale);
|
|
7234
|
+
const tail = peak * TAIL_RATIO;
|
|
7235
|
+
const stopAt = t0 + e.duration + MIN_RELEASE_SEC;
|
|
7181
7236
|
if (tone.decay) {
|
|
7182
7237
|
gain.gain.setValueAtTime(1e-4, t0);
|
|
7183
7238
|
gain.gain.linearRampToValueAtTime(peak, t0 + Math.max(3e-3, attack));
|
|
7184
|
-
gain.gain.exponentialRampToValueAtTime(
|
|
7239
|
+
gain.gain.exponentialRampToValueAtTime(tail, t0 + e.duration);
|
|
7185
7240
|
} else {
|
|
7186
7241
|
const releaseTime = Math.min(0.02, e.duration * 0.1);
|
|
7187
7242
|
const sustainDuration = e.duration - releaseTime;
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
} else {
|
|
7195
|
-
gain.gain.setValueAtTime(peak, t0);
|
|
7196
|
-
}
|
|
7243
|
+
const attackTime = Math.min(
|
|
7244
|
+
Math.max(attack, MIN_ATTACK_SEC),
|
|
7245
|
+
sustainDuration
|
|
7246
|
+
);
|
|
7247
|
+
gain.gain.setValueAtTime(1e-4, t0);
|
|
7248
|
+
gain.gain.linearRampToValueAtTime(peak, t0 + attackTime);
|
|
7197
7249
|
gain.gain.setValueAtTime(peak, t0 + sustainDuration);
|
|
7198
|
-
gain.gain.exponentialRampToValueAtTime(
|
|
7250
|
+
gain.gain.exponentialRampToValueAtTime(tail, t0 + e.duration);
|
|
7199
7251
|
}
|
|
7252
|
+
gain.gain.linearRampToValueAtTime(0, stopAt);
|
|
7200
7253
|
osc.connect(gain);
|
|
7201
7254
|
let panner = null;
|
|
7202
7255
|
if (typeof ctx.createStereoPanner === "function" && e.pan) {
|
|
@@ -7208,7 +7261,7 @@ var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
|
7208
7261
|
gain.connect(compressor);
|
|
7209
7262
|
}
|
|
7210
7263
|
osc.start(t0);
|
|
7211
|
-
osc.stop(
|
|
7264
|
+
osc.stop(stopAt);
|
|
7212
7265
|
osc.onended = () => {
|
|
7213
7266
|
osc.disconnect();
|
|
7214
7267
|
gain.disconnect();
|
|
@@ -7223,14 +7276,21 @@ var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
|
7223
7276
|
if (isKick) {
|
|
7224
7277
|
const osc = ctx.createOscillator();
|
|
7225
7278
|
const g2 = ctx.createGain();
|
|
7279
|
+
const kickPeak = vol * 0.135;
|
|
7280
|
+
const kickDecayEnd = t0 + 0.18;
|
|
7281
|
+
const kickEnd = kickDecayEnd + MIN_RELEASE_SEC;
|
|
7226
7282
|
osc.frequency.setValueAtTime(150, t0);
|
|
7227
7283
|
osc.frequency.exponentialRampToValueAtTime(50, t0 + 0.12);
|
|
7228
|
-
g2.gain.setValueAtTime(
|
|
7229
|
-
g2.gain.exponentialRampToValueAtTime(
|
|
7284
|
+
g2.gain.setValueAtTime(kickPeak, t0);
|
|
7285
|
+
g2.gain.exponentialRampToValueAtTime(kickPeak * TAIL_RATIO, kickDecayEnd);
|
|
7286
|
+
g2.gain.linearRampToValueAtTime(0, kickEnd);
|
|
7230
7287
|
osc.connect(g2).connect(compressor);
|
|
7231
7288
|
osc.start(t0);
|
|
7232
|
-
osc.stop(
|
|
7233
|
-
osc.onended = () =>
|
|
7289
|
+
osc.stop(kickEnd);
|
|
7290
|
+
osc.onended = () => {
|
|
7291
|
+
osc.disconnect();
|
|
7292
|
+
g2.disconnect();
|
|
7293
|
+
};
|
|
7234
7294
|
return;
|
|
7235
7295
|
}
|
|
7236
7296
|
const dur = isSnareLike ? 0.18 : 0.05;
|
|
@@ -7244,8 +7304,13 @@ var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
|
7244
7304
|
filter.type = isSnareLike ? "bandpass" : "highpass";
|
|
7245
7305
|
filter.frequency.value = isSnareLike ? 2e3 : 8e3;
|
|
7246
7306
|
const g = ctx.createGain();
|
|
7247
|
-
|
|
7248
|
-
g.gain.
|
|
7307
|
+
const noisePeak = vol * (isSnareLike ? 0.105 : 0.06);
|
|
7308
|
+
g.gain.setValueAtTime(noisePeak, t0);
|
|
7309
|
+
g.gain.exponentialRampToValueAtTime(
|
|
7310
|
+
noisePeak * TAIL_RATIO,
|
|
7311
|
+
t0 + dur - MIN_RELEASE_SEC
|
|
7312
|
+
);
|
|
7313
|
+
g.gain.linearRampToValueAtTime(0, t0 + dur);
|
|
7249
7314
|
src.connect(filter).connect(g).connect(compressor);
|
|
7250
7315
|
src.start(t0);
|
|
7251
7316
|
src.stop(t0 + dur);
|
|
@@ -7307,7 +7372,7 @@ var playPlacements = (placements, options) => {
|
|
|
7307
7372
|
reverbPreDelay.connect(reverbConvolver);
|
|
7308
7373
|
reverbConvolver.connect(reverbWetGain);
|
|
7309
7374
|
reverbWetGain.connect(finalMix);
|
|
7310
|
-
finalMix.connect(rawDestination);
|
|
7375
|
+
finalMix.connect(createSafetyLimiter(ctx, rawDestination));
|
|
7311
7376
|
const channelStrips = /* @__PURE__ */ new Map();
|
|
7312
7377
|
const getChannelStrip = (index) => {
|
|
7313
7378
|
let strip = channelStrips.get(index);
|
|
@@ -9762,17 +9827,24 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
9762
9827
|
if (!audioCtx) audioCtx = new AudioContext();
|
|
9763
9828
|
return audioCtx;
|
|
9764
9829
|
};
|
|
9830
|
+
let limiterNode = null;
|
|
9831
|
+
const ensureOutput = () => {
|
|
9832
|
+
const ctx = ensureCtx();
|
|
9833
|
+
if (!limiterNode) limiterNode = createSafetyLimiter(ctx, ctx.destination);
|
|
9834
|
+
return limiterNode;
|
|
9835
|
+
};
|
|
9765
9836
|
let synthInstance = null;
|
|
9766
9837
|
const ensureSynth = () => {
|
|
9767
|
-
if (!synthInstance)
|
|
9838
|
+
if (!synthInstance) {
|
|
9839
|
+
synthInstance = createSynth(ensureCtx(), ensureOutput());
|
|
9840
|
+
}
|
|
9768
9841
|
return synthInstance;
|
|
9769
9842
|
};
|
|
9770
9843
|
let voices = null;
|
|
9771
9844
|
const ensureVoices = () => {
|
|
9772
9845
|
if (options.singingVoices) return options.singingVoices;
|
|
9773
9846
|
if (!voices) {
|
|
9774
|
-
|
|
9775
|
-
voices = createSingingVoices(ctx, ctx.destination);
|
|
9847
|
+
voices = createSingingVoices(ensureCtx(), ensureOutput());
|
|
9776
9848
|
voices.setVolume(trackVolume / 100 * (masterVolume / 100));
|
|
9777
9849
|
}
|
|
9778
9850
|
return voices;
|
|
@@ -10834,6 +10906,12 @@ var SoundFont = class _SoundFont {
|
|
|
10834
10906
|
// ファミリごとに変える。
|
|
10835
10907
|
/** アタック(無音からピークまで)秒。クリック防止の最小限。全楽器共通。 */
|
|
10836
10908
|
static attackSec = 5e-3;
|
|
10909
|
+
/**
|
|
10910
|
+
* 消え際に最低限確保する秒数。アタックと対になるクリック防止で、**サンプルが
|
|
10911
|
+
* 終わるまでに**必ずここまでに 0 へ落とし切る。振幅が残ったままバッファが尽きたり
|
|
10912
|
+
* `stop()` が来たりすると、その瞬間の値がそのまま段差になりプチノイズが出る。
|
|
10913
|
+
*/
|
|
10914
|
+
static minReleaseSec = 4e-3;
|
|
10837
10915
|
/**
|
|
10838
10916
|
* 減衰の型ごとのエンベロープ。
|
|
10839
10917
|
*
|
|
@@ -11020,14 +11098,23 @@ var SoundFont = class _SoundFont {
|
|
|
11020
11098
|
const startGainTime = Math.max(ctx.currentTime, _when);
|
|
11021
11099
|
g.gain.setValueAtTime(0, startGainTime);
|
|
11022
11100
|
const env = _SoundFont.envelopes[this.style.env] ?? _SoundFont.envelopes.sustain;
|
|
11023
|
-
const
|
|
11101
|
+
const playRate = _param.playbackRate * 2 ** ((humanizeCents + detuneCents) / 1200);
|
|
11102
|
+
const sampleEnd = _when + (buffer.duration - startOffsetSec) / playRate;
|
|
11103
|
+
const limit = src.loop ? Number.POSITIVE_INFINITY : sampleEnd;
|
|
11024
11104
|
const attackEnd = Math.min(startGainTime + _SoundFont.attackSec, limit);
|
|
11025
11105
|
const decayEnd = Math.min(
|
|
11026
11106
|
Math.max(attackEnd, attackEnd + env.decaySec),
|
|
11027
11107
|
limit
|
|
11028
11108
|
);
|
|
11029
|
-
const
|
|
11030
|
-
|
|
11109
|
+
const tailRoom = Math.min(
|
|
11110
|
+
_SoundFont.minReleaseSec,
|
|
11111
|
+
Math.max(0, limit - decayEnd)
|
|
11112
|
+
);
|
|
11113
|
+
const noteOff = Math.min(
|
|
11114
|
+
Math.max(decayEnd, _when + duration),
|
|
11115
|
+
limit - tailRoom
|
|
11116
|
+
);
|
|
11117
|
+
const end = isDrum ? sampleEnd : Math.max(Math.min(noteOff + env.releaseSec, limit), noteOff + tailRoom);
|
|
11031
11118
|
if (!isDrum) {
|
|
11032
11119
|
const sustainVolume = effectiveVolume * env.sustain;
|
|
11033
11120
|
g.gain.linearRampToValueAtTime(effectiveVolume, attackEnd);
|
|
@@ -11036,6 +11123,11 @@ var SoundFont = class _SoundFont {
|
|
|
11036
11123
|
g.gain.linearRampToValueAtTime(0, end);
|
|
11037
11124
|
} else {
|
|
11038
11125
|
g.gain.linearRampToValueAtTime(effectiveVolume, attackEnd);
|
|
11126
|
+
const releaseStart = Math.max(attackEnd, end - _SoundFont.minReleaseSec);
|
|
11127
|
+
if (releaseStart > attackEnd) {
|
|
11128
|
+
g.gain.setValueAtTime(effectiveVolume, releaseStart);
|
|
11129
|
+
}
|
|
11130
|
+
g.gain.linearRampToValueAtTime(0, end);
|
|
11039
11131
|
}
|
|
11040
11132
|
if (filter) src.connect(filter).connect(g);
|
|
11041
11133
|
else src.connect(g);
|
|
@@ -11245,12 +11337,10 @@ var addParam = (zone, pitch) => {
|
|
|
11245
11337
|
coarseTune,
|
|
11246
11338
|
fineTune,
|
|
11247
11339
|
sampleRate,
|
|
11248
|
-
delay
|
|
11249
|
-
buffer
|
|
11340
|
+
delay
|
|
11250
11341
|
} = zone;
|
|
11251
11342
|
const baseDetune = originalPitch - 100 * coarseTune - fineTune;
|
|
11252
11343
|
const playbackRate = 2 ** ((100 * pitch - baseDetune) / 1200);
|
|
11253
|
-
const max = (buffer?.duration ?? 0) / playbackRate;
|
|
11254
11344
|
const src = {
|
|
11255
11345
|
loop: loopStart >= 1 && loopStart < loopEnd
|
|
11256
11346
|
};
|
|
@@ -11258,7 +11348,7 @@ var addParam = (zone, pitch) => {
|
|
|
11258
11348
|
[src.loopStart, src.loopEnd] = [loopStart, loopEnd].map(
|
|
11259
11349
|
(v) => v / sampleRate + delay
|
|
11260
11350
|
);
|
|
11261
|
-
zone._param = { playbackRate,
|
|
11351
|
+
zone._param = { playbackRate, src };
|
|
11262
11352
|
};
|
|
11263
11353
|
|
|
11264
11354
|
// src/chord-player.ts
|
|
@@ -12556,29 +12646,30 @@ var CORPUS_BANDS = {
|
|
|
12556
12646
|
climaxPeaks: [1, 2, 13.5, 26],
|
|
12557
12647
|
complementarity: [0, 0.026, 0.179, 0.405]
|
|
12558
12648
|
};
|
|
12559
|
-
var
|
|
12560
|
-
entropy
|
|
12561
|
-
valueKinds
|
|
12562
|
-
restRatio
|
|
12563
|
-
leapRatio
|
|
12564
|
-
stepRatio
|
|
12565
|
-
chromaticRatio
|
|
12566
|
-
maxLeap
|
|
12567
|
-
melodyRange
|
|
12568
|
-
notesPerBar
|
|
12569
|
-
shortNoteRatio
|
|
12570
|
-
barDensityCv
|
|
12571
|
-
densityCliff
|
|
12572
|
-
sim1
|
|
12573
|
-
sim2
|
|
12574
|
-
sim4
|
|
12575
|
-
sim8
|
|
12576
|
-
phraseBreath
|
|
12577
|
-
turnRatio
|
|
12578
|
-
climaxPosition
|
|
12579
|
-
climaxPeaks
|
|
12580
|
-
complementarity
|
|
12581
|
-
|
|
12649
|
+
var CORPUS_PROFILE_KEYS = [
|
|
12650
|
+
"entropy",
|
|
12651
|
+
"valueKinds",
|
|
12652
|
+
"restRatio",
|
|
12653
|
+
"leapRatio",
|
|
12654
|
+
"stepRatio",
|
|
12655
|
+
"chromaticRatio",
|
|
12656
|
+
"maxLeap",
|
|
12657
|
+
"melodyRange",
|
|
12658
|
+
"notesPerBar",
|
|
12659
|
+
"shortNoteRatio",
|
|
12660
|
+
"barDensityCv",
|
|
12661
|
+
"densityCliff",
|
|
12662
|
+
"sim1",
|
|
12663
|
+
"sim2",
|
|
12664
|
+
"sim4",
|
|
12665
|
+
"sim8",
|
|
12666
|
+
"phraseBreath",
|
|
12667
|
+
"turnRatio",
|
|
12668
|
+
"climaxPosition",
|
|
12669
|
+
"climaxPeaks",
|
|
12670
|
+
"complementarity"
|
|
12671
|
+
];
|
|
12672
|
+
var CORPUS_DEVIATION_BUDGET = 4;
|
|
12582
12673
|
var CORPUS_CELL_WEIGHTS = {
|
|
12583
12674
|
"0,2,4,6,8,10,12,14": 584,
|
|
12584
12675
|
"0,2,4,6,8,12,14": 512,
|
|
@@ -13443,16 +13534,15 @@ var tensionFeatures = (barTension, opts) => {
|
|
|
13443
13534
|
return { rise, resolve };
|
|
13444
13535
|
};
|
|
13445
13536
|
var band = (v, lo, idealLo, idealHi, hi) => {
|
|
13446
|
-
if (v <= lo || v >= hi) return 0;
|
|
13447
13537
|
if (v >= idealLo && v <= idealHi) return 1;
|
|
13538
|
+
if (v <= lo || v >= hi) return 0;
|
|
13448
13539
|
if (v < idealLo) return idealLo === lo ? 0 : (v - lo) / (idealLo - lo);
|
|
13449
13540
|
return hi === idealHi ? 0 : (hi - v) / (hi - idealHi);
|
|
13450
13541
|
};
|
|
13451
|
-
var
|
|
13452
|
-
const
|
|
13453
|
-
|
|
13454
|
-
|
|
13455
|
-
return 1 - 0.15 * Math.min(1, Math.abs(v - median) / spread);
|
|
13542
|
+
var plausibleBand = (v, b) => {
|
|
13543
|
+
const span = b[3] - b[0];
|
|
13544
|
+
const margin = span > 0 ? span * 0.5 : 1;
|
|
13545
|
+
return band(v, b[0] - margin, b[0], b[3], b[3] + margin);
|
|
13456
13546
|
};
|
|
13457
13547
|
var featureVector = (f) => [
|
|
13458
13548
|
f.entropy / 3,
|
|
@@ -14355,6 +14445,7 @@ var sectionAt = (plan, bar) => {
|
|
|
14355
14445
|
// src/compose.ts
|
|
14356
14446
|
var STEP_SEMITONES = 2;
|
|
14357
14447
|
var MAX_LEAP_SEMITONES = 10;
|
|
14448
|
+
var LEAP_CEILINGS = [7, 8, 9, 10, 10, 12, 14, 16];
|
|
14358
14449
|
var MAX_BAR_LEAP_SEMITONES = 10;
|
|
14359
14450
|
var HARD = {
|
|
14360
14451
|
/** メロディが1音も無い、音域が半音未満(同じ音を並べただけ)。 */
|
|
@@ -14413,13 +14504,16 @@ var WEIGHTS = {
|
|
|
14413
14504
|
// --- 直近に作った曲と違うか ---
|
|
14414
14505
|
novelty: 1.2
|
|
14415
14506
|
};
|
|
14507
|
+
var DEVIATION_BUDGET = CORPUS_DEVIATION_BUDGET;
|
|
14508
|
+
var BUDGETED_KEYS = new Set(CORPUS_PROFILE_KEYS);
|
|
14416
14509
|
var HAND_BANDS = {
|
|
14417
14510
|
/** サブメロの音数/小節。少なすぎると「置いただけ」、多すぎるとメロディを食う。 */
|
|
14418
14511
|
subDensity: [0.5, 1.8, 4.5, 8],
|
|
14419
14512
|
complementarity: [0.05, 0.25, 0.7, 0.95],
|
|
14420
14513
|
climaxPeaks: [0, 1, 2, 5]
|
|
14421
14514
|
};
|
|
14422
|
-
var DRAW_COUNT =
|
|
14515
|
+
var DRAW_COUNT = 12;
|
|
14516
|
+
var SELECT_TEMPERATURE = 0.05;
|
|
14423
14517
|
var BASE_STEPS_PER_BAR = 192;
|
|
14424
14518
|
var WHOLE = 192;
|
|
14425
14519
|
var DOT_HALF = 144;
|
|
@@ -15135,6 +15229,14 @@ for (const c of [...MOTIF_CELLS, ...RHYTHM_CELLS]) {
|
|
|
15135
15229
|
const key = onsetKeyOf(c.value);
|
|
15136
15230
|
cellEntryCount.set(key, (cellEntryCount.get(key) ?? 0) + 1);
|
|
15137
15231
|
}
|
|
15232
|
+
var resolveMelodyForm = (choice, rnd) => {
|
|
15233
|
+
const c = (choice ?? "").trim() || "auto";
|
|
15234
|
+
if (c === "motif" || c === "ostinato" || c === "through") return c;
|
|
15235
|
+
const r = rnd();
|
|
15236
|
+
if (r < 0.25) return "ostinato";
|
|
15237
|
+
if (r < 0.4) return "through";
|
|
15238
|
+
return "motif";
|
|
15239
|
+
};
|
|
15138
15240
|
var groovyCells = (cells, groove, rnd) => {
|
|
15139
15241
|
const hasSixteenth = (c) => c.value.some((v) => Math.abs(v) <= SIXTEENTH);
|
|
15140
15242
|
if (groove === "eighth") {
|
|
@@ -15310,7 +15412,7 @@ var clampSemi = (semi, low, high) => {
|
|
|
15310
15412
|
while (s > high) s -= 12;
|
|
15311
15413
|
return s;
|
|
15312
15414
|
};
|
|
15313
|
-
var leapTarget = (from, tones, rnd) => {
|
|
15415
|
+
var leapTarget = (from, tones, rnd, maxLeap = MAX_LEAP_SEMITONES) => {
|
|
15314
15416
|
const candidates = [];
|
|
15315
15417
|
for (const tone of tones) {
|
|
15316
15418
|
const base = pitchClass(tone.semi);
|
|
@@ -15318,7 +15420,7 @@ var leapTarget = (from, tones, rnd) => {
|
|
|
15318
15420
|
const semi = base + oct * 12;
|
|
15319
15421
|
if (semi < MELODY_LOW || semi > MELODY_HIGH) continue;
|
|
15320
15422
|
const gap = Math.abs(semi - from);
|
|
15321
|
-
if (gap >= 3 && gap <=
|
|
15423
|
+
if (gap >= 3 && gap <= maxLeap) candidates.push(semi);
|
|
15322
15424
|
}
|
|
15323
15425
|
}
|
|
15324
15426
|
if (candidates.length === 0) return null;
|
|
@@ -15475,7 +15577,7 @@ var barDegrees = (role, slots, tones, style, scale, motifContour, startDegree, c
|
|
|
15475
15577
|
if (slots[i2].value >= quarterSteps) {
|
|
15476
15578
|
if (rnd() < style.leapAffinity) {
|
|
15477
15579
|
const from = degreeToPitch(scale, out[i2 - 1]).semi;
|
|
15478
|
-
const target = leapTarget(from, tones, rnd);
|
|
15580
|
+
const target = leapTarget(from, tones, rnd, style.maxLeap);
|
|
15479
15581
|
if (target !== null) out[i2] = semitoneToDegree(scale, target);
|
|
15480
15582
|
}
|
|
15481
15583
|
continue;
|
|
@@ -15486,13 +15588,13 @@ var barDegrees = (role, slots, tones, style, scale, motifContour, startDegree, c
|
|
|
15486
15588
|
}
|
|
15487
15589
|
return out;
|
|
15488
15590
|
};
|
|
15489
|
-
var fitMotif = (degrees, slots, tones, prevSemi, quarterSteps, scale, pentatonic, preferShift) => {
|
|
15591
|
+
var fitMotif = (degrees, slots, tones, prevSemi, quarterSteps, scale, pentatonic, preferShift, maxShift = 3) => {
|
|
15490
15592
|
let best = degrees;
|
|
15491
15593
|
let bestShift = 0;
|
|
15492
15594
|
let bestScore = Number.NEGATIVE_INFINITY;
|
|
15493
15595
|
let preferScore = Number.NEGATIVE_INFINITY;
|
|
15494
15596
|
let preferMoved = null;
|
|
15495
|
-
for (let shift = -
|
|
15597
|
+
for (let shift = -maxShift; shift <= maxShift; shift++) {
|
|
15496
15598
|
const moved = degrees.map(
|
|
15497
15599
|
(d) => pentatonic ? coreToDegree(scale, degreeToCore(scale, d) + shift) : d + shift
|
|
15498
15600
|
);
|
|
@@ -15549,7 +15651,7 @@ var shapeBar = (degrees, slots, tones, prevSemi, opts) => {
|
|
|
15549
15651
|
MELODY_LOW,
|
|
15550
15652
|
MELODY_HIGH
|
|
15551
15653
|
);
|
|
15552
|
-
const limit = i2 === 0 ? MAX_BAR_LEAP_SEMITONES :
|
|
15654
|
+
const limit = i2 === 0 ? MAX_BAR_LEAP_SEMITONES : opts.maxLeap;
|
|
15553
15655
|
if (!opts.allowLeap && Math.abs(semi - prev) > limit) {
|
|
15554
15656
|
semi = clampSemi(
|
|
15555
15657
|
walk(opts.scale, prev, Math.sign(semi - prev) * 3),
|
|
@@ -15942,6 +16044,7 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
15942
16044
|
landing = (landing + pick([2, 4], rnd)) % size;
|
|
15943
16045
|
return landing;
|
|
15944
16046
|
};
|
|
16047
|
+
const form = resolveMelodyForm(options.form, rnd);
|
|
15945
16048
|
const units2 = [];
|
|
15946
16049
|
for (const section of sectionPlan) {
|
|
15947
16050
|
const unitCount = Math.max(1, Math.round(section.bars / 2));
|
|
@@ -15962,16 +16065,31 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
15962
16065
|
const isLast = u === unitCount - 1;
|
|
15963
16066
|
if (u % 2 === 0) {
|
|
15964
16067
|
units2.push({
|
|
15965
|
-
|
|
15966
|
-
|
|
16068
|
+
// リフ主体の曲は**変形しない**。セクエンツもオクターブ上げも入れず、
|
|
16069
|
+
// 同じ型を回し続ける。セクションの対比は編曲側(ドラム・楽器・レイヤ)
|
|
16070
|
+
// が担う——ヤツメ穴型の曲がまさにその作りで、120小節を通して
|
|
16071
|
+
// 5半音のセルが変わらない。
|
|
16072
|
+
role: form === "ostinato" ? "motif" : form === "through" ? (
|
|
16073
|
+
// 通し作曲は素材を戻さない。`step` の書法(アーチ・谷・波)で
|
|
16074
|
+
// 独立した線を書き、たまに走句と山を挟んで単調さを避ける。
|
|
16075
|
+
u % 3 === 2 ? "run" : u % 3 === 1 ? "climax" : "step"
|
|
16076
|
+
) : section.kind === "bridge" ? "step" : section.kind === "chorus" ? "climax" : src === "a2" || u > 0 ? "sequence" : "motif",
|
|
16077
|
+
// リフ型は素材も1つに揃える(`sourceOf` でセクションごとに
|
|
16078
|
+
// 変えると、そこだけ別の型が始まってオスティナートにならない)。
|
|
16079
|
+
source: form === "ostinato" ? "a" : src,
|
|
15967
16080
|
landing: null,
|
|
15968
16081
|
section
|
|
15969
16082
|
});
|
|
15970
16083
|
} else {
|
|
15971
16084
|
const landing = landingOf(section);
|
|
16085
|
+
const riff = form === "ostinato" && !(isLast && landing === 0);
|
|
15972
16086
|
units2.push({
|
|
15973
|
-
role: isLast && landing === 0 ? "cadence" : "
|
|
15974
|
-
|
|
16087
|
+
role: isLast && landing === 0 ? "cadence" : riff ? "motif" : (
|
|
16088
|
+
// 通し作曲は「問いと答え」で閉じない。answer は問いのリズムを
|
|
16089
|
+
// 受けて着地音だけ変える形なので、そのままだと反復が戻る。
|
|
16090
|
+
form === "through" ? "step" : "answer"
|
|
16091
|
+
),
|
|
16092
|
+
source: riff ? "a" : "answer",
|
|
15975
16093
|
landing: isLast ? landing : null,
|
|
15976
16094
|
section
|
|
15977
16095
|
});
|
|
@@ -15989,6 +16107,7 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
15989
16107
|
const restatementOf = (bar) => {
|
|
15990
16108
|
const curSec = sectionAt(sectionPlan, bar);
|
|
15991
16109
|
if (!curSec.spec.melody) return null;
|
|
16110
|
+
if (form === "through") return null;
|
|
15992
16111
|
if (curSec.restatement) {
|
|
15993
16112
|
const firstSec = sectionPlan.find(
|
|
15994
16113
|
(s) => s.kind === curSec.kind && !s.restatement
|
|
@@ -16010,13 +16129,15 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16010
16129
|
}
|
|
16011
16130
|
return null;
|
|
16012
16131
|
};
|
|
16132
|
+
const registerSpread = form === "ostinato" ? rnd() ** 2 * 0.5 : 0.25 + rnd() * 0.75;
|
|
16013
16133
|
const style = {
|
|
16014
16134
|
groove: pick(["eighth", "sixteenth"], rnd),
|
|
16015
16135
|
arcPeriod: pick([4, 8, 8, 16], rnd),
|
|
16016
16136
|
arcPhase: pick([0, 1, 2], rnd),
|
|
16017
|
-
arcAmp:
|
|
16137
|
+
arcAmp: 5 * registerSpread,
|
|
16018
16138
|
// オクターブ跳躍は参考曲では音程の1.0%しかない。上げすぎると音域が広がる。
|
|
16019
|
-
octaveAffinity: 0.
|
|
16139
|
+
octaveAffinity: 0.18 * registerSpread,
|
|
16140
|
+
maxLeap: pick(LEAP_CEILINGS, rnd),
|
|
16020
16141
|
// **音階を厳しく締める曲は必ず中核音の歩数で組む。** ダイアトニックの度数で輪郭を
|
|
16021
16142
|
// 作ると、琉球音階なのにレやラが輪郭の中に入り込む。ファ・シを自由に使う
|
|
16022
16143
|
// 陽・民謡だけが、曲ごとに掛けたり掛けなかったりする({@link ComposeScale.strict})。
|
|
@@ -16069,8 +16190,11 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16069
16190
|
subInterval: pick([3, 4, 8, 9], rnd)
|
|
16070
16191
|
};
|
|
16071
16192
|
const motifPool = groovyCells(MOTIF_CELLS, style.groove, rnd);
|
|
16072
|
-
const
|
|
16073
|
-
const
|
|
16193
|
+
const targetRestRatio = 0.02 + rnd() ** 2 * 0.42;
|
|
16194
|
+
const targetNotesPerBar = Math.max(
|
|
16195
|
+
2.8,
|
|
16196
|
+
Math.min(9.5, 7.4 - 6 * targetRestRatio + (rnd() * 4 - 2))
|
|
16197
|
+
);
|
|
16074
16198
|
const cellNotes = (c) => c.value.filter((v) => v > 0).length;
|
|
16075
16199
|
const cellRest = (c) => {
|
|
16076
16200
|
let rest = 0;
|
|
@@ -16093,9 +16217,13 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16093
16217
|
return pool[pool.length - 1];
|
|
16094
16218
|
};
|
|
16095
16219
|
const pickCell = (pool, densityMul = 1) => {
|
|
16096
|
-
|
|
16220
|
+
const want = targetRestRatio / Math.max(0.5, densityMul);
|
|
16221
|
+
const tol = Math.max(0.08, want * 0.6);
|
|
16222
|
+
const near = pool.filter((c) => Math.abs(cellRest(c) - want) <= tol);
|
|
16223
|
+
const from = near.length > 0 ? near : pool;
|
|
16224
|
+
let best = weightedPick(from);
|
|
16097
16225
|
for (let i2 = 0; i2 < 2; i2++) {
|
|
16098
|
-
const c = weightedPick(
|
|
16226
|
+
const c = weightedPick(from);
|
|
16099
16227
|
if (cellDistance(c, densityMul) < cellDistance(best, densityMul))
|
|
16100
16228
|
best = c;
|
|
16101
16229
|
}
|
|
@@ -16351,13 +16479,16 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16351
16479
|
quarterSteps,
|
|
16352
16480
|
scale,
|
|
16353
16481
|
style.pentatonicMotif,
|
|
16354
|
-
motifShiftMemo.get(shiftKey) ?? null
|
|
16482
|
+
motifShiftMemo.get(shiftKey) ?? null,
|
|
16483
|
+
// リフ型は和音へ寄せない。同じセルを回し続けるのが役目。
|
|
16484
|
+
form === "ostinato" ? 0 : 3
|
|
16355
16485
|
);
|
|
16356
16486
|
fitted = r.degrees;
|
|
16357
16487
|
motifShiftMemo.set(shiftKey, r.shift);
|
|
16358
16488
|
}
|
|
16359
16489
|
const pitches = shapeBar(fitted, slots, tones, prevSemi, {
|
|
16360
16490
|
scale,
|
|
16491
|
+
maxLeap: style.maxLeap,
|
|
16361
16492
|
allowLeap: role === "climax",
|
|
16362
16493
|
allowArpeggio: role === "climax" || role === "run" && style.runShape === "broken" || role === "cadence" && style.cadenceShape !== "descend",
|
|
16363
16494
|
quarterSteps,
|
|
@@ -16803,6 +16934,7 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16803
16934
|
(n) => n.durationSteps >= quarterSteps || rnd() < octaveCoverage
|
|
16804
16935
|
).map((n) => ({ ...n, velocity: Math.max(40, n.velocity - 26) })) : [];
|
|
16805
16936
|
return {
|
|
16937
|
+
form,
|
|
16806
16938
|
chordProgression,
|
|
16807
16939
|
chordPattern,
|
|
16808
16940
|
rootShift,
|
|
@@ -16889,7 +17021,7 @@ var evaluate = (d, recent) => {
|
|
|
16889
17021
|
Math.min(...recent.map((r) => featureDistance(fingerprint, r))) / 1
|
|
16890
17022
|
);
|
|
16891
17023
|
const at = (b, v) => band(v, b[0], b[1], b[2], b[3]);
|
|
16892
|
-
const atc = (key, v) =>
|
|
17024
|
+
const atc = (key, v) => plausibleBand(v, CORPUS_BANDS[key]);
|
|
16893
17025
|
const peakBand = d.bars > 24 ? [0, 1, Math.round(d.bars / 16), Math.round(d.bars / 8) + 2] : HAND_BANDS.climaxPeaks;
|
|
16894
17026
|
const scoreBreakdown = {
|
|
16895
17027
|
entropy: atc("entropy", entropy),
|
|
@@ -16925,9 +17057,16 @@ var evaluate = (d, recent) => {
|
|
|
16925
17057
|
tensionResolve: d.tonal.floating ? 1 : tension.resolve,
|
|
16926
17058
|
novelty
|
|
16927
17059
|
};
|
|
17060
|
+
const forgiven = new Set(
|
|
17061
|
+
Object.entries(WEIGHTS).filter(([key]) => BUDGETED_KEYS.has(key)).map(([key, weight]) => ({
|
|
17062
|
+
key,
|
|
17063
|
+
deficit: weight * (1 - (scoreBreakdown[key] ?? 0))
|
|
17064
|
+
})).sort((a, b) => b.deficit - a.deficit).slice(0, DEVIATION_BUDGET).filter((e) => e.deficit > 0).map((e) => e.key)
|
|
17065
|
+
);
|
|
16928
17066
|
let weighted = 0;
|
|
16929
17067
|
let weightSum = 0;
|
|
16930
17068
|
for (const [key, weight] of Object.entries(WEIGHTS)) {
|
|
17069
|
+
if (forgiven.has(key)) continue;
|
|
16931
17070
|
weighted += (scoreBreakdown[key] ?? 0) * weight;
|
|
16932
17071
|
weightSum += weight;
|
|
16933
17072
|
}
|
|
@@ -16964,49 +17103,64 @@ var composeSong = (options) => {
|
|
|
16964
17103
|
resolvedKey.mode === "minor",
|
|
16965
17104
|
rnd
|
|
16966
17105
|
);
|
|
16967
|
-
|
|
16968
|
-
|
|
16969
|
-
let bestIsValid = false;
|
|
17106
|
+
const valid = [];
|
|
17107
|
+
const invalid = [];
|
|
16970
17108
|
let rejected = 0;
|
|
16971
17109
|
for (let attempt = 1; attempt <= count; attempt++) {
|
|
16972
|
-
const
|
|
16973
|
-
const { stats, ok } = evaluate(
|
|
16974
|
-
if (
|
|
16975
|
-
|
|
16976
|
-
|
|
16977
|
-
|
|
16978
|
-
|
|
16979
|
-
|
|
16980
|
-
|
|
16981
|
-
|
|
16982
|
-
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
|
|
16986
|
-
|
|
16987
|
-
|
|
16988
|
-
|
|
16989
|
-
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
|
|
16993
|
-
|
|
16994
|
-
sections: d.sections,
|
|
16995
|
-
bars: d.bars,
|
|
16996
|
-
vocal: d.vocal,
|
|
16997
|
-
tonal: d.tonal,
|
|
16998
|
-
melody: d.melody,
|
|
16999
|
-
submelody: d.submelody,
|
|
17000
|
-
bass: d.bass,
|
|
17001
|
-
harmony: d.harmony,
|
|
17002
|
-
harmony2: d.harmony2,
|
|
17003
|
-
octave: d.octave,
|
|
17004
|
-
pad: d.pad,
|
|
17005
|
-
solo: d.solo,
|
|
17006
|
-
stats: { ...stats, attempts: attempt, rejected }
|
|
17007
|
-
};
|
|
17110
|
+
const d2 = draw(options, resolvedKey, scale, rnd);
|
|
17111
|
+
const { stats, ok } = evaluate(d2, recent);
|
|
17112
|
+
if (ok) valid.push({ d: d2, stats });
|
|
17113
|
+
else {
|
|
17114
|
+
rejected++;
|
|
17115
|
+
invalid.push({ d: d2, stats });
|
|
17116
|
+
}
|
|
17117
|
+
}
|
|
17118
|
+
const pool = valid.length > 0 ? valid : invalid;
|
|
17119
|
+
const top = Math.max(...pool.map((c) => c.stats.score));
|
|
17120
|
+
const weights = pool.map(
|
|
17121
|
+
(c) => Math.exp((c.stats.score - top) / SELECT_TEMPERATURE)
|
|
17122
|
+
);
|
|
17123
|
+
const total = weights.reduce((a, b) => a + b, 0);
|
|
17124
|
+
let ticket = rnd() * total;
|
|
17125
|
+
let chosen = pool[pool.length - 1];
|
|
17126
|
+
for (let i2 = 0; i2 < pool.length; i2++) {
|
|
17127
|
+
ticket -= weights[i2];
|
|
17128
|
+
if (ticket <= 0) {
|
|
17129
|
+
chosen = pool[i2];
|
|
17130
|
+
break;
|
|
17131
|
+
}
|
|
17008
17132
|
}
|
|
17009
|
-
const
|
|
17133
|
+
const d = chosen.d;
|
|
17134
|
+
const result = {
|
|
17135
|
+
// ドラム・楽器・編曲プランは勝った候補にだけ後から付ける(メロディに
|
|
17136
|
+
// 依存しないので候補ごとに引いても採点は動かず、候補数ぶん無駄になる)。
|
|
17137
|
+
drum: "",
|
|
17138
|
+
instrument: "",
|
|
17139
|
+
arrange: EMPTY_ARRANGE,
|
|
17140
|
+
chordProgression: d.chordProgression,
|
|
17141
|
+
chordPattern: d.chordPattern,
|
|
17142
|
+
rootShift: d.rootShift,
|
|
17143
|
+
keyName: d.keyName,
|
|
17144
|
+
keyLabel: d.keyLabel,
|
|
17145
|
+
scaleId: scale.id,
|
|
17146
|
+
scaleLabel: scale.label,
|
|
17147
|
+
form: d.form,
|
|
17148
|
+
moodLabel: d.moodLabel,
|
|
17149
|
+
bpm: d.bpm,
|
|
17150
|
+
sections: d.sections,
|
|
17151
|
+
bars: d.bars,
|
|
17152
|
+
vocal: d.vocal,
|
|
17153
|
+
tonal: d.tonal,
|
|
17154
|
+
melody: d.melody,
|
|
17155
|
+
submelody: d.submelody,
|
|
17156
|
+
bass: d.bass,
|
|
17157
|
+
harmony: d.harmony,
|
|
17158
|
+
harmony2: d.harmony2,
|
|
17159
|
+
octave: d.octave,
|
|
17160
|
+
pad: d.pad,
|
|
17161
|
+
solo: d.solo,
|
|
17162
|
+
stats: { ...chosen.stats, attempts: count, rejected }
|
|
17163
|
+
};
|
|
17010
17164
|
result.stats.attempts = count;
|
|
17011
17165
|
result.stats.rejected = rejected;
|
|
17012
17166
|
result.drum = pickBuiltinDrum(result, rnd);
|
|
@@ -17207,157 +17361,643 @@ var composeLyrics = (melody, options) => {
|
|
|
17207
17361
|
return out.join("");
|
|
17208
17362
|
};
|
|
17209
17363
|
|
|
17210
|
-
// src/
|
|
17211
|
-
var
|
|
17212
|
-
|
|
17213
|
-
|
|
17214
|
-
|
|
17215
|
-
|
|
17216
|
-
|
|
17217
|
-
|
|
17218
|
-
|
|
17219
|
-
|
|
17220
|
-
|
|
17221
|
-
|
|
17222
|
-
}
|
|
17223
|
-
|
|
17224
|
-
|
|
17225
|
-
|
|
17226
|
-
|
|
17227
|
-
|
|
17228
|
-
|
|
17229
|
-
|
|
17230
|
-
|
|
17231
|
-
|
|
17232
|
-
|
|
17233
|
-
|
|
17234
|
-
|
|
17235
|
-
|
|
17236
|
-
|
|
17237
|
-
|
|
17238
|
-
|
|
17239
|
-
|
|
17240
|
-
|
|
17241
|
-
|
|
17242
|
-
|
|
17243
|
-
|
|
17244
|
-
|
|
17245
|
-
|
|
17246
|
-
|
|
17247
|
-
|
|
17248
|
-
|
|
17249
|
-
|
|
17250
|
-
|
|
17251
|
-
|
|
17252
|
-
|
|
17253
|
-
|
|
17254
|
-
|
|
17255
|
-
|
|
17256
|
-
|
|
17257
|
-
|
|
17258
|
-
|
|
17259
|
-
|
|
17260
|
-
|
|
17261
|
-
|
|
17262
|
-
|
|
17263
|
-
|
|
17264
|
-
|
|
17265
|
-
|
|
17266
|
-
|
|
17267
|
-
|
|
17268
|
-
|
|
17269
|
-
|
|
17270
|
-
|
|
17271
|
-
|
|
17272
|
-
|
|
17273
|
-
|
|
17274
|
-
|
|
17275
|
-
|
|
17276
|
-
|
|
17277
|
-
|
|
17278
|
-
|
|
17279
|
-
|
|
17280
|
-
|
|
17281
|
-
|
|
17282
|
-
|
|
17283
|
-
|
|
17284
|
-
|
|
17285
|
-
|
|
17286
|
-
|
|
17287
|
-
|
|
17288
|
-
|
|
17289
|
-
|
|
17290
|
-
|
|
17291
|
-
|
|
17292
|
-
|
|
17293
|
-
|
|
17294
|
-
|
|
17295
|
-
|
|
17296
|
-
"
|
|
17297
|
-
|
|
17298
|
-
|
|
17299
|
-
|
|
17300
|
-
|
|
17301
|
-
|
|
17302
|
-
|
|
17303
|
-
|
|
17304
|
-
|
|
17305
|
-
|
|
17364
|
+
// src/instrument-presets.ts
|
|
17365
|
+
var INSTRUMENT_PRESETS = {
|
|
17366
|
+
// --- STANDARD: 汎用性と完成度重視 ---
|
|
17367
|
+
piano: {
|
|
17368
|
+
displayName: "\u30B0\u30E9\u30F3\u30C9\u30D4\u30A2\u30CE",
|
|
17369
|
+
description: "\u6700\u3082\u7834\u7DBB\u3057\u306B\u304F\u3044\u69CB\u6210\u3002\u697D\u66F2\u5236\u4F5C\u306E\u30B9\u30B1\u30C3\u30C1\u306B\u3082\u6700\u9069\u3002",
|
|
17370
|
+
melody: "Acoustic Grand Piano",
|
|
17371
|
+
submelody: "Vibraphone",
|
|
17372
|
+
bass: "Electric Bass (finger)",
|
|
17373
|
+
chord: "Pad 2 (warm)",
|
|
17374
|
+
solo: "Electric Guitar (clean)",
|
|
17375
|
+
// **Glockenspiel は使わない。音源側のサンプルが音程を外している**(実聴で確認)。
|
|
17376
|
+
// 音域の問題(G5〜の高音楽器)なら {@link GM_BRIGHT_CEILING} で下げれば済むが、
|
|
17377
|
+
// ピッチそのものがずれているものは置き場所を変えても直らない。同じ役割
|
|
17378
|
+
// (サビの上に乗る明るい音板)で、素直に鳴る Celesta へ差し替えてある。
|
|
17379
|
+
// ※ {@link GM_INSTRUMENT_RANGE} / {@link GM_BRIGHT_CEILING} の Glockenspiel の
|
|
17380
|
+
// 項は残す。手で選んだときの置き場所の判断はそのまま要るため。
|
|
17381
|
+
chorusLead: "Celesta",
|
|
17382
|
+
chordAlt: "Electric Piano 1",
|
|
17383
|
+
sparkle: "Music Box",
|
|
17384
|
+
bassAlt: "Acoustic Bass",
|
|
17385
|
+
harmonyAlt: "Choir Aahs"
|
|
17386
|
+
},
|
|
17387
|
+
acoustic: {
|
|
17388
|
+
displayName: "\u30A2\u30B3\u30FC\u30B9\u30C6\u30A3\u30C3\u30AF",
|
|
17389
|
+
description: "\u751F\u697D\u5668\u306E\u6E29\u304B\u307F\u3092\u91CD\u8996\u3002\u30D5\u30A9\u30FC\u30AF\u3084\u30DD\u30C3\u30D7\u30B9\u306B\u3002",
|
|
17390
|
+
melody: "Acoustic Guitar (steel)",
|
|
17391
|
+
submelody: "Harmonica",
|
|
17392
|
+
bass: "Acoustic Bass",
|
|
17393
|
+
chord: "Acoustic Guitar (nylon)",
|
|
17394
|
+
solo: "Overdriven Guitar",
|
|
17395
|
+
chorusLead: "String Ensemble 1",
|
|
17396
|
+
chordAlt: "Acoustic Grand Piano",
|
|
17397
|
+
sparkle: "Celesta",
|
|
17398
|
+
bassAlt: "Electric Bass (finger)",
|
|
17399
|
+
harmonyAlt: "Choir Aahs"
|
|
17400
|
+
},
|
|
17401
|
+
jazz_night: {
|
|
17402
|
+
displayName: "\u30B8\u30E3\u30BA\u30FB\u30CA\u30A4\u30C8",
|
|
17403
|
+
description: "Rhodes\u98A8\u306EEP\u3068\u30A6\u30C3\u30C9\u30D9\u30FC\u30B9\u306B\u3088\u308B\u3001\u5927\u4EBA\u3073\u305F\u30A2\u30F3\u30B5\u30F3\u30D6\u30EB\u3002",
|
|
17404
|
+
melody: "Electric Piano 1",
|
|
17405
|
+
submelody: "Flute",
|
|
17406
|
+
bass: "Acoustic Bass",
|
|
17407
|
+
chord: "Electric Guitar (jazz)",
|
|
17408
|
+
solo: "Tenor Sax",
|
|
17409
|
+
chorusLead: "Muted Trumpet",
|
|
17410
|
+
chordAlt: "Vibraphone",
|
|
17411
|
+
sparkle: "Celesta",
|
|
17412
|
+
bassAlt: "Electric Bass (finger)",
|
|
17413
|
+
harmonyAlt: "Choir Aahs"
|
|
17414
|
+
},
|
|
17415
|
+
// --- MODERN & VIBE: エッジの効いた現代的な響き ---
|
|
17416
|
+
synth_pop: {
|
|
17417
|
+
displayName: "\u30B7\u30F3\u30BB\u30DD\u30C3\u30D7",
|
|
17418
|
+
description: "80s\u301C\u73FE\u4EE3\u307E\u3067\u3002\u629C\u3051\u308B\u30EA\u30FC\u30C9\u3068\u592A\u3044\u30D9\u30FC\u30B9\u306E\u738B\u9053\u3002",
|
|
17419
|
+
melody: "Lead 2 (sawtooth)",
|
|
17420
|
+
submelody: "Lead 4 (chiff)",
|
|
17421
|
+
bass: "Synth Bass 2",
|
|
17422
|
+
chord: "Pad 3 (polysynth)",
|
|
17423
|
+
solo: "Distortion Guitar",
|
|
17424
|
+
chorusLead: "Synth Brass 1",
|
|
17425
|
+
chordAlt: "Electric Piano 2",
|
|
17426
|
+
sparkle: "FX 3 (crystal)",
|
|
17427
|
+
bassAlt: "Synth Bass 1",
|
|
17428
|
+
harmonyAlt: "Synth Choir"
|
|
17429
|
+
},
|
|
17430
|
+
cyber_punk: {
|
|
17431
|
+
displayName: "\u30B5\u30A4\u30D0\u30FC\u30D1\u30F3\u30AF",
|
|
17432
|
+
description: "\u30C7\u30B8\u30BF\u30EB\u306A\u51B7\u305F\u3055\u3068\u6B6A\u307F\u304C\u6DF7\u3056\u308A\u5408\u3046\u3001\u672A\u6765\u7684\u306A\u97FF\u304D\u3002",
|
|
17433
|
+
melody: "Lead 8 (bass + lead)",
|
|
17434
|
+
submelody: "Lead 5 (charang)",
|
|
17435
|
+
bass: "Synth Bass 2",
|
|
17436
|
+
chord: "Pad 8 (sweep)",
|
|
17437
|
+
solo: "Distortion Guitar",
|
|
17438
|
+
chorusLead: "Lead 7 (fifths)",
|
|
17439
|
+
chordAlt: "Pad 4 (choir)",
|
|
17440
|
+
sparkle: "FX 3 (crystal)",
|
|
17441
|
+
bassAlt: "Synth Bass 1",
|
|
17442
|
+
harmonyAlt: "Synth Choir"
|
|
17443
|
+
},
|
|
17444
|
+
rock: {
|
|
17445
|
+
displayName: "\u30CF\u30FC\u30C9\u30ED\u30C3\u30AF",
|
|
17446
|
+
description: "\u6B6A\u307F\u30AE\u30BF\u30FC\u3068\u91CD\u539A\u306A\u30D9\u30FC\u30B9\u3067\u3001\u30D1\u30EF\u30FC\u3092\u524D\u9762\u306B\u3002",
|
|
17447
|
+
melody: "Distortion Guitar",
|
|
17448
|
+
submelody: "Rock Organ",
|
|
17449
|
+
bass: "Electric Bass (pick)",
|
|
17450
|
+
chord: "Overdriven Guitar",
|
|
17451
|
+
solo: "Distortion Guitar",
|
|
17452
|
+
chorusLead: "Brass Section",
|
|
17453
|
+
chordAlt: "Electric Guitar (clean)",
|
|
17454
|
+
sparkle: "Electric Guitar (muted)",
|
|
17455
|
+
bassAlt: "Electric Bass (finger)",
|
|
17456
|
+
harmonyAlt: "Choir Aahs"
|
|
17457
|
+
},
|
|
17458
|
+
// --- WORLD & CLASSIC: 特定のジャンル・地域 ---
|
|
17459
|
+
orchestra: {
|
|
17460
|
+
displayName: "\u30AA\u30FC\u30B1\u30B9\u30C8\u30E9",
|
|
17461
|
+
description: "\u58EE\u5927\u306A\u7269\u8A9E\u3092\u4E88\u611F\u3055\u305B\u308B\u3001\u7BA1\u5F26\u697D\u5668\u306E\u91CD\u539A\u306A\u97FF\u304D\u3002",
|
|
17462
|
+
melody: "French Horn",
|
|
17463
|
+
submelody: "Pizzicato Strings",
|
|
17464
|
+
bass: "Cello",
|
|
17465
|
+
chord: "Tremolo Strings",
|
|
17466
|
+
solo: "Violin",
|
|
17467
|
+
chorusLead: "Trumpet",
|
|
17468
|
+
chordAlt: "String Ensemble 1",
|
|
17469
|
+
sparkle: "Orchestral Harp",
|
|
17470
|
+
bassAlt: "Contrabass",
|
|
17471
|
+
harmonyAlt: "Choir Aahs"
|
|
17472
|
+
},
|
|
17473
|
+
japanese_wa: {
|
|
17474
|
+
displayName: "\u548C\u98A8\u30FB\u96C5",
|
|
17475
|
+
description: "\u7434\u3068\u4E09\u5473\u7DDA\u306E\u7E4A\u7D30\u306A\u8ABF\u3079\u306B\u3001\u5C3A\u516B\u306E\u60C5\u7DD2\u3092\u6DFB\u3048\u3066\u3002",
|
|
17476
|
+
melody: "Koto",
|
|
17477
|
+
submelody: "Shamisen",
|
|
17478
|
+
bass: "Taiko Drum",
|
|
17479
|
+
chord: "Shakuhachi",
|
|
17480
|
+
solo: "Shakuhachi",
|
|
17481
|
+
// piano プリセットと同じ理由で Glockenspiel を避ける(音源のピッチずれ)。
|
|
17482
|
+
chorusLead: "Celesta",
|
|
17483
|
+
chordAlt: "Kalimba",
|
|
17484
|
+
sparkle: "Music Box",
|
|
17485
|
+
bassAlt: "Acoustic Bass",
|
|
17486
|
+
harmonyAlt: "Choir Aahs"
|
|
17487
|
+
},
|
|
17488
|
+
arabic_exotic: {
|
|
17489
|
+
displayName: "\u30A8\u30AD\u30BE\u30C1\u30C3\u30AF",
|
|
17490
|
+
description: "\u30B7\u30BF\u30FC\u30EB\u3084\u30D0\u30B0\u30D1\u30A4\u30D7\u306B\u3088\u308B\u3001\u7570\u56FD\u60C5\u7DD2\u6EA2\u308C\u308B\u30B5\u30A6\u30F3\u30C9\u3002",
|
|
17491
|
+
melody: "Sitar",
|
|
17492
|
+
submelody: "Bagpipe",
|
|
17493
|
+
bass: "Fretless Bass",
|
|
17494
|
+
chord: "Kalimba",
|
|
17495
|
+
solo: "Shanai",
|
|
17496
|
+
chorusLead: "Steel Drums",
|
|
17497
|
+
chordAlt: "Orchestral Harp",
|
|
17498
|
+
sparkle: "Tinkle Bell",
|
|
17499
|
+
bassAlt: "Acoustic Bass",
|
|
17500
|
+
harmonyAlt: "Choir Aahs"
|
|
17501
|
+
},
|
|
17502
|
+
// --- FANTASY & ATMOSPHERE: 雰囲気と余韻 ---
|
|
17503
|
+
fantasy_rpg: {
|
|
17504
|
+
displayName: "\u30D5\u30A1\u30F3\u30BF\u30B8\u30FCRPG",
|
|
17505
|
+
description: "\u30AA\u30AB\u30EA\u30CA\u3068\u30CF\u30FC\u30D7\u304C\u7D21\u3050\u3001\u5192\u967A\u3068\u9B54\u6CD5\u306E\u4E16\u754C\u89B3\u3002",
|
|
17506
|
+
melody: "Ocarina",
|
|
17507
|
+
submelody: "Celesta",
|
|
17508
|
+
bass: "Timpani",
|
|
17509
|
+
chord: "Orchestral Harp",
|
|
17510
|
+
solo: "Pan Flute",
|
|
17511
|
+
chorusLead: "Choir Aahs",
|
|
17512
|
+
chordAlt: "String Ensemble 2",
|
|
17513
|
+
sparkle: "Tinkle Bell",
|
|
17514
|
+
bassAlt: "Contrabass",
|
|
17515
|
+
harmonyAlt: "Choir Aahs"
|
|
17516
|
+
},
|
|
17517
|
+
ambient_cloud: {
|
|
17518
|
+
displayName: "\u30A2\u30F3\u30D3\u30A8\u30F3\u30C8",
|
|
17519
|
+
description: "\u8F2A\u90ED\u3092\u307C\u304B\u3057\u305F\u97F3\u8272\u3067\u3001\u6DF1\u3044\u6CA1\u5165\u611F\u3068\u4F59\u97FB\u3092\u6F14\u51FA\u3002",
|
|
17520
|
+
melody: "Lead 6 (voice)",
|
|
17521
|
+
submelody: "Music Box",
|
|
17522
|
+
bass: "Synth Bass 1",
|
|
17523
|
+
chord: "Pad 7 (halo)",
|
|
17524
|
+
solo: "Lead 3 (calliope)",
|
|
17525
|
+
chorusLead: "Synth Choir",
|
|
17526
|
+
chordAlt: "Pad 5 (bowed)",
|
|
17527
|
+
sparkle: "FX 3 (crystal)",
|
|
17528
|
+
bassAlt: "Synth Bass 2",
|
|
17529
|
+
harmonyAlt: "Synth Choir"
|
|
17530
|
+
},
|
|
17531
|
+
retro_game: {
|
|
17532
|
+
displayName: "8-bit \u30EC\u30C8\u30ED",
|
|
17533
|
+
description: "\u77E9\u5F62\u6CE2\u3092\u60F3\u8D77\u3055\u305B\u308B\u3001\u521D\u671F\u30B2\u30FC\u30E0\u6A5F\u306E\u3088\u3046\u306A\u61D0\u304B\u3057\u3044\u97FF\u304D\u3002",
|
|
17534
|
+
melody: "Lead 1 (square)",
|
|
17535
|
+
submelody: "Lead 2 (sawtooth)",
|
|
17536
|
+
bass: "Synth Bass 1",
|
|
17537
|
+
chord: "Clavinet",
|
|
17538
|
+
solo: "Lead 8 (bass + lead)",
|
|
17539
|
+
chorusLead: "Lead 4 (chiff)",
|
|
17540
|
+
chordAlt: "Lead 5 (charang)",
|
|
17541
|
+
sparkle: "Xylophone",
|
|
17542
|
+
bassAlt: "Synth Bass 2",
|
|
17543
|
+
harmonyAlt: "Lead 6 (voice)"
|
|
17306
17544
|
}
|
|
17307
17545
|
};
|
|
17546
|
+
var GM_INSTRUMENT_RANGE = {
|
|
17547
|
+
// 鍵盤・音板
|
|
17548
|
+
"Acoustic Grand Piano": [21, 108],
|
|
17549
|
+
"Electric Piano 1": [28, 103],
|
|
17550
|
+
Clavinet: [36, 96],
|
|
17551
|
+
Celesta: [60, 108],
|
|
17552
|
+
Glockenspiel: [79, 108],
|
|
17553
|
+
"Music Box": [72, 108],
|
|
17554
|
+
Vibraphone: [53, 89],
|
|
17555
|
+
Kalimba: [60, 84],
|
|
17556
|
+
"Orchestral Harp": [23, 104],
|
|
17557
|
+
// 弦・撥弦
|
|
17558
|
+
"Acoustic Guitar (steel)": [40, 83],
|
|
17559
|
+
"Acoustic Guitar (nylon)": [40, 83],
|
|
17560
|
+
"Electric Guitar (clean)": [40, 86],
|
|
17561
|
+
"Electric Guitar (jazz)": [40, 86],
|
|
17562
|
+
"Overdriven Guitar": [40, 88],
|
|
17563
|
+
"Distortion Guitar": [40, 88],
|
|
17564
|
+
Violin: [55, 103],
|
|
17565
|
+
Cello: [36, 76],
|
|
17566
|
+
"String Ensemble 1": [28, 100],
|
|
17567
|
+
"Tremolo Strings": [28, 100],
|
|
17568
|
+
"Pizzicato Strings": [28, 96],
|
|
17569
|
+
Sitar: [48, 79],
|
|
17570
|
+
Shamisen: [48, 84],
|
|
17571
|
+
Koto: [41, 77],
|
|
17572
|
+
// ベース
|
|
17573
|
+
"Acoustic Bass": [28, 60],
|
|
17574
|
+
"Electric Bass (finger)": [28, 67],
|
|
17575
|
+
"Electric Bass (pick)": [28, 67],
|
|
17576
|
+
"Fretless Bass": [28, 67],
|
|
17577
|
+
"Synth Bass 1": [24, 72],
|
|
17578
|
+
"Synth Bass 2": [24, 72],
|
|
17579
|
+
// 管
|
|
17580
|
+
Flute: [60, 96],
|
|
17581
|
+
"Pan Flute": [60, 91],
|
|
17582
|
+
Shakuhachi: [62, 86],
|
|
17583
|
+
Ocarina: [60, 84],
|
|
17584
|
+
Harmonica: [60, 96],
|
|
17585
|
+
Bagpipe: [62, 86],
|
|
17586
|
+
Shanai: [60, 86],
|
|
17587
|
+
"Tenor Sax": [44, 75],
|
|
17588
|
+
Trumpet: [55, 82],
|
|
17589
|
+
"Muted Trumpet": [55, 82],
|
|
17590
|
+
"French Horn": [41, 77],
|
|
17591
|
+
"Brass Section": [41, 84],
|
|
17592
|
+
// 声・打・オルガン
|
|
17593
|
+
"Choir Aahs": [43, 84],
|
|
17594
|
+
"Synth Choir": [43, 84],
|
|
17595
|
+
"Steel Drums": [55, 86],
|
|
17596
|
+
"Taiko Drum": [30, 60],
|
|
17597
|
+
Timpani: [36, 57],
|
|
17598
|
+
"Rock Organ": [36, 96],
|
|
17599
|
+
// シンセ(実物が無いので一般的な使用域)
|
|
17600
|
+
"Synth Brass 1": [36, 96],
|
|
17601
|
+
"Lead 1 (square)": [36, 96],
|
|
17602
|
+
"Lead 2 (sawtooth)": [36, 96],
|
|
17603
|
+
"Lead 3 (calliope)": [48, 96],
|
|
17604
|
+
"Lead 4 (chiff)": [48, 96],
|
|
17605
|
+
"Lead 5 (charang)": [40, 96],
|
|
17606
|
+
"Lead 6 (voice)": [43, 91],
|
|
17607
|
+
"Lead 7 (fifths)": [36, 84],
|
|
17608
|
+
"Lead 8 (bass + lead)": [28, 91],
|
|
17609
|
+
"Pad 2 (warm)": [24, 96],
|
|
17610
|
+
"Pad 3 (polysynth)": [24, 96],
|
|
17611
|
+
"Pad 7 (halo)": [24, 96],
|
|
17612
|
+
"Pad 8 (sweep)": [24, 96]
|
|
17613
|
+
};
|
|
17614
|
+
var GM_BRIGHT_CEILING = {
|
|
17615
|
+
Glockenspiel: 72,
|
|
17616
|
+
"Tinkle Bell": 72,
|
|
17617
|
+
"Music Box": 79,
|
|
17618
|
+
Celesta: 84,
|
|
17619
|
+
Kalimba: 79,
|
|
17620
|
+
"Steel Drums": 84,
|
|
17621
|
+
"FX 3 (crystal)": 79
|
|
17622
|
+
};
|
|
17623
|
+
var OCTAVE_FIT_TOLERANCE = 3;
|
|
17624
|
+
var fitInstrumentOctave = (semitoneRange, instrument, wanted) => {
|
|
17625
|
+
const range = GM_INSTRUMENT_RANGE[instrument];
|
|
17626
|
+
if (!range || !semitoneRange) return wanted;
|
|
17627
|
+
const hi = Math.min(range[1], GM_BRIGHT_CEILING[instrument] ?? range[1]);
|
|
17628
|
+
let octave = wanted;
|
|
17629
|
+
while (octave > wanted - 2) {
|
|
17630
|
+
if (semitoneRange[1] + octave * 12 <= hi + OCTAVE_FIT_TOLERANCE) break;
|
|
17631
|
+
octave--;
|
|
17632
|
+
}
|
|
17633
|
+
return octave;
|
|
17634
|
+
};
|
|
17308
17635
|
|
|
17309
|
-
// src/
|
|
17310
|
-
var
|
|
17311
|
-
|
|
17312
|
-
const {
|
|
17313
|
-
|
|
17314
|
-
|
|
17315
|
-
|
|
17316
|
-
|
|
17317
|
-
|
|
17318
|
-
|
|
17319
|
-
|
|
17320
|
-
|
|
17321
|
-
|
|
17322
|
-
|
|
17323
|
-
|
|
17324
|
-
).
|
|
17325
|
-
|
|
17326
|
-
|
|
17327
|
-
|
|
17328
|
-
|
|
17329
|
-
|
|
17330
|
-
|
|
17331
|
-
|
|
17332
|
-
|
|
17333
|
-
|
|
17334
|
-
|
|
17335
|
-
|
|
17336
|
-
|
|
17337
|
-
|
|
17338
|
-
|
|
17339
|
-
|
|
17340
|
-
|
|
17341
|
-
|
|
17342
|
-
|
|
17343
|
-
|
|
17344
|
-
|
|
17345
|
-
|
|
17346
|
-
|
|
17347
|
-
|
|
17348
|
-
|
|
17349
|
-
|
|
17350
|
-
|
|
17351
|
-
|
|
17352
|
-
|
|
17353
|
-
|
|
17354
|
-
|
|
17355
|
-
|
|
17356
|
-
|
|
17357
|
-
|
|
17358
|
-
|
|
17359
|
-
|
|
17360
|
-
|
|
17636
|
+
// src/advanced-layers.ts
|
|
17637
|
+
var buildAdvancedLayers = (song, config) => {
|
|
17638
|
+
const { edo, stepsPerBar, preset } = config;
|
|
17639
|
+
const semitoneRange = (notes) => {
|
|
17640
|
+
if (notes.length === 0) return null;
|
|
17641
|
+
let lo = Number.POSITIVE_INFINITY;
|
|
17642
|
+
let hi = Number.NEGATIVE_INFINITY;
|
|
17643
|
+
for (const n of notes) {
|
|
17644
|
+
const semi = n.pitchUnits / UNITS_PER_SEMITONE;
|
|
17645
|
+
if (semi < lo) lo = semi;
|
|
17646
|
+
if (semi > hi) hi = semi;
|
|
17647
|
+
}
|
|
17648
|
+
return [Math.round(lo), Math.round(hi)];
|
|
17649
|
+
};
|
|
17650
|
+
const fit = (notes, slot, wanted) => fitInstrumentOctave(semitoneRange(notes), preset[slot], wanted);
|
|
17651
|
+
const kindAtBar = (bar) => song.sections.find(
|
|
17652
|
+
(sec) => bar >= sec.startBar && bar < sec.startBar + sec.bars
|
|
17653
|
+
)?.kind ?? null;
|
|
17654
|
+
const onlyIn = (notes, kinds) => {
|
|
17655
|
+
if (!kinds) return notes;
|
|
17656
|
+
const want = new Set(kinds);
|
|
17657
|
+
return notes.filter((n) => {
|
|
17658
|
+
const kind = kindAtBar(Math.floor(n.startStep / stepsPerBar));
|
|
17659
|
+
return kind !== null && want.has(kind);
|
|
17660
|
+
});
|
|
17661
|
+
};
|
|
17662
|
+
const chordNotes = (pattern, velocityShift = 0) => buildChordPlacements({
|
|
17663
|
+
edo,
|
|
17664
|
+
chordStr: song.chordProgression,
|
|
17665
|
+
patternType: pattern,
|
|
17666
|
+
rootShift: song.rootShift,
|
|
17667
|
+
bpm: song.bpm,
|
|
17668
|
+
stepsPerBar
|
|
17669
|
+
}).map((p) => ({
|
|
17670
|
+
startStep: p.startStep,
|
|
17671
|
+
pitchUnits: p.pitchUnits,
|
|
17672
|
+
durationSteps: p.durationSteps,
|
|
17673
|
+
velocity: Math.max(30, p.velocity + velocityShift)
|
|
17674
|
+
}));
|
|
17675
|
+
const plan = song.arrange;
|
|
17676
|
+
const kindsPresent = [...new Set(song.sections.map((sec) => sec.kind))];
|
|
17677
|
+
const LOUD = ["chorus", "drop_chorus"];
|
|
17678
|
+
const loudKinds = kindsPresent.filter((k) => LOUD.includes(k));
|
|
17679
|
+
const quietKinds = kindsPresent.filter((k) => !LOUD.includes(k));
|
|
17680
|
+
const splittable = loudKinds.length > 0 && quietKinds.length > 0;
|
|
17681
|
+
const leadNotes = plan.lead ? onlyIn(song.melody, plan.lead.sections) : [];
|
|
17682
|
+
const leadOctave = fit(leadNotes, "chorusLead", plan.lead?.octave ?? 0);
|
|
17683
|
+
const leadLayer = {
|
|
17684
|
+
index: 1,
|
|
17685
|
+
notes: leadNotes,
|
|
17686
|
+
octave: leadOctave,
|
|
17687
|
+
// ユニゾンで重ねるときは、オクターブ上より前に出やすいので少し引く。
|
|
17688
|
+
volume: leadOctave === 0 ? 54 : 62,
|
|
17689
|
+
slot: "chorusLead"
|
|
17690
|
+
};
|
|
17691
|
+
const bassNotes = plan.bassLayer ? onlyIn(song.bass, plan.bassLayer.sections) : [];
|
|
17692
|
+
const bassOctave = fit(bassNotes, "bass", plan.bassLayer?.octave ?? 0);
|
|
17693
|
+
const wantBassLayer = plan.bassLayer !== null && bassOctave !== 0;
|
|
17694
|
+
const bassLayer = wantBassLayer ? {
|
|
17695
|
+
index: 5,
|
|
17696
|
+
notes: bassNotes,
|
|
17697
|
+
octave: bassOctave,
|
|
17698
|
+
volume: 58,
|
|
17699
|
+
slot: "bass"
|
|
17700
|
+
} : {
|
|
17701
|
+
index: 5,
|
|
17702
|
+
notes: splittable ? onlyIn(song.bass, loudKinds) : [],
|
|
17703
|
+
octave: 0,
|
|
17704
|
+
volume: 92,
|
|
17705
|
+
slot: "bassAlt"
|
|
17706
|
+
};
|
|
17707
|
+
const padNotes = onlyIn(song.pad, plan.padSections);
|
|
17708
|
+
const layers = [
|
|
17709
|
+
{ index: 0, notes: song.melody, octave: 0, volume: 104, slot: "melody" },
|
|
17710
|
+
leadLayer,
|
|
17711
|
+
// t2 ハモリ。**セクションで分割しない。**
|
|
17712
|
+
// 一度 t12 と loud/quiet で分けてみたが、ハモリは音の87%が盛り上がる側にあり、
|
|
17713
|
+
// 71%の曲は静かな側に1音も無い——分割すると t2 が空になって中身が t12 へ移る。
|
|
17714
|
+
// 「トラック番号と役割の対応は固定」(ピアノロールのどこに何があるかが曲ごとに
|
|
17715
|
+
// 動くとユーザーが編集できない)という原則にも反するので、ここは丸ごと持つ。
|
|
17716
|
+
{ index: 2, notes: song.harmony, octave: 0, volume: 82 },
|
|
17717
|
+
// t3 サブメロ。t14 が間奏ソロを持たないとき、そちらへ盛り上がる側を渡して
|
|
17718
|
+
// こちらは静かな側だけ持つ。対旋律・合いの手がセクションで音色替えする。
|
|
17719
|
+
{
|
|
17720
|
+
index: 3,
|
|
17721
|
+
notes: song.solo.length === 0 && splittable ? onlyIn(song.submelody, quietKinds) : song.submelody,
|
|
17722
|
+
octave: 0,
|
|
17723
|
+
volume: 86,
|
|
17724
|
+
slot: "submelody"
|
|
17725
|
+
},
|
|
17726
|
+
// t4。t5 が `bassAlt` で盛り上がる側を持つときは、こちらは静かな側だけ持つ
|
|
17727
|
+
// (分割であって間引きではない——2本合わせて元のベースが漏れなく鳴る)。
|
|
17728
|
+
{
|
|
17729
|
+
index: 4,
|
|
17730
|
+
notes: !wantBassLayer && splittable ? onlyIn(song.bass, quietKinds) : song.bass,
|
|
17731
|
+
octave: 0,
|
|
17732
|
+
volume: 92,
|
|
17733
|
+
slot: "bass"
|
|
17734
|
+
},
|
|
17735
|
+
bassLayer,
|
|
17736
|
+
// **パッドは伴奏用の楽器で、伴奏より1.5オクターブ高いところを鳴らす。**
|
|
17737
|
+
// そのまま置くとナイロンギターで10半音、カリンバで9半音、尺八で7半音ぶん
|
|
17738
|
+
// 音域を突き抜ける。楽器に合わせて下げる。
|
|
17739
|
+
{
|
|
17740
|
+
index: 6,
|
|
17741
|
+
notes: padNotes,
|
|
17742
|
+
octave: fit(padNotes, "chord", 0),
|
|
17743
|
+
volume: 64,
|
|
17744
|
+
slot: "chord"
|
|
17745
|
+
}
|
|
17746
|
+
];
|
|
17747
|
+
const backingVolumes = [62, 54, 50];
|
|
17748
|
+
for (let i2 = 0; i2 < 3; i2++) {
|
|
17749
|
+
const layer = plan.backing[i2];
|
|
17750
|
+
layers.push({
|
|
17751
|
+
index: 7 + i2,
|
|
17752
|
+
notes: layer ? onlyIn(chordNotes(layer.pattern), layer.sections) : [],
|
|
17753
|
+
octave: layer?.octave ?? 0,
|
|
17754
|
+
volume: backingVolumes[i2],
|
|
17755
|
+
slot: i2 % 2 === 0 ? "chordAlt" : "chord"
|
|
17756
|
+
});
|
|
17757
|
+
}
|
|
17758
|
+
const sparkleNotes = plan.sparkle ? onlyIn(chordNotes(plan.sparkle.pattern, -14), plan.sparkle.sections) : [];
|
|
17759
|
+
layers.push(
|
|
17760
|
+
{
|
|
17761
|
+
index: 10,
|
|
17762
|
+
notes: sparkleNotes,
|
|
17763
|
+
// 装飾は伴奏と別の音色にする(以前はここも `chord` の使い回しだった)。
|
|
17764
|
+
octave: fit(sparkleNotes, "sparkle", plan.sparkle?.octave ?? 0),
|
|
17765
|
+
volume: 56,
|
|
17766
|
+
slot: "sparkle"
|
|
17767
|
+
},
|
|
17768
|
+
// 掛け合い(デュエット)の相手。**歌入り作曲のときだけ**中身が入る。
|
|
17769
|
+
{ index: 11, notes: [], octave: 0, volume: 104, slot: "melody" },
|
|
17770
|
+
// 2声目のハモリ(主旋律を上下から挟む3声)と、主旋律のオクターブ下の重ね。
|
|
17771
|
+
// どちらも曲ごとに出るかどうかが決まる(`song.vocal`)。
|
|
17772
|
+
// t12。3声目のハモリは曲ごとに出るかが決まり、実測28%。
|
|
17773
|
+
//
|
|
17774
|
+
// 出ない曲では**パッド(t6)が鳴っていないセクション**を、別の音色
|
|
17775
|
+
// (`harmonyAlt` =声もの・柔らかい持続音)で受け持つ。パッドはセクション種別の
|
|
17776
|
+
// 39%しか覆っておらず補集合は常に存在するので、ここは**いま何も鳴っていない
|
|
17777
|
+
// 場所に持続音を足す**ことになる——重ねでも写しでもなく、純粋な追加。
|
|
17778
|
+
song.harmony2.length > 0 ? { index: 12, notes: song.harmony2, octave: 0, volume: 74 } : (() => {
|
|
17779
|
+
const rest = plan.padSections ? kindsPresent.filter((k) => !plan.padSections?.includes(k)) : [];
|
|
17780
|
+
const notes = rest.length > 0 ? onlyIn(song.pad, rest) : [];
|
|
17781
|
+
return {
|
|
17782
|
+
index: 12,
|
|
17783
|
+
notes,
|
|
17784
|
+
octave: fit(notes, "harmonyAlt", 0),
|
|
17785
|
+
volume: 60,
|
|
17786
|
+
slot: "harmonyAlt"
|
|
17787
|
+
};
|
|
17788
|
+
})(),
|
|
17789
|
+
// t13。既定は**主旋律のオクターブ下の重ね**で、歌入り作曲ではここに歌詞が付く
|
|
17790
|
+
// (オクターブ下でハモる歌手。単なる写しではない)。
|
|
17791
|
+
//
|
|
17792
|
+
// ただしこの層は曲ごとに出るかどうかが決まり、実測で**18%しか鳴らない**。
|
|
17793
|
+
// 残り82%はトラックが1本まるごと遊ぶ。オクターブ等価の写しに常設の価値は
|
|
17794
|
+
// 無いが、**空けておく価値はもっと無い**ので、層が無い曲では
|
|
17795
|
+
// 「t1(サビ重ね)が担当しないセクションの主旋律を、別の楽器でなぞる」層に回す。
|
|
17796
|
+
// t1 は CHORUS/LOUD 側を持つので、こちらは静かな側を持つ——結果として
|
|
17797
|
+
// **セクションの境目で主旋律に付く音色が入れ替わる**。1トラック1楽器の制約下で
|
|
17798
|
+
// 「パートごとに楽器が変わる」を作れるのは、この書き分けだけ。
|
|
17799
|
+
song.octave.length > 0 ? {
|
|
17800
|
+
index: 13,
|
|
17801
|
+
notes: song.octave,
|
|
17802
|
+
octave: -1,
|
|
17803
|
+
volume: 56,
|
|
17804
|
+
slot: "melody"
|
|
17805
|
+
} : (() => {
|
|
17806
|
+
const leadKinds = new Set(plan.lead?.sections ?? []);
|
|
17807
|
+
const quiet = song.sections.filter((sec) => sec.spec.melody && !leadKinds.has(sec.kind)).map((sec) => sec.kind);
|
|
17808
|
+
const notes = quiet.length > 0 ? onlyIn(song.melody, quiet) : [];
|
|
17809
|
+
const slot = ["solo", "chorusLead", "submelody", "chordAlt"].find(
|
|
17810
|
+
(k) => preset[k] !== preset.melody && preset[k] !== preset.chord
|
|
17811
|
+
) ?? "submelody";
|
|
17812
|
+
return {
|
|
17813
|
+
index: 13,
|
|
17814
|
+
notes,
|
|
17815
|
+
// ユニゾンで置く。**音色だけを変えるのが狙い**なので、
|
|
17816
|
+
// オクターブを動かすと「オクターブ写し」に戻ってしまう。
|
|
17817
|
+
octave: fit(notes, slot, 0),
|
|
17818
|
+
volume: 58,
|
|
17819
|
+
slot
|
|
17820
|
+
};
|
|
17821
|
+
})(),
|
|
17822
|
+
// **間奏のソロ。** 音が入るのは間奏の小節だけなので、この1本だけを
|
|
17823
|
+
// 別の楽器にしても他のセクションの鳴りは変わらない。歌の音域をそのまま
|
|
17824
|
+
// 渡すと管楽器が上へ抜ける(テナーサックスで10半音)ので、ここも合わせる。
|
|
17825
|
+
// t14。**間奏のソロ。** ただし間奏は既定の構成(`DEFAULT_SECTIONS`)に無く、
|
|
17826
|
+
// テンプレートも `jpop_standard` しか含まないので、実測で**0%**——設計上の
|
|
17827
|
+
// 出番が既定では一度も来ないトラックだった。
|
|
17828
|
+
// 間奏が無い曲では、サブメロの盛り上がる側を `solo` の音色で受け持つ
|
|
17829
|
+
// (t3 は静かな側。分割なので音数は増えない)。
|
|
17830
|
+
song.solo.length > 0 ? {
|
|
17831
|
+
index: 14,
|
|
17832
|
+
notes: song.solo,
|
|
17833
|
+
octave: fit(song.solo, "solo", 0),
|
|
17834
|
+
volume: 100,
|
|
17835
|
+
slot: "solo"
|
|
17836
|
+
} : (() => {
|
|
17837
|
+
const notes = splittable ? onlyIn(song.submelody, loudKinds) : [];
|
|
17838
|
+
return {
|
|
17839
|
+
index: 14,
|
|
17840
|
+
notes,
|
|
17841
|
+
octave: fit(notes, "solo", 0),
|
|
17842
|
+
volume: 86,
|
|
17843
|
+
slot: "solo"
|
|
17844
|
+
};
|
|
17845
|
+
})()
|
|
17846
|
+
);
|
|
17847
|
+
return layers;
|
|
17848
|
+
};
|
|
17849
|
+
|
|
17850
|
+
// src/delay.ts
|
|
17851
|
+
var DELAY_DIVISIONS = [
|
|
17852
|
+
{ value: "4", label: "4\u5206", beats: 1 },
|
|
17853
|
+
{ value: "8", label: "8\u5206", beats: 0.5 },
|
|
17854
|
+
{ value: "8d", label: "\u4ED8\u70B98\u5206", beats: 0.75 },
|
|
17855
|
+
{ value: "16", label: "16\u5206", beats: 0.25 }
|
|
17856
|
+
];
|
|
17857
|
+
var clamp4 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
17858
|
+
var divisionToSeconds = (division, bpm) => {
|
|
17859
|
+
const beats = DELAY_DIVISIONS.find((d) => d.value === division)?.beats ?? 0.5;
|
|
17860
|
+
const safeBpm = bpm > 0 ? bpm : 120;
|
|
17861
|
+
return 60 / safeBpm * beats;
|
|
17862
|
+
};
|
|
17863
|
+
var DELAY_MAX_WET = 0.45;
|
|
17864
|
+
var delayAmountToGain = (amount) => clamp4(amount, 0, 100) / 100 * DELAY_MAX_WET;
|
|
17865
|
+
var FEEDBACK_GAIN = 0.3;
|
|
17866
|
+
var MAX_DELAY_SEC = 2;
|
|
17867
|
+
var createDelayBus = (ctx, destination, options = {}) => {
|
|
17868
|
+
const input = ctx.createGain();
|
|
17869
|
+
const delayNode = ctx.createDelay(MAX_DELAY_SEC);
|
|
17870
|
+
const feedback = ctx.createGain();
|
|
17871
|
+
feedback.gain.value = FEEDBACK_GAIN;
|
|
17872
|
+
const wetGain = ctx.createGain();
|
|
17873
|
+
wetGain.gain.value = delayAmountToGain(options.amount ?? 0);
|
|
17874
|
+
input.connect(delayNode);
|
|
17875
|
+
delayNode.connect(feedback);
|
|
17876
|
+
feedback.connect(delayNode);
|
|
17877
|
+
delayNode.connect(wetGain);
|
|
17878
|
+
wetGain.connect(destination);
|
|
17879
|
+
let bpm = options.bpm ?? 120;
|
|
17880
|
+
let division = options.division ?? "8";
|
|
17881
|
+
const applyTime = () => {
|
|
17882
|
+
delayNode.delayTime.setTargetAtTime(
|
|
17883
|
+
divisionToSeconds(division, bpm),
|
|
17884
|
+
ctx.currentTime,
|
|
17885
|
+
0.05
|
|
17886
|
+
);
|
|
17887
|
+
};
|
|
17888
|
+
applyTime();
|
|
17889
|
+
return {
|
|
17890
|
+
input,
|
|
17891
|
+
setAmount: (amount) => {
|
|
17892
|
+
wetGain.gain.setTargetAtTime(
|
|
17893
|
+
delayAmountToGain(amount),
|
|
17894
|
+
ctx.currentTime,
|
|
17895
|
+
0.02
|
|
17896
|
+
);
|
|
17897
|
+
},
|
|
17898
|
+
setDivision: (d) => {
|
|
17899
|
+
division = d;
|
|
17900
|
+
applyTime();
|
|
17901
|
+
},
|
|
17902
|
+
setBpm: (b) => {
|
|
17903
|
+
bpm = b;
|
|
17904
|
+
applyTime();
|
|
17905
|
+
},
|
|
17906
|
+
dispose: () => {
|
|
17907
|
+
input.disconnect();
|
|
17908
|
+
delayNode.disconnect();
|
|
17909
|
+
feedback.disconnect();
|
|
17910
|
+
wetGain.disconnect();
|
|
17911
|
+
}
|
|
17912
|
+
};
|
|
17913
|
+
};
|
|
17914
|
+
|
|
17915
|
+
// src/panel-state.ts
|
|
17916
|
+
var STORAGE_PREFIX = "dtm-panel-open:";
|
|
17917
|
+
var readPanelOpen = (key) => {
|
|
17918
|
+
try {
|
|
17919
|
+
if (typeof localStorage === "undefined" || !localStorage) return null;
|
|
17920
|
+
const raw = localStorage.getItem(STORAGE_PREFIX + key);
|
|
17921
|
+
if (raw === "1") return true;
|
|
17922
|
+
if (raw === "0") return false;
|
|
17923
|
+
} catch (_) {
|
|
17924
|
+
}
|
|
17925
|
+
return null;
|
|
17926
|
+
};
|
|
17927
|
+
var writePanelOpen = (key, open) => {
|
|
17928
|
+
try {
|
|
17929
|
+
if (typeof localStorage === "undefined" || !localStorage) return;
|
|
17930
|
+
localStorage.setItem(STORAGE_PREFIX + key, open ? "1" : "0");
|
|
17931
|
+
} catch (_) {
|
|
17932
|
+
}
|
|
17933
|
+
};
|
|
17934
|
+
var persistPanels = (root) => {
|
|
17935
|
+
const panels = root.querySelectorAll(
|
|
17936
|
+
"details[data-dtm-acc]"
|
|
17937
|
+
);
|
|
17938
|
+
for (const panel of panels) {
|
|
17939
|
+
const key = panel.dataset.dtmAcc;
|
|
17940
|
+
if (!key) continue;
|
|
17941
|
+
const stored = readPanelOpen(key);
|
|
17942
|
+
if (stored !== null) panel.open = stored;
|
|
17943
|
+
panel.addEventListener("toggle", () => {
|
|
17944
|
+
writePanelOpen(key, panel.open);
|
|
17945
|
+
});
|
|
17946
|
+
}
|
|
17947
|
+
};
|
|
17948
|
+
|
|
17949
|
+
// src/daw-ui.ts
|
|
17950
|
+
var q = (root, sel) => root.querySelector(sel);
|
|
17951
|
+
var buildUI = (target, options) => {
|
|
17952
|
+
const {
|
|
17953
|
+
drumPatterns,
|
|
17954
|
+
defaultDrumPattern,
|
|
17955
|
+
defaultBpm,
|
|
17956
|
+
showMidi,
|
|
17957
|
+
showMidiSearch,
|
|
17958
|
+
showCompose
|
|
17959
|
+
} = options;
|
|
17960
|
+
const drumOptions = [`<option value="none">\u306A\u3057</option>`].concat(
|
|
17961
|
+
drumPatterns.map(
|
|
17962
|
+
(p) => `<option value="${p.value}" ${p.value === defaultDrumPattern ? "selected" : ""}>${p.label}</option>`
|
|
17963
|
+
)
|
|
17964
|
+
).join("");
|
|
17965
|
+
target.innerHTML = `
|
|
17966
|
+
<div class="dtm-daw" data-dtm="root">
|
|
17967
|
+
<div class="dtm-topbar" data-dtm="transport">
|
|
17968
|
+
<div class="dtm-topbar-row1">
|
|
17969
|
+
<button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
|
|
17970
|
+
<button class="dtm-play" data-dtm="play" disabled>${icon("play")}</button>
|
|
17971
|
+
<button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
|
|
17972
|
+
<label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
|
|
17973
|
+
<span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
|
|
17974
|
+
<button class="dtm-clip-badge dtm-hidden" data-dtm="clip-badge" title="\u97F3\u5272\u308C\u691C\u77E5\uFF08\u30AF\u30EA\u30C3\u30AF\u3067\u6D88\u3059\uFF09">CLIP</button>
|
|
17975
|
+
<span class="dtm-grow"></span>
|
|
17976
|
+
<span class="dtm-label">BPM</span>
|
|
17977
|
+
<input type="number" class="dtm-input dtm-input--num" data-dtm="bpm" value="${defaultBpm}" min="20" max="300">
|
|
17978
|
+
</div>
|
|
17979
|
+
<div class="dtm-tracks" data-dtm="track-tabs"></div>
|
|
17980
|
+
</div>
|
|
17981
|
+
|
|
17982
|
+
<div class="dtm-tooldock">
|
|
17983
|
+
<div class="dtm-seg">
|
|
17984
|
+
<button class="dtm-segbtn dtm-segbtn--active" data-dtm="tool-pen" title="\u30DA\u30F3">${icon("pen")}</button>
|
|
17985
|
+
<button class="dtm-segbtn" data-dtm="tool-select" title="\u9078\u629E">${icon("select")}</button>
|
|
17986
|
+
<button class="dtm-segbtn" data-dtm="tool-eraser" title="\u6D88\u3057\u30B4\u30E0">${icon("eraser")}</button>
|
|
17987
|
+
</div>
|
|
17988
|
+
<button class="dtm-iconbtn" data-dtm="undo" title="\u5143\u306B\u623B\u3059" disabled>${icon("undo")}</button>
|
|
17989
|
+
<button class="dtm-iconbtn" data-dtm="redo" title="\u3084\u308A\u76F4\u3057" disabled>${icon("redo")}</button>
|
|
17990
|
+
<select class="dtm-select dtm-grow" data-dtm="note-length" title="\u97F3\u7B26\u306E\u9577\u3055">
|
|
17991
|
+
<option value="48">4\u5206</option>
|
|
17992
|
+
<option value="32">3\u90234</option>
|
|
17993
|
+
<option value="24">8\u5206</option>
|
|
17994
|
+
<option value="16">3\u90238</option>
|
|
17995
|
+
<option value="12" selected>16\u5206</option>
|
|
17996
|
+
<option value="8">3\u902316</option>
|
|
17997
|
+
<option value="6">32\u5206</option>
|
|
17998
|
+
<option value="4">3\u902332</option>
|
|
17999
|
+
</select>
|
|
18000
|
+
</div>
|
|
17361
18001
|
|
|
17362
18002
|
<div class="dtm-roll-wrap">
|
|
17363
18003
|
<div class="dtm-roll" data-dtm="roll">
|
|
@@ -17762,355 +18402,135 @@ var buildUI = (target, options) => {
|
|
|
17762
18402
|
<div class="dtm-modal-header">
|
|
17763
18403
|
<span class="dtm-modal-title" data-dtm="modal-title"></span>
|
|
17764
18404
|
<button class="dtm-modal-close" data-dtm="modal-close">×</button>
|
|
17765
|
-
</div>
|
|
17766
|
-
<div class="dtm-modal-body" data-dtm="modal-body"></div>
|
|
17767
|
-
</div>
|
|
17768
|
-
</div>
|
|
17769
|
-
|
|
17770
|
-
</div>`;
|
|
17771
|
-
const root = q(target, '[data-dtm="root"]');
|
|
17772
|
-
persistPanels(root);
|
|
17773
|
-
const sel = (name) => q(root, `[data-dtm="${name}"]`);
|
|
17774
|
-
return {
|
|
17775
|
-
root,
|
|
17776
|
-
topbar: sel("transport"),
|
|
17777
|
-
topbarLoading: sel("topbar-loading"),
|
|
17778
|
-
playBtn: sel("play"),
|
|
17779
|
-
prevBarBtn: sel("prev-bar"),
|
|
17780
|
-
nextBarBtn: sel("next-bar"),
|
|
17781
|
-
soloCheckbox: sel("solo"),
|
|
17782
|
-
clipBadge: sel("clip-badge"),
|
|
17783
|
-
toolPen: sel("tool-pen"),
|
|
17784
|
-
toolSelect: sel("tool-select"),
|
|
17785
|
-
toolEraser: sel("tool-eraser"),
|
|
17786
|
-
undoBtn: sel("undo"),
|
|
17787
|
-
redoBtn: sel("redo"),
|
|
17788
|
-
noteLengthSelect: sel("note-length"),
|
|
17789
|
-
bpmInput: sel("bpm"),
|
|
17790
|
-
zoomXLabel: sel("zoomx-label"),
|
|
17791
|
-
zoomYLabel: sel("zoomy-label"),
|
|
17792
|
-
zoomXIn: sel("zoomx-in"),
|
|
17793
|
-
zoomXOut: sel("zoomx-out"),
|
|
17794
|
-
zoomYIn: sel("zoomy-in"),
|
|
17795
|
-
zoomYOut: sel("zoomy-out"),
|
|
17796
|
-
bgFileInput: sel("bg-file-input"),
|
|
17797
|
-
bgUploadBtn: sel("bg-upload"),
|
|
17798
|
-
bgRemoveBtn: sel("bg-remove"),
|
|
17799
|
-
bgOpacityInput: sel("bg-opacity"),
|
|
17800
|
-
bgOpacityRow: sel("bg-opacity-row"),
|
|
17801
|
-
rollContainer: sel("roll"),
|
|
17802
|
-
wrapper: sel("wrapper"),
|
|
17803
|
-
vScroll: sel("vscroll"),
|
|
17804
|
-
vScrollThumb: sel("vscroll-thumb"),
|
|
17805
|
-
hScroll: sel("hscroll"),
|
|
17806
|
-
hScrollThumb: sel("hscroll-thumb"),
|
|
17807
|
-
loopToggle: sel("loop-toggle"),
|
|
17808
|
-
loopToggleLabel: sel("loop-toggle-label"),
|
|
17809
|
-
loopInfoBtn: sel("loop-info"),
|
|
17810
|
-
masterVolume: sel("master-volume"),
|
|
17811
|
-
masterVolumeLabel: sel("master-volume-label"),
|
|
17812
|
-
masterComp: sel("master-comp"),
|
|
17813
|
-
masterCompLabel: sel("master-comp-label"),
|
|
17814
|
-
masterCompInfoBtn: sel("master-comp-info"),
|
|
17815
|
-
reverbAmount: sel("reverb-amount"),
|
|
17816
|
-
reverbAmountLabel: sel("reverb-amount-label"),
|
|
17817
|
-
reverbAmountInfoBtn: sel("reverb-amount-info"),
|
|
17818
|
-
reverbDecay: sel("reverb-decay"),
|
|
17819
|
-
reverbDecayLabel: sel("reverb-decay-label"),
|
|
17820
|
-
reverbPreDelay: sel("reverb-predelay"),
|
|
17821
|
-
reverbPreDelayLabel: sel("reverb-predelay-label"),
|
|
17822
|
-
delayAmount: sel("delay-amount"),
|
|
17823
|
-
delayAmountLabel: sel("delay-amount-label"),
|
|
17824
|
-
delayAmountInfoBtn: sel("delay-amount-info"),
|
|
17825
|
-
delayDivision: sel("delay-division"),
|
|
17826
|
-
fadeIn: sel("fade-in"),
|
|
17827
|
-
fadeInLabel: sel("fade-in-label"),
|
|
17828
|
-
fadeOut: sel("fade-out"),
|
|
17829
|
-
fadeOutLabel: sel("fade-out-label"),
|
|
17830
|
-
fadeInfoBtn: sel("fade-info"),
|
|
17831
|
-
autoMasterBtn: sel("auto-master"),
|
|
17832
|
-
autoMasterInfoBtn: sel("auto-master-info"),
|
|
17833
|
-
trackTabs: sel("track-tabs"),
|
|
17834
|
-
trackBody: sel("track-body"),
|
|
17835
|
-
drumSelect: sel("drum-select"),
|
|
17836
|
-
drumFontSelect: sel("drum-font-select"),
|
|
17837
|
-
drumVolume: sel("drum-volume"),
|
|
17838
|
-
drumVolumeLabel: sel("drum-volume-label"),
|
|
17839
|
-
midiInput: sel("midi-input"),
|
|
17840
|
-
midiLoadBtn: sel("midi-load"),
|
|
17841
|
-
midiInfoBtn: sel("midi-info"),
|
|
17842
|
-
midiTrackSelection: sel("midi-track-selection"),
|
|
17843
|
-
midiPanel: sel("midi-panel"),
|
|
17844
|
-
midiSearchOpenBtn: sel("midi-search-open"),
|
|
17845
|
-
mmlInput: sel("mml-input"),
|
|
17846
|
-
mmlLoadBtn: sel("mml-load"),
|
|
17847
|
-
mmlLoadNote: sel("mml-load-note"),
|
|
17848
|
-
applyActiveOnly: sel("apply-active-only"),
|
|
17849
|
-
shiftSelect: sel("shift-select"),
|
|
17850
|
-
shiftApplyBtn: sel("shift-apply"),
|
|
17851
|
-
transposeSelect: sel("transpose-select"),
|
|
17852
|
-
transposeApplyBtn: sel("transpose-apply"),
|
|
17853
|
-
transposeInfoBtn: sel("transpose-info"),
|
|
17854
|
-
macroCompose: sel("macro-compose"),
|
|
17855
|
-
composeTemplate: sel("compose-template"),
|
|
17856
|
-
composeSections: sel("compose-sections"),
|
|
17857
|
-
composeSectionsLen: sel("compose-sections-len"),
|
|
17858
|
-
composeKey: sel("compose-key"),
|
|
17859
|
-
composeKeyHint: sel("compose-key-hint"),
|
|
17860
|
-
composeScale: sel("compose-scale"),
|
|
17861
|
-
composeScaleHint: sel("compose-scale-hint"),
|
|
17862
|
-
macroComposeVocal: sel("macro-compose-vocal"),
|
|
17863
|
-
macroComposeInfo: sel("macro-compose-info"),
|
|
17864
|
-
macroClear: sel("macro-clear"),
|
|
17865
|
-
macroRandom: sel("macro-random"),
|
|
17866
|
-
macroHarmonic: sel("macro-harmonic"),
|
|
17867
|
-
macroMono: sel("macro-mono"),
|
|
17868
|
-
exportMidiBtn: sel("export-midi"),
|
|
17869
|
-
exportWavBtn: sel("export-wav"),
|
|
17870
|
-
drumJsonExportBtn: sel("drum-json-export"),
|
|
17871
|
-
drumJsonOutput: sel("drum-json-output"),
|
|
17872
|
-
drumJsonStatus: sel("drum-json-status"),
|
|
17873
|
-
drumJsonText: sel("drum-json-text"),
|
|
17874
|
-
drumJsonCopyBtn: sel("drum-json-copy"),
|
|
17875
|
-
generateMmlBtn: sel("generate-mml"),
|
|
17876
|
-
decomposeChordToggle: sel("decompose-chord"),
|
|
17877
|
-
ignoreChordHeavyToggle: sel("ignore-chord-heavy"),
|
|
17878
|
-
barLimitSelect: sel("bar-limit"),
|
|
17879
|
-
outputContainer: sel("output-container"),
|
|
17880
|
-
outputStatus: sel("output-status"),
|
|
17881
|
-
outputFull: sel("output-full"),
|
|
17882
|
-
outputMini: sel("output-mini"),
|
|
17883
|
-
copyFullBtn: sel("copy-full"),
|
|
17884
|
-
copyMiniBtn: sel("copy-mini"),
|
|
17885
|
-
overlay: sel("overlay"),
|
|
17886
|
-
mmlInfoBtn: sel("mml-info"),
|
|
17887
|
-
edoSelect: sel("edo-select"),
|
|
17888
|
-
edoInfoBtn: sel("edo-info"),
|
|
17889
|
-
modalOverlay: sel("modal-overlay"),
|
|
17890
|
-
modalTitle: sel("modal-title"),
|
|
17891
|
-
modalBody: sel("modal-body"),
|
|
17892
|
-
modalClose: sel("modal-close")
|
|
17893
|
-
};
|
|
17894
|
-
};
|
|
17895
|
-
|
|
17896
|
-
// src/instrument-presets.ts
|
|
17897
|
-
var INSTRUMENT_PRESETS = {
|
|
17898
|
-
// --- STANDARD: 汎用性と完成度重視 ---
|
|
17899
|
-
piano: {
|
|
17900
|
-
displayName: "\u30B0\u30E9\u30F3\u30C9\u30D4\u30A2\u30CE",
|
|
17901
|
-
description: "\u6700\u3082\u7834\u7DBB\u3057\u306B\u304F\u3044\u69CB\u6210\u3002\u697D\u66F2\u5236\u4F5C\u306E\u30B9\u30B1\u30C3\u30C1\u306B\u3082\u6700\u9069\u3002",
|
|
17902
|
-
melody: "Acoustic Grand Piano",
|
|
17903
|
-
submelody: "Vibraphone",
|
|
17904
|
-
bass: "Electric Bass (finger)",
|
|
17905
|
-
chord: "Pad 2 (warm)",
|
|
17906
|
-
solo: "Electric Guitar (clean)",
|
|
17907
|
-
// グロッケンは実物の音域が G5(79)〜 の高音楽器で、旋律の音域へそのまま置くと
|
|
17908
|
-
// 金切り音になる。**外すのではなくオクターブで下げて使う**
|
|
17909
|
-
// ({@link GM_BRIGHT_CEILING} / {@link fitInstrumentOctave})。
|
|
17910
|
-
chorusLead: "Glockenspiel"
|
|
17911
|
-
},
|
|
17912
|
-
acoustic: {
|
|
17913
|
-
displayName: "\u30A2\u30B3\u30FC\u30B9\u30C6\u30A3\u30C3\u30AF",
|
|
17914
|
-
description: "\u751F\u697D\u5668\u306E\u6E29\u304B\u307F\u3092\u91CD\u8996\u3002\u30D5\u30A9\u30FC\u30AF\u3084\u30DD\u30C3\u30D7\u30B9\u306B\u3002",
|
|
17915
|
-
melody: "Acoustic Guitar (steel)",
|
|
17916
|
-
submelody: "Harmonica",
|
|
17917
|
-
bass: "Acoustic Bass",
|
|
17918
|
-
chord: "Acoustic Guitar (nylon)",
|
|
17919
|
-
solo: "Overdriven Guitar",
|
|
17920
|
-
chorusLead: "String Ensemble 1"
|
|
17921
|
-
},
|
|
17922
|
-
jazz_night: {
|
|
17923
|
-
displayName: "\u30B8\u30E3\u30BA\u30FB\u30CA\u30A4\u30C8",
|
|
17924
|
-
description: "Rhodes\u98A8\u306EEP\u3068\u30A6\u30C3\u30C9\u30D9\u30FC\u30B9\u306B\u3088\u308B\u3001\u5927\u4EBA\u3073\u305F\u30A2\u30F3\u30B5\u30F3\u30D6\u30EB\u3002",
|
|
17925
|
-
melody: "Electric Piano 1",
|
|
17926
|
-
submelody: "Flute",
|
|
17927
|
-
bass: "Acoustic Bass",
|
|
17928
|
-
chord: "Electric Guitar (jazz)",
|
|
17929
|
-
solo: "Tenor Sax",
|
|
17930
|
-
chorusLead: "Muted Trumpet"
|
|
17931
|
-
},
|
|
17932
|
-
// --- MODERN & VIBE: エッジの効いた現代的な響き ---
|
|
17933
|
-
synth_pop: {
|
|
17934
|
-
displayName: "\u30B7\u30F3\u30BB\u30DD\u30C3\u30D7",
|
|
17935
|
-
description: "80s\u301C\u73FE\u4EE3\u307E\u3067\u3002\u629C\u3051\u308B\u30EA\u30FC\u30C9\u3068\u592A\u3044\u30D9\u30FC\u30B9\u306E\u738B\u9053\u3002",
|
|
17936
|
-
melody: "Lead 2 (sawtooth)",
|
|
17937
|
-
submelody: "Lead 4 (chiff)",
|
|
17938
|
-
bass: "Synth Bass 2",
|
|
17939
|
-
chord: "Pad 3 (polysynth)",
|
|
17940
|
-
solo: "Distortion Guitar",
|
|
17941
|
-
chorusLead: "Synth Brass 1"
|
|
17942
|
-
},
|
|
17943
|
-
cyber_punk: {
|
|
17944
|
-
displayName: "\u30B5\u30A4\u30D0\u30FC\u30D1\u30F3\u30AF",
|
|
17945
|
-
description: "\u30C7\u30B8\u30BF\u30EB\u306A\u51B7\u305F\u3055\u3068\u6B6A\u307F\u304C\u6DF7\u3056\u308A\u5408\u3046\u3001\u672A\u6765\u7684\u306A\u97FF\u304D\u3002",
|
|
17946
|
-
melody: "Lead 8 (bass + lead)",
|
|
17947
|
-
submelody: "Lead 5 (charang)",
|
|
17948
|
-
bass: "Synth Bass 2",
|
|
17949
|
-
chord: "Pad 8 (sweep)",
|
|
17950
|
-
solo: "Distortion Guitar",
|
|
17951
|
-
chorusLead: "Lead 7 (fifths)"
|
|
17952
|
-
},
|
|
17953
|
-
rock: {
|
|
17954
|
-
displayName: "\u30CF\u30FC\u30C9\u30ED\u30C3\u30AF",
|
|
17955
|
-
description: "\u6B6A\u307F\u30AE\u30BF\u30FC\u3068\u91CD\u539A\u306A\u30D9\u30FC\u30B9\u3067\u3001\u30D1\u30EF\u30FC\u3092\u524D\u9762\u306B\u3002",
|
|
17956
|
-
melody: "Distortion Guitar",
|
|
17957
|
-
submelody: "Rock Organ",
|
|
17958
|
-
bass: "Electric Bass (pick)",
|
|
17959
|
-
chord: "Overdriven Guitar",
|
|
17960
|
-
solo: "Distortion Guitar",
|
|
17961
|
-
chorusLead: "Brass Section"
|
|
17962
|
-
},
|
|
17963
|
-
// --- WORLD & CLASSIC: 特定のジャンル・地域 ---
|
|
17964
|
-
orchestra: {
|
|
17965
|
-
displayName: "\u30AA\u30FC\u30B1\u30B9\u30C8\u30E9",
|
|
17966
|
-
description: "\u58EE\u5927\u306A\u7269\u8A9E\u3092\u4E88\u611F\u3055\u305B\u308B\u3001\u7BA1\u5F26\u697D\u5668\u306E\u91CD\u539A\u306A\u97FF\u304D\u3002",
|
|
17967
|
-
melody: "French Horn",
|
|
17968
|
-
submelody: "Pizzicato Strings",
|
|
17969
|
-
bass: "Cello",
|
|
17970
|
-
chord: "Tremolo Strings",
|
|
17971
|
-
solo: "Violin",
|
|
17972
|
-
chorusLead: "Trumpet"
|
|
17973
|
-
},
|
|
17974
|
-
japanese_wa: {
|
|
17975
|
-
displayName: "\u548C\u98A8\u30FB\u96C5",
|
|
17976
|
-
description: "\u7434\u3068\u4E09\u5473\u7DDA\u306E\u7E4A\u7D30\u306A\u8ABF\u3079\u306B\u3001\u5C3A\u516B\u306E\u60C5\u7DD2\u3092\u6DFB\u3048\u3066\u3002",
|
|
17977
|
-
melody: "Koto",
|
|
17978
|
-
submelody: "Shamisen",
|
|
17979
|
-
bass: "Taiko Drum",
|
|
17980
|
-
chord: "Shakuhachi",
|
|
17981
|
-
solo: "Shakuhachi",
|
|
17982
|
-
chorusLead: "Glockenspiel"
|
|
17983
|
-
},
|
|
17984
|
-
arabic_exotic: {
|
|
17985
|
-
displayName: "\u30A8\u30AD\u30BE\u30C1\u30C3\u30AF",
|
|
17986
|
-
description: "\u30B7\u30BF\u30FC\u30EB\u3084\u30D0\u30B0\u30D1\u30A4\u30D7\u306B\u3088\u308B\u3001\u7570\u56FD\u60C5\u7DD2\u6EA2\u308C\u308B\u30B5\u30A6\u30F3\u30C9\u3002",
|
|
17987
|
-
melody: "Sitar",
|
|
17988
|
-
submelody: "Bagpipe",
|
|
17989
|
-
bass: "Fretless Bass",
|
|
17990
|
-
chord: "Kalimba",
|
|
17991
|
-
solo: "Shanai",
|
|
17992
|
-
chorusLead: "Steel Drums"
|
|
17993
|
-
},
|
|
17994
|
-
// --- FANTASY & ATMOSPHERE: 雰囲気と余韻 ---
|
|
17995
|
-
fantasy_rpg: {
|
|
17996
|
-
displayName: "\u30D5\u30A1\u30F3\u30BF\u30B8\u30FCRPG",
|
|
17997
|
-
description: "\u30AA\u30AB\u30EA\u30CA\u3068\u30CF\u30FC\u30D7\u304C\u7D21\u3050\u3001\u5192\u967A\u3068\u9B54\u6CD5\u306E\u4E16\u754C\u89B3\u3002",
|
|
17998
|
-
melody: "Ocarina",
|
|
17999
|
-
submelody: "Celesta",
|
|
18000
|
-
bass: "Timpani",
|
|
18001
|
-
chord: "Orchestral Harp",
|
|
18002
|
-
solo: "Pan Flute",
|
|
18003
|
-
chorusLead: "Choir Aahs"
|
|
18004
|
-
},
|
|
18005
|
-
ambient_cloud: {
|
|
18006
|
-
displayName: "\u30A2\u30F3\u30D3\u30A8\u30F3\u30C8",
|
|
18007
|
-
description: "\u8F2A\u90ED\u3092\u307C\u304B\u3057\u305F\u97F3\u8272\u3067\u3001\u6DF1\u3044\u6CA1\u5165\u611F\u3068\u4F59\u97FB\u3092\u6F14\u51FA\u3002",
|
|
18008
|
-
melody: "Lead 6 (voice)",
|
|
18009
|
-
submelody: "Music Box",
|
|
18010
|
-
bass: "Synth Bass 1",
|
|
18011
|
-
chord: "Pad 7 (halo)",
|
|
18012
|
-
solo: "Lead 3 (calliope)",
|
|
18013
|
-
chorusLead: "Synth Choir"
|
|
18014
|
-
},
|
|
18015
|
-
retro_game: {
|
|
18016
|
-
displayName: "8-bit \u30EC\u30C8\u30ED",
|
|
18017
|
-
description: "\u77E9\u5F62\u6CE2\u3092\u60F3\u8D77\u3055\u305B\u308B\u3001\u521D\u671F\u30B2\u30FC\u30E0\u6A5F\u306E\u3088\u3046\u306A\u61D0\u304B\u3057\u3044\u97FF\u304D\u3002",
|
|
18018
|
-
melody: "Lead 1 (square)",
|
|
18019
|
-
submelody: "Lead 2 (sawtooth)",
|
|
18020
|
-
bass: "Synth Bass 1",
|
|
18021
|
-
chord: "Clavinet",
|
|
18022
|
-
solo: "Lead 8 (bass + lead)",
|
|
18023
|
-
chorusLead: "Lead 4 (chiff)"
|
|
18024
|
-
}
|
|
18025
|
-
};
|
|
18026
|
-
var GM_INSTRUMENT_RANGE = {
|
|
18027
|
-
// 鍵盤・音板
|
|
18028
|
-
"Acoustic Grand Piano": [21, 108],
|
|
18029
|
-
"Electric Piano 1": [28, 103],
|
|
18030
|
-
Clavinet: [36, 96],
|
|
18031
|
-
Celesta: [60, 108],
|
|
18032
|
-
Glockenspiel: [79, 108],
|
|
18033
|
-
"Music Box": [72, 108],
|
|
18034
|
-
Vibraphone: [53, 89],
|
|
18035
|
-
Kalimba: [60, 84],
|
|
18036
|
-
"Orchestral Harp": [23, 104],
|
|
18037
|
-
// 弦・撥弦
|
|
18038
|
-
"Acoustic Guitar (steel)": [40, 83],
|
|
18039
|
-
"Acoustic Guitar (nylon)": [40, 83],
|
|
18040
|
-
"Electric Guitar (clean)": [40, 86],
|
|
18041
|
-
"Electric Guitar (jazz)": [40, 86],
|
|
18042
|
-
"Overdriven Guitar": [40, 88],
|
|
18043
|
-
"Distortion Guitar": [40, 88],
|
|
18044
|
-
Violin: [55, 103],
|
|
18045
|
-
Cello: [36, 76],
|
|
18046
|
-
"String Ensemble 1": [28, 100],
|
|
18047
|
-
"Tremolo Strings": [28, 100],
|
|
18048
|
-
"Pizzicato Strings": [28, 96],
|
|
18049
|
-
Sitar: [48, 79],
|
|
18050
|
-
Shamisen: [48, 84],
|
|
18051
|
-
Koto: [41, 77],
|
|
18052
|
-
// ベース
|
|
18053
|
-
"Acoustic Bass": [28, 60],
|
|
18054
|
-
"Electric Bass (finger)": [28, 67],
|
|
18055
|
-
"Electric Bass (pick)": [28, 67],
|
|
18056
|
-
"Fretless Bass": [28, 67],
|
|
18057
|
-
"Synth Bass 1": [24, 72],
|
|
18058
|
-
"Synth Bass 2": [24, 72],
|
|
18059
|
-
// 管
|
|
18060
|
-
Flute: [60, 96],
|
|
18061
|
-
"Pan Flute": [60, 91],
|
|
18062
|
-
Shakuhachi: [62, 86],
|
|
18063
|
-
Ocarina: [60, 84],
|
|
18064
|
-
Harmonica: [60, 96],
|
|
18065
|
-
Bagpipe: [62, 86],
|
|
18066
|
-
Shanai: [60, 86],
|
|
18067
|
-
"Tenor Sax": [44, 75],
|
|
18068
|
-
Trumpet: [55, 82],
|
|
18069
|
-
"Muted Trumpet": [55, 82],
|
|
18070
|
-
"French Horn": [41, 77],
|
|
18071
|
-
"Brass Section": [41, 84],
|
|
18072
|
-
// 声・打・オルガン
|
|
18073
|
-
"Choir Aahs": [43, 84],
|
|
18074
|
-
"Synth Choir": [43, 84],
|
|
18075
|
-
"Steel Drums": [55, 86],
|
|
18076
|
-
"Taiko Drum": [30, 60],
|
|
18077
|
-
Timpani: [36, 57],
|
|
18078
|
-
"Rock Organ": [36, 96],
|
|
18079
|
-
// シンセ(実物が無いので一般的な使用域)
|
|
18080
|
-
"Synth Brass 1": [36, 96],
|
|
18081
|
-
"Lead 1 (square)": [36, 96],
|
|
18082
|
-
"Lead 2 (sawtooth)": [36, 96],
|
|
18083
|
-
"Lead 3 (calliope)": [48, 96],
|
|
18084
|
-
"Lead 4 (chiff)": [48, 96],
|
|
18085
|
-
"Lead 5 (charang)": [40, 96],
|
|
18086
|
-
"Lead 6 (voice)": [43, 91],
|
|
18087
|
-
"Lead 7 (fifths)": [36, 84],
|
|
18088
|
-
"Lead 8 (bass + lead)": [28, 91],
|
|
18089
|
-
"Pad 2 (warm)": [24, 96],
|
|
18090
|
-
"Pad 3 (polysynth)": [24, 96],
|
|
18091
|
-
"Pad 7 (halo)": [24, 96],
|
|
18092
|
-
"Pad 8 (sweep)": [24, 96]
|
|
18093
|
-
};
|
|
18094
|
-
var GM_BRIGHT_CEILING = {
|
|
18095
|
-
Glockenspiel: 72,
|
|
18096
|
-
"Tinkle Bell": 72,
|
|
18097
|
-
"Music Box": 79,
|
|
18098
|
-
Celesta: 84,
|
|
18099
|
-
Kalimba: 79,
|
|
18100
|
-
"Steel Drums": 84,
|
|
18101
|
-
"FX 3 (crystal)": 79
|
|
18102
|
-
};
|
|
18103
|
-
var OCTAVE_FIT_TOLERANCE = 3;
|
|
18104
|
-
var fitInstrumentOctave = (semitoneRange, instrument, wanted) => {
|
|
18105
|
-
const range = GM_INSTRUMENT_RANGE[instrument];
|
|
18106
|
-
if (!range || !semitoneRange) return wanted;
|
|
18107
|
-
const hi = Math.min(range[1], GM_BRIGHT_CEILING[instrument] ?? range[1]);
|
|
18108
|
-
let octave = wanted;
|
|
18109
|
-
while (octave > wanted - 2) {
|
|
18110
|
-
if (semitoneRange[1] + octave * 12 <= hi + OCTAVE_FIT_TOLERANCE) break;
|
|
18111
|
-
octave--;
|
|
18112
|
-
}
|
|
18113
|
-
return octave;
|
|
18405
|
+
</div>
|
|
18406
|
+
<div class="dtm-modal-body" data-dtm="modal-body"></div>
|
|
18407
|
+
</div>
|
|
18408
|
+
</div>
|
|
18409
|
+
|
|
18410
|
+
</div>`;
|
|
18411
|
+
const root = q(target, '[data-dtm="root"]');
|
|
18412
|
+
persistPanels(root);
|
|
18413
|
+
const sel = (name) => q(root, `[data-dtm="${name}"]`);
|
|
18414
|
+
return {
|
|
18415
|
+
root,
|
|
18416
|
+
topbar: sel("transport"),
|
|
18417
|
+
topbarLoading: sel("topbar-loading"),
|
|
18418
|
+
playBtn: sel("play"),
|
|
18419
|
+
prevBarBtn: sel("prev-bar"),
|
|
18420
|
+
nextBarBtn: sel("next-bar"),
|
|
18421
|
+
soloCheckbox: sel("solo"),
|
|
18422
|
+
clipBadge: sel("clip-badge"),
|
|
18423
|
+
toolPen: sel("tool-pen"),
|
|
18424
|
+
toolSelect: sel("tool-select"),
|
|
18425
|
+
toolEraser: sel("tool-eraser"),
|
|
18426
|
+
undoBtn: sel("undo"),
|
|
18427
|
+
redoBtn: sel("redo"),
|
|
18428
|
+
noteLengthSelect: sel("note-length"),
|
|
18429
|
+
bpmInput: sel("bpm"),
|
|
18430
|
+
zoomXLabel: sel("zoomx-label"),
|
|
18431
|
+
zoomYLabel: sel("zoomy-label"),
|
|
18432
|
+
zoomXIn: sel("zoomx-in"),
|
|
18433
|
+
zoomXOut: sel("zoomx-out"),
|
|
18434
|
+
zoomYIn: sel("zoomy-in"),
|
|
18435
|
+
zoomYOut: sel("zoomy-out"),
|
|
18436
|
+
bgFileInput: sel("bg-file-input"),
|
|
18437
|
+
bgUploadBtn: sel("bg-upload"),
|
|
18438
|
+
bgRemoveBtn: sel("bg-remove"),
|
|
18439
|
+
bgOpacityInput: sel("bg-opacity"),
|
|
18440
|
+
bgOpacityRow: sel("bg-opacity-row"),
|
|
18441
|
+
rollContainer: sel("roll"),
|
|
18442
|
+
wrapper: sel("wrapper"),
|
|
18443
|
+
vScroll: sel("vscroll"),
|
|
18444
|
+
vScrollThumb: sel("vscroll-thumb"),
|
|
18445
|
+
hScroll: sel("hscroll"),
|
|
18446
|
+
hScrollThumb: sel("hscroll-thumb"),
|
|
18447
|
+
loopToggle: sel("loop-toggle"),
|
|
18448
|
+
loopToggleLabel: sel("loop-toggle-label"),
|
|
18449
|
+
loopInfoBtn: sel("loop-info"),
|
|
18450
|
+
masterVolume: sel("master-volume"),
|
|
18451
|
+
masterVolumeLabel: sel("master-volume-label"),
|
|
18452
|
+
masterComp: sel("master-comp"),
|
|
18453
|
+
masterCompLabel: sel("master-comp-label"),
|
|
18454
|
+
masterCompInfoBtn: sel("master-comp-info"),
|
|
18455
|
+
reverbAmount: sel("reverb-amount"),
|
|
18456
|
+
reverbAmountLabel: sel("reverb-amount-label"),
|
|
18457
|
+
reverbAmountInfoBtn: sel("reverb-amount-info"),
|
|
18458
|
+
reverbDecay: sel("reverb-decay"),
|
|
18459
|
+
reverbDecayLabel: sel("reverb-decay-label"),
|
|
18460
|
+
reverbPreDelay: sel("reverb-predelay"),
|
|
18461
|
+
reverbPreDelayLabel: sel("reverb-predelay-label"),
|
|
18462
|
+
delayAmount: sel("delay-amount"),
|
|
18463
|
+
delayAmountLabel: sel("delay-amount-label"),
|
|
18464
|
+
delayAmountInfoBtn: sel("delay-amount-info"),
|
|
18465
|
+
delayDivision: sel("delay-division"),
|
|
18466
|
+
fadeIn: sel("fade-in"),
|
|
18467
|
+
fadeInLabel: sel("fade-in-label"),
|
|
18468
|
+
fadeOut: sel("fade-out"),
|
|
18469
|
+
fadeOutLabel: sel("fade-out-label"),
|
|
18470
|
+
fadeInfoBtn: sel("fade-info"),
|
|
18471
|
+
autoMasterBtn: sel("auto-master"),
|
|
18472
|
+
autoMasterInfoBtn: sel("auto-master-info"),
|
|
18473
|
+
trackTabs: sel("track-tabs"),
|
|
18474
|
+
trackBody: sel("track-body"),
|
|
18475
|
+
drumSelect: sel("drum-select"),
|
|
18476
|
+
drumFontSelect: sel("drum-font-select"),
|
|
18477
|
+
drumVolume: sel("drum-volume"),
|
|
18478
|
+
drumVolumeLabel: sel("drum-volume-label"),
|
|
18479
|
+
midiInput: sel("midi-input"),
|
|
18480
|
+
midiLoadBtn: sel("midi-load"),
|
|
18481
|
+
midiInfoBtn: sel("midi-info"),
|
|
18482
|
+
midiTrackSelection: sel("midi-track-selection"),
|
|
18483
|
+
midiPanel: sel("midi-panel"),
|
|
18484
|
+
midiSearchOpenBtn: sel("midi-search-open"),
|
|
18485
|
+
mmlInput: sel("mml-input"),
|
|
18486
|
+
mmlLoadBtn: sel("mml-load"),
|
|
18487
|
+
mmlLoadNote: sel("mml-load-note"),
|
|
18488
|
+
applyActiveOnly: sel("apply-active-only"),
|
|
18489
|
+
shiftSelect: sel("shift-select"),
|
|
18490
|
+
shiftApplyBtn: sel("shift-apply"),
|
|
18491
|
+
transposeSelect: sel("transpose-select"),
|
|
18492
|
+
transposeApplyBtn: sel("transpose-apply"),
|
|
18493
|
+
transposeInfoBtn: sel("transpose-info"),
|
|
18494
|
+
macroCompose: sel("macro-compose"),
|
|
18495
|
+
composeTemplate: sel("compose-template"),
|
|
18496
|
+
composeSections: sel("compose-sections"),
|
|
18497
|
+
composeSectionsLen: sel("compose-sections-len"),
|
|
18498
|
+
composeKey: sel("compose-key"),
|
|
18499
|
+
composeKeyHint: sel("compose-key-hint"),
|
|
18500
|
+
composeScale: sel("compose-scale"),
|
|
18501
|
+
composeScaleHint: sel("compose-scale-hint"),
|
|
18502
|
+
macroComposeVocal: sel("macro-compose-vocal"),
|
|
18503
|
+
macroComposeInfo: sel("macro-compose-info"),
|
|
18504
|
+
macroClear: sel("macro-clear"),
|
|
18505
|
+
macroRandom: sel("macro-random"),
|
|
18506
|
+
macroHarmonic: sel("macro-harmonic"),
|
|
18507
|
+
macroMono: sel("macro-mono"),
|
|
18508
|
+
exportMidiBtn: sel("export-midi"),
|
|
18509
|
+
exportWavBtn: sel("export-wav"),
|
|
18510
|
+
drumJsonExportBtn: sel("drum-json-export"),
|
|
18511
|
+
drumJsonOutput: sel("drum-json-output"),
|
|
18512
|
+
drumJsonStatus: sel("drum-json-status"),
|
|
18513
|
+
drumJsonText: sel("drum-json-text"),
|
|
18514
|
+
drumJsonCopyBtn: sel("drum-json-copy"),
|
|
18515
|
+
generateMmlBtn: sel("generate-mml"),
|
|
18516
|
+
decomposeChordToggle: sel("decompose-chord"),
|
|
18517
|
+
ignoreChordHeavyToggle: sel("ignore-chord-heavy"),
|
|
18518
|
+
barLimitSelect: sel("bar-limit"),
|
|
18519
|
+
outputContainer: sel("output-container"),
|
|
18520
|
+
outputStatus: sel("output-status"),
|
|
18521
|
+
outputFull: sel("output-full"),
|
|
18522
|
+
outputMini: sel("output-mini"),
|
|
18523
|
+
copyFullBtn: sel("copy-full"),
|
|
18524
|
+
copyMiniBtn: sel("copy-mini"),
|
|
18525
|
+
overlay: sel("overlay"),
|
|
18526
|
+
mmlInfoBtn: sel("mml-info"),
|
|
18527
|
+
edoSelect: sel("edo-select"),
|
|
18528
|
+
edoInfoBtn: sel("edo-info"),
|
|
18529
|
+
modalOverlay: sel("modal-overlay"),
|
|
18530
|
+
modalTitle: sel("modal-title"),
|
|
18531
|
+
modalBody: sel("modal-body"),
|
|
18532
|
+
modalClose: sel("modal-close")
|
|
18533
|
+
};
|
|
18114
18534
|
};
|
|
18115
18535
|
|
|
18116
18536
|
// src/macro-state.ts
|
|
@@ -20575,128 +20995,6 @@ var pickComposeVocal = (exclude) => {
|
|
|
20575
20995
|
const list = pool.length > 0 ? pool : COMPOSE_VOCAL_POOL;
|
|
20576
20996
|
return list[Math.floor(Math.random() * list.length)] ?? "klatt";
|
|
20577
20997
|
};
|
|
20578
|
-
var buildAdvancedLayers = (song, config) => {
|
|
20579
|
-
const { edo, stepsPerBar, preset } = config;
|
|
20580
|
-
const semitoneRange = (notes) => {
|
|
20581
|
-
if (notes.length === 0) return null;
|
|
20582
|
-
let lo = Number.POSITIVE_INFINITY;
|
|
20583
|
-
let hi = Number.NEGATIVE_INFINITY;
|
|
20584
|
-
for (const n of notes) {
|
|
20585
|
-
const semi = n.pitchUnits / UNITS_PER_SEMITONE;
|
|
20586
|
-
if (semi < lo) lo = semi;
|
|
20587
|
-
if (semi > hi) hi = semi;
|
|
20588
|
-
}
|
|
20589
|
-
return [Math.round(lo), Math.round(hi)];
|
|
20590
|
-
};
|
|
20591
|
-
const fit = (notes, slot, wanted) => fitInstrumentOctave(semitoneRange(notes), preset[slot], wanted);
|
|
20592
|
-
const kindAtBar = (bar) => song.sections.find(
|
|
20593
|
-
(sec) => bar >= sec.startBar && bar < sec.startBar + sec.bars
|
|
20594
|
-
)?.kind ?? null;
|
|
20595
|
-
const onlyIn = (notes, kinds) => {
|
|
20596
|
-
if (!kinds) return notes;
|
|
20597
|
-
const want = new Set(kinds);
|
|
20598
|
-
return notes.filter((n) => {
|
|
20599
|
-
const kind = kindAtBar(Math.floor(n.startStep / stepsPerBar));
|
|
20600
|
-
return kind !== null && want.has(kind);
|
|
20601
|
-
});
|
|
20602
|
-
};
|
|
20603
|
-
const chordNotes = (pattern, velocityShift = 0) => buildChordPlacements({
|
|
20604
|
-
edo,
|
|
20605
|
-
chordStr: song.chordProgression,
|
|
20606
|
-
patternType: pattern,
|
|
20607
|
-
rootShift: song.rootShift,
|
|
20608
|
-
bpm: song.bpm,
|
|
20609
|
-
stepsPerBar
|
|
20610
|
-
}).map((p) => ({
|
|
20611
|
-
startStep: p.startStep,
|
|
20612
|
-
pitchUnits: p.pitchUnits,
|
|
20613
|
-
durationSteps: p.durationSteps,
|
|
20614
|
-
velocity: Math.max(30, p.velocity + velocityShift)
|
|
20615
|
-
}));
|
|
20616
|
-
const plan = song.arrange;
|
|
20617
|
-
const leadNotes = plan.lead ? onlyIn(song.melody, plan.lead.sections) : [];
|
|
20618
|
-
const leadOctave = fit(leadNotes, "chorusLead", plan.lead?.octave ?? 0);
|
|
20619
|
-
const leadLayer = {
|
|
20620
|
-
index: 1,
|
|
20621
|
-
notes: leadNotes,
|
|
20622
|
-
octave: leadOctave,
|
|
20623
|
-
// ユニゾンで重ねるときは、オクターブ上より前に出やすいので少し引く。
|
|
20624
|
-
volume: leadOctave === 0 ? 54 : 62,
|
|
20625
|
-
slot: "chorusLead"
|
|
20626
|
-
};
|
|
20627
|
-
const bassNotes = plan.bassLayer ? onlyIn(song.bass, plan.bassLayer.sections) : [];
|
|
20628
|
-
const bassOctave = fit(bassNotes, "bass", plan.bassLayer?.octave ?? 0);
|
|
20629
|
-
const bassLayer = {
|
|
20630
|
-
index: 5,
|
|
20631
|
-
notes: bassOctave === 0 ? [] : bassNotes,
|
|
20632
|
-
octave: bassOctave,
|
|
20633
|
-
volume: 58,
|
|
20634
|
-
slot: "bass"
|
|
20635
|
-
};
|
|
20636
|
-
const padNotes = onlyIn(song.pad, plan.padSections);
|
|
20637
|
-
const layers = [
|
|
20638
|
-
{ index: 0, notes: song.melody, octave: 0, volume: 104, slot: "melody" },
|
|
20639
|
-
leadLayer,
|
|
20640
|
-
{ index: 2, notes: song.harmony, octave: 0, volume: 82 },
|
|
20641
|
-
{
|
|
20642
|
-
index: 3,
|
|
20643
|
-
notes: song.submelody,
|
|
20644
|
-
octave: 0,
|
|
20645
|
-
volume: 86,
|
|
20646
|
-
slot: "submelody"
|
|
20647
|
-
},
|
|
20648
|
-
{ index: 4, notes: song.bass, octave: 0, volume: 92, slot: "bass" },
|
|
20649
|
-
bassLayer,
|
|
20650
|
-
// **パッドは伴奏用の楽器で、伴奏より1.5オクターブ高いところを鳴らす。**
|
|
20651
|
-
// そのまま置くとナイロンギターで10半音、カリンバで9半音、尺八で7半音ぶん
|
|
20652
|
-
// 音域を突き抜ける。楽器に合わせて下げる。
|
|
20653
|
-
{
|
|
20654
|
-
index: 6,
|
|
20655
|
-
notes: padNotes,
|
|
20656
|
-
octave: fit(padNotes, "chord", 0),
|
|
20657
|
-
volume: 64,
|
|
20658
|
-
slot: "chord"
|
|
20659
|
-
}
|
|
20660
|
-
];
|
|
20661
|
-
const backingVolumes = [62, 54, 50];
|
|
20662
|
-
for (let i2 = 0; i2 < 3; i2++) {
|
|
20663
|
-
const layer = plan.backing[i2];
|
|
20664
|
-
layers.push({
|
|
20665
|
-
index: 7 + i2,
|
|
20666
|
-
notes: layer ? onlyIn(chordNotes(layer.pattern), layer.sections) : [],
|
|
20667
|
-
octave: layer?.octave ?? 0,
|
|
20668
|
-
volume: backingVolumes[i2],
|
|
20669
|
-
slot: "chord"
|
|
20670
|
-
});
|
|
20671
|
-
}
|
|
20672
|
-
const sparkleNotes = plan.sparkle ? onlyIn(chordNotes(plan.sparkle.pattern, -14), plan.sparkle.sections) : [];
|
|
20673
|
-
layers.push(
|
|
20674
|
-
{
|
|
20675
|
-
index: 10,
|
|
20676
|
-
notes: sparkleNotes,
|
|
20677
|
-
octave: fit(sparkleNotes, "chord", plan.sparkle?.octave ?? 0),
|
|
20678
|
-
volume: 56,
|
|
20679
|
-
slot: "chord"
|
|
20680
|
-
},
|
|
20681
|
-
// 掛け合い(デュエット)の相手。**歌入り作曲のときだけ**中身が入る。
|
|
20682
|
-
{ index: 11, notes: [], octave: 0, volume: 104, slot: "melody" },
|
|
20683
|
-
// 2声目のハモリ(主旋律を上下から挟む3声)と、主旋律のオクターブ下の重ね。
|
|
20684
|
-
// どちらも曲ごとに出るかどうかが決まる(`song.vocal`)。
|
|
20685
|
-
{ index: 12, notes: song.harmony2, octave: 0, volume: 74 },
|
|
20686
|
-
{ index: 13, notes: song.octave, octave: -1, volume: 56, slot: "melody" },
|
|
20687
|
-
// **間奏のソロ。** 音が入るのは間奏の小節だけなので、この1本だけを
|
|
20688
|
-
// 別の楽器にしても他のセクションの鳴りは変わらない。歌の音域をそのまま
|
|
20689
|
-
// 渡すと管楽器が上へ抜ける(テナーサックスで10半音)ので、ここも合わせる。
|
|
20690
|
-
{
|
|
20691
|
-
index: 14,
|
|
20692
|
-
notes: song.solo,
|
|
20693
|
-
octave: fit(song.solo, "solo", 0),
|
|
20694
|
-
volume: 100,
|
|
20695
|
-
slot: "solo"
|
|
20696
|
-
}
|
|
20697
|
-
);
|
|
20698
|
-
return layers;
|
|
20699
|
-
};
|
|
20700
20998
|
var LYRIC_MODEL_CATEGORIES = [
|
|
20701
20999
|
{
|
|
20702
21000
|
label: "kusa\u30D7\u30EA\u30BB\u30C3\u30C8",
|
|
@@ -25845,8 +26143,9 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
25845
26143
|
});
|
|
25846
26144
|
const ownsCtx = !options.audioContext;
|
|
25847
26145
|
const ctx = options.audioContext ?? new AudioContext();
|
|
25848
|
-
const
|
|
26146
|
+
const rawDestination = options.destination ?? ctx.destination;
|
|
25849
26147
|
const useSynth = options.synth ?? !options.onPlayNote;
|
|
26148
|
+
const destination = createSafetyLimiter(ctx, rawDestination);
|
|
25850
26149
|
const synth = useSynth ? createSynth(ctx, destination) : null;
|
|
25851
26150
|
const pauseWhenHidden = options.pauseWhenHidden ?? ownsCtx;
|
|
25852
26151
|
let playing = false;
|
|
@@ -26851,21 +27150,29 @@ var createDtmStudio = async (options = {}) => {
|
|
|
26851
27150
|
};
|
|
26852
27151
|
applyGlueCompression(options.masterCompression ?? 0);
|
|
26853
27152
|
const setMasterCompression = (amount) => applyGlueCompression(amount);
|
|
26854
|
-
const safetyLimiter = audioCtx.createDynamicsCompressor();
|
|
26855
|
-
safetyLimiter.threshold.value = -1;
|
|
26856
|
-
safetyLimiter.knee.value = 0;
|
|
26857
|
-
safetyLimiter.ratio.value = 20;
|
|
26858
|
-
safetyLimiter.attack.value = 1e-3;
|
|
26859
|
-
safetyLimiter.release.value = 0.1;
|
|
26860
|
-
glueMakeup.connect(safetyLimiter);
|
|
26861
27153
|
const fadeGain = audioCtx.createGain();
|
|
26862
27154
|
fadeGain.gain.value = 1;
|
|
26863
|
-
safetyLimiter.connect(fadeGain);
|
|
26864
27155
|
fadeGain.connect(options.destination ?? audioCtx.destination);
|
|
27156
|
+
const safetyLimiter = createSafetyLimiter(audioCtx, fadeGain);
|
|
27157
|
+
glueMakeup.connect(safetyLimiter);
|
|
27158
|
+
const FADE_RESTORE_SEC = 0.02;
|
|
27159
|
+
const rampFadeGainToUnity = (from, at) => {
|
|
27160
|
+
fadeGain.gain.setValueAtTime(from, at);
|
|
27161
|
+
fadeGain.gain.linearRampToValueAtTime(1, at + FADE_RESTORE_SEC);
|
|
27162
|
+
};
|
|
27163
|
+
const restoreFadeGainIfMuted = () => {
|
|
27164
|
+
const current = fadeGain.gain.value;
|
|
27165
|
+
if (current >= 1) return;
|
|
27166
|
+
const now = audioCtx.currentTime;
|
|
27167
|
+
fadeGain.gain.cancelScheduledValues(now);
|
|
27168
|
+
rampFadeGainToUnity(current, now);
|
|
27169
|
+
};
|
|
26865
27170
|
const scheduleFade = (params) => {
|
|
26866
|
-
|
|
27171
|
+
const now = audioCtx.currentTime;
|
|
27172
|
+
const current = fadeGain.gain.value;
|
|
27173
|
+
fadeGain.gain.cancelScheduledValues(now);
|
|
26867
27174
|
if (!params) {
|
|
26868
|
-
|
|
27175
|
+
rampFadeGainToUnity(current, now);
|
|
26869
27176
|
return;
|
|
26870
27177
|
}
|
|
26871
27178
|
const { fadeInStartAt, fadeInEndAt, fadeOutStartAt, fadeOutEndAt } = params;
|
|
@@ -26873,13 +27180,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
26873
27180
|
fadeGain.gain.setValueAtTime(0, fadeInStartAt);
|
|
26874
27181
|
fadeGain.gain.linearRampToValueAtTime(1, fadeInEndAt);
|
|
26875
27182
|
} else {
|
|
26876
|
-
|
|
27183
|
+
rampFadeGainToUnity(current, now);
|
|
26877
27184
|
}
|
|
26878
27185
|
if (fadeOutStartAt !== void 0 && fadeOutEndAt !== void 0) {
|
|
26879
27186
|
fadeGain.gain.setValueAtTime(1, fadeOutStartAt);
|
|
26880
27187
|
fadeGain.gain.linearRampToValueAtTime(0, fadeOutEndAt);
|
|
26881
|
-
|
|
26882
|
-
fadeGain.gain.setValueAtTime(1, fadeOutEndAt + 2);
|
|
27188
|
+
rampFadeGainToUnity(0, fadeOutEndAt + 2);
|
|
26883
27189
|
}
|
|
26884
27190
|
};
|
|
26885
27191
|
const clipMeter = createClipMeter(audioCtx, glueMakeup);
|
|
@@ -26939,8 +27245,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
26939
27245
|
};
|
|
26940
27246
|
};
|
|
26941
27247
|
const resumeAudio = () => {
|
|
26942
|
-
|
|
26943
|
-
fadeGain.gain.setValueAtTime(1, audioCtx.currentTime);
|
|
27248
|
+
restoreFadeGainIfMuted();
|
|
26944
27249
|
if (audioCtx.state === "closed") return Promise.resolve();
|
|
26945
27250
|
return audioCtx.resume();
|
|
26946
27251
|
};
|
|
@@ -27151,12 +27456,6 @@ var createDtmStudio = async (options = {}) => {
|
|
|
27151
27456
|
await listReady;
|
|
27152
27457
|
nameToKey = await buildNameToKeyMapping();
|
|
27153
27458
|
await Promise.all([drumReady, loadPreset(defaultPreset)]);
|
|
27154
|
-
const restoreFadeGainIfMuted = () => {
|
|
27155
|
-
if (fadeGain.gain.value < 1) {
|
|
27156
|
-
fadeGain.gain.cancelScheduledValues(audioCtx.currentTime);
|
|
27157
|
-
fadeGain.gain.setValueAtTime(1, audioCtx.currentTime);
|
|
27158
|
-
}
|
|
27159
|
-
};
|
|
27160
27459
|
const playDrum = (e) => {
|
|
27161
27460
|
if (!sfDrum.font) return;
|
|
27162
27461
|
if (e.when === 0) restoreFadeGainIfMuted();
|
|
@@ -27907,7 +28206,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
27907
28206
|
};
|
|
27908
28207
|
const setMasterVolume = (volume) => {
|
|
27909
28208
|
const g = Math.max(0, Math.min(100, volume)) / 100;
|
|
27910
|
-
masterGain.gain.
|
|
28209
|
+
masterGain.gain.setTargetAtTime(g, audioCtx.currentTime, 0.02);
|
|
27911
28210
|
};
|
|
27912
28211
|
let recordDestNode = null;
|
|
27913
28212
|
const createMediaStreamDestination = () => {
|