@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.js
CHANGED
|
@@ -1970,11 +1970,19 @@ var createChannelStrip = (ctx, destination, options = {}) => {
|
|
|
1970
1970
|
const duckGain = ctx.createGain();
|
|
1971
1971
|
duckGain.gain.value = 1;
|
|
1972
1972
|
const DUCK_ATTACK_SEC = 8e-3;
|
|
1973
|
+
let duckReleaseEndAt = 0;
|
|
1973
1974
|
const duck = (atTime, depth = 0.3, releaseSec = 0.18) => {
|
|
1974
1975
|
const t = Math.max(atTime, ctx.currentTime);
|
|
1975
1976
|
const floor = clamp(1 - depth, 0, 1);
|
|
1976
|
-
duckGain.gain
|
|
1977
|
-
|
|
1977
|
+
const param = duckGain.gain;
|
|
1978
|
+
if (t >= duckReleaseEndAt) {
|
|
1979
|
+
param.setValueAtTime(1, t);
|
|
1980
|
+
} else if (typeof param.cancelAndHoldAtTime === "function") {
|
|
1981
|
+
param.cancelAndHoldAtTime(t);
|
|
1982
|
+
}
|
|
1983
|
+
param.linearRampToValueAtTime(floor, t + DUCK_ATTACK_SEC);
|
|
1984
|
+
param.linearRampToValueAtTime(1, t + DUCK_ATTACK_SEC + releaseSec);
|
|
1985
|
+
duckReleaseEndAt = t + DUCK_ATTACK_SEC + releaseSec;
|
|
1978
1986
|
};
|
|
1979
1987
|
eqHigh.connect(compressor);
|
|
1980
1988
|
compressor.connect(duckGain);
|
|
@@ -2271,12 +2279,15 @@ var buildChordPlacements = (options) => {
|
|
|
2271
2279
|
}
|
|
2272
2280
|
}
|
|
2273
2281
|
} else if (patternType === "alternating") {
|
|
2282
|
+
const quarterSteps = Math.floor(stepsPerBar / 4);
|
|
2274
2283
|
notes.forEach((noteOffset, i2) => {
|
|
2275
|
-
const stepOffset = i2 *
|
|
2284
|
+
const stepOffset = i2 * quarterSteps;
|
|
2285
|
+
const isLast = i2 === notes.length - 1;
|
|
2286
|
+
const durationSteps = isLast ? Math.max(12, noteLength - stepOffset) : Math.max(12, quarterSteps);
|
|
2276
2287
|
placements.push({
|
|
2277
2288
|
startStep: chord.whenStep + stepOffset,
|
|
2278
2289
|
pitchUnits: toUnits(noteOffset),
|
|
2279
|
-
durationSteps
|
|
2290
|
+
durationSteps,
|
|
2280
2291
|
velocity: 100
|
|
2281
2292
|
});
|
|
2282
2293
|
});
|
|
@@ -4904,6 +4915,45 @@ var createReverbImpulse = (ctx, decaySec = DEFAULT_REVERB_DECAY_SEC) => {
|
|
|
4904
4915
|
};
|
|
4905
4916
|
var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100;
|
|
4906
4917
|
|
|
4918
|
+
// src/safety-limiter.ts
|
|
4919
|
+
var THRESHOLD_DB = -1;
|
|
4920
|
+
var KNEE_DB = 0;
|
|
4921
|
+
var RATIO = 20;
|
|
4922
|
+
var ATTACK_SEC = 1e-3;
|
|
4923
|
+
var RELEASE_SEC = 0.1;
|
|
4924
|
+
var CURVE_SAMPLES2 = 2048;
|
|
4925
|
+
var SOFT_KNEE = 0.85;
|
|
4926
|
+
var CEILING = 0.98;
|
|
4927
|
+
var SHAPER_RANGE = 4;
|
|
4928
|
+
var OVERSAMPLE2 = "none";
|
|
4929
|
+
var createSoftClipCurve = () => {
|
|
4930
|
+
const curve = new Float32Array(CURVE_SAMPLES2);
|
|
4931
|
+
const span = CEILING - SOFT_KNEE;
|
|
4932
|
+
for (let i2 = 0; i2 < CURVE_SAMPLES2; i2++) {
|
|
4933
|
+
const x2 = (i2 / (CURVE_SAMPLES2 - 1) * 2 - 1) * SHAPER_RANGE;
|
|
4934
|
+
const a = Math.abs(x2);
|
|
4935
|
+
curve[i2] = Math.sign(x2) * (a <= SOFT_KNEE ? a : SOFT_KNEE + span * Math.tanh((a - SOFT_KNEE) / span));
|
|
4936
|
+
}
|
|
4937
|
+
return curve;
|
|
4938
|
+
};
|
|
4939
|
+
var createSafetyLimiter = (ctx, destination) => {
|
|
4940
|
+
const limiter = ctx.createDynamicsCompressor();
|
|
4941
|
+
limiter.threshold.value = THRESHOLD_DB;
|
|
4942
|
+
limiter.knee.value = KNEE_DB;
|
|
4943
|
+
limiter.ratio.value = RATIO;
|
|
4944
|
+
limiter.attack.value = ATTACK_SEC;
|
|
4945
|
+
limiter.release.value = RELEASE_SEC;
|
|
4946
|
+
const preScale = ctx.createGain();
|
|
4947
|
+
preScale.gain.value = 1 / SHAPER_RANGE;
|
|
4948
|
+
const softClip = ctx.createWaveShaper();
|
|
4949
|
+
softClip.curve = createSoftClipCurve();
|
|
4950
|
+
softClip.oversample = OVERSAMPLE2;
|
|
4951
|
+
limiter.connect(preScale);
|
|
4952
|
+
preScale.connect(softClip);
|
|
4953
|
+
softClip.connect(destination);
|
|
4954
|
+
return limiter;
|
|
4955
|
+
};
|
|
4956
|
+
|
|
4907
4957
|
// src/sequencer.ts
|
|
4908
4958
|
var STEPS_PER_BEAT = 48;
|
|
4909
4959
|
var PLAN_TIME = 0.5;
|
|
@@ -7367,6 +7417,9 @@ var SONG_DRUM_PATTERNS = {
|
|
|
7367
7417
|
|
|
7368
7418
|
// src/synth.ts
|
|
7369
7419
|
var freqFromPitch = (pitchUnits) => unitsToHz(pitchUnits);
|
|
7420
|
+
var MIN_ATTACK_SEC = 15e-4;
|
|
7421
|
+
var MIN_RELEASE_SEC = 4e-3;
|
|
7422
|
+
var TAIL_RATIO = 1e-3;
|
|
7370
7423
|
var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
7371
7424
|
const wave = tone.wave ?? "square";
|
|
7372
7425
|
const attack = tone.attack ?? 0;
|
|
@@ -7385,25 +7438,25 @@ var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
|
7385
7438
|
osc.frequency.value = freqFromPitch(e.pitchUnits);
|
|
7386
7439
|
const t0 = ctx.currentTime + e.when;
|
|
7387
7440
|
const peak = Math.max(1e-4, 0.06 * e.volume * 1.5 * gainScale);
|
|
7441
|
+
const tail = peak * TAIL_RATIO;
|
|
7442
|
+
const stopAt = t0 + e.duration + MIN_RELEASE_SEC;
|
|
7388
7443
|
if (tone.decay) {
|
|
7389
7444
|
gain.gain.setValueAtTime(1e-4, t0);
|
|
7390
7445
|
gain.gain.linearRampToValueAtTime(peak, t0 + Math.max(3e-3, attack));
|
|
7391
|
-
gain.gain.exponentialRampToValueAtTime(
|
|
7446
|
+
gain.gain.exponentialRampToValueAtTime(tail, t0 + e.duration);
|
|
7392
7447
|
} else {
|
|
7393
7448
|
const releaseTime = Math.min(0.02, e.duration * 0.1);
|
|
7394
7449
|
const sustainDuration = e.duration - releaseTime;
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
} else {
|
|
7402
|
-
gain.gain.setValueAtTime(peak, t0);
|
|
7403
|
-
}
|
|
7450
|
+
const attackTime = Math.min(
|
|
7451
|
+
Math.max(attack, MIN_ATTACK_SEC),
|
|
7452
|
+
sustainDuration
|
|
7453
|
+
);
|
|
7454
|
+
gain.gain.setValueAtTime(1e-4, t0);
|
|
7455
|
+
gain.gain.linearRampToValueAtTime(peak, t0 + attackTime);
|
|
7404
7456
|
gain.gain.setValueAtTime(peak, t0 + sustainDuration);
|
|
7405
|
-
gain.gain.exponentialRampToValueAtTime(
|
|
7457
|
+
gain.gain.exponentialRampToValueAtTime(tail, t0 + e.duration);
|
|
7406
7458
|
}
|
|
7459
|
+
gain.gain.linearRampToValueAtTime(0, stopAt);
|
|
7407
7460
|
osc.connect(gain);
|
|
7408
7461
|
let panner = null;
|
|
7409
7462
|
if (typeof ctx.createStereoPanner === "function" && e.pan) {
|
|
@@ -7415,7 +7468,7 @@ var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
|
7415
7468
|
gain.connect(compressor);
|
|
7416
7469
|
}
|
|
7417
7470
|
osc.start(t0);
|
|
7418
|
-
osc.stop(
|
|
7471
|
+
osc.stop(stopAt);
|
|
7419
7472
|
osc.onended = () => {
|
|
7420
7473
|
osc.disconnect();
|
|
7421
7474
|
gain.disconnect();
|
|
@@ -7430,14 +7483,21 @@ var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
|
7430
7483
|
if (isKick) {
|
|
7431
7484
|
const osc = ctx.createOscillator();
|
|
7432
7485
|
const g2 = ctx.createGain();
|
|
7486
|
+
const kickPeak = vol * 0.135;
|
|
7487
|
+
const kickDecayEnd = t0 + 0.18;
|
|
7488
|
+
const kickEnd = kickDecayEnd + MIN_RELEASE_SEC;
|
|
7433
7489
|
osc.frequency.setValueAtTime(150, t0);
|
|
7434
7490
|
osc.frequency.exponentialRampToValueAtTime(50, t0 + 0.12);
|
|
7435
|
-
g2.gain.setValueAtTime(
|
|
7436
|
-
g2.gain.exponentialRampToValueAtTime(
|
|
7491
|
+
g2.gain.setValueAtTime(kickPeak, t0);
|
|
7492
|
+
g2.gain.exponentialRampToValueAtTime(kickPeak * TAIL_RATIO, kickDecayEnd);
|
|
7493
|
+
g2.gain.linearRampToValueAtTime(0, kickEnd);
|
|
7437
7494
|
osc.connect(g2).connect(compressor);
|
|
7438
7495
|
osc.start(t0);
|
|
7439
|
-
osc.stop(
|
|
7440
|
-
osc.onended = () =>
|
|
7496
|
+
osc.stop(kickEnd);
|
|
7497
|
+
osc.onended = () => {
|
|
7498
|
+
osc.disconnect();
|
|
7499
|
+
g2.disconnect();
|
|
7500
|
+
};
|
|
7441
7501
|
return;
|
|
7442
7502
|
}
|
|
7443
7503
|
const dur = isSnareLike ? 0.18 : 0.05;
|
|
@@ -7451,8 +7511,13 @@ var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
|
7451
7511
|
filter.type = isSnareLike ? "bandpass" : "highpass";
|
|
7452
7512
|
filter.frequency.value = isSnareLike ? 2e3 : 8e3;
|
|
7453
7513
|
const g = ctx.createGain();
|
|
7454
|
-
|
|
7455
|
-
g.gain.
|
|
7514
|
+
const noisePeak = vol * (isSnareLike ? 0.105 : 0.06);
|
|
7515
|
+
g.gain.setValueAtTime(noisePeak, t0);
|
|
7516
|
+
g.gain.exponentialRampToValueAtTime(
|
|
7517
|
+
noisePeak * TAIL_RATIO,
|
|
7518
|
+
t0 + dur - MIN_RELEASE_SEC
|
|
7519
|
+
);
|
|
7520
|
+
g.gain.linearRampToValueAtTime(0, t0 + dur);
|
|
7456
7521
|
src.connect(filter).connect(g).connect(compressor);
|
|
7457
7522
|
src.start(t0);
|
|
7458
7523
|
src.stop(t0 + dur);
|
|
@@ -7514,7 +7579,7 @@ var playPlacements = (placements, options) => {
|
|
|
7514
7579
|
reverbPreDelay.connect(reverbConvolver);
|
|
7515
7580
|
reverbConvolver.connect(reverbWetGain);
|
|
7516
7581
|
reverbWetGain.connect(finalMix);
|
|
7517
|
-
finalMix.connect(rawDestination);
|
|
7582
|
+
finalMix.connect(createSafetyLimiter(ctx, rawDestination));
|
|
7518
7583
|
const channelStrips = /* @__PURE__ */ new Map();
|
|
7519
7584
|
const getChannelStrip = (index) => {
|
|
7520
7585
|
let strip = channelStrips.get(index);
|
|
@@ -9969,17 +10034,24 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
9969
10034
|
if (!audioCtx) audioCtx = new AudioContext();
|
|
9970
10035
|
return audioCtx;
|
|
9971
10036
|
};
|
|
10037
|
+
let limiterNode = null;
|
|
10038
|
+
const ensureOutput = () => {
|
|
10039
|
+
const ctx = ensureCtx();
|
|
10040
|
+
if (!limiterNode) limiterNode = createSafetyLimiter(ctx, ctx.destination);
|
|
10041
|
+
return limiterNode;
|
|
10042
|
+
};
|
|
9972
10043
|
let synthInstance = null;
|
|
9973
10044
|
const ensureSynth = () => {
|
|
9974
|
-
if (!synthInstance)
|
|
10045
|
+
if (!synthInstance) {
|
|
10046
|
+
synthInstance = createSynth(ensureCtx(), ensureOutput());
|
|
10047
|
+
}
|
|
9975
10048
|
return synthInstance;
|
|
9976
10049
|
};
|
|
9977
10050
|
let voices = null;
|
|
9978
10051
|
const ensureVoices = () => {
|
|
9979
10052
|
if (options.singingVoices) return options.singingVoices;
|
|
9980
10053
|
if (!voices) {
|
|
9981
|
-
|
|
9982
|
-
voices = createSingingVoices(ctx, ctx.destination);
|
|
10054
|
+
voices = createSingingVoices(ensureCtx(), ensureOutput());
|
|
9983
10055
|
voices.setVolume(trackVolume / 100 * (masterVolume / 100));
|
|
9984
10056
|
}
|
|
9985
10057
|
return voices;
|
|
@@ -11041,6 +11113,12 @@ var SoundFont = class _SoundFont {
|
|
|
11041
11113
|
// ファミリごとに変える。
|
|
11042
11114
|
/** アタック(無音からピークまで)秒。クリック防止の最小限。全楽器共通。 */
|
|
11043
11115
|
static attackSec = 5e-3;
|
|
11116
|
+
/**
|
|
11117
|
+
* 消え際に最低限確保する秒数。アタックと対になるクリック防止で、**サンプルが
|
|
11118
|
+
* 終わるまでに**必ずここまでに 0 へ落とし切る。振幅が残ったままバッファが尽きたり
|
|
11119
|
+
* `stop()` が来たりすると、その瞬間の値がそのまま段差になりプチノイズが出る。
|
|
11120
|
+
*/
|
|
11121
|
+
static minReleaseSec = 4e-3;
|
|
11044
11122
|
/**
|
|
11045
11123
|
* 減衰の型ごとのエンベロープ。
|
|
11046
11124
|
*
|
|
@@ -11227,14 +11305,23 @@ var SoundFont = class _SoundFont {
|
|
|
11227
11305
|
const startGainTime = Math.max(ctx.currentTime, _when);
|
|
11228
11306
|
g.gain.setValueAtTime(0, startGainTime);
|
|
11229
11307
|
const env = _SoundFont.envelopes[this.style.env] ?? _SoundFont.envelopes.sustain;
|
|
11230
|
-
const
|
|
11308
|
+
const playRate = _param.playbackRate * 2 ** ((humanizeCents + detuneCents) / 1200);
|
|
11309
|
+
const sampleEnd = _when + (buffer.duration - startOffsetSec) / playRate;
|
|
11310
|
+
const limit = src.loop ? Number.POSITIVE_INFINITY : sampleEnd;
|
|
11231
11311
|
const attackEnd = Math.min(startGainTime + _SoundFont.attackSec, limit);
|
|
11232
11312
|
const decayEnd = Math.min(
|
|
11233
11313
|
Math.max(attackEnd, attackEnd + env.decaySec),
|
|
11234
11314
|
limit
|
|
11235
11315
|
);
|
|
11236
|
-
const
|
|
11237
|
-
|
|
11316
|
+
const tailRoom = Math.min(
|
|
11317
|
+
_SoundFont.minReleaseSec,
|
|
11318
|
+
Math.max(0, limit - decayEnd)
|
|
11319
|
+
);
|
|
11320
|
+
const noteOff = Math.min(
|
|
11321
|
+
Math.max(decayEnd, _when + duration),
|
|
11322
|
+
limit - tailRoom
|
|
11323
|
+
);
|
|
11324
|
+
const end = isDrum ? sampleEnd : Math.max(Math.min(noteOff + env.releaseSec, limit), noteOff + tailRoom);
|
|
11238
11325
|
if (!isDrum) {
|
|
11239
11326
|
const sustainVolume = effectiveVolume * env.sustain;
|
|
11240
11327
|
g.gain.linearRampToValueAtTime(effectiveVolume, attackEnd);
|
|
@@ -11243,6 +11330,11 @@ var SoundFont = class _SoundFont {
|
|
|
11243
11330
|
g.gain.linearRampToValueAtTime(0, end);
|
|
11244
11331
|
} else {
|
|
11245
11332
|
g.gain.linearRampToValueAtTime(effectiveVolume, attackEnd);
|
|
11333
|
+
const releaseStart = Math.max(attackEnd, end - _SoundFont.minReleaseSec);
|
|
11334
|
+
if (releaseStart > attackEnd) {
|
|
11335
|
+
g.gain.setValueAtTime(effectiveVolume, releaseStart);
|
|
11336
|
+
}
|
|
11337
|
+
g.gain.linearRampToValueAtTime(0, end);
|
|
11246
11338
|
}
|
|
11247
11339
|
if (filter) src.connect(filter).connect(g);
|
|
11248
11340
|
else src.connect(g);
|
|
@@ -11452,12 +11544,10 @@ var addParam = (zone, pitch) => {
|
|
|
11452
11544
|
coarseTune,
|
|
11453
11545
|
fineTune,
|
|
11454
11546
|
sampleRate,
|
|
11455
|
-
delay
|
|
11456
|
-
buffer
|
|
11547
|
+
delay
|
|
11457
11548
|
} = zone;
|
|
11458
11549
|
const baseDetune = originalPitch - 100 * coarseTune - fineTune;
|
|
11459
11550
|
const playbackRate = 2 ** ((100 * pitch - baseDetune) / 1200);
|
|
11460
|
-
const max = (buffer?.duration ?? 0) / playbackRate;
|
|
11461
11551
|
const src = {
|
|
11462
11552
|
loop: loopStart >= 1 && loopStart < loopEnd
|
|
11463
11553
|
};
|
|
@@ -11465,7 +11555,7 @@ var addParam = (zone, pitch) => {
|
|
|
11465
11555
|
[src.loopStart, src.loopEnd] = [loopStart, loopEnd].map(
|
|
11466
11556
|
(v) => v / sampleRate + delay
|
|
11467
11557
|
);
|
|
11468
|
-
zone._param = { playbackRate,
|
|
11558
|
+
zone._param = { playbackRate, src };
|
|
11469
11559
|
};
|
|
11470
11560
|
|
|
11471
11561
|
// src/chord-player.ts
|
|
@@ -12763,29 +12853,30 @@ var CORPUS_BANDS = {
|
|
|
12763
12853
|
climaxPeaks: [1, 2, 13.5, 26],
|
|
12764
12854
|
complementarity: [0, 0.026, 0.179, 0.405]
|
|
12765
12855
|
};
|
|
12766
|
-
var
|
|
12767
|
-
entropy
|
|
12768
|
-
valueKinds
|
|
12769
|
-
restRatio
|
|
12770
|
-
leapRatio
|
|
12771
|
-
stepRatio
|
|
12772
|
-
chromaticRatio
|
|
12773
|
-
maxLeap
|
|
12774
|
-
melodyRange
|
|
12775
|
-
notesPerBar
|
|
12776
|
-
shortNoteRatio
|
|
12777
|
-
barDensityCv
|
|
12778
|
-
densityCliff
|
|
12779
|
-
sim1
|
|
12780
|
-
sim2
|
|
12781
|
-
sim4
|
|
12782
|
-
sim8
|
|
12783
|
-
phraseBreath
|
|
12784
|
-
turnRatio
|
|
12785
|
-
climaxPosition
|
|
12786
|
-
climaxPeaks
|
|
12787
|
-
complementarity
|
|
12788
|
-
|
|
12856
|
+
var CORPUS_PROFILE_KEYS = [
|
|
12857
|
+
"entropy",
|
|
12858
|
+
"valueKinds",
|
|
12859
|
+
"restRatio",
|
|
12860
|
+
"leapRatio",
|
|
12861
|
+
"stepRatio",
|
|
12862
|
+
"chromaticRatio",
|
|
12863
|
+
"maxLeap",
|
|
12864
|
+
"melodyRange",
|
|
12865
|
+
"notesPerBar",
|
|
12866
|
+
"shortNoteRatio",
|
|
12867
|
+
"barDensityCv",
|
|
12868
|
+
"densityCliff",
|
|
12869
|
+
"sim1",
|
|
12870
|
+
"sim2",
|
|
12871
|
+
"sim4",
|
|
12872
|
+
"sim8",
|
|
12873
|
+
"phraseBreath",
|
|
12874
|
+
"turnRatio",
|
|
12875
|
+
"climaxPosition",
|
|
12876
|
+
"climaxPeaks",
|
|
12877
|
+
"complementarity"
|
|
12878
|
+
];
|
|
12879
|
+
var CORPUS_DEVIATION_BUDGET = 4;
|
|
12789
12880
|
var CORPUS_CELL_WEIGHTS = {
|
|
12790
12881
|
"0,2,4,6,8,10,12,14": 584,
|
|
12791
12882
|
"0,2,4,6,8,12,14": 512,
|
|
@@ -13650,16 +13741,15 @@ var tensionFeatures = (barTension, opts) => {
|
|
|
13650
13741
|
return { rise, resolve };
|
|
13651
13742
|
};
|
|
13652
13743
|
var band = (v, lo, idealLo, idealHi, hi) => {
|
|
13653
|
-
if (v <= lo || v >= hi) return 0;
|
|
13654
13744
|
if (v >= idealLo && v <= idealHi) return 1;
|
|
13745
|
+
if (v <= lo || v >= hi) return 0;
|
|
13655
13746
|
if (v < idealLo) return idealLo === lo ? 0 : (v - lo) / (idealLo - lo);
|
|
13656
13747
|
return hi === idealHi ? 0 : (hi - v) / (hi - idealHi);
|
|
13657
13748
|
};
|
|
13658
|
-
var
|
|
13659
|
-
const
|
|
13660
|
-
|
|
13661
|
-
|
|
13662
|
-
return 1 - 0.15 * Math.min(1, Math.abs(v - median) / spread);
|
|
13749
|
+
var plausibleBand = (v, b) => {
|
|
13750
|
+
const span = b[3] - b[0];
|
|
13751
|
+
const margin = span > 0 ? span * 0.5 : 1;
|
|
13752
|
+
return band(v, b[0] - margin, b[0], b[3], b[3] + margin);
|
|
13663
13753
|
};
|
|
13664
13754
|
var featureVector = (f) => [
|
|
13665
13755
|
f.entropy / 3,
|
|
@@ -14562,6 +14652,7 @@ var sectionAt = (plan, bar) => {
|
|
|
14562
14652
|
// src/compose.ts
|
|
14563
14653
|
var STEP_SEMITONES = 2;
|
|
14564
14654
|
var MAX_LEAP_SEMITONES = 10;
|
|
14655
|
+
var LEAP_CEILINGS = [7, 8, 9, 10, 10, 12, 14, 16];
|
|
14565
14656
|
var MAX_BAR_LEAP_SEMITONES = 10;
|
|
14566
14657
|
var HARD = {
|
|
14567
14658
|
/** メロディが1音も無い、音域が半音未満(同じ音を並べただけ)。 */
|
|
@@ -14620,13 +14711,16 @@ var WEIGHTS = {
|
|
|
14620
14711
|
// --- 直近に作った曲と違うか ---
|
|
14621
14712
|
novelty: 1.2
|
|
14622
14713
|
};
|
|
14714
|
+
var DEVIATION_BUDGET = CORPUS_DEVIATION_BUDGET;
|
|
14715
|
+
var BUDGETED_KEYS = new Set(CORPUS_PROFILE_KEYS);
|
|
14623
14716
|
var HAND_BANDS = {
|
|
14624
14717
|
/** サブメロの音数/小節。少なすぎると「置いただけ」、多すぎるとメロディを食う。 */
|
|
14625
14718
|
subDensity: [0.5, 1.8, 4.5, 8],
|
|
14626
14719
|
complementarity: [0.05, 0.25, 0.7, 0.95],
|
|
14627
14720
|
climaxPeaks: [0, 1, 2, 5]
|
|
14628
14721
|
};
|
|
14629
|
-
var DRAW_COUNT =
|
|
14722
|
+
var DRAW_COUNT = 12;
|
|
14723
|
+
var SELECT_TEMPERATURE = 0.05;
|
|
14630
14724
|
var BASE_STEPS_PER_BAR = 192;
|
|
14631
14725
|
var WHOLE = 192;
|
|
14632
14726
|
var DOT_HALF = 144;
|
|
@@ -15342,6 +15436,14 @@ for (const c of [...MOTIF_CELLS, ...RHYTHM_CELLS]) {
|
|
|
15342
15436
|
const key = onsetKeyOf(c.value);
|
|
15343
15437
|
cellEntryCount.set(key, (cellEntryCount.get(key) ?? 0) + 1);
|
|
15344
15438
|
}
|
|
15439
|
+
var resolveMelodyForm = (choice, rnd) => {
|
|
15440
|
+
const c = (choice ?? "").trim() || "auto";
|
|
15441
|
+
if (c === "motif" || c === "ostinato" || c === "through") return c;
|
|
15442
|
+
const r = rnd();
|
|
15443
|
+
if (r < 0.25) return "ostinato";
|
|
15444
|
+
if (r < 0.4) return "through";
|
|
15445
|
+
return "motif";
|
|
15446
|
+
};
|
|
15345
15447
|
var groovyCells = (cells, groove, rnd) => {
|
|
15346
15448
|
const hasSixteenth = (c) => c.value.some((v) => Math.abs(v) <= SIXTEENTH);
|
|
15347
15449
|
if (groove === "eighth") {
|
|
@@ -15517,7 +15619,7 @@ var clampSemi = (semi, low, high) => {
|
|
|
15517
15619
|
while (s > high) s -= 12;
|
|
15518
15620
|
return s;
|
|
15519
15621
|
};
|
|
15520
|
-
var leapTarget = (from, tones, rnd) => {
|
|
15622
|
+
var leapTarget = (from, tones, rnd, maxLeap = MAX_LEAP_SEMITONES) => {
|
|
15521
15623
|
const candidates = [];
|
|
15522
15624
|
for (const tone of tones) {
|
|
15523
15625
|
const base = pitchClass(tone.semi);
|
|
@@ -15525,7 +15627,7 @@ var leapTarget = (from, tones, rnd) => {
|
|
|
15525
15627
|
const semi = base + oct * 12;
|
|
15526
15628
|
if (semi < MELODY_LOW || semi > MELODY_HIGH) continue;
|
|
15527
15629
|
const gap = Math.abs(semi - from);
|
|
15528
|
-
if (gap >= 3 && gap <=
|
|
15630
|
+
if (gap >= 3 && gap <= maxLeap) candidates.push(semi);
|
|
15529
15631
|
}
|
|
15530
15632
|
}
|
|
15531
15633
|
if (candidates.length === 0) return null;
|
|
@@ -15682,7 +15784,7 @@ var barDegrees = (role, slots, tones, style, scale, motifContour, startDegree, c
|
|
|
15682
15784
|
if (slots[i2].value >= quarterSteps) {
|
|
15683
15785
|
if (rnd() < style.leapAffinity) {
|
|
15684
15786
|
const from = degreeToPitch(scale, out[i2 - 1]).semi;
|
|
15685
|
-
const target = leapTarget(from, tones, rnd);
|
|
15787
|
+
const target = leapTarget(from, tones, rnd, style.maxLeap);
|
|
15686
15788
|
if (target !== null) out[i2] = semitoneToDegree(scale, target);
|
|
15687
15789
|
}
|
|
15688
15790
|
continue;
|
|
@@ -15693,13 +15795,13 @@ var barDegrees = (role, slots, tones, style, scale, motifContour, startDegree, c
|
|
|
15693
15795
|
}
|
|
15694
15796
|
return out;
|
|
15695
15797
|
};
|
|
15696
|
-
var fitMotif = (degrees, slots, tones, prevSemi, quarterSteps, scale, pentatonic, preferShift) => {
|
|
15798
|
+
var fitMotif = (degrees, slots, tones, prevSemi, quarterSteps, scale, pentatonic, preferShift, maxShift = 3) => {
|
|
15697
15799
|
let best = degrees;
|
|
15698
15800
|
let bestShift = 0;
|
|
15699
15801
|
let bestScore = Number.NEGATIVE_INFINITY;
|
|
15700
15802
|
let preferScore = Number.NEGATIVE_INFINITY;
|
|
15701
15803
|
let preferMoved = null;
|
|
15702
|
-
for (let shift = -
|
|
15804
|
+
for (let shift = -maxShift; shift <= maxShift; shift++) {
|
|
15703
15805
|
const moved = degrees.map(
|
|
15704
15806
|
(d) => pentatonic ? coreToDegree(scale, degreeToCore(scale, d) + shift) : d + shift
|
|
15705
15807
|
);
|
|
@@ -15756,7 +15858,7 @@ var shapeBar = (degrees, slots, tones, prevSemi, opts) => {
|
|
|
15756
15858
|
MELODY_LOW,
|
|
15757
15859
|
MELODY_HIGH
|
|
15758
15860
|
);
|
|
15759
|
-
const limit = i2 === 0 ? MAX_BAR_LEAP_SEMITONES :
|
|
15861
|
+
const limit = i2 === 0 ? MAX_BAR_LEAP_SEMITONES : opts.maxLeap;
|
|
15760
15862
|
if (!opts.allowLeap && Math.abs(semi - prev) > limit) {
|
|
15761
15863
|
semi = clampSemi(
|
|
15762
15864
|
walk(opts.scale, prev, Math.sign(semi - prev) * 3),
|
|
@@ -16149,6 +16251,7 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16149
16251
|
landing = (landing + pick([2, 4], rnd)) % size;
|
|
16150
16252
|
return landing;
|
|
16151
16253
|
};
|
|
16254
|
+
const form = resolveMelodyForm(options.form, rnd);
|
|
16152
16255
|
const units2 = [];
|
|
16153
16256
|
for (const section of sectionPlan) {
|
|
16154
16257
|
const unitCount = Math.max(1, Math.round(section.bars / 2));
|
|
@@ -16169,16 +16272,31 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16169
16272
|
const isLast = u === unitCount - 1;
|
|
16170
16273
|
if (u % 2 === 0) {
|
|
16171
16274
|
units2.push({
|
|
16172
|
-
|
|
16173
|
-
|
|
16275
|
+
// リフ主体の曲は**変形しない**。セクエンツもオクターブ上げも入れず、
|
|
16276
|
+
// 同じ型を回し続ける。セクションの対比は編曲側(ドラム・楽器・レイヤ)
|
|
16277
|
+
// が担う——ヤツメ穴型の曲がまさにその作りで、120小節を通して
|
|
16278
|
+
// 5半音のセルが変わらない。
|
|
16279
|
+
role: form === "ostinato" ? "motif" : form === "through" ? (
|
|
16280
|
+
// 通し作曲は素材を戻さない。`step` の書法(アーチ・谷・波)で
|
|
16281
|
+
// 独立した線を書き、たまに走句と山を挟んで単調さを避ける。
|
|
16282
|
+
u % 3 === 2 ? "run" : u % 3 === 1 ? "climax" : "step"
|
|
16283
|
+
) : section.kind === "bridge" ? "step" : section.kind === "chorus" ? "climax" : src === "a2" || u > 0 ? "sequence" : "motif",
|
|
16284
|
+
// リフ型は素材も1つに揃える(`sourceOf` でセクションごとに
|
|
16285
|
+
// 変えると、そこだけ別の型が始まってオスティナートにならない)。
|
|
16286
|
+
source: form === "ostinato" ? "a" : src,
|
|
16174
16287
|
landing: null,
|
|
16175
16288
|
section
|
|
16176
16289
|
});
|
|
16177
16290
|
} else {
|
|
16178
16291
|
const landing = landingOf(section);
|
|
16292
|
+
const riff = form === "ostinato" && !(isLast && landing === 0);
|
|
16179
16293
|
units2.push({
|
|
16180
|
-
role: isLast && landing === 0 ? "cadence" : "
|
|
16181
|
-
|
|
16294
|
+
role: isLast && landing === 0 ? "cadence" : riff ? "motif" : (
|
|
16295
|
+
// 通し作曲は「問いと答え」で閉じない。answer は問いのリズムを
|
|
16296
|
+
// 受けて着地音だけ変える形なので、そのままだと反復が戻る。
|
|
16297
|
+
form === "through" ? "step" : "answer"
|
|
16298
|
+
),
|
|
16299
|
+
source: riff ? "a" : "answer",
|
|
16182
16300
|
landing: isLast ? landing : null,
|
|
16183
16301
|
section
|
|
16184
16302
|
});
|
|
@@ -16196,6 +16314,7 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16196
16314
|
const restatementOf = (bar) => {
|
|
16197
16315
|
const curSec = sectionAt(sectionPlan, bar);
|
|
16198
16316
|
if (!curSec.spec.melody) return null;
|
|
16317
|
+
if (form === "through") return null;
|
|
16199
16318
|
if (curSec.restatement) {
|
|
16200
16319
|
const firstSec = sectionPlan.find(
|
|
16201
16320
|
(s) => s.kind === curSec.kind && !s.restatement
|
|
@@ -16217,13 +16336,15 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16217
16336
|
}
|
|
16218
16337
|
return null;
|
|
16219
16338
|
};
|
|
16339
|
+
const registerSpread = form === "ostinato" ? rnd() ** 2 * 0.5 : 0.25 + rnd() * 0.75;
|
|
16220
16340
|
const style = {
|
|
16221
16341
|
groove: pick(["eighth", "sixteenth"], rnd),
|
|
16222
16342
|
arcPeriod: pick([4, 8, 8, 16], rnd),
|
|
16223
16343
|
arcPhase: pick([0, 1, 2], rnd),
|
|
16224
|
-
arcAmp:
|
|
16344
|
+
arcAmp: 5 * registerSpread,
|
|
16225
16345
|
// オクターブ跳躍は参考曲では音程の1.0%しかない。上げすぎると音域が広がる。
|
|
16226
|
-
octaveAffinity: 0.
|
|
16346
|
+
octaveAffinity: 0.18 * registerSpread,
|
|
16347
|
+
maxLeap: pick(LEAP_CEILINGS, rnd),
|
|
16227
16348
|
// **音階を厳しく締める曲は必ず中核音の歩数で組む。** ダイアトニックの度数で輪郭を
|
|
16228
16349
|
// 作ると、琉球音階なのにレやラが輪郭の中に入り込む。ファ・シを自由に使う
|
|
16229
16350
|
// 陽・民謡だけが、曲ごとに掛けたり掛けなかったりする({@link ComposeScale.strict})。
|
|
@@ -16276,8 +16397,11 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16276
16397
|
subInterval: pick([3, 4, 8, 9], rnd)
|
|
16277
16398
|
};
|
|
16278
16399
|
const motifPool = groovyCells(MOTIF_CELLS, style.groove, rnd);
|
|
16279
|
-
const
|
|
16280
|
-
const
|
|
16400
|
+
const targetRestRatio = 0.02 + rnd() ** 2 * 0.42;
|
|
16401
|
+
const targetNotesPerBar = Math.max(
|
|
16402
|
+
2.8,
|
|
16403
|
+
Math.min(9.5, 7.4 - 6 * targetRestRatio + (rnd() * 4 - 2))
|
|
16404
|
+
);
|
|
16281
16405
|
const cellNotes = (c) => c.value.filter((v) => v > 0).length;
|
|
16282
16406
|
const cellRest = (c) => {
|
|
16283
16407
|
let rest = 0;
|
|
@@ -16300,9 +16424,13 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16300
16424
|
return pool[pool.length - 1];
|
|
16301
16425
|
};
|
|
16302
16426
|
const pickCell = (pool, densityMul = 1) => {
|
|
16303
|
-
|
|
16427
|
+
const want = targetRestRatio / Math.max(0.5, densityMul);
|
|
16428
|
+
const tol = Math.max(0.08, want * 0.6);
|
|
16429
|
+
const near = pool.filter((c) => Math.abs(cellRest(c) - want) <= tol);
|
|
16430
|
+
const from = near.length > 0 ? near : pool;
|
|
16431
|
+
let best = weightedPick(from);
|
|
16304
16432
|
for (let i2 = 0; i2 < 2; i2++) {
|
|
16305
|
-
const c = weightedPick(
|
|
16433
|
+
const c = weightedPick(from);
|
|
16306
16434
|
if (cellDistance(c, densityMul) < cellDistance(best, densityMul))
|
|
16307
16435
|
best = c;
|
|
16308
16436
|
}
|
|
@@ -16558,13 +16686,16 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
16558
16686
|
quarterSteps,
|
|
16559
16687
|
scale,
|
|
16560
16688
|
style.pentatonicMotif,
|
|
16561
|
-
motifShiftMemo.get(shiftKey) ?? null
|
|
16689
|
+
motifShiftMemo.get(shiftKey) ?? null,
|
|
16690
|
+
// リフ型は和音へ寄せない。同じセルを回し続けるのが役目。
|
|
16691
|
+
form === "ostinato" ? 0 : 3
|
|
16562
16692
|
);
|
|
16563
16693
|
fitted = r.degrees;
|
|
16564
16694
|
motifShiftMemo.set(shiftKey, r.shift);
|
|
16565
16695
|
}
|
|
16566
16696
|
const pitches = shapeBar(fitted, slots, tones, prevSemi, {
|
|
16567
16697
|
scale,
|
|
16698
|
+
maxLeap: style.maxLeap,
|
|
16568
16699
|
allowLeap: role === "climax",
|
|
16569
16700
|
allowArpeggio: role === "climax" || role === "run" && style.runShape === "broken" || role === "cadence" && style.cadenceShape !== "descend",
|
|
16570
16701
|
quarterSteps,
|
|
@@ -17010,6 +17141,7 @@ var draw = (options, resolvedKey, scale, rnd) => {
|
|
|
17010
17141
|
(n) => n.durationSteps >= quarterSteps || rnd() < octaveCoverage
|
|
17011
17142
|
).map((n) => ({ ...n, velocity: Math.max(40, n.velocity - 26) })) : [];
|
|
17012
17143
|
return {
|
|
17144
|
+
form,
|
|
17013
17145
|
chordProgression,
|
|
17014
17146
|
chordPattern,
|
|
17015
17147
|
rootShift,
|
|
@@ -17096,7 +17228,7 @@ var evaluate = (d, recent) => {
|
|
|
17096
17228
|
Math.min(...recent.map((r) => featureDistance(fingerprint, r))) / 1
|
|
17097
17229
|
);
|
|
17098
17230
|
const at = (b, v) => band(v, b[0], b[1], b[2], b[3]);
|
|
17099
|
-
const atc = (key, v) =>
|
|
17231
|
+
const atc = (key, v) => plausibleBand(v, CORPUS_BANDS[key]);
|
|
17100
17232
|
const peakBand = d.bars > 24 ? [0, 1, Math.round(d.bars / 16), Math.round(d.bars / 8) + 2] : HAND_BANDS.climaxPeaks;
|
|
17101
17233
|
const scoreBreakdown = {
|
|
17102
17234
|
entropy: atc("entropy", entropy),
|
|
@@ -17132,9 +17264,16 @@ var evaluate = (d, recent) => {
|
|
|
17132
17264
|
tensionResolve: d.tonal.floating ? 1 : tension.resolve,
|
|
17133
17265
|
novelty
|
|
17134
17266
|
};
|
|
17267
|
+
const forgiven = new Set(
|
|
17268
|
+
Object.entries(WEIGHTS).filter(([key]) => BUDGETED_KEYS.has(key)).map(([key, weight]) => ({
|
|
17269
|
+
key,
|
|
17270
|
+
deficit: weight * (1 - (scoreBreakdown[key] ?? 0))
|
|
17271
|
+
})).sort((a, b) => b.deficit - a.deficit).slice(0, DEVIATION_BUDGET).filter((e) => e.deficit > 0).map((e) => e.key)
|
|
17272
|
+
);
|
|
17135
17273
|
let weighted = 0;
|
|
17136
17274
|
let weightSum = 0;
|
|
17137
17275
|
for (const [key, weight] of Object.entries(WEIGHTS)) {
|
|
17276
|
+
if (forgiven.has(key)) continue;
|
|
17138
17277
|
weighted += (scoreBreakdown[key] ?? 0) * weight;
|
|
17139
17278
|
weightSum += weight;
|
|
17140
17279
|
}
|
|
@@ -17171,49 +17310,64 @@ var composeSong = (options) => {
|
|
|
17171
17310
|
resolvedKey.mode === "minor",
|
|
17172
17311
|
rnd
|
|
17173
17312
|
);
|
|
17174
|
-
|
|
17175
|
-
|
|
17176
|
-
let bestIsValid = false;
|
|
17313
|
+
const valid = [];
|
|
17314
|
+
const invalid = [];
|
|
17177
17315
|
let rejected = 0;
|
|
17178
17316
|
for (let attempt = 1; attempt <= count; attempt++) {
|
|
17179
|
-
const
|
|
17180
|
-
const { stats, ok } = evaluate(
|
|
17181
|
-
if (
|
|
17182
|
-
|
|
17183
|
-
|
|
17184
|
-
|
|
17185
|
-
|
|
17186
|
-
|
|
17187
|
-
|
|
17188
|
-
|
|
17189
|
-
|
|
17190
|
-
|
|
17191
|
-
|
|
17192
|
-
|
|
17193
|
-
|
|
17194
|
-
|
|
17195
|
-
|
|
17196
|
-
|
|
17197
|
-
|
|
17198
|
-
|
|
17199
|
-
|
|
17200
|
-
|
|
17201
|
-
sections: d.sections,
|
|
17202
|
-
bars: d.bars,
|
|
17203
|
-
vocal: d.vocal,
|
|
17204
|
-
tonal: d.tonal,
|
|
17205
|
-
melody: d.melody,
|
|
17206
|
-
submelody: d.submelody,
|
|
17207
|
-
bass: d.bass,
|
|
17208
|
-
harmony: d.harmony,
|
|
17209
|
-
harmony2: d.harmony2,
|
|
17210
|
-
octave: d.octave,
|
|
17211
|
-
pad: d.pad,
|
|
17212
|
-
solo: d.solo,
|
|
17213
|
-
stats: { ...stats, attempts: attempt, rejected }
|
|
17214
|
-
};
|
|
17317
|
+
const d2 = draw(options, resolvedKey, scale, rnd);
|
|
17318
|
+
const { stats, ok } = evaluate(d2, recent);
|
|
17319
|
+
if (ok) valid.push({ d: d2, stats });
|
|
17320
|
+
else {
|
|
17321
|
+
rejected++;
|
|
17322
|
+
invalid.push({ d: d2, stats });
|
|
17323
|
+
}
|
|
17324
|
+
}
|
|
17325
|
+
const pool = valid.length > 0 ? valid : invalid;
|
|
17326
|
+
const top = Math.max(...pool.map((c) => c.stats.score));
|
|
17327
|
+
const weights = pool.map(
|
|
17328
|
+
(c) => Math.exp((c.stats.score - top) / SELECT_TEMPERATURE)
|
|
17329
|
+
);
|
|
17330
|
+
const total = weights.reduce((a, b) => a + b, 0);
|
|
17331
|
+
let ticket = rnd() * total;
|
|
17332
|
+
let chosen = pool[pool.length - 1];
|
|
17333
|
+
for (let i2 = 0; i2 < pool.length; i2++) {
|
|
17334
|
+
ticket -= weights[i2];
|
|
17335
|
+
if (ticket <= 0) {
|
|
17336
|
+
chosen = pool[i2];
|
|
17337
|
+
break;
|
|
17338
|
+
}
|
|
17215
17339
|
}
|
|
17216
|
-
const
|
|
17340
|
+
const d = chosen.d;
|
|
17341
|
+
const result = {
|
|
17342
|
+
// ドラム・楽器・編曲プランは勝った候補にだけ後から付ける(メロディに
|
|
17343
|
+
// 依存しないので候補ごとに引いても採点は動かず、候補数ぶん無駄になる)。
|
|
17344
|
+
drum: "",
|
|
17345
|
+
instrument: "",
|
|
17346
|
+
arrange: EMPTY_ARRANGE,
|
|
17347
|
+
chordProgression: d.chordProgression,
|
|
17348
|
+
chordPattern: d.chordPattern,
|
|
17349
|
+
rootShift: d.rootShift,
|
|
17350
|
+
keyName: d.keyName,
|
|
17351
|
+
keyLabel: d.keyLabel,
|
|
17352
|
+
scaleId: scale.id,
|
|
17353
|
+
scaleLabel: scale.label,
|
|
17354
|
+
form: d.form,
|
|
17355
|
+
moodLabel: d.moodLabel,
|
|
17356
|
+
bpm: d.bpm,
|
|
17357
|
+
sections: d.sections,
|
|
17358
|
+
bars: d.bars,
|
|
17359
|
+
vocal: d.vocal,
|
|
17360
|
+
tonal: d.tonal,
|
|
17361
|
+
melody: d.melody,
|
|
17362
|
+
submelody: d.submelody,
|
|
17363
|
+
bass: d.bass,
|
|
17364
|
+
harmony: d.harmony,
|
|
17365
|
+
harmony2: d.harmony2,
|
|
17366
|
+
octave: d.octave,
|
|
17367
|
+
pad: d.pad,
|
|
17368
|
+
solo: d.solo,
|
|
17369
|
+
stats: { ...chosen.stats, attempts: count, rejected }
|
|
17370
|
+
};
|
|
17217
17371
|
result.stats.attempts = count;
|
|
17218
17372
|
result.stats.rejected = rejected;
|
|
17219
17373
|
result.drum = pickBuiltinDrum(result, rnd);
|
|
@@ -17414,157 +17568,643 @@ var composeLyrics = (melody, options) => {
|
|
|
17414
17568
|
return out.join("");
|
|
17415
17569
|
};
|
|
17416
17570
|
|
|
17417
|
-
// src/
|
|
17418
|
-
var
|
|
17419
|
-
|
|
17420
|
-
|
|
17421
|
-
|
|
17422
|
-
|
|
17423
|
-
|
|
17424
|
-
|
|
17425
|
-
|
|
17426
|
-
|
|
17427
|
-
|
|
17428
|
-
|
|
17429
|
-
}
|
|
17430
|
-
|
|
17431
|
-
|
|
17432
|
-
|
|
17433
|
-
|
|
17434
|
-
|
|
17435
|
-
|
|
17436
|
-
|
|
17437
|
-
|
|
17438
|
-
|
|
17439
|
-
|
|
17440
|
-
|
|
17441
|
-
|
|
17442
|
-
|
|
17443
|
-
|
|
17444
|
-
|
|
17445
|
-
|
|
17446
|
-
|
|
17447
|
-
|
|
17448
|
-
|
|
17449
|
-
|
|
17450
|
-
|
|
17451
|
-
|
|
17452
|
-
|
|
17453
|
-
|
|
17454
|
-
|
|
17455
|
-
|
|
17456
|
-
|
|
17457
|
-
|
|
17458
|
-
|
|
17459
|
-
|
|
17460
|
-
|
|
17461
|
-
|
|
17462
|
-
|
|
17463
|
-
|
|
17464
|
-
|
|
17465
|
-
|
|
17466
|
-
|
|
17467
|
-
|
|
17468
|
-
|
|
17469
|
-
|
|
17470
|
-
|
|
17471
|
-
|
|
17472
|
-
|
|
17473
|
-
|
|
17474
|
-
|
|
17475
|
-
|
|
17476
|
-
|
|
17477
|
-
|
|
17478
|
-
|
|
17479
|
-
|
|
17480
|
-
|
|
17481
|
-
|
|
17482
|
-
|
|
17483
|
-
|
|
17484
|
-
|
|
17485
|
-
|
|
17486
|
-
|
|
17487
|
-
|
|
17488
|
-
|
|
17489
|
-
|
|
17490
|
-
|
|
17491
|
-
|
|
17492
|
-
|
|
17493
|
-
|
|
17494
|
-
|
|
17495
|
-
|
|
17496
|
-
|
|
17497
|
-
|
|
17498
|
-
|
|
17499
|
-
|
|
17500
|
-
|
|
17501
|
-
|
|
17502
|
-
|
|
17503
|
-
"
|
|
17504
|
-
|
|
17505
|
-
|
|
17506
|
-
|
|
17507
|
-
|
|
17508
|
-
|
|
17509
|
-
|
|
17510
|
-
|
|
17511
|
-
|
|
17512
|
-
|
|
17571
|
+
// src/instrument-presets.ts
|
|
17572
|
+
var INSTRUMENT_PRESETS = {
|
|
17573
|
+
// --- STANDARD: 汎用性と完成度重視 ---
|
|
17574
|
+
piano: {
|
|
17575
|
+
displayName: "\u30B0\u30E9\u30F3\u30C9\u30D4\u30A2\u30CE",
|
|
17576
|
+
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",
|
|
17577
|
+
melody: "Acoustic Grand Piano",
|
|
17578
|
+
submelody: "Vibraphone",
|
|
17579
|
+
bass: "Electric Bass (finger)",
|
|
17580
|
+
chord: "Pad 2 (warm)",
|
|
17581
|
+
solo: "Electric Guitar (clean)",
|
|
17582
|
+
// **Glockenspiel は使わない。音源側のサンプルが音程を外している**(実聴で確認)。
|
|
17583
|
+
// 音域の問題(G5〜の高音楽器)なら {@link GM_BRIGHT_CEILING} で下げれば済むが、
|
|
17584
|
+
// ピッチそのものがずれているものは置き場所を変えても直らない。同じ役割
|
|
17585
|
+
// (サビの上に乗る明るい音板)で、素直に鳴る Celesta へ差し替えてある。
|
|
17586
|
+
// ※ {@link GM_INSTRUMENT_RANGE} / {@link GM_BRIGHT_CEILING} の Glockenspiel の
|
|
17587
|
+
// 項は残す。手で選んだときの置き場所の判断はそのまま要るため。
|
|
17588
|
+
chorusLead: "Celesta",
|
|
17589
|
+
chordAlt: "Electric Piano 1",
|
|
17590
|
+
sparkle: "Music Box",
|
|
17591
|
+
bassAlt: "Acoustic Bass",
|
|
17592
|
+
harmonyAlt: "Choir Aahs"
|
|
17593
|
+
},
|
|
17594
|
+
acoustic: {
|
|
17595
|
+
displayName: "\u30A2\u30B3\u30FC\u30B9\u30C6\u30A3\u30C3\u30AF",
|
|
17596
|
+
description: "\u751F\u697D\u5668\u306E\u6E29\u304B\u307F\u3092\u91CD\u8996\u3002\u30D5\u30A9\u30FC\u30AF\u3084\u30DD\u30C3\u30D7\u30B9\u306B\u3002",
|
|
17597
|
+
melody: "Acoustic Guitar (steel)",
|
|
17598
|
+
submelody: "Harmonica",
|
|
17599
|
+
bass: "Acoustic Bass",
|
|
17600
|
+
chord: "Acoustic Guitar (nylon)",
|
|
17601
|
+
solo: "Overdriven Guitar",
|
|
17602
|
+
chorusLead: "String Ensemble 1",
|
|
17603
|
+
chordAlt: "Acoustic Grand Piano",
|
|
17604
|
+
sparkle: "Celesta",
|
|
17605
|
+
bassAlt: "Electric Bass (finger)",
|
|
17606
|
+
harmonyAlt: "Choir Aahs"
|
|
17607
|
+
},
|
|
17608
|
+
jazz_night: {
|
|
17609
|
+
displayName: "\u30B8\u30E3\u30BA\u30FB\u30CA\u30A4\u30C8",
|
|
17610
|
+
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",
|
|
17611
|
+
melody: "Electric Piano 1",
|
|
17612
|
+
submelody: "Flute",
|
|
17613
|
+
bass: "Acoustic Bass",
|
|
17614
|
+
chord: "Electric Guitar (jazz)",
|
|
17615
|
+
solo: "Tenor Sax",
|
|
17616
|
+
chorusLead: "Muted Trumpet",
|
|
17617
|
+
chordAlt: "Vibraphone",
|
|
17618
|
+
sparkle: "Celesta",
|
|
17619
|
+
bassAlt: "Electric Bass (finger)",
|
|
17620
|
+
harmonyAlt: "Choir Aahs"
|
|
17621
|
+
},
|
|
17622
|
+
// --- MODERN & VIBE: エッジの効いた現代的な響き ---
|
|
17623
|
+
synth_pop: {
|
|
17624
|
+
displayName: "\u30B7\u30F3\u30BB\u30DD\u30C3\u30D7",
|
|
17625
|
+
description: "80s\u301C\u73FE\u4EE3\u307E\u3067\u3002\u629C\u3051\u308B\u30EA\u30FC\u30C9\u3068\u592A\u3044\u30D9\u30FC\u30B9\u306E\u738B\u9053\u3002",
|
|
17626
|
+
melody: "Lead 2 (sawtooth)",
|
|
17627
|
+
submelody: "Lead 4 (chiff)",
|
|
17628
|
+
bass: "Synth Bass 2",
|
|
17629
|
+
chord: "Pad 3 (polysynth)",
|
|
17630
|
+
solo: "Distortion Guitar",
|
|
17631
|
+
chorusLead: "Synth Brass 1",
|
|
17632
|
+
chordAlt: "Electric Piano 2",
|
|
17633
|
+
sparkle: "FX 3 (crystal)",
|
|
17634
|
+
bassAlt: "Synth Bass 1",
|
|
17635
|
+
harmonyAlt: "Synth Choir"
|
|
17636
|
+
},
|
|
17637
|
+
cyber_punk: {
|
|
17638
|
+
displayName: "\u30B5\u30A4\u30D0\u30FC\u30D1\u30F3\u30AF",
|
|
17639
|
+
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",
|
|
17640
|
+
melody: "Lead 8 (bass + lead)",
|
|
17641
|
+
submelody: "Lead 5 (charang)",
|
|
17642
|
+
bass: "Synth Bass 2",
|
|
17643
|
+
chord: "Pad 8 (sweep)",
|
|
17644
|
+
solo: "Distortion Guitar",
|
|
17645
|
+
chorusLead: "Lead 7 (fifths)",
|
|
17646
|
+
chordAlt: "Pad 4 (choir)",
|
|
17647
|
+
sparkle: "FX 3 (crystal)",
|
|
17648
|
+
bassAlt: "Synth Bass 1",
|
|
17649
|
+
harmonyAlt: "Synth Choir"
|
|
17650
|
+
},
|
|
17651
|
+
rock: {
|
|
17652
|
+
displayName: "\u30CF\u30FC\u30C9\u30ED\u30C3\u30AF",
|
|
17653
|
+
description: "\u6B6A\u307F\u30AE\u30BF\u30FC\u3068\u91CD\u539A\u306A\u30D9\u30FC\u30B9\u3067\u3001\u30D1\u30EF\u30FC\u3092\u524D\u9762\u306B\u3002",
|
|
17654
|
+
melody: "Distortion Guitar",
|
|
17655
|
+
submelody: "Rock Organ",
|
|
17656
|
+
bass: "Electric Bass (pick)",
|
|
17657
|
+
chord: "Overdriven Guitar",
|
|
17658
|
+
solo: "Distortion Guitar",
|
|
17659
|
+
chorusLead: "Brass Section",
|
|
17660
|
+
chordAlt: "Electric Guitar (clean)",
|
|
17661
|
+
sparkle: "Electric Guitar (muted)",
|
|
17662
|
+
bassAlt: "Electric Bass (finger)",
|
|
17663
|
+
harmonyAlt: "Choir Aahs"
|
|
17664
|
+
},
|
|
17665
|
+
// --- WORLD & CLASSIC: 特定のジャンル・地域 ---
|
|
17666
|
+
orchestra: {
|
|
17667
|
+
displayName: "\u30AA\u30FC\u30B1\u30B9\u30C8\u30E9",
|
|
17668
|
+
description: "\u58EE\u5927\u306A\u7269\u8A9E\u3092\u4E88\u611F\u3055\u305B\u308B\u3001\u7BA1\u5F26\u697D\u5668\u306E\u91CD\u539A\u306A\u97FF\u304D\u3002",
|
|
17669
|
+
melody: "French Horn",
|
|
17670
|
+
submelody: "Pizzicato Strings",
|
|
17671
|
+
bass: "Cello",
|
|
17672
|
+
chord: "Tremolo Strings",
|
|
17673
|
+
solo: "Violin",
|
|
17674
|
+
chorusLead: "Trumpet",
|
|
17675
|
+
chordAlt: "String Ensemble 1",
|
|
17676
|
+
sparkle: "Orchestral Harp",
|
|
17677
|
+
bassAlt: "Contrabass",
|
|
17678
|
+
harmonyAlt: "Choir Aahs"
|
|
17679
|
+
},
|
|
17680
|
+
japanese_wa: {
|
|
17681
|
+
displayName: "\u548C\u98A8\u30FB\u96C5",
|
|
17682
|
+
description: "\u7434\u3068\u4E09\u5473\u7DDA\u306E\u7E4A\u7D30\u306A\u8ABF\u3079\u306B\u3001\u5C3A\u516B\u306E\u60C5\u7DD2\u3092\u6DFB\u3048\u3066\u3002",
|
|
17683
|
+
melody: "Koto",
|
|
17684
|
+
submelody: "Shamisen",
|
|
17685
|
+
bass: "Taiko Drum",
|
|
17686
|
+
chord: "Shakuhachi",
|
|
17687
|
+
solo: "Shakuhachi",
|
|
17688
|
+
// piano プリセットと同じ理由で Glockenspiel を避ける(音源のピッチずれ)。
|
|
17689
|
+
chorusLead: "Celesta",
|
|
17690
|
+
chordAlt: "Kalimba",
|
|
17691
|
+
sparkle: "Music Box",
|
|
17692
|
+
bassAlt: "Acoustic Bass",
|
|
17693
|
+
harmonyAlt: "Choir Aahs"
|
|
17694
|
+
},
|
|
17695
|
+
arabic_exotic: {
|
|
17696
|
+
displayName: "\u30A8\u30AD\u30BE\u30C1\u30C3\u30AF",
|
|
17697
|
+
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",
|
|
17698
|
+
melody: "Sitar",
|
|
17699
|
+
submelody: "Bagpipe",
|
|
17700
|
+
bass: "Fretless Bass",
|
|
17701
|
+
chord: "Kalimba",
|
|
17702
|
+
solo: "Shanai",
|
|
17703
|
+
chorusLead: "Steel Drums",
|
|
17704
|
+
chordAlt: "Orchestral Harp",
|
|
17705
|
+
sparkle: "Tinkle Bell",
|
|
17706
|
+
bassAlt: "Acoustic Bass",
|
|
17707
|
+
harmonyAlt: "Choir Aahs"
|
|
17708
|
+
},
|
|
17709
|
+
// --- FANTASY & ATMOSPHERE: 雰囲気と余韻 ---
|
|
17710
|
+
fantasy_rpg: {
|
|
17711
|
+
displayName: "\u30D5\u30A1\u30F3\u30BF\u30B8\u30FCRPG",
|
|
17712
|
+
description: "\u30AA\u30AB\u30EA\u30CA\u3068\u30CF\u30FC\u30D7\u304C\u7D21\u3050\u3001\u5192\u967A\u3068\u9B54\u6CD5\u306E\u4E16\u754C\u89B3\u3002",
|
|
17713
|
+
melody: "Ocarina",
|
|
17714
|
+
submelody: "Celesta",
|
|
17715
|
+
bass: "Timpani",
|
|
17716
|
+
chord: "Orchestral Harp",
|
|
17717
|
+
solo: "Pan Flute",
|
|
17718
|
+
chorusLead: "Choir Aahs",
|
|
17719
|
+
chordAlt: "String Ensemble 2",
|
|
17720
|
+
sparkle: "Tinkle Bell",
|
|
17721
|
+
bassAlt: "Contrabass",
|
|
17722
|
+
harmonyAlt: "Choir Aahs"
|
|
17723
|
+
},
|
|
17724
|
+
ambient_cloud: {
|
|
17725
|
+
displayName: "\u30A2\u30F3\u30D3\u30A8\u30F3\u30C8",
|
|
17726
|
+
description: "\u8F2A\u90ED\u3092\u307C\u304B\u3057\u305F\u97F3\u8272\u3067\u3001\u6DF1\u3044\u6CA1\u5165\u611F\u3068\u4F59\u97FB\u3092\u6F14\u51FA\u3002",
|
|
17727
|
+
melody: "Lead 6 (voice)",
|
|
17728
|
+
submelody: "Music Box",
|
|
17729
|
+
bass: "Synth Bass 1",
|
|
17730
|
+
chord: "Pad 7 (halo)",
|
|
17731
|
+
solo: "Lead 3 (calliope)",
|
|
17732
|
+
chorusLead: "Synth Choir",
|
|
17733
|
+
chordAlt: "Pad 5 (bowed)",
|
|
17734
|
+
sparkle: "FX 3 (crystal)",
|
|
17735
|
+
bassAlt: "Synth Bass 2",
|
|
17736
|
+
harmonyAlt: "Synth Choir"
|
|
17737
|
+
},
|
|
17738
|
+
retro_game: {
|
|
17739
|
+
displayName: "8-bit \u30EC\u30C8\u30ED",
|
|
17740
|
+
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",
|
|
17741
|
+
melody: "Lead 1 (square)",
|
|
17742
|
+
submelody: "Lead 2 (sawtooth)",
|
|
17743
|
+
bass: "Synth Bass 1",
|
|
17744
|
+
chord: "Clavinet",
|
|
17745
|
+
solo: "Lead 8 (bass + lead)",
|
|
17746
|
+
chorusLead: "Lead 4 (chiff)",
|
|
17747
|
+
chordAlt: "Lead 5 (charang)",
|
|
17748
|
+
sparkle: "Xylophone",
|
|
17749
|
+
bassAlt: "Synth Bass 2",
|
|
17750
|
+
harmonyAlt: "Lead 6 (voice)"
|
|
17513
17751
|
}
|
|
17514
17752
|
};
|
|
17753
|
+
var GM_INSTRUMENT_RANGE = {
|
|
17754
|
+
// 鍵盤・音板
|
|
17755
|
+
"Acoustic Grand Piano": [21, 108],
|
|
17756
|
+
"Electric Piano 1": [28, 103],
|
|
17757
|
+
Clavinet: [36, 96],
|
|
17758
|
+
Celesta: [60, 108],
|
|
17759
|
+
Glockenspiel: [79, 108],
|
|
17760
|
+
"Music Box": [72, 108],
|
|
17761
|
+
Vibraphone: [53, 89],
|
|
17762
|
+
Kalimba: [60, 84],
|
|
17763
|
+
"Orchestral Harp": [23, 104],
|
|
17764
|
+
// 弦・撥弦
|
|
17765
|
+
"Acoustic Guitar (steel)": [40, 83],
|
|
17766
|
+
"Acoustic Guitar (nylon)": [40, 83],
|
|
17767
|
+
"Electric Guitar (clean)": [40, 86],
|
|
17768
|
+
"Electric Guitar (jazz)": [40, 86],
|
|
17769
|
+
"Overdriven Guitar": [40, 88],
|
|
17770
|
+
"Distortion Guitar": [40, 88],
|
|
17771
|
+
Violin: [55, 103],
|
|
17772
|
+
Cello: [36, 76],
|
|
17773
|
+
"String Ensemble 1": [28, 100],
|
|
17774
|
+
"Tremolo Strings": [28, 100],
|
|
17775
|
+
"Pizzicato Strings": [28, 96],
|
|
17776
|
+
Sitar: [48, 79],
|
|
17777
|
+
Shamisen: [48, 84],
|
|
17778
|
+
Koto: [41, 77],
|
|
17779
|
+
// ベース
|
|
17780
|
+
"Acoustic Bass": [28, 60],
|
|
17781
|
+
"Electric Bass (finger)": [28, 67],
|
|
17782
|
+
"Electric Bass (pick)": [28, 67],
|
|
17783
|
+
"Fretless Bass": [28, 67],
|
|
17784
|
+
"Synth Bass 1": [24, 72],
|
|
17785
|
+
"Synth Bass 2": [24, 72],
|
|
17786
|
+
// 管
|
|
17787
|
+
Flute: [60, 96],
|
|
17788
|
+
"Pan Flute": [60, 91],
|
|
17789
|
+
Shakuhachi: [62, 86],
|
|
17790
|
+
Ocarina: [60, 84],
|
|
17791
|
+
Harmonica: [60, 96],
|
|
17792
|
+
Bagpipe: [62, 86],
|
|
17793
|
+
Shanai: [60, 86],
|
|
17794
|
+
"Tenor Sax": [44, 75],
|
|
17795
|
+
Trumpet: [55, 82],
|
|
17796
|
+
"Muted Trumpet": [55, 82],
|
|
17797
|
+
"French Horn": [41, 77],
|
|
17798
|
+
"Brass Section": [41, 84],
|
|
17799
|
+
// 声・打・オルガン
|
|
17800
|
+
"Choir Aahs": [43, 84],
|
|
17801
|
+
"Synth Choir": [43, 84],
|
|
17802
|
+
"Steel Drums": [55, 86],
|
|
17803
|
+
"Taiko Drum": [30, 60],
|
|
17804
|
+
Timpani: [36, 57],
|
|
17805
|
+
"Rock Organ": [36, 96],
|
|
17806
|
+
// シンセ(実物が無いので一般的な使用域)
|
|
17807
|
+
"Synth Brass 1": [36, 96],
|
|
17808
|
+
"Lead 1 (square)": [36, 96],
|
|
17809
|
+
"Lead 2 (sawtooth)": [36, 96],
|
|
17810
|
+
"Lead 3 (calliope)": [48, 96],
|
|
17811
|
+
"Lead 4 (chiff)": [48, 96],
|
|
17812
|
+
"Lead 5 (charang)": [40, 96],
|
|
17813
|
+
"Lead 6 (voice)": [43, 91],
|
|
17814
|
+
"Lead 7 (fifths)": [36, 84],
|
|
17815
|
+
"Lead 8 (bass + lead)": [28, 91],
|
|
17816
|
+
"Pad 2 (warm)": [24, 96],
|
|
17817
|
+
"Pad 3 (polysynth)": [24, 96],
|
|
17818
|
+
"Pad 7 (halo)": [24, 96],
|
|
17819
|
+
"Pad 8 (sweep)": [24, 96]
|
|
17820
|
+
};
|
|
17821
|
+
var GM_BRIGHT_CEILING = {
|
|
17822
|
+
Glockenspiel: 72,
|
|
17823
|
+
"Tinkle Bell": 72,
|
|
17824
|
+
"Music Box": 79,
|
|
17825
|
+
Celesta: 84,
|
|
17826
|
+
Kalimba: 79,
|
|
17827
|
+
"Steel Drums": 84,
|
|
17828
|
+
"FX 3 (crystal)": 79
|
|
17829
|
+
};
|
|
17830
|
+
var OCTAVE_FIT_TOLERANCE = 3;
|
|
17831
|
+
var fitInstrumentOctave = (semitoneRange, instrument, wanted) => {
|
|
17832
|
+
const range = GM_INSTRUMENT_RANGE[instrument];
|
|
17833
|
+
if (!range || !semitoneRange) return wanted;
|
|
17834
|
+
const hi = Math.min(range[1], GM_BRIGHT_CEILING[instrument] ?? range[1]);
|
|
17835
|
+
let octave = wanted;
|
|
17836
|
+
while (octave > wanted - 2) {
|
|
17837
|
+
if (semitoneRange[1] + octave * 12 <= hi + OCTAVE_FIT_TOLERANCE) break;
|
|
17838
|
+
octave--;
|
|
17839
|
+
}
|
|
17840
|
+
return octave;
|
|
17841
|
+
};
|
|
17515
17842
|
|
|
17516
|
-
// src/
|
|
17517
|
-
var
|
|
17518
|
-
|
|
17519
|
-
const {
|
|
17520
|
-
|
|
17521
|
-
|
|
17522
|
-
|
|
17523
|
-
|
|
17524
|
-
|
|
17525
|
-
|
|
17526
|
-
|
|
17527
|
-
|
|
17528
|
-
|
|
17529
|
-
|
|
17530
|
-
|
|
17531
|
-
).
|
|
17532
|
-
|
|
17533
|
-
|
|
17534
|
-
|
|
17535
|
-
|
|
17536
|
-
|
|
17537
|
-
|
|
17538
|
-
|
|
17539
|
-
|
|
17540
|
-
|
|
17541
|
-
|
|
17542
|
-
|
|
17543
|
-
|
|
17544
|
-
|
|
17545
|
-
|
|
17546
|
-
|
|
17547
|
-
|
|
17548
|
-
|
|
17549
|
-
|
|
17550
|
-
|
|
17551
|
-
|
|
17552
|
-
|
|
17553
|
-
|
|
17554
|
-
|
|
17555
|
-
|
|
17556
|
-
|
|
17557
|
-
|
|
17558
|
-
|
|
17559
|
-
|
|
17560
|
-
|
|
17561
|
-
|
|
17562
|
-
|
|
17563
|
-
|
|
17564
|
-
|
|
17565
|
-
|
|
17566
|
-
|
|
17567
|
-
|
|
17843
|
+
// src/advanced-layers.ts
|
|
17844
|
+
var buildAdvancedLayers = (song, config) => {
|
|
17845
|
+
const { edo, stepsPerBar, preset } = config;
|
|
17846
|
+
const semitoneRange = (notes) => {
|
|
17847
|
+
if (notes.length === 0) return null;
|
|
17848
|
+
let lo = Number.POSITIVE_INFINITY;
|
|
17849
|
+
let hi = Number.NEGATIVE_INFINITY;
|
|
17850
|
+
for (const n of notes) {
|
|
17851
|
+
const semi = n.pitchUnits / UNITS_PER_SEMITONE;
|
|
17852
|
+
if (semi < lo) lo = semi;
|
|
17853
|
+
if (semi > hi) hi = semi;
|
|
17854
|
+
}
|
|
17855
|
+
return [Math.round(lo), Math.round(hi)];
|
|
17856
|
+
};
|
|
17857
|
+
const fit = (notes, slot, wanted) => fitInstrumentOctave(semitoneRange(notes), preset[slot], wanted);
|
|
17858
|
+
const kindAtBar = (bar) => song.sections.find(
|
|
17859
|
+
(sec) => bar >= sec.startBar && bar < sec.startBar + sec.bars
|
|
17860
|
+
)?.kind ?? null;
|
|
17861
|
+
const onlyIn = (notes, kinds) => {
|
|
17862
|
+
if (!kinds) return notes;
|
|
17863
|
+
const want = new Set(kinds);
|
|
17864
|
+
return notes.filter((n) => {
|
|
17865
|
+
const kind = kindAtBar(Math.floor(n.startStep / stepsPerBar));
|
|
17866
|
+
return kind !== null && want.has(kind);
|
|
17867
|
+
});
|
|
17868
|
+
};
|
|
17869
|
+
const chordNotes = (pattern, velocityShift = 0) => buildChordPlacements({
|
|
17870
|
+
edo,
|
|
17871
|
+
chordStr: song.chordProgression,
|
|
17872
|
+
patternType: pattern,
|
|
17873
|
+
rootShift: song.rootShift,
|
|
17874
|
+
bpm: song.bpm,
|
|
17875
|
+
stepsPerBar
|
|
17876
|
+
}).map((p) => ({
|
|
17877
|
+
startStep: p.startStep,
|
|
17878
|
+
pitchUnits: p.pitchUnits,
|
|
17879
|
+
durationSteps: p.durationSteps,
|
|
17880
|
+
velocity: Math.max(30, p.velocity + velocityShift)
|
|
17881
|
+
}));
|
|
17882
|
+
const plan = song.arrange;
|
|
17883
|
+
const kindsPresent = [...new Set(song.sections.map((sec) => sec.kind))];
|
|
17884
|
+
const LOUD = ["chorus", "drop_chorus"];
|
|
17885
|
+
const loudKinds = kindsPresent.filter((k) => LOUD.includes(k));
|
|
17886
|
+
const quietKinds = kindsPresent.filter((k) => !LOUD.includes(k));
|
|
17887
|
+
const splittable = loudKinds.length > 0 && quietKinds.length > 0;
|
|
17888
|
+
const leadNotes = plan.lead ? onlyIn(song.melody, plan.lead.sections) : [];
|
|
17889
|
+
const leadOctave = fit(leadNotes, "chorusLead", plan.lead?.octave ?? 0);
|
|
17890
|
+
const leadLayer = {
|
|
17891
|
+
index: 1,
|
|
17892
|
+
notes: leadNotes,
|
|
17893
|
+
octave: leadOctave,
|
|
17894
|
+
// ユニゾンで重ねるときは、オクターブ上より前に出やすいので少し引く。
|
|
17895
|
+
volume: leadOctave === 0 ? 54 : 62,
|
|
17896
|
+
slot: "chorusLead"
|
|
17897
|
+
};
|
|
17898
|
+
const bassNotes = plan.bassLayer ? onlyIn(song.bass, plan.bassLayer.sections) : [];
|
|
17899
|
+
const bassOctave = fit(bassNotes, "bass", plan.bassLayer?.octave ?? 0);
|
|
17900
|
+
const wantBassLayer = plan.bassLayer !== null && bassOctave !== 0;
|
|
17901
|
+
const bassLayer = wantBassLayer ? {
|
|
17902
|
+
index: 5,
|
|
17903
|
+
notes: bassNotes,
|
|
17904
|
+
octave: bassOctave,
|
|
17905
|
+
volume: 58,
|
|
17906
|
+
slot: "bass"
|
|
17907
|
+
} : {
|
|
17908
|
+
index: 5,
|
|
17909
|
+
notes: splittable ? onlyIn(song.bass, loudKinds) : [],
|
|
17910
|
+
octave: 0,
|
|
17911
|
+
volume: 92,
|
|
17912
|
+
slot: "bassAlt"
|
|
17913
|
+
};
|
|
17914
|
+
const padNotes = onlyIn(song.pad, plan.padSections);
|
|
17915
|
+
const layers = [
|
|
17916
|
+
{ index: 0, notes: song.melody, octave: 0, volume: 104, slot: "melody" },
|
|
17917
|
+
leadLayer,
|
|
17918
|
+
// t2 ハモリ。**セクションで分割しない。**
|
|
17919
|
+
// 一度 t12 と loud/quiet で分けてみたが、ハモリは音の87%が盛り上がる側にあり、
|
|
17920
|
+
// 71%の曲は静かな側に1音も無い——分割すると t2 が空になって中身が t12 へ移る。
|
|
17921
|
+
// 「トラック番号と役割の対応は固定」(ピアノロールのどこに何があるかが曲ごとに
|
|
17922
|
+
// 動くとユーザーが編集できない)という原則にも反するので、ここは丸ごと持つ。
|
|
17923
|
+
{ index: 2, notes: song.harmony, octave: 0, volume: 82 },
|
|
17924
|
+
// t3 サブメロ。t14 が間奏ソロを持たないとき、そちらへ盛り上がる側を渡して
|
|
17925
|
+
// こちらは静かな側だけ持つ。対旋律・合いの手がセクションで音色替えする。
|
|
17926
|
+
{
|
|
17927
|
+
index: 3,
|
|
17928
|
+
notes: song.solo.length === 0 && splittable ? onlyIn(song.submelody, quietKinds) : song.submelody,
|
|
17929
|
+
octave: 0,
|
|
17930
|
+
volume: 86,
|
|
17931
|
+
slot: "submelody"
|
|
17932
|
+
},
|
|
17933
|
+
// t4。t5 が `bassAlt` で盛り上がる側を持つときは、こちらは静かな側だけ持つ
|
|
17934
|
+
// (分割であって間引きではない——2本合わせて元のベースが漏れなく鳴る)。
|
|
17935
|
+
{
|
|
17936
|
+
index: 4,
|
|
17937
|
+
notes: !wantBassLayer && splittable ? onlyIn(song.bass, quietKinds) : song.bass,
|
|
17938
|
+
octave: 0,
|
|
17939
|
+
volume: 92,
|
|
17940
|
+
slot: "bass"
|
|
17941
|
+
},
|
|
17942
|
+
bassLayer,
|
|
17943
|
+
// **パッドは伴奏用の楽器で、伴奏より1.5オクターブ高いところを鳴らす。**
|
|
17944
|
+
// そのまま置くとナイロンギターで10半音、カリンバで9半音、尺八で7半音ぶん
|
|
17945
|
+
// 音域を突き抜ける。楽器に合わせて下げる。
|
|
17946
|
+
{
|
|
17947
|
+
index: 6,
|
|
17948
|
+
notes: padNotes,
|
|
17949
|
+
octave: fit(padNotes, "chord", 0),
|
|
17950
|
+
volume: 64,
|
|
17951
|
+
slot: "chord"
|
|
17952
|
+
}
|
|
17953
|
+
];
|
|
17954
|
+
const backingVolumes = [62, 54, 50];
|
|
17955
|
+
for (let i2 = 0; i2 < 3; i2++) {
|
|
17956
|
+
const layer = plan.backing[i2];
|
|
17957
|
+
layers.push({
|
|
17958
|
+
index: 7 + i2,
|
|
17959
|
+
notes: layer ? onlyIn(chordNotes(layer.pattern), layer.sections) : [],
|
|
17960
|
+
octave: layer?.octave ?? 0,
|
|
17961
|
+
volume: backingVolumes[i2],
|
|
17962
|
+
slot: i2 % 2 === 0 ? "chordAlt" : "chord"
|
|
17963
|
+
});
|
|
17964
|
+
}
|
|
17965
|
+
const sparkleNotes = plan.sparkle ? onlyIn(chordNotes(plan.sparkle.pattern, -14), plan.sparkle.sections) : [];
|
|
17966
|
+
layers.push(
|
|
17967
|
+
{
|
|
17968
|
+
index: 10,
|
|
17969
|
+
notes: sparkleNotes,
|
|
17970
|
+
// 装飾は伴奏と別の音色にする(以前はここも `chord` の使い回しだった)。
|
|
17971
|
+
octave: fit(sparkleNotes, "sparkle", plan.sparkle?.octave ?? 0),
|
|
17972
|
+
volume: 56,
|
|
17973
|
+
slot: "sparkle"
|
|
17974
|
+
},
|
|
17975
|
+
// 掛け合い(デュエット)の相手。**歌入り作曲のときだけ**中身が入る。
|
|
17976
|
+
{ index: 11, notes: [], octave: 0, volume: 104, slot: "melody" },
|
|
17977
|
+
// 2声目のハモリ(主旋律を上下から挟む3声)と、主旋律のオクターブ下の重ね。
|
|
17978
|
+
// どちらも曲ごとに出るかどうかが決まる(`song.vocal`)。
|
|
17979
|
+
// t12。3声目のハモリは曲ごとに出るかが決まり、実測28%。
|
|
17980
|
+
//
|
|
17981
|
+
// 出ない曲では**パッド(t6)が鳴っていないセクション**を、別の音色
|
|
17982
|
+
// (`harmonyAlt` =声もの・柔らかい持続音)で受け持つ。パッドはセクション種別の
|
|
17983
|
+
// 39%しか覆っておらず補集合は常に存在するので、ここは**いま何も鳴っていない
|
|
17984
|
+
// 場所に持続音を足す**ことになる——重ねでも写しでもなく、純粋な追加。
|
|
17985
|
+
song.harmony2.length > 0 ? { index: 12, notes: song.harmony2, octave: 0, volume: 74 } : (() => {
|
|
17986
|
+
const rest = plan.padSections ? kindsPresent.filter((k) => !plan.padSections?.includes(k)) : [];
|
|
17987
|
+
const notes = rest.length > 0 ? onlyIn(song.pad, rest) : [];
|
|
17988
|
+
return {
|
|
17989
|
+
index: 12,
|
|
17990
|
+
notes,
|
|
17991
|
+
octave: fit(notes, "harmonyAlt", 0),
|
|
17992
|
+
volume: 60,
|
|
17993
|
+
slot: "harmonyAlt"
|
|
17994
|
+
};
|
|
17995
|
+
})(),
|
|
17996
|
+
// t13。既定は**主旋律のオクターブ下の重ね**で、歌入り作曲ではここに歌詞が付く
|
|
17997
|
+
// (オクターブ下でハモる歌手。単なる写しではない)。
|
|
17998
|
+
//
|
|
17999
|
+
// ただしこの層は曲ごとに出るかどうかが決まり、実測で**18%しか鳴らない**。
|
|
18000
|
+
// 残り82%はトラックが1本まるごと遊ぶ。オクターブ等価の写しに常設の価値は
|
|
18001
|
+
// 無いが、**空けておく価値はもっと無い**ので、層が無い曲では
|
|
18002
|
+
// 「t1(サビ重ね)が担当しないセクションの主旋律を、別の楽器でなぞる」層に回す。
|
|
18003
|
+
// t1 は CHORUS/LOUD 側を持つので、こちらは静かな側を持つ——結果として
|
|
18004
|
+
// **セクションの境目で主旋律に付く音色が入れ替わる**。1トラック1楽器の制約下で
|
|
18005
|
+
// 「パートごとに楽器が変わる」を作れるのは、この書き分けだけ。
|
|
18006
|
+
song.octave.length > 0 ? {
|
|
18007
|
+
index: 13,
|
|
18008
|
+
notes: song.octave,
|
|
18009
|
+
octave: -1,
|
|
18010
|
+
volume: 56,
|
|
18011
|
+
slot: "melody"
|
|
18012
|
+
} : (() => {
|
|
18013
|
+
const leadKinds = new Set(plan.lead?.sections ?? []);
|
|
18014
|
+
const quiet = song.sections.filter((sec) => sec.spec.melody && !leadKinds.has(sec.kind)).map((sec) => sec.kind);
|
|
18015
|
+
const notes = quiet.length > 0 ? onlyIn(song.melody, quiet) : [];
|
|
18016
|
+
const slot = ["solo", "chorusLead", "submelody", "chordAlt"].find(
|
|
18017
|
+
(k) => preset[k] !== preset.melody && preset[k] !== preset.chord
|
|
18018
|
+
) ?? "submelody";
|
|
18019
|
+
return {
|
|
18020
|
+
index: 13,
|
|
18021
|
+
notes,
|
|
18022
|
+
// ユニゾンで置く。**音色だけを変えるのが狙い**なので、
|
|
18023
|
+
// オクターブを動かすと「オクターブ写し」に戻ってしまう。
|
|
18024
|
+
octave: fit(notes, slot, 0),
|
|
18025
|
+
volume: 58,
|
|
18026
|
+
slot
|
|
18027
|
+
};
|
|
18028
|
+
})(),
|
|
18029
|
+
// **間奏のソロ。** 音が入るのは間奏の小節だけなので、この1本だけを
|
|
18030
|
+
// 別の楽器にしても他のセクションの鳴りは変わらない。歌の音域をそのまま
|
|
18031
|
+
// 渡すと管楽器が上へ抜ける(テナーサックスで10半音)ので、ここも合わせる。
|
|
18032
|
+
// t14。**間奏のソロ。** ただし間奏は既定の構成(`DEFAULT_SECTIONS`)に無く、
|
|
18033
|
+
// テンプレートも `jpop_standard` しか含まないので、実測で**0%**——設計上の
|
|
18034
|
+
// 出番が既定では一度も来ないトラックだった。
|
|
18035
|
+
// 間奏が無い曲では、サブメロの盛り上がる側を `solo` の音色で受け持つ
|
|
18036
|
+
// (t3 は静かな側。分割なので音数は増えない)。
|
|
18037
|
+
song.solo.length > 0 ? {
|
|
18038
|
+
index: 14,
|
|
18039
|
+
notes: song.solo,
|
|
18040
|
+
octave: fit(song.solo, "solo", 0),
|
|
18041
|
+
volume: 100,
|
|
18042
|
+
slot: "solo"
|
|
18043
|
+
} : (() => {
|
|
18044
|
+
const notes = splittable ? onlyIn(song.submelody, loudKinds) : [];
|
|
18045
|
+
return {
|
|
18046
|
+
index: 14,
|
|
18047
|
+
notes,
|
|
18048
|
+
octave: fit(notes, "solo", 0),
|
|
18049
|
+
volume: 86,
|
|
18050
|
+
slot: "solo"
|
|
18051
|
+
};
|
|
18052
|
+
})()
|
|
18053
|
+
);
|
|
18054
|
+
return layers;
|
|
18055
|
+
};
|
|
18056
|
+
|
|
18057
|
+
// src/delay.ts
|
|
18058
|
+
var DELAY_DIVISIONS = [
|
|
18059
|
+
{ value: "4", label: "4\u5206", beats: 1 },
|
|
18060
|
+
{ value: "8", label: "8\u5206", beats: 0.5 },
|
|
18061
|
+
{ value: "8d", label: "\u4ED8\u70B98\u5206", beats: 0.75 },
|
|
18062
|
+
{ value: "16", label: "16\u5206", beats: 0.25 }
|
|
18063
|
+
];
|
|
18064
|
+
var clamp4 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
18065
|
+
var divisionToSeconds = (division, bpm) => {
|
|
18066
|
+
const beats = DELAY_DIVISIONS.find((d) => d.value === division)?.beats ?? 0.5;
|
|
18067
|
+
const safeBpm = bpm > 0 ? bpm : 120;
|
|
18068
|
+
return 60 / safeBpm * beats;
|
|
18069
|
+
};
|
|
18070
|
+
var DELAY_MAX_WET = 0.45;
|
|
18071
|
+
var delayAmountToGain = (amount) => clamp4(amount, 0, 100) / 100 * DELAY_MAX_WET;
|
|
18072
|
+
var FEEDBACK_GAIN = 0.3;
|
|
18073
|
+
var MAX_DELAY_SEC = 2;
|
|
18074
|
+
var createDelayBus = (ctx, destination, options = {}) => {
|
|
18075
|
+
const input = ctx.createGain();
|
|
18076
|
+
const delayNode = ctx.createDelay(MAX_DELAY_SEC);
|
|
18077
|
+
const feedback = ctx.createGain();
|
|
18078
|
+
feedback.gain.value = FEEDBACK_GAIN;
|
|
18079
|
+
const wetGain = ctx.createGain();
|
|
18080
|
+
wetGain.gain.value = delayAmountToGain(options.amount ?? 0);
|
|
18081
|
+
input.connect(delayNode);
|
|
18082
|
+
delayNode.connect(feedback);
|
|
18083
|
+
feedback.connect(delayNode);
|
|
18084
|
+
delayNode.connect(wetGain);
|
|
18085
|
+
wetGain.connect(destination);
|
|
18086
|
+
let bpm = options.bpm ?? 120;
|
|
18087
|
+
let division = options.division ?? "8";
|
|
18088
|
+
const applyTime = () => {
|
|
18089
|
+
delayNode.delayTime.setTargetAtTime(
|
|
18090
|
+
divisionToSeconds(division, bpm),
|
|
18091
|
+
ctx.currentTime,
|
|
18092
|
+
0.05
|
|
18093
|
+
);
|
|
18094
|
+
};
|
|
18095
|
+
applyTime();
|
|
18096
|
+
return {
|
|
18097
|
+
input,
|
|
18098
|
+
setAmount: (amount) => {
|
|
18099
|
+
wetGain.gain.setTargetAtTime(
|
|
18100
|
+
delayAmountToGain(amount),
|
|
18101
|
+
ctx.currentTime,
|
|
18102
|
+
0.02
|
|
18103
|
+
);
|
|
18104
|
+
},
|
|
18105
|
+
setDivision: (d) => {
|
|
18106
|
+
division = d;
|
|
18107
|
+
applyTime();
|
|
18108
|
+
},
|
|
18109
|
+
setBpm: (b) => {
|
|
18110
|
+
bpm = b;
|
|
18111
|
+
applyTime();
|
|
18112
|
+
},
|
|
18113
|
+
dispose: () => {
|
|
18114
|
+
input.disconnect();
|
|
18115
|
+
delayNode.disconnect();
|
|
18116
|
+
feedback.disconnect();
|
|
18117
|
+
wetGain.disconnect();
|
|
18118
|
+
}
|
|
18119
|
+
};
|
|
18120
|
+
};
|
|
18121
|
+
|
|
18122
|
+
// src/panel-state.ts
|
|
18123
|
+
var STORAGE_PREFIX = "dtm-panel-open:";
|
|
18124
|
+
var readPanelOpen = (key) => {
|
|
18125
|
+
try {
|
|
18126
|
+
if (typeof localStorage === "undefined" || !localStorage) return null;
|
|
18127
|
+
const raw = localStorage.getItem(STORAGE_PREFIX + key);
|
|
18128
|
+
if (raw === "1") return true;
|
|
18129
|
+
if (raw === "0") return false;
|
|
18130
|
+
} catch (_) {
|
|
18131
|
+
}
|
|
18132
|
+
return null;
|
|
18133
|
+
};
|
|
18134
|
+
var writePanelOpen = (key, open) => {
|
|
18135
|
+
try {
|
|
18136
|
+
if (typeof localStorage === "undefined" || !localStorage) return;
|
|
18137
|
+
localStorage.setItem(STORAGE_PREFIX + key, open ? "1" : "0");
|
|
18138
|
+
} catch (_) {
|
|
18139
|
+
}
|
|
18140
|
+
};
|
|
18141
|
+
var persistPanels = (root) => {
|
|
18142
|
+
const panels = root.querySelectorAll(
|
|
18143
|
+
"details[data-dtm-acc]"
|
|
18144
|
+
);
|
|
18145
|
+
for (const panel of panels) {
|
|
18146
|
+
const key = panel.dataset.dtmAcc;
|
|
18147
|
+
if (!key) continue;
|
|
18148
|
+
const stored = readPanelOpen(key);
|
|
18149
|
+
if (stored !== null) panel.open = stored;
|
|
18150
|
+
panel.addEventListener("toggle", () => {
|
|
18151
|
+
writePanelOpen(key, panel.open);
|
|
18152
|
+
});
|
|
18153
|
+
}
|
|
18154
|
+
};
|
|
18155
|
+
|
|
18156
|
+
// src/daw-ui.ts
|
|
18157
|
+
var q = (root, sel) => root.querySelector(sel);
|
|
18158
|
+
var buildUI = (target, options) => {
|
|
18159
|
+
const {
|
|
18160
|
+
drumPatterns,
|
|
18161
|
+
defaultDrumPattern,
|
|
18162
|
+
defaultBpm,
|
|
18163
|
+
showMidi,
|
|
18164
|
+
showMidiSearch,
|
|
18165
|
+
showCompose
|
|
18166
|
+
} = options;
|
|
18167
|
+
const drumOptions = [`<option value="none">\u306A\u3057</option>`].concat(
|
|
18168
|
+
drumPatterns.map(
|
|
18169
|
+
(p) => `<option value="${p.value}" ${p.value === defaultDrumPattern ? "selected" : ""}>${p.label}</option>`
|
|
18170
|
+
)
|
|
18171
|
+
).join("");
|
|
18172
|
+
target.innerHTML = `
|
|
18173
|
+
<div class="dtm-daw" data-dtm="root">
|
|
18174
|
+
<div class="dtm-topbar" data-dtm="transport">
|
|
18175
|
+
<div class="dtm-topbar-row1">
|
|
18176
|
+
<button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
|
|
18177
|
+
<button class="dtm-play" data-dtm="play" disabled>${icon("play")}</button>
|
|
18178
|
+
<button class="dtm-iconbtn" data-dtm="next-bar" title="1\u5C0F\u7BC0\u5F8C">${icon("chevronRight")}</button>
|
|
18179
|
+
<label class="dtm-toggle"><input type="checkbox" data-dtm="solo"><span>\u30BD\u30ED</span></label>
|
|
18180
|
+
<span class="dtm-topbar-loading dtm-blink" data-dtm="topbar-loading">... LOADING ...</span>
|
|
18181
|
+
<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>
|
|
18182
|
+
<span class="dtm-grow"></span>
|
|
18183
|
+
<span class="dtm-label">BPM</span>
|
|
18184
|
+
<input type="number" class="dtm-input dtm-input--num" data-dtm="bpm" value="${defaultBpm}" min="20" max="300">
|
|
18185
|
+
</div>
|
|
18186
|
+
<div class="dtm-tracks" data-dtm="track-tabs"></div>
|
|
18187
|
+
</div>
|
|
18188
|
+
|
|
18189
|
+
<div class="dtm-tooldock">
|
|
18190
|
+
<div class="dtm-seg">
|
|
18191
|
+
<button class="dtm-segbtn dtm-segbtn--active" data-dtm="tool-pen" title="\u30DA\u30F3">${icon("pen")}</button>
|
|
18192
|
+
<button class="dtm-segbtn" data-dtm="tool-select" title="\u9078\u629E">${icon("select")}</button>
|
|
18193
|
+
<button class="dtm-segbtn" data-dtm="tool-eraser" title="\u6D88\u3057\u30B4\u30E0">${icon("eraser")}</button>
|
|
18194
|
+
</div>
|
|
18195
|
+
<button class="dtm-iconbtn" data-dtm="undo" title="\u5143\u306B\u623B\u3059" disabled>${icon("undo")}</button>
|
|
18196
|
+
<button class="dtm-iconbtn" data-dtm="redo" title="\u3084\u308A\u76F4\u3057" disabled>${icon("redo")}</button>
|
|
18197
|
+
<select class="dtm-select dtm-grow" data-dtm="note-length" title="\u97F3\u7B26\u306E\u9577\u3055">
|
|
18198
|
+
<option value="48">4\u5206</option>
|
|
18199
|
+
<option value="32">3\u90234</option>
|
|
18200
|
+
<option value="24">8\u5206</option>
|
|
18201
|
+
<option value="16">3\u90238</option>
|
|
18202
|
+
<option value="12" selected>16\u5206</option>
|
|
18203
|
+
<option value="8">3\u902316</option>
|
|
18204
|
+
<option value="6">32\u5206</option>
|
|
18205
|
+
<option value="4">3\u902332</option>
|
|
18206
|
+
</select>
|
|
18207
|
+
</div>
|
|
17568
18208
|
|
|
17569
18209
|
<div class="dtm-roll-wrap">
|
|
17570
18210
|
<div class="dtm-roll" data-dtm="roll">
|
|
@@ -17969,355 +18609,135 @@ var buildUI = (target, options) => {
|
|
|
17969
18609
|
<div class="dtm-modal-header">
|
|
17970
18610
|
<span class="dtm-modal-title" data-dtm="modal-title"></span>
|
|
17971
18611
|
<button class="dtm-modal-close" data-dtm="modal-close">×</button>
|
|
17972
|
-
</div>
|
|
17973
|
-
<div class="dtm-modal-body" data-dtm="modal-body"></div>
|
|
17974
|
-
</div>
|
|
17975
|
-
</div>
|
|
17976
|
-
|
|
17977
|
-
</div>`;
|
|
17978
|
-
const root = q(target, '[data-dtm="root"]');
|
|
17979
|
-
persistPanels(root);
|
|
17980
|
-
const sel = (name) => q(root, `[data-dtm="${name}"]`);
|
|
17981
|
-
return {
|
|
17982
|
-
root,
|
|
17983
|
-
topbar: sel("transport"),
|
|
17984
|
-
topbarLoading: sel("topbar-loading"),
|
|
17985
|
-
playBtn: sel("play"),
|
|
17986
|
-
prevBarBtn: sel("prev-bar"),
|
|
17987
|
-
nextBarBtn: sel("next-bar"),
|
|
17988
|
-
soloCheckbox: sel("solo"),
|
|
17989
|
-
clipBadge: sel("clip-badge"),
|
|
17990
|
-
toolPen: sel("tool-pen"),
|
|
17991
|
-
toolSelect: sel("tool-select"),
|
|
17992
|
-
toolEraser: sel("tool-eraser"),
|
|
17993
|
-
undoBtn: sel("undo"),
|
|
17994
|
-
redoBtn: sel("redo"),
|
|
17995
|
-
noteLengthSelect: sel("note-length"),
|
|
17996
|
-
bpmInput: sel("bpm"),
|
|
17997
|
-
zoomXLabel: sel("zoomx-label"),
|
|
17998
|
-
zoomYLabel: sel("zoomy-label"),
|
|
17999
|
-
zoomXIn: sel("zoomx-in"),
|
|
18000
|
-
zoomXOut: sel("zoomx-out"),
|
|
18001
|
-
zoomYIn: sel("zoomy-in"),
|
|
18002
|
-
zoomYOut: sel("zoomy-out"),
|
|
18003
|
-
bgFileInput: sel("bg-file-input"),
|
|
18004
|
-
bgUploadBtn: sel("bg-upload"),
|
|
18005
|
-
bgRemoveBtn: sel("bg-remove"),
|
|
18006
|
-
bgOpacityInput: sel("bg-opacity"),
|
|
18007
|
-
bgOpacityRow: sel("bg-opacity-row"),
|
|
18008
|
-
rollContainer: sel("roll"),
|
|
18009
|
-
wrapper: sel("wrapper"),
|
|
18010
|
-
vScroll: sel("vscroll"),
|
|
18011
|
-
vScrollThumb: sel("vscroll-thumb"),
|
|
18012
|
-
hScroll: sel("hscroll"),
|
|
18013
|
-
hScrollThumb: sel("hscroll-thumb"),
|
|
18014
|
-
loopToggle: sel("loop-toggle"),
|
|
18015
|
-
loopToggleLabel: sel("loop-toggle-label"),
|
|
18016
|
-
loopInfoBtn: sel("loop-info"),
|
|
18017
|
-
masterVolume: sel("master-volume"),
|
|
18018
|
-
masterVolumeLabel: sel("master-volume-label"),
|
|
18019
|
-
masterComp: sel("master-comp"),
|
|
18020
|
-
masterCompLabel: sel("master-comp-label"),
|
|
18021
|
-
masterCompInfoBtn: sel("master-comp-info"),
|
|
18022
|
-
reverbAmount: sel("reverb-amount"),
|
|
18023
|
-
reverbAmountLabel: sel("reverb-amount-label"),
|
|
18024
|
-
reverbAmountInfoBtn: sel("reverb-amount-info"),
|
|
18025
|
-
reverbDecay: sel("reverb-decay"),
|
|
18026
|
-
reverbDecayLabel: sel("reverb-decay-label"),
|
|
18027
|
-
reverbPreDelay: sel("reverb-predelay"),
|
|
18028
|
-
reverbPreDelayLabel: sel("reverb-predelay-label"),
|
|
18029
|
-
delayAmount: sel("delay-amount"),
|
|
18030
|
-
delayAmountLabel: sel("delay-amount-label"),
|
|
18031
|
-
delayAmountInfoBtn: sel("delay-amount-info"),
|
|
18032
|
-
delayDivision: sel("delay-division"),
|
|
18033
|
-
fadeIn: sel("fade-in"),
|
|
18034
|
-
fadeInLabel: sel("fade-in-label"),
|
|
18035
|
-
fadeOut: sel("fade-out"),
|
|
18036
|
-
fadeOutLabel: sel("fade-out-label"),
|
|
18037
|
-
fadeInfoBtn: sel("fade-info"),
|
|
18038
|
-
autoMasterBtn: sel("auto-master"),
|
|
18039
|
-
autoMasterInfoBtn: sel("auto-master-info"),
|
|
18040
|
-
trackTabs: sel("track-tabs"),
|
|
18041
|
-
trackBody: sel("track-body"),
|
|
18042
|
-
drumSelect: sel("drum-select"),
|
|
18043
|
-
drumFontSelect: sel("drum-font-select"),
|
|
18044
|
-
drumVolume: sel("drum-volume"),
|
|
18045
|
-
drumVolumeLabel: sel("drum-volume-label"),
|
|
18046
|
-
midiInput: sel("midi-input"),
|
|
18047
|
-
midiLoadBtn: sel("midi-load"),
|
|
18048
|
-
midiInfoBtn: sel("midi-info"),
|
|
18049
|
-
midiTrackSelection: sel("midi-track-selection"),
|
|
18050
|
-
midiPanel: sel("midi-panel"),
|
|
18051
|
-
midiSearchOpenBtn: sel("midi-search-open"),
|
|
18052
|
-
mmlInput: sel("mml-input"),
|
|
18053
|
-
mmlLoadBtn: sel("mml-load"),
|
|
18054
|
-
mmlLoadNote: sel("mml-load-note"),
|
|
18055
|
-
applyActiveOnly: sel("apply-active-only"),
|
|
18056
|
-
shiftSelect: sel("shift-select"),
|
|
18057
|
-
shiftApplyBtn: sel("shift-apply"),
|
|
18058
|
-
transposeSelect: sel("transpose-select"),
|
|
18059
|
-
transposeApplyBtn: sel("transpose-apply"),
|
|
18060
|
-
transposeInfoBtn: sel("transpose-info"),
|
|
18061
|
-
macroCompose: sel("macro-compose"),
|
|
18062
|
-
composeTemplate: sel("compose-template"),
|
|
18063
|
-
composeSections: sel("compose-sections"),
|
|
18064
|
-
composeSectionsLen: sel("compose-sections-len"),
|
|
18065
|
-
composeKey: sel("compose-key"),
|
|
18066
|
-
composeKeyHint: sel("compose-key-hint"),
|
|
18067
|
-
composeScale: sel("compose-scale"),
|
|
18068
|
-
composeScaleHint: sel("compose-scale-hint"),
|
|
18069
|
-
macroComposeVocal: sel("macro-compose-vocal"),
|
|
18070
|
-
macroComposeInfo: sel("macro-compose-info"),
|
|
18071
|
-
macroClear: sel("macro-clear"),
|
|
18072
|
-
macroRandom: sel("macro-random"),
|
|
18073
|
-
macroHarmonic: sel("macro-harmonic"),
|
|
18074
|
-
macroMono: sel("macro-mono"),
|
|
18075
|
-
exportMidiBtn: sel("export-midi"),
|
|
18076
|
-
exportWavBtn: sel("export-wav"),
|
|
18077
|
-
drumJsonExportBtn: sel("drum-json-export"),
|
|
18078
|
-
drumJsonOutput: sel("drum-json-output"),
|
|
18079
|
-
drumJsonStatus: sel("drum-json-status"),
|
|
18080
|
-
drumJsonText: sel("drum-json-text"),
|
|
18081
|
-
drumJsonCopyBtn: sel("drum-json-copy"),
|
|
18082
|
-
generateMmlBtn: sel("generate-mml"),
|
|
18083
|
-
decomposeChordToggle: sel("decompose-chord"),
|
|
18084
|
-
ignoreChordHeavyToggle: sel("ignore-chord-heavy"),
|
|
18085
|
-
barLimitSelect: sel("bar-limit"),
|
|
18086
|
-
outputContainer: sel("output-container"),
|
|
18087
|
-
outputStatus: sel("output-status"),
|
|
18088
|
-
outputFull: sel("output-full"),
|
|
18089
|
-
outputMini: sel("output-mini"),
|
|
18090
|
-
copyFullBtn: sel("copy-full"),
|
|
18091
|
-
copyMiniBtn: sel("copy-mini"),
|
|
18092
|
-
overlay: sel("overlay"),
|
|
18093
|
-
mmlInfoBtn: sel("mml-info"),
|
|
18094
|
-
edoSelect: sel("edo-select"),
|
|
18095
|
-
edoInfoBtn: sel("edo-info"),
|
|
18096
|
-
modalOverlay: sel("modal-overlay"),
|
|
18097
|
-
modalTitle: sel("modal-title"),
|
|
18098
|
-
modalBody: sel("modal-body"),
|
|
18099
|
-
modalClose: sel("modal-close")
|
|
18100
|
-
};
|
|
18101
|
-
};
|
|
18102
|
-
|
|
18103
|
-
// src/instrument-presets.ts
|
|
18104
|
-
var INSTRUMENT_PRESETS = {
|
|
18105
|
-
// --- STANDARD: 汎用性と完成度重視 ---
|
|
18106
|
-
piano: {
|
|
18107
|
-
displayName: "\u30B0\u30E9\u30F3\u30C9\u30D4\u30A2\u30CE",
|
|
18108
|
-
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",
|
|
18109
|
-
melody: "Acoustic Grand Piano",
|
|
18110
|
-
submelody: "Vibraphone",
|
|
18111
|
-
bass: "Electric Bass (finger)",
|
|
18112
|
-
chord: "Pad 2 (warm)",
|
|
18113
|
-
solo: "Electric Guitar (clean)",
|
|
18114
|
-
// グロッケンは実物の音域が G5(79)〜 の高音楽器で、旋律の音域へそのまま置くと
|
|
18115
|
-
// 金切り音になる。**外すのではなくオクターブで下げて使う**
|
|
18116
|
-
// ({@link GM_BRIGHT_CEILING} / {@link fitInstrumentOctave})。
|
|
18117
|
-
chorusLead: "Glockenspiel"
|
|
18118
|
-
},
|
|
18119
|
-
acoustic: {
|
|
18120
|
-
displayName: "\u30A2\u30B3\u30FC\u30B9\u30C6\u30A3\u30C3\u30AF",
|
|
18121
|
-
description: "\u751F\u697D\u5668\u306E\u6E29\u304B\u307F\u3092\u91CD\u8996\u3002\u30D5\u30A9\u30FC\u30AF\u3084\u30DD\u30C3\u30D7\u30B9\u306B\u3002",
|
|
18122
|
-
melody: "Acoustic Guitar (steel)",
|
|
18123
|
-
submelody: "Harmonica",
|
|
18124
|
-
bass: "Acoustic Bass",
|
|
18125
|
-
chord: "Acoustic Guitar (nylon)",
|
|
18126
|
-
solo: "Overdriven Guitar",
|
|
18127
|
-
chorusLead: "String Ensemble 1"
|
|
18128
|
-
},
|
|
18129
|
-
jazz_night: {
|
|
18130
|
-
displayName: "\u30B8\u30E3\u30BA\u30FB\u30CA\u30A4\u30C8",
|
|
18131
|
-
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",
|
|
18132
|
-
melody: "Electric Piano 1",
|
|
18133
|
-
submelody: "Flute",
|
|
18134
|
-
bass: "Acoustic Bass",
|
|
18135
|
-
chord: "Electric Guitar (jazz)",
|
|
18136
|
-
solo: "Tenor Sax",
|
|
18137
|
-
chorusLead: "Muted Trumpet"
|
|
18138
|
-
},
|
|
18139
|
-
// --- MODERN & VIBE: エッジの効いた現代的な響き ---
|
|
18140
|
-
synth_pop: {
|
|
18141
|
-
displayName: "\u30B7\u30F3\u30BB\u30DD\u30C3\u30D7",
|
|
18142
|
-
description: "80s\u301C\u73FE\u4EE3\u307E\u3067\u3002\u629C\u3051\u308B\u30EA\u30FC\u30C9\u3068\u592A\u3044\u30D9\u30FC\u30B9\u306E\u738B\u9053\u3002",
|
|
18143
|
-
melody: "Lead 2 (sawtooth)",
|
|
18144
|
-
submelody: "Lead 4 (chiff)",
|
|
18145
|
-
bass: "Synth Bass 2",
|
|
18146
|
-
chord: "Pad 3 (polysynth)",
|
|
18147
|
-
solo: "Distortion Guitar",
|
|
18148
|
-
chorusLead: "Synth Brass 1"
|
|
18149
|
-
},
|
|
18150
|
-
cyber_punk: {
|
|
18151
|
-
displayName: "\u30B5\u30A4\u30D0\u30FC\u30D1\u30F3\u30AF",
|
|
18152
|
-
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",
|
|
18153
|
-
melody: "Lead 8 (bass + lead)",
|
|
18154
|
-
submelody: "Lead 5 (charang)",
|
|
18155
|
-
bass: "Synth Bass 2",
|
|
18156
|
-
chord: "Pad 8 (sweep)",
|
|
18157
|
-
solo: "Distortion Guitar",
|
|
18158
|
-
chorusLead: "Lead 7 (fifths)"
|
|
18159
|
-
},
|
|
18160
|
-
rock: {
|
|
18161
|
-
displayName: "\u30CF\u30FC\u30C9\u30ED\u30C3\u30AF",
|
|
18162
|
-
description: "\u6B6A\u307F\u30AE\u30BF\u30FC\u3068\u91CD\u539A\u306A\u30D9\u30FC\u30B9\u3067\u3001\u30D1\u30EF\u30FC\u3092\u524D\u9762\u306B\u3002",
|
|
18163
|
-
melody: "Distortion Guitar",
|
|
18164
|
-
submelody: "Rock Organ",
|
|
18165
|
-
bass: "Electric Bass (pick)",
|
|
18166
|
-
chord: "Overdriven Guitar",
|
|
18167
|
-
solo: "Distortion Guitar",
|
|
18168
|
-
chorusLead: "Brass Section"
|
|
18169
|
-
},
|
|
18170
|
-
// --- WORLD & CLASSIC: 特定のジャンル・地域 ---
|
|
18171
|
-
orchestra: {
|
|
18172
|
-
displayName: "\u30AA\u30FC\u30B1\u30B9\u30C8\u30E9",
|
|
18173
|
-
description: "\u58EE\u5927\u306A\u7269\u8A9E\u3092\u4E88\u611F\u3055\u305B\u308B\u3001\u7BA1\u5F26\u697D\u5668\u306E\u91CD\u539A\u306A\u97FF\u304D\u3002",
|
|
18174
|
-
melody: "French Horn",
|
|
18175
|
-
submelody: "Pizzicato Strings",
|
|
18176
|
-
bass: "Cello",
|
|
18177
|
-
chord: "Tremolo Strings",
|
|
18178
|
-
solo: "Violin",
|
|
18179
|
-
chorusLead: "Trumpet"
|
|
18180
|
-
},
|
|
18181
|
-
japanese_wa: {
|
|
18182
|
-
displayName: "\u548C\u98A8\u30FB\u96C5",
|
|
18183
|
-
description: "\u7434\u3068\u4E09\u5473\u7DDA\u306E\u7E4A\u7D30\u306A\u8ABF\u3079\u306B\u3001\u5C3A\u516B\u306E\u60C5\u7DD2\u3092\u6DFB\u3048\u3066\u3002",
|
|
18184
|
-
melody: "Koto",
|
|
18185
|
-
submelody: "Shamisen",
|
|
18186
|
-
bass: "Taiko Drum",
|
|
18187
|
-
chord: "Shakuhachi",
|
|
18188
|
-
solo: "Shakuhachi",
|
|
18189
|
-
chorusLead: "Glockenspiel"
|
|
18190
|
-
},
|
|
18191
|
-
arabic_exotic: {
|
|
18192
|
-
displayName: "\u30A8\u30AD\u30BE\u30C1\u30C3\u30AF",
|
|
18193
|
-
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",
|
|
18194
|
-
melody: "Sitar",
|
|
18195
|
-
submelody: "Bagpipe",
|
|
18196
|
-
bass: "Fretless Bass",
|
|
18197
|
-
chord: "Kalimba",
|
|
18198
|
-
solo: "Shanai",
|
|
18199
|
-
chorusLead: "Steel Drums"
|
|
18200
|
-
},
|
|
18201
|
-
// --- FANTASY & ATMOSPHERE: 雰囲気と余韻 ---
|
|
18202
|
-
fantasy_rpg: {
|
|
18203
|
-
displayName: "\u30D5\u30A1\u30F3\u30BF\u30B8\u30FCRPG",
|
|
18204
|
-
description: "\u30AA\u30AB\u30EA\u30CA\u3068\u30CF\u30FC\u30D7\u304C\u7D21\u3050\u3001\u5192\u967A\u3068\u9B54\u6CD5\u306E\u4E16\u754C\u89B3\u3002",
|
|
18205
|
-
melody: "Ocarina",
|
|
18206
|
-
submelody: "Celesta",
|
|
18207
|
-
bass: "Timpani",
|
|
18208
|
-
chord: "Orchestral Harp",
|
|
18209
|
-
solo: "Pan Flute",
|
|
18210
|
-
chorusLead: "Choir Aahs"
|
|
18211
|
-
},
|
|
18212
|
-
ambient_cloud: {
|
|
18213
|
-
displayName: "\u30A2\u30F3\u30D3\u30A8\u30F3\u30C8",
|
|
18214
|
-
description: "\u8F2A\u90ED\u3092\u307C\u304B\u3057\u305F\u97F3\u8272\u3067\u3001\u6DF1\u3044\u6CA1\u5165\u611F\u3068\u4F59\u97FB\u3092\u6F14\u51FA\u3002",
|
|
18215
|
-
melody: "Lead 6 (voice)",
|
|
18216
|
-
submelody: "Music Box",
|
|
18217
|
-
bass: "Synth Bass 1",
|
|
18218
|
-
chord: "Pad 7 (halo)",
|
|
18219
|
-
solo: "Lead 3 (calliope)",
|
|
18220
|
-
chorusLead: "Synth Choir"
|
|
18221
|
-
},
|
|
18222
|
-
retro_game: {
|
|
18223
|
-
displayName: "8-bit \u30EC\u30C8\u30ED",
|
|
18224
|
-
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",
|
|
18225
|
-
melody: "Lead 1 (square)",
|
|
18226
|
-
submelody: "Lead 2 (sawtooth)",
|
|
18227
|
-
bass: "Synth Bass 1",
|
|
18228
|
-
chord: "Clavinet",
|
|
18229
|
-
solo: "Lead 8 (bass + lead)",
|
|
18230
|
-
chorusLead: "Lead 4 (chiff)"
|
|
18231
|
-
}
|
|
18232
|
-
};
|
|
18233
|
-
var GM_INSTRUMENT_RANGE = {
|
|
18234
|
-
// 鍵盤・音板
|
|
18235
|
-
"Acoustic Grand Piano": [21, 108],
|
|
18236
|
-
"Electric Piano 1": [28, 103],
|
|
18237
|
-
Clavinet: [36, 96],
|
|
18238
|
-
Celesta: [60, 108],
|
|
18239
|
-
Glockenspiel: [79, 108],
|
|
18240
|
-
"Music Box": [72, 108],
|
|
18241
|
-
Vibraphone: [53, 89],
|
|
18242
|
-
Kalimba: [60, 84],
|
|
18243
|
-
"Orchestral Harp": [23, 104],
|
|
18244
|
-
// 弦・撥弦
|
|
18245
|
-
"Acoustic Guitar (steel)": [40, 83],
|
|
18246
|
-
"Acoustic Guitar (nylon)": [40, 83],
|
|
18247
|
-
"Electric Guitar (clean)": [40, 86],
|
|
18248
|
-
"Electric Guitar (jazz)": [40, 86],
|
|
18249
|
-
"Overdriven Guitar": [40, 88],
|
|
18250
|
-
"Distortion Guitar": [40, 88],
|
|
18251
|
-
Violin: [55, 103],
|
|
18252
|
-
Cello: [36, 76],
|
|
18253
|
-
"String Ensemble 1": [28, 100],
|
|
18254
|
-
"Tremolo Strings": [28, 100],
|
|
18255
|
-
"Pizzicato Strings": [28, 96],
|
|
18256
|
-
Sitar: [48, 79],
|
|
18257
|
-
Shamisen: [48, 84],
|
|
18258
|
-
Koto: [41, 77],
|
|
18259
|
-
// ベース
|
|
18260
|
-
"Acoustic Bass": [28, 60],
|
|
18261
|
-
"Electric Bass (finger)": [28, 67],
|
|
18262
|
-
"Electric Bass (pick)": [28, 67],
|
|
18263
|
-
"Fretless Bass": [28, 67],
|
|
18264
|
-
"Synth Bass 1": [24, 72],
|
|
18265
|
-
"Synth Bass 2": [24, 72],
|
|
18266
|
-
// 管
|
|
18267
|
-
Flute: [60, 96],
|
|
18268
|
-
"Pan Flute": [60, 91],
|
|
18269
|
-
Shakuhachi: [62, 86],
|
|
18270
|
-
Ocarina: [60, 84],
|
|
18271
|
-
Harmonica: [60, 96],
|
|
18272
|
-
Bagpipe: [62, 86],
|
|
18273
|
-
Shanai: [60, 86],
|
|
18274
|
-
"Tenor Sax": [44, 75],
|
|
18275
|
-
Trumpet: [55, 82],
|
|
18276
|
-
"Muted Trumpet": [55, 82],
|
|
18277
|
-
"French Horn": [41, 77],
|
|
18278
|
-
"Brass Section": [41, 84],
|
|
18279
|
-
// 声・打・オルガン
|
|
18280
|
-
"Choir Aahs": [43, 84],
|
|
18281
|
-
"Synth Choir": [43, 84],
|
|
18282
|
-
"Steel Drums": [55, 86],
|
|
18283
|
-
"Taiko Drum": [30, 60],
|
|
18284
|
-
Timpani: [36, 57],
|
|
18285
|
-
"Rock Organ": [36, 96],
|
|
18286
|
-
// シンセ(実物が無いので一般的な使用域)
|
|
18287
|
-
"Synth Brass 1": [36, 96],
|
|
18288
|
-
"Lead 1 (square)": [36, 96],
|
|
18289
|
-
"Lead 2 (sawtooth)": [36, 96],
|
|
18290
|
-
"Lead 3 (calliope)": [48, 96],
|
|
18291
|
-
"Lead 4 (chiff)": [48, 96],
|
|
18292
|
-
"Lead 5 (charang)": [40, 96],
|
|
18293
|
-
"Lead 6 (voice)": [43, 91],
|
|
18294
|
-
"Lead 7 (fifths)": [36, 84],
|
|
18295
|
-
"Lead 8 (bass + lead)": [28, 91],
|
|
18296
|
-
"Pad 2 (warm)": [24, 96],
|
|
18297
|
-
"Pad 3 (polysynth)": [24, 96],
|
|
18298
|
-
"Pad 7 (halo)": [24, 96],
|
|
18299
|
-
"Pad 8 (sweep)": [24, 96]
|
|
18300
|
-
};
|
|
18301
|
-
var GM_BRIGHT_CEILING = {
|
|
18302
|
-
Glockenspiel: 72,
|
|
18303
|
-
"Tinkle Bell": 72,
|
|
18304
|
-
"Music Box": 79,
|
|
18305
|
-
Celesta: 84,
|
|
18306
|
-
Kalimba: 79,
|
|
18307
|
-
"Steel Drums": 84,
|
|
18308
|
-
"FX 3 (crystal)": 79
|
|
18309
|
-
};
|
|
18310
|
-
var OCTAVE_FIT_TOLERANCE = 3;
|
|
18311
|
-
var fitInstrumentOctave = (semitoneRange, instrument, wanted) => {
|
|
18312
|
-
const range = GM_INSTRUMENT_RANGE[instrument];
|
|
18313
|
-
if (!range || !semitoneRange) return wanted;
|
|
18314
|
-
const hi = Math.min(range[1], GM_BRIGHT_CEILING[instrument] ?? range[1]);
|
|
18315
|
-
let octave = wanted;
|
|
18316
|
-
while (octave > wanted - 2) {
|
|
18317
|
-
if (semitoneRange[1] + octave * 12 <= hi + OCTAVE_FIT_TOLERANCE) break;
|
|
18318
|
-
octave--;
|
|
18319
|
-
}
|
|
18320
|
-
return octave;
|
|
18612
|
+
</div>
|
|
18613
|
+
<div class="dtm-modal-body" data-dtm="modal-body"></div>
|
|
18614
|
+
</div>
|
|
18615
|
+
</div>
|
|
18616
|
+
|
|
18617
|
+
</div>`;
|
|
18618
|
+
const root = q(target, '[data-dtm="root"]');
|
|
18619
|
+
persistPanels(root);
|
|
18620
|
+
const sel = (name) => q(root, `[data-dtm="${name}"]`);
|
|
18621
|
+
return {
|
|
18622
|
+
root,
|
|
18623
|
+
topbar: sel("transport"),
|
|
18624
|
+
topbarLoading: sel("topbar-loading"),
|
|
18625
|
+
playBtn: sel("play"),
|
|
18626
|
+
prevBarBtn: sel("prev-bar"),
|
|
18627
|
+
nextBarBtn: sel("next-bar"),
|
|
18628
|
+
soloCheckbox: sel("solo"),
|
|
18629
|
+
clipBadge: sel("clip-badge"),
|
|
18630
|
+
toolPen: sel("tool-pen"),
|
|
18631
|
+
toolSelect: sel("tool-select"),
|
|
18632
|
+
toolEraser: sel("tool-eraser"),
|
|
18633
|
+
undoBtn: sel("undo"),
|
|
18634
|
+
redoBtn: sel("redo"),
|
|
18635
|
+
noteLengthSelect: sel("note-length"),
|
|
18636
|
+
bpmInput: sel("bpm"),
|
|
18637
|
+
zoomXLabel: sel("zoomx-label"),
|
|
18638
|
+
zoomYLabel: sel("zoomy-label"),
|
|
18639
|
+
zoomXIn: sel("zoomx-in"),
|
|
18640
|
+
zoomXOut: sel("zoomx-out"),
|
|
18641
|
+
zoomYIn: sel("zoomy-in"),
|
|
18642
|
+
zoomYOut: sel("zoomy-out"),
|
|
18643
|
+
bgFileInput: sel("bg-file-input"),
|
|
18644
|
+
bgUploadBtn: sel("bg-upload"),
|
|
18645
|
+
bgRemoveBtn: sel("bg-remove"),
|
|
18646
|
+
bgOpacityInput: sel("bg-opacity"),
|
|
18647
|
+
bgOpacityRow: sel("bg-opacity-row"),
|
|
18648
|
+
rollContainer: sel("roll"),
|
|
18649
|
+
wrapper: sel("wrapper"),
|
|
18650
|
+
vScroll: sel("vscroll"),
|
|
18651
|
+
vScrollThumb: sel("vscroll-thumb"),
|
|
18652
|
+
hScroll: sel("hscroll"),
|
|
18653
|
+
hScrollThumb: sel("hscroll-thumb"),
|
|
18654
|
+
loopToggle: sel("loop-toggle"),
|
|
18655
|
+
loopToggleLabel: sel("loop-toggle-label"),
|
|
18656
|
+
loopInfoBtn: sel("loop-info"),
|
|
18657
|
+
masterVolume: sel("master-volume"),
|
|
18658
|
+
masterVolumeLabel: sel("master-volume-label"),
|
|
18659
|
+
masterComp: sel("master-comp"),
|
|
18660
|
+
masterCompLabel: sel("master-comp-label"),
|
|
18661
|
+
masterCompInfoBtn: sel("master-comp-info"),
|
|
18662
|
+
reverbAmount: sel("reverb-amount"),
|
|
18663
|
+
reverbAmountLabel: sel("reverb-amount-label"),
|
|
18664
|
+
reverbAmountInfoBtn: sel("reverb-amount-info"),
|
|
18665
|
+
reverbDecay: sel("reverb-decay"),
|
|
18666
|
+
reverbDecayLabel: sel("reverb-decay-label"),
|
|
18667
|
+
reverbPreDelay: sel("reverb-predelay"),
|
|
18668
|
+
reverbPreDelayLabel: sel("reverb-predelay-label"),
|
|
18669
|
+
delayAmount: sel("delay-amount"),
|
|
18670
|
+
delayAmountLabel: sel("delay-amount-label"),
|
|
18671
|
+
delayAmountInfoBtn: sel("delay-amount-info"),
|
|
18672
|
+
delayDivision: sel("delay-division"),
|
|
18673
|
+
fadeIn: sel("fade-in"),
|
|
18674
|
+
fadeInLabel: sel("fade-in-label"),
|
|
18675
|
+
fadeOut: sel("fade-out"),
|
|
18676
|
+
fadeOutLabel: sel("fade-out-label"),
|
|
18677
|
+
fadeInfoBtn: sel("fade-info"),
|
|
18678
|
+
autoMasterBtn: sel("auto-master"),
|
|
18679
|
+
autoMasterInfoBtn: sel("auto-master-info"),
|
|
18680
|
+
trackTabs: sel("track-tabs"),
|
|
18681
|
+
trackBody: sel("track-body"),
|
|
18682
|
+
drumSelect: sel("drum-select"),
|
|
18683
|
+
drumFontSelect: sel("drum-font-select"),
|
|
18684
|
+
drumVolume: sel("drum-volume"),
|
|
18685
|
+
drumVolumeLabel: sel("drum-volume-label"),
|
|
18686
|
+
midiInput: sel("midi-input"),
|
|
18687
|
+
midiLoadBtn: sel("midi-load"),
|
|
18688
|
+
midiInfoBtn: sel("midi-info"),
|
|
18689
|
+
midiTrackSelection: sel("midi-track-selection"),
|
|
18690
|
+
midiPanel: sel("midi-panel"),
|
|
18691
|
+
midiSearchOpenBtn: sel("midi-search-open"),
|
|
18692
|
+
mmlInput: sel("mml-input"),
|
|
18693
|
+
mmlLoadBtn: sel("mml-load"),
|
|
18694
|
+
mmlLoadNote: sel("mml-load-note"),
|
|
18695
|
+
applyActiveOnly: sel("apply-active-only"),
|
|
18696
|
+
shiftSelect: sel("shift-select"),
|
|
18697
|
+
shiftApplyBtn: sel("shift-apply"),
|
|
18698
|
+
transposeSelect: sel("transpose-select"),
|
|
18699
|
+
transposeApplyBtn: sel("transpose-apply"),
|
|
18700
|
+
transposeInfoBtn: sel("transpose-info"),
|
|
18701
|
+
macroCompose: sel("macro-compose"),
|
|
18702
|
+
composeTemplate: sel("compose-template"),
|
|
18703
|
+
composeSections: sel("compose-sections"),
|
|
18704
|
+
composeSectionsLen: sel("compose-sections-len"),
|
|
18705
|
+
composeKey: sel("compose-key"),
|
|
18706
|
+
composeKeyHint: sel("compose-key-hint"),
|
|
18707
|
+
composeScale: sel("compose-scale"),
|
|
18708
|
+
composeScaleHint: sel("compose-scale-hint"),
|
|
18709
|
+
macroComposeVocal: sel("macro-compose-vocal"),
|
|
18710
|
+
macroComposeInfo: sel("macro-compose-info"),
|
|
18711
|
+
macroClear: sel("macro-clear"),
|
|
18712
|
+
macroRandom: sel("macro-random"),
|
|
18713
|
+
macroHarmonic: sel("macro-harmonic"),
|
|
18714
|
+
macroMono: sel("macro-mono"),
|
|
18715
|
+
exportMidiBtn: sel("export-midi"),
|
|
18716
|
+
exportWavBtn: sel("export-wav"),
|
|
18717
|
+
drumJsonExportBtn: sel("drum-json-export"),
|
|
18718
|
+
drumJsonOutput: sel("drum-json-output"),
|
|
18719
|
+
drumJsonStatus: sel("drum-json-status"),
|
|
18720
|
+
drumJsonText: sel("drum-json-text"),
|
|
18721
|
+
drumJsonCopyBtn: sel("drum-json-copy"),
|
|
18722
|
+
generateMmlBtn: sel("generate-mml"),
|
|
18723
|
+
decomposeChordToggle: sel("decompose-chord"),
|
|
18724
|
+
ignoreChordHeavyToggle: sel("ignore-chord-heavy"),
|
|
18725
|
+
barLimitSelect: sel("bar-limit"),
|
|
18726
|
+
outputContainer: sel("output-container"),
|
|
18727
|
+
outputStatus: sel("output-status"),
|
|
18728
|
+
outputFull: sel("output-full"),
|
|
18729
|
+
outputMini: sel("output-mini"),
|
|
18730
|
+
copyFullBtn: sel("copy-full"),
|
|
18731
|
+
copyMiniBtn: sel("copy-mini"),
|
|
18732
|
+
overlay: sel("overlay"),
|
|
18733
|
+
mmlInfoBtn: sel("mml-info"),
|
|
18734
|
+
edoSelect: sel("edo-select"),
|
|
18735
|
+
edoInfoBtn: sel("edo-info"),
|
|
18736
|
+
modalOverlay: sel("modal-overlay"),
|
|
18737
|
+
modalTitle: sel("modal-title"),
|
|
18738
|
+
modalBody: sel("modal-body"),
|
|
18739
|
+
modalClose: sel("modal-close")
|
|
18740
|
+
};
|
|
18321
18741
|
};
|
|
18322
18742
|
|
|
18323
18743
|
// src/macro-state.ts
|
|
@@ -20782,128 +21202,6 @@ var pickComposeVocal = (exclude) => {
|
|
|
20782
21202
|
const list = pool.length > 0 ? pool : COMPOSE_VOCAL_POOL;
|
|
20783
21203
|
return list[Math.floor(Math.random() * list.length)] ?? "klatt";
|
|
20784
21204
|
};
|
|
20785
|
-
var buildAdvancedLayers = (song, config) => {
|
|
20786
|
-
const { edo, stepsPerBar, preset } = config;
|
|
20787
|
-
const semitoneRange = (notes) => {
|
|
20788
|
-
if (notes.length === 0) return null;
|
|
20789
|
-
let lo = Number.POSITIVE_INFINITY;
|
|
20790
|
-
let hi = Number.NEGATIVE_INFINITY;
|
|
20791
|
-
for (const n of notes) {
|
|
20792
|
-
const semi = n.pitchUnits / UNITS_PER_SEMITONE;
|
|
20793
|
-
if (semi < lo) lo = semi;
|
|
20794
|
-
if (semi > hi) hi = semi;
|
|
20795
|
-
}
|
|
20796
|
-
return [Math.round(lo), Math.round(hi)];
|
|
20797
|
-
};
|
|
20798
|
-
const fit = (notes, slot, wanted) => fitInstrumentOctave(semitoneRange(notes), preset[slot], wanted);
|
|
20799
|
-
const kindAtBar = (bar) => song.sections.find(
|
|
20800
|
-
(sec) => bar >= sec.startBar && bar < sec.startBar + sec.bars
|
|
20801
|
-
)?.kind ?? null;
|
|
20802
|
-
const onlyIn = (notes, kinds) => {
|
|
20803
|
-
if (!kinds) return notes;
|
|
20804
|
-
const want = new Set(kinds);
|
|
20805
|
-
return notes.filter((n) => {
|
|
20806
|
-
const kind = kindAtBar(Math.floor(n.startStep / stepsPerBar));
|
|
20807
|
-
return kind !== null && want.has(kind);
|
|
20808
|
-
});
|
|
20809
|
-
};
|
|
20810
|
-
const chordNotes = (pattern, velocityShift = 0) => buildChordPlacements({
|
|
20811
|
-
edo,
|
|
20812
|
-
chordStr: song.chordProgression,
|
|
20813
|
-
patternType: pattern,
|
|
20814
|
-
rootShift: song.rootShift,
|
|
20815
|
-
bpm: song.bpm,
|
|
20816
|
-
stepsPerBar
|
|
20817
|
-
}).map((p) => ({
|
|
20818
|
-
startStep: p.startStep,
|
|
20819
|
-
pitchUnits: p.pitchUnits,
|
|
20820
|
-
durationSteps: p.durationSteps,
|
|
20821
|
-
velocity: Math.max(30, p.velocity + velocityShift)
|
|
20822
|
-
}));
|
|
20823
|
-
const plan = song.arrange;
|
|
20824
|
-
const leadNotes = plan.lead ? onlyIn(song.melody, plan.lead.sections) : [];
|
|
20825
|
-
const leadOctave = fit(leadNotes, "chorusLead", plan.lead?.octave ?? 0);
|
|
20826
|
-
const leadLayer = {
|
|
20827
|
-
index: 1,
|
|
20828
|
-
notes: leadNotes,
|
|
20829
|
-
octave: leadOctave,
|
|
20830
|
-
// ユニゾンで重ねるときは、オクターブ上より前に出やすいので少し引く。
|
|
20831
|
-
volume: leadOctave === 0 ? 54 : 62,
|
|
20832
|
-
slot: "chorusLead"
|
|
20833
|
-
};
|
|
20834
|
-
const bassNotes = plan.bassLayer ? onlyIn(song.bass, plan.bassLayer.sections) : [];
|
|
20835
|
-
const bassOctave = fit(bassNotes, "bass", plan.bassLayer?.octave ?? 0);
|
|
20836
|
-
const bassLayer = {
|
|
20837
|
-
index: 5,
|
|
20838
|
-
notes: bassOctave === 0 ? [] : bassNotes,
|
|
20839
|
-
octave: bassOctave,
|
|
20840
|
-
volume: 58,
|
|
20841
|
-
slot: "bass"
|
|
20842
|
-
};
|
|
20843
|
-
const padNotes = onlyIn(song.pad, plan.padSections);
|
|
20844
|
-
const layers = [
|
|
20845
|
-
{ index: 0, notes: song.melody, octave: 0, volume: 104, slot: "melody" },
|
|
20846
|
-
leadLayer,
|
|
20847
|
-
{ index: 2, notes: song.harmony, octave: 0, volume: 82 },
|
|
20848
|
-
{
|
|
20849
|
-
index: 3,
|
|
20850
|
-
notes: song.submelody,
|
|
20851
|
-
octave: 0,
|
|
20852
|
-
volume: 86,
|
|
20853
|
-
slot: "submelody"
|
|
20854
|
-
},
|
|
20855
|
-
{ index: 4, notes: song.bass, octave: 0, volume: 92, slot: "bass" },
|
|
20856
|
-
bassLayer,
|
|
20857
|
-
// **パッドは伴奏用の楽器で、伴奏より1.5オクターブ高いところを鳴らす。**
|
|
20858
|
-
// そのまま置くとナイロンギターで10半音、カリンバで9半音、尺八で7半音ぶん
|
|
20859
|
-
// 音域を突き抜ける。楽器に合わせて下げる。
|
|
20860
|
-
{
|
|
20861
|
-
index: 6,
|
|
20862
|
-
notes: padNotes,
|
|
20863
|
-
octave: fit(padNotes, "chord", 0),
|
|
20864
|
-
volume: 64,
|
|
20865
|
-
slot: "chord"
|
|
20866
|
-
}
|
|
20867
|
-
];
|
|
20868
|
-
const backingVolumes = [62, 54, 50];
|
|
20869
|
-
for (let i2 = 0; i2 < 3; i2++) {
|
|
20870
|
-
const layer = plan.backing[i2];
|
|
20871
|
-
layers.push({
|
|
20872
|
-
index: 7 + i2,
|
|
20873
|
-
notes: layer ? onlyIn(chordNotes(layer.pattern), layer.sections) : [],
|
|
20874
|
-
octave: layer?.octave ?? 0,
|
|
20875
|
-
volume: backingVolumes[i2],
|
|
20876
|
-
slot: "chord"
|
|
20877
|
-
});
|
|
20878
|
-
}
|
|
20879
|
-
const sparkleNotes = plan.sparkle ? onlyIn(chordNotes(plan.sparkle.pattern, -14), plan.sparkle.sections) : [];
|
|
20880
|
-
layers.push(
|
|
20881
|
-
{
|
|
20882
|
-
index: 10,
|
|
20883
|
-
notes: sparkleNotes,
|
|
20884
|
-
octave: fit(sparkleNotes, "chord", plan.sparkle?.octave ?? 0),
|
|
20885
|
-
volume: 56,
|
|
20886
|
-
slot: "chord"
|
|
20887
|
-
},
|
|
20888
|
-
// 掛け合い(デュエット)の相手。**歌入り作曲のときだけ**中身が入る。
|
|
20889
|
-
{ index: 11, notes: [], octave: 0, volume: 104, slot: "melody" },
|
|
20890
|
-
// 2声目のハモリ(主旋律を上下から挟む3声)と、主旋律のオクターブ下の重ね。
|
|
20891
|
-
// どちらも曲ごとに出るかどうかが決まる(`song.vocal`)。
|
|
20892
|
-
{ index: 12, notes: song.harmony2, octave: 0, volume: 74 },
|
|
20893
|
-
{ index: 13, notes: song.octave, octave: -1, volume: 56, slot: "melody" },
|
|
20894
|
-
// **間奏のソロ。** 音が入るのは間奏の小節だけなので、この1本だけを
|
|
20895
|
-
// 別の楽器にしても他のセクションの鳴りは変わらない。歌の音域をそのまま
|
|
20896
|
-
// 渡すと管楽器が上へ抜ける(テナーサックスで10半音)ので、ここも合わせる。
|
|
20897
|
-
{
|
|
20898
|
-
index: 14,
|
|
20899
|
-
notes: song.solo,
|
|
20900
|
-
octave: fit(song.solo, "solo", 0),
|
|
20901
|
-
volume: 100,
|
|
20902
|
-
slot: "solo"
|
|
20903
|
-
}
|
|
20904
|
-
);
|
|
20905
|
-
return layers;
|
|
20906
|
-
};
|
|
20907
21205
|
var LYRIC_MODEL_CATEGORIES = [
|
|
20908
21206
|
{
|
|
20909
21207
|
label: "kusa\u30D7\u30EA\u30BB\u30C3\u30C8",
|
|
@@ -26052,8 +26350,9 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
26052
26350
|
});
|
|
26053
26351
|
const ownsCtx = !options.audioContext;
|
|
26054
26352
|
const ctx = options.audioContext ?? new AudioContext();
|
|
26055
|
-
const
|
|
26353
|
+
const rawDestination = options.destination ?? ctx.destination;
|
|
26056
26354
|
const useSynth = options.synth ?? !options.onPlayNote;
|
|
26355
|
+
const destination = createSafetyLimiter(ctx, rawDestination);
|
|
26057
26356
|
const synth = useSynth ? createSynth(ctx, destination) : null;
|
|
26058
26357
|
const pauseWhenHidden = options.pauseWhenHidden ?? ownsCtx;
|
|
26059
26358
|
let playing = false;
|
|
@@ -27059,21 +27358,29 @@ var createDtmStudio = async (options = {}) => {
|
|
|
27059
27358
|
};
|
|
27060
27359
|
applyGlueCompression(options.masterCompression ?? 0);
|
|
27061
27360
|
const setMasterCompression = (amount) => applyGlueCompression(amount);
|
|
27062
|
-
const safetyLimiter = audioCtx.createDynamicsCompressor();
|
|
27063
|
-
safetyLimiter.threshold.value = -1;
|
|
27064
|
-
safetyLimiter.knee.value = 0;
|
|
27065
|
-
safetyLimiter.ratio.value = 20;
|
|
27066
|
-
safetyLimiter.attack.value = 1e-3;
|
|
27067
|
-
safetyLimiter.release.value = 0.1;
|
|
27068
|
-
glueMakeup.connect(safetyLimiter);
|
|
27069
27361
|
const fadeGain = audioCtx.createGain();
|
|
27070
27362
|
fadeGain.gain.value = 1;
|
|
27071
|
-
safetyLimiter.connect(fadeGain);
|
|
27072
27363
|
fadeGain.connect(options.destination ?? audioCtx.destination);
|
|
27364
|
+
const safetyLimiter = createSafetyLimiter(audioCtx, fadeGain);
|
|
27365
|
+
glueMakeup.connect(safetyLimiter);
|
|
27366
|
+
const FADE_RESTORE_SEC = 0.02;
|
|
27367
|
+
const rampFadeGainToUnity = (from, at) => {
|
|
27368
|
+
fadeGain.gain.setValueAtTime(from, at);
|
|
27369
|
+
fadeGain.gain.linearRampToValueAtTime(1, at + FADE_RESTORE_SEC);
|
|
27370
|
+
};
|
|
27371
|
+
const restoreFadeGainIfMuted = () => {
|
|
27372
|
+
const current = fadeGain.gain.value;
|
|
27373
|
+
if (current >= 1) return;
|
|
27374
|
+
const now = audioCtx.currentTime;
|
|
27375
|
+
fadeGain.gain.cancelScheduledValues(now);
|
|
27376
|
+
rampFadeGainToUnity(current, now);
|
|
27377
|
+
};
|
|
27073
27378
|
const scheduleFade = (params) => {
|
|
27074
|
-
|
|
27379
|
+
const now = audioCtx.currentTime;
|
|
27380
|
+
const current = fadeGain.gain.value;
|
|
27381
|
+
fadeGain.gain.cancelScheduledValues(now);
|
|
27075
27382
|
if (!params) {
|
|
27076
|
-
|
|
27383
|
+
rampFadeGainToUnity(current, now);
|
|
27077
27384
|
return;
|
|
27078
27385
|
}
|
|
27079
27386
|
const { fadeInStartAt, fadeInEndAt, fadeOutStartAt, fadeOutEndAt } = params;
|
|
@@ -27081,13 +27388,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
27081
27388
|
fadeGain.gain.setValueAtTime(0, fadeInStartAt);
|
|
27082
27389
|
fadeGain.gain.linearRampToValueAtTime(1, fadeInEndAt);
|
|
27083
27390
|
} else {
|
|
27084
|
-
|
|
27391
|
+
rampFadeGainToUnity(current, now);
|
|
27085
27392
|
}
|
|
27086
27393
|
if (fadeOutStartAt !== void 0 && fadeOutEndAt !== void 0) {
|
|
27087
27394
|
fadeGain.gain.setValueAtTime(1, fadeOutStartAt);
|
|
27088
27395
|
fadeGain.gain.linearRampToValueAtTime(0, fadeOutEndAt);
|
|
27089
|
-
|
|
27090
|
-
fadeGain.gain.setValueAtTime(1, fadeOutEndAt + 2);
|
|
27396
|
+
rampFadeGainToUnity(0, fadeOutEndAt + 2);
|
|
27091
27397
|
}
|
|
27092
27398
|
};
|
|
27093
27399
|
const clipMeter = createClipMeter(audioCtx, glueMakeup);
|
|
@@ -27147,8 +27453,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
27147
27453
|
};
|
|
27148
27454
|
};
|
|
27149
27455
|
const resumeAudio = () => {
|
|
27150
|
-
|
|
27151
|
-
fadeGain.gain.setValueAtTime(1, audioCtx.currentTime);
|
|
27456
|
+
restoreFadeGainIfMuted();
|
|
27152
27457
|
if (audioCtx.state === "closed") return Promise.resolve();
|
|
27153
27458
|
return audioCtx.resume();
|
|
27154
27459
|
};
|
|
@@ -27359,12 +27664,6 @@ var createDtmStudio = async (options = {}) => {
|
|
|
27359
27664
|
await listReady;
|
|
27360
27665
|
nameToKey = await buildNameToKeyMapping();
|
|
27361
27666
|
await Promise.all([drumReady, loadPreset(defaultPreset)]);
|
|
27362
|
-
const restoreFadeGainIfMuted = () => {
|
|
27363
|
-
if (fadeGain.gain.value < 1) {
|
|
27364
|
-
fadeGain.gain.cancelScheduledValues(audioCtx.currentTime);
|
|
27365
|
-
fadeGain.gain.setValueAtTime(1, audioCtx.currentTime);
|
|
27366
|
-
}
|
|
27367
|
-
};
|
|
27368
27667
|
const playDrum = (e) => {
|
|
27369
27668
|
if (!sfDrum.font) return;
|
|
27370
27669
|
if (e.when === 0) restoreFadeGainIfMuted();
|
|
@@ -28115,7 +28414,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
28115
28414
|
};
|
|
28116
28415
|
const setMasterVolume = (volume) => {
|
|
28117
28416
|
const g = Math.max(0, Math.min(100, volume)) / 100;
|
|
28118
|
-
masterGain.gain.
|
|
28417
|
+
masterGain.gain.setTargetAtTime(g, audioCtx.currentTime, 0.02);
|
|
28119
28418
|
};
|
|
28120
28419
|
let recordDestNode = null;
|
|
28121
28420
|
const createMediaStreamDestination = () => {
|