@onjmin/dtm 0.1.88 → 0.1.90
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 +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +325 -249
- package/dist/index.mjs +325 -249
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1466,6 +1466,149 @@ var getDrumPatternKeys = (name, dict) => {
|
|
|
1466
1466
|
return Array.from(keys);
|
|
1467
1467
|
};
|
|
1468
1468
|
|
|
1469
|
+
// src/channel-strip.ts
|
|
1470
|
+
var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
1471
|
+
var compressionParams = (amount) => {
|
|
1472
|
+
const t = clamp(amount, 0, 100) / 100;
|
|
1473
|
+
return {
|
|
1474
|
+
threshold: 0 + (-24 - 0) * t,
|
|
1475
|
+
ratio: 1 + (12 - 1) * t,
|
|
1476
|
+
knee: 0 + (6 - 0) * t,
|
|
1477
|
+
attack: 0.02 + (3e-3 - 0.02) * t,
|
|
1478
|
+
release: 0.25 + (0.15 - 0.25) * t
|
|
1479
|
+
};
|
|
1480
|
+
};
|
|
1481
|
+
var EQ_LOW_FREQ = 200;
|
|
1482
|
+
var EQ_MID_FREQ = 1e3;
|
|
1483
|
+
var EQ_HIGH_FREQ = 5e3;
|
|
1484
|
+
var EQ_MID_Q = 1;
|
|
1485
|
+
var EQ_MAX_DB = 12;
|
|
1486
|
+
var createChannelStrip = (ctx, destination, options = {}) => {
|
|
1487
|
+
const input = ctx.createGain();
|
|
1488
|
+
const eqLow = ctx.createBiquadFilter();
|
|
1489
|
+
eqLow.type = "lowshelf";
|
|
1490
|
+
eqLow.frequency.value = EQ_LOW_FREQ;
|
|
1491
|
+
eqLow.gain.value = clamp(options.eqLow ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
1492
|
+
const eqMid = ctx.createBiquadFilter();
|
|
1493
|
+
eqMid.type = "peaking";
|
|
1494
|
+
eqMid.frequency.value = EQ_MID_FREQ;
|
|
1495
|
+
eqMid.Q.value = EQ_MID_Q;
|
|
1496
|
+
eqMid.gain.value = clamp(options.eqMid ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
1497
|
+
const eqHigh = ctx.createBiquadFilter();
|
|
1498
|
+
eqHigh.type = "highshelf";
|
|
1499
|
+
eqHigh.frequency.value = EQ_HIGH_FREQ;
|
|
1500
|
+
eqHigh.gain.value = clamp(options.eqHigh ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
1501
|
+
input.connect(eqLow);
|
|
1502
|
+
eqLow.connect(eqMid);
|
|
1503
|
+
eqMid.connect(eqHigh);
|
|
1504
|
+
const compressor = ctx.createDynamicsCompressor();
|
|
1505
|
+
const applyCompression = (amount) => {
|
|
1506
|
+
const p = compressionParams(amount);
|
|
1507
|
+
const now = ctx.currentTime;
|
|
1508
|
+
compressor.threshold.setValueAtTime(p.threshold, now);
|
|
1509
|
+
compressor.ratio.setValueAtTime(p.ratio, now);
|
|
1510
|
+
compressor.knee.setValueAtTime(p.knee, now);
|
|
1511
|
+
compressor.attack.setValueAtTime(p.attack, now);
|
|
1512
|
+
compressor.release.setValueAtTime(p.release, now);
|
|
1513
|
+
};
|
|
1514
|
+
applyCompression(options.compression ?? 0);
|
|
1515
|
+
const splitter = ctx.createChannelSplitter(2);
|
|
1516
|
+
const mid = ctx.createGain();
|
|
1517
|
+
mid.gain.value = 0.5;
|
|
1518
|
+
splitter.connect(mid, 0);
|
|
1519
|
+
splitter.connect(mid, 1);
|
|
1520
|
+
const sideSum = ctx.createGain();
|
|
1521
|
+
sideSum.gain.value = 1;
|
|
1522
|
+
const sideL = ctx.createGain();
|
|
1523
|
+
sideL.gain.value = 0.5;
|
|
1524
|
+
const sideR = ctx.createGain();
|
|
1525
|
+
sideR.gain.value = -0.5;
|
|
1526
|
+
splitter.connect(sideL, 0);
|
|
1527
|
+
splitter.connect(sideR, 1);
|
|
1528
|
+
sideL.connect(sideSum);
|
|
1529
|
+
sideR.connect(sideSum);
|
|
1530
|
+
const widthGain = ctx.createGain();
|
|
1531
|
+
sideSum.connect(widthGain);
|
|
1532
|
+
const widthInv = ctx.createGain();
|
|
1533
|
+
widthInv.gain.value = -1;
|
|
1534
|
+
widthGain.connect(widthInv);
|
|
1535
|
+
const outL = ctx.createGain();
|
|
1536
|
+
mid.connect(outL);
|
|
1537
|
+
widthGain.connect(outL);
|
|
1538
|
+
const outR = ctx.createGain();
|
|
1539
|
+
mid.connect(outR);
|
|
1540
|
+
widthInv.connect(outR);
|
|
1541
|
+
const merger = ctx.createChannelMerger(2);
|
|
1542
|
+
outL.connect(merger, 0, 0);
|
|
1543
|
+
outR.connect(merger, 0, 1);
|
|
1544
|
+
const setWidth = (width) => {
|
|
1545
|
+
widthGain.gain.setTargetAtTime(
|
|
1546
|
+
clamp(width, 0, 200) / 100,
|
|
1547
|
+
ctx.currentTime,
|
|
1548
|
+
0.02
|
|
1549
|
+
);
|
|
1550
|
+
};
|
|
1551
|
+
setWidth(options.width ?? 100);
|
|
1552
|
+
eqHigh.connect(compressor);
|
|
1553
|
+
compressor.connect(splitter);
|
|
1554
|
+
merger.connect(destination);
|
|
1555
|
+
const reverbSendGain = ctx.createGain();
|
|
1556
|
+
reverbSendGain.gain.value = clamp(options.reverbSend ?? 0, 0, 100) / 100;
|
|
1557
|
+
merger.connect(reverbSendGain);
|
|
1558
|
+
if (options.reverbBus) reverbSendGain.connect(options.reverbBus);
|
|
1559
|
+
return {
|
|
1560
|
+
input,
|
|
1561
|
+
setEqLow: (db) => {
|
|
1562
|
+
eqLow.gain.setTargetAtTime(
|
|
1563
|
+
clamp(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
1564
|
+
ctx.currentTime,
|
|
1565
|
+
0.02
|
|
1566
|
+
);
|
|
1567
|
+
},
|
|
1568
|
+
setEqMid: (db) => {
|
|
1569
|
+
eqMid.gain.setTargetAtTime(
|
|
1570
|
+
clamp(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
1571
|
+
ctx.currentTime,
|
|
1572
|
+
0.02
|
|
1573
|
+
);
|
|
1574
|
+
},
|
|
1575
|
+
setEqHigh: (db) => {
|
|
1576
|
+
eqHigh.gain.setTargetAtTime(
|
|
1577
|
+
clamp(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
1578
|
+
ctx.currentTime,
|
|
1579
|
+
0.02
|
|
1580
|
+
);
|
|
1581
|
+
},
|
|
1582
|
+
setCompression: applyCompression,
|
|
1583
|
+
setWidth,
|
|
1584
|
+
setReverbSend: (amount) => {
|
|
1585
|
+
reverbSendGain.gain.setTargetAtTime(
|
|
1586
|
+
clamp(amount, 0, 100) / 100,
|
|
1587
|
+
ctx.currentTime,
|
|
1588
|
+
0.02
|
|
1589
|
+
);
|
|
1590
|
+
},
|
|
1591
|
+
dispose: () => {
|
|
1592
|
+
input.disconnect();
|
|
1593
|
+
eqLow.disconnect();
|
|
1594
|
+
eqMid.disconnect();
|
|
1595
|
+
eqHigh.disconnect();
|
|
1596
|
+
compressor.disconnect();
|
|
1597
|
+
splitter.disconnect();
|
|
1598
|
+
mid.disconnect();
|
|
1599
|
+
sideSum.disconnect();
|
|
1600
|
+
sideL.disconnect();
|
|
1601
|
+
sideR.disconnect();
|
|
1602
|
+
widthGain.disconnect();
|
|
1603
|
+
widthInv.disconnect();
|
|
1604
|
+
outL.disconnect();
|
|
1605
|
+
outR.disconnect();
|
|
1606
|
+
merger.disconnect();
|
|
1607
|
+
reverbSendGain.disconnect();
|
|
1608
|
+
}
|
|
1609
|
+
};
|
|
1610
|
+
};
|
|
1611
|
+
|
|
1469
1612
|
// src/chords.ts
|
|
1470
1613
|
var C3 = 48;
|
|
1471
1614
|
var buildChordPlacements = (options) => {
|
|
@@ -2295,7 +2438,7 @@ var normalizeLyricLines = (lines) => {
|
|
|
2295
2438
|
var LYRIC_LINE = /^@@(\d+)\s*(.*)$/;
|
|
2296
2439
|
var isLyricContinuation = (seg) => !/^[@#]/.test(seg);
|
|
2297
2440
|
var splitSegments = (mml) => mml.split(/[;\n\r]+/).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
2298
|
-
var
|
|
2441
|
+
var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
|
|
2299
2442
|
var MAX_VOCAL_VOLUME = 400;
|
|
2300
2443
|
var VOCAL_BOOST_DB_PER_PERCENT = 0.08;
|
|
2301
2444
|
var vocalVolumeToGain = (v) => {
|
|
@@ -2329,7 +2472,7 @@ var parseLyrics = (mml) => {
|
|
|
2329
2472
|
if (modelMatch) {
|
|
2330
2473
|
model = modelMatch[1].toLowerCase();
|
|
2331
2474
|
if (modelMatch[2]) {
|
|
2332
|
-
volume =
|
|
2475
|
+
volume = clamp2(Number.parseInt(modelMatch[2], 10), 0, MAX_VOCAL_VOLUME);
|
|
2333
2476
|
}
|
|
2334
2477
|
metaTokens.push(modelMatch[0]);
|
|
2335
2478
|
rest = rest.substring(modelMatch[0].length).trim();
|
|
@@ -2337,28 +2480,28 @@ var parseLyrics = (mml) => {
|
|
|
2337
2480
|
while (true) {
|
|
2338
2481
|
const vMatch = rest.match(/^v(\d+)/i);
|
|
2339
2482
|
if (vMatch) {
|
|
2340
|
-
volume =
|
|
2483
|
+
volume = clamp2(Number.parseInt(vMatch[1], 10), 0, MAX_VOCAL_VOLUME);
|
|
2341
2484
|
metaTokens.push(vMatch[0]);
|
|
2342
2485
|
rest = rest.substring(vMatch[0].length).trim();
|
|
2343
2486
|
continue;
|
|
2344
2487
|
}
|
|
2345
2488
|
const qMatch = rest.match(/^q(\d+)/i);
|
|
2346
2489
|
if (qMatch) {
|
|
2347
|
-
gate =
|
|
2490
|
+
gate = clamp2(Number.parseInt(qMatch[1], 10), 0, 100);
|
|
2348
2491
|
metaTokens.push(qMatch[0]);
|
|
2349
2492
|
rest = rest.substring(qMatch[0].length).trim();
|
|
2350
2493
|
continue;
|
|
2351
2494
|
}
|
|
2352
2495
|
const pMatch = rest.match(/^p(\d+)/i);
|
|
2353
2496
|
if (pMatch) {
|
|
2354
|
-
pan =
|
|
2497
|
+
pan = clamp2(Number.parseInt(pMatch[1], 10), 0, 127);
|
|
2355
2498
|
metaTokens.push(pMatch[0]);
|
|
2356
2499
|
rest = rest.substring(pMatch[0].length).trim();
|
|
2357
2500
|
continue;
|
|
2358
2501
|
}
|
|
2359
2502
|
const oMatch = rest.match(/^o(-?\d+)/i);
|
|
2360
2503
|
if (oMatch) {
|
|
2361
|
-
octave =
|
|
2504
|
+
octave = clamp2(Number.parseInt(oMatch[1], 10), -2, 2);
|
|
2362
2505
|
metaTokens.push(oMatch[0]);
|
|
2363
2506
|
rest = rest.substring(oMatch[0].length).trim();
|
|
2364
2507
|
continue;
|
|
@@ -2372,28 +2515,28 @@ var parseLyrics = (mml) => {
|
|
|
2372
2515
|
}
|
|
2373
2516
|
const rMatch = rest.match(/^r(\d+)/i);
|
|
2374
2517
|
if (rMatch) {
|
|
2375
|
-
reverb =
|
|
2518
|
+
reverb = clamp2(Number.parseInt(rMatch[1], 10), 0, 100);
|
|
2376
2519
|
metaTokens.push(rMatch[0]);
|
|
2377
2520
|
rest = rest.substring(rMatch[0].length).trim();
|
|
2378
2521
|
continue;
|
|
2379
2522
|
}
|
|
2380
2523
|
const gMatch = rest.match(/^g(\d+)/i);
|
|
2381
2524
|
if (gMatch) {
|
|
2382
|
-
gender =
|
|
2525
|
+
gender = clamp2(Number.parseInt(gMatch[1], 10), 0, 100);
|
|
2383
2526
|
metaTokens.push(gMatch[0]);
|
|
2384
2527
|
rest = rest.substring(gMatch[0].length).trim();
|
|
2385
2528
|
continue;
|
|
2386
2529
|
}
|
|
2387
2530
|
const hMatch = rest.match(/^h(\d+)/i);
|
|
2388
2531
|
if (hMatch) {
|
|
2389
|
-
breathiness =
|
|
2532
|
+
breathiness = clamp2(Number.parseInt(hMatch[1], 10), 0, 100);
|
|
2390
2533
|
metaTokens.push(hMatch[0]);
|
|
2391
2534
|
rest = rest.substring(hMatch[0].length).trim();
|
|
2392
2535
|
continue;
|
|
2393
2536
|
}
|
|
2394
2537
|
const eMatch = rest.match(/^e(\d+)/i);
|
|
2395
2538
|
if (eMatch) {
|
|
2396
|
-
delay =
|
|
2539
|
+
delay = clamp2(Number.parseInt(eMatch[1], 10), 0, 100);
|
|
2397
2540
|
metaTokens.push(eMatch[0]);
|
|
2398
2541
|
rest = rest.substring(eMatch[0].length).trim();
|
|
2399
2542
|
continue;
|
|
@@ -3309,7 +3452,7 @@ var PITCH_MAP = {
|
|
|
3309
3452
|
a: 9,
|
|
3310
3453
|
b: 11
|
|
3311
3454
|
};
|
|
3312
|
-
var
|
|
3455
|
+
var clamp3 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
|
|
3313
3456
|
var META_DIRECTIVE = /#(inst|drum|drumfont|volume|drumvolume|reverb|reverbdecay|reverbpredelay|delay|delaydiv|fadein|fadeout|mode)=([\w-]+)/gi;
|
|
3314
3457
|
var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
|
|
3315
3458
|
var TRACK_COMP_DIRECTIVE = /#t(\d+)comp=(\d+)/gi;
|
|
@@ -3333,24 +3476,24 @@ var parseMmlMeta = (mml) => {
|
|
|
3333
3476
|
if (!Number.isNaN(dv)) meta.drumVolume = dv;
|
|
3334
3477
|
} else if (key === "reverb") {
|
|
3335
3478
|
const rv = Number.parseInt(m[2], 10);
|
|
3336
|
-
if (!Number.isNaN(rv)) meta.reverb =
|
|
3479
|
+
if (!Number.isNaN(rv)) meta.reverb = clamp3(rv, 0, 100);
|
|
3337
3480
|
} else if (key === "reverbdecay") {
|
|
3338
3481
|
const rd = Number.parseInt(m[2], 10);
|
|
3339
|
-
if (!Number.isNaN(rd)) meta.reverbDecay =
|
|
3482
|
+
if (!Number.isNaN(rd)) meta.reverbDecay = clamp3(rd, 3, 40);
|
|
3340
3483
|
} else if (key === "reverbpredelay") {
|
|
3341
3484
|
const rp = Number.parseInt(m[2], 10);
|
|
3342
|
-
if (!Number.isNaN(rp)) meta.reverbPreDelay =
|
|
3485
|
+
if (!Number.isNaN(rp)) meta.reverbPreDelay = clamp3(rp, 0, 150);
|
|
3343
3486
|
} else if (key === "delay") {
|
|
3344
3487
|
const dv = Number.parseInt(m[2], 10);
|
|
3345
|
-
if (!Number.isNaN(dv)) meta.delay =
|
|
3488
|
+
if (!Number.isNaN(dv)) meta.delay = clamp3(dv, 0, 100);
|
|
3346
3489
|
} else if (key === "delaydiv") {
|
|
3347
3490
|
if (["4", "8", "8d", "16"].includes(m[2])) meta.delayDivision = m[2];
|
|
3348
3491
|
} else if (key === "fadein") {
|
|
3349
3492
|
const fi = Number.parseInt(m[2], 10);
|
|
3350
|
-
if (!Number.isNaN(fi)) meta.fadeIn =
|
|
3493
|
+
if (!Number.isNaN(fi)) meta.fadeIn = clamp3(fi, 0, 100);
|
|
3351
3494
|
} else if (key === "fadeout") {
|
|
3352
3495
|
const fo = Number.parseInt(m[2], 10);
|
|
3353
|
-
if (!Number.isNaN(fo)) meta.fadeOut =
|
|
3496
|
+
if (!Number.isNaN(fo)) meta.fadeOut = clamp3(fo, 0, 100);
|
|
3354
3497
|
} else if (key === "mode") {
|
|
3355
3498
|
if (m[2] === "simple" || m[2] === "advanced") {
|
|
3356
3499
|
meta.mode = m[2];
|
|
@@ -3367,7 +3510,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3367
3510
|
}
|
|
3368
3511
|
for (const m of mml.matchAll(TRACK_COMP_DIRECTIVE)) {
|
|
3369
3512
|
const idx = Number.parseInt(m[1], 10);
|
|
3370
|
-
const val =
|
|
3513
|
+
const val = clamp3(Number.parseInt(m[2], 10), 0, 100);
|
|
3371
3514
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3372
3515
|
meta.trackCompression ??= {};
|
|
3373
3516
|
meta.trackCompression[idx] = val;
|
|
@@ -3375,7 +3518,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3375
3518
|
}
|
|
3376
3519
|
for (const m of mml.matchAll(TRACK_WIDTH_DIRECTIVE)) {
|
|
3377
3520
|
const idx = Number.parseInt(m[1], 10);
|
|
3378
|
-
const val =
|
|
3521
|
+
const val = clamp3(Number.parseInt(m[2], 10), 0, 200);
|
|
3379
3522
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3380
3523
|
meta.trackWidth ??= {};
|
|
3381
3524
|
meta.trackWidth[idx] = val;
|
|
@@ -3383,7 +3526,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3383
3526
|
}
|
|
3384
3527
|
for (const m of mml.matchAll(TRACK_REVERBSEND_DIRECTIVE)) {
|
|
3385
3528
|
const idx = Number.parseInt(m[1], 10);
|
|
3386
|
-
const val =
|
|
3529
|
+
const val = clamp3(Number.parseInt(m[2], 10), 0, 100);
|
|
3387
3530
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3388
3531
|
meta.trackReverbSend ??= {};
|
|
3389
3532
|
meta.trackReverbSend[idx] = val;
|
|
@@ -3391,7 +3534,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3391
3534
|
}
|
|
3392
3535
|
for (const m of mml.matchAll(TRACK_EQLOW_DIRECTIVE)) {
|
|
3393
3536
|
const idx = Number.parseInt(m[1], 10);
|
|
3394
|
-
const val =
|
|
3537
|
+
const val = clamp3(Number.parseInt(m[2], 10), -12, 12);
|
|
3395
3538
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3396
3539
|
meta.trackEqLow ??= {};
|
|
3397
3540
|
meta.trackEqLow[idx] = val;
|
|
@@ -3399,7 +3542,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3399
3542
|
}
|
|
3400
3543
|
for (const m of mml.matchAll(TRACK_EQMID_DIRECTIVE)) {
|
|
3401
3544
|
const idx = Number.parseInt(m[1], 10);
|
|
3402
|
-
const val =
|
|
3545
|
+
const val = clamp3(Number.parseInt(m[2], 10), -12, 12);
|
|
3403
3546
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3404
3547
|
meta.trackEqMid ??= {};
|
|
3405
3548
|
meta.trackEqMid[idx] = val;
|
|
@@ -3407,7 +3550,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3407
3550
|
}
|
|
3408
3551
|
for (const m of mml.matchAll(TRACK_EQHIGH_DIRECTIVE)) {
|
|
3409
3552
|
const idx = Number.parseInt(m[1], 10);
|
|
3410
|
-
const val =
|
|
3553
|
+
const val = clamp3(Number.parseInt(m[2], 10), -12, 12);
|
|
3411
3554
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3412
3555
|
meta.trackEqHigh ??= {};
|
|
3413
3556
|
meta.trackEqHigh[idx] = val;
|
|
@@ -3556,7 +3699,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
3556
3699
|
numStr += body[j];
|
|
3557
3700
|
j++;
|
|
3558
3701
|
}
|
|
3559
|
-
const len = numStr ?
|
|
3702
|
+
const len = numStr ? clamp3(Number.parseInt(numStr, 10), 1, 64) : baseLength;
|
|
3560
3703
|
let steps = Math.round(stepsPerBar / len);
|
|
3561
3704
|
while (j < body.length && body[j] === ".") {
|
|
3562
3705
|
steps = Math.round(steps * 1.5);
|
|
@@ -3574,7 +3717,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
3574
3717
|
numStr += body[j];
|
|
3575
3718
|
j++;
|
|
3576
3719
|
}
|
|
3577
|
-
octave = numStr ?
|
|
3720
|
+
octave = numStr ? clamp3(Number.parseInt(numStr, 10), 0, 8) : 4;
|
|
3578
3721
|
pushTok("octave", currentStep, 0, tokStart);
|
|
3579
3722
|
} else if (ch === ">") {
|
|
3580
3723
|
octave = Math.min(8, octave + 1);
|
|
@@ -3591,7 +3734,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
3591
3734
|
numStr += body[j];
|
|
3592
3735
|
j++;
|
|
3593
3736
|
}
|
|
3594
|
-
baseLength =
|
|
3737
|
+
baseLength = clamp3(Number.parseInt(numStr, 10) || 16, 1, 64);
|
|
3595
3738
|
pushTok("length", currentStep, 0, tokStart);
|
|
3596
3739
|
} else if (ch === "r") {
|
|
3597
3740
|
j++;
|
|
@@ -3608,10 +3751,10 @@ var parseMML = (mml, options = {}) => {
|
|
|
3608
3751
|
}
|
|
3609
3752
|
if (ch === "t" && numStr) {
|
|
3610
3753
|
if (bpm === null) {
|
|
3611
|
-
bpm =
|
|
3754
|
+
bpm = clamp3(Number.parseInt(numStr, 10), 1, 255);
|
|
3612
3755
|
}
|
|
3613
3756
|
} else if (ch === "v" && numStr) {
|
|
3614
|
-
velocity =
|
|
3757
|
+
velocity = clamp3(Number.parseInt(numStr, 10), 0, 127);
|
|
3615
3758
|
trackVelocity.set(trackIndex, velocity);
|
|
3616
3759
|
}
|
|
3617
3760
|
pushTok("ctrl", currentStep, 0, tokStart);
|
|
@@ -3645,7 +3788,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
3645
3788
|
numStr += body[j];
|
|
3646
3789
|
j++;
|
|
3647
3790
|
}
|
|
3648
|
-
octave = numStr ?
|
|
3791
|
+
octave = numStr ? clamp3(Number.parseInt(numStr, 10), 0, 8) : 4;
|
|
3649
3792
|
} else {
|
|
3650
3793
|
j++;
|
|
3651
3794
|
}
|
|
@@ -3707,6 +3850,37 @@ var parseMML = (mml, options = {}) => {
|
|
|
3707
3850
|
};
|
|
3708
3851
|
};
|
|
3709
3852
|
|
|
3853
|
+
// src/reverb.ts
|
|
3854
|
+
var DEFAULT_REVERB_DECAY_SEC = 2.2;
|
|
3855
|
+
var MIN_REVERB_DECAY_SEC = 0.3;
|
|
3856
|
+
var MAX_REVERB_DECAY_SEC = 4;
|
|
3857
|
+
var IMPULSE_DECAY_CURVE = 2.5;
|
|
3858
|
+
var DEFAULT_REVERB_PREDELAY_MS = 0;
|
|
3859
|
+
var MIN_REVERB_PREDELAY_MS = 0;
|
|
3860
|
+
var MAX_REVERB_PREDELAY_MS = 150;
|
|
3861
|
+
var createReverbImpulse = (ctx, decaySec = DEFAULT_REVERB_DECAY_SEC) => {
|
|
3862
|
+
const rate = ctx.sampleRate;
|
|
3863
|
+
const length = Math.max(
|
|
3864
|
+
1,
|
|
3865
|
+
Math.floor(
|
|
3866
|
+
rate * Math.max(
|
|
3867
|
+
MIN_REVERB_DECAY_SEC,
|
|
3868
|
+
Math.min(MAX_REVERB_DECAY_SEC, decaySec)
|
|
3869
|
+
)
|
|
3870
|
+
)
|
|
3871
|
+
);
|
|
3872
|
+
const impulse = ctx.createBuffer(2, length, rate);
|
|
3873
|
+
for (let ch = 0; ch < impulse.numberOfChannels; ch++) {
|
|
3874
|
+
const data = impulse.getChannelData(ch);
|
|
3875
|
+
for (let i2 = 0; i2 < length; i2++) {
|
|
3876
|
+
const envelope = (1 - i2 / length) ** IMPULSE_DECAY_CURVE;
|
|
3877
|
+
data[i2] = (Math.random() * 2 - 1) * envelope;
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
return impulse;
|
|
3881
|
+
};
|
|
3882
|
+
var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100;
|
|
3883
|
+
|
|
3710
3884
|
// src/sequencer.ts
|
|
3711
3885
|
var STEPS_PER_BEAT = 48;
|
|
3712
3886
|
var PLAN_TIME = 0.5;
|
|
@@ -6284,9 +6458,54 @@ var playPlacements = (placements, options) => {
|
|
|
6284
6458
|
});
|
|
6285
6459
|
const ownsCtx = !options.audioContext;
|
|
6286
6460
|
const ctx = options.audioContext ?? new AudioContext();
|
|
6287
|
-
const
|
|
6461
|
+
const rawDestination = options.destination ?? ctx.destination;
|
|
6288
6462
|
const useSynth = options.synth ?? !options.onPlayNote;
|
|
6289
|
-
const
|
|
6463
|
+
const finalMix = ctx.createGain();
|
|
6464
|
+
const masterGain = ctx.createGain();
|
|
6465
|
+
masterGain.connect(finalMix);
|
|
6466
|
+
const reverbPreDelay = ctx.createDelay(MAX_REVERB_PREDELAY_MS / 1e3);
|
|
6467
|
+
reverbPreDelay.delayTime.value = (options.metaReverbPreDelay ?? DEFAULT_REVERB_PREDELAY_MS) / 1e3;
|
|
6468
|
+
const reverbConvolver = ctx.createConvolver();
|
|
6469
|
+
const reverbDecaySec = (options.metaReverbDecay ?? 22) / 10 || DEFAULT_REVERB_DECAY_SEC;
|
|
6470
|
+
reverbConvolver.buffer = createReverbImpulse(ctx, reverbDecaySec);
|
|
6471
|
+
reverbConvolver.normalize = true;
|
|
6472
|
+
const reverbWetGain = ctx.createGain();
|
|
6473
|
+
reverbWetGain.gain.value = reverbAmountToGain(options.metaReverb ?? 0);
|
|
6474
|
+
reverbPreDelay.connect(reverbConvolver);
|
|
6475
|
+
reverbConvolver.connect(reverbWetGain);
|
|
6476
|
+
reverbWetGain.connect(finalMix);
|
|
6477
|
+
finalMix.connect(rawDestination);
|
|
6478
|
+
const channelStrips = /* @__PURE__ */ new Map();
|
|
6479
|
+
const getChannelStrip = (index) => {
|
|
6480
|
+
let strip = channelStrips.get(index);
|
|
6481
|
+
if (!strip) {
|
|
6482
|
+
strip = createChannelStrip(ctx, masterGain, {
|
|
6483
|
+
compression: options.trackCompression?.[index] ?? 0,
|
|
6484
|
+
width: options.trackWidth?.[index] ?? 100,
|
|
6485
|
+
eqLow: options.trackEqLow?.[index] ?? 0,
|
|
6486
|
+
eqMid: options.trackEqMid?.[index] ?? 0,
|
|
6487
|
+
eqHigh: options.trackEqHigh?.[index] ?? 0,
|
|
6488
|
+
reverbSend: options.trackReverbSend?.[index] ?? 0,
|
|
6489
|
+
reverbBus: reverbPreDelay
|
|
6490
|
+
});
|
|
6491
|
+
channelStrips.set(index, strip);
|
|
6492
|
+
}
|
|
6493
|
+
return strip;
|
|
6494
|
+
};
|
|
6495
|
+
const synths = /* @__PURE__ */ new Map();
|
|
6496
|
+
const getSynth = (index) => {
|
|
6497
|
+
let s = synths.get(index);
|
|
6498
|
+
if (!s) {
|
|
6499
|
+
s = createSynth(ctx, getChannelStrip(index).input);
|
|
6500
|
+
synths.set(index, s);
|
|
6501
|
+
}
|
|
6502
|
+
return s;
|
|
6503
|
+
};
|
|
6504
|
+
const drumSynth = useSynth ? createSynth(ctx, masterGain) : null;
|
|
6505
|
+
const trackIndexById = /* @__PURE__ */ new Map();
|
|
6506
|
+
trackIndices.forEach((idx) => {
|
|
6507
|
+
trackIndexById.set(TRACK_ID_BY_INDEX[idx] ?? `t${idx}`, idx);
|
|
6508
|
+
});
|
|
6290
6509
|
const pauseWhenHidden = options.pauseWhenHidden ?? ownsCtx;
|
|
6291
6510
|
let playing = false;
|
|
6292
6511
|
const seq = createSequencer({
|
|
@@ -6301,12 +6520,14 @@ var playPlacements = (placements, options) => {
|
|
|
6301
6520
|
getAudioTime: () => ctx.currentTime,
|
|
6302
6521
|
onPlayNote: (e) => {
|
|
6303
6522
|
options.onPlayNote?.(e);
|
|
6304
|
-
|
|
6523
|
+
if (!useSynth) return;
|
|
6524
|
+
const index = trackIndexById.get(e.trackId);
|
|
6525
|
+
(index === void 0 ? drumSynth : getSynth(index))?.playNote(e);
|
|
6305
6526
|
},
|
|
6306
6527
|
onPlayDrum: (e) => {
|
|
6307
6528
|
const velocity = e.velocity * (drumVolume / 100) * (masterVolume / 100);
|
|
6308
6529
|
options.onPlayDrum?.({ ...e, velocity });
|
|
6309
|
-
|
|
6530
|
+
drumSynth?.playDrum({ ...e, velocity });
|
|
6310
6531
|
},
|
|
6311
6532
|
onTick: (step) => {
|
|
6312
6533
|
options.onTick?.(step);
|
|
@@ -6357,6 +6578,7 @@ var playPlacements = (placements, options) => {
|
|
|
6357
6578
|
if (pauseWhenHidden && typeof document !== "undefined") {
|
|
6358
6579
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
6359
6580
|
}
|
|
6581
|
+
for (const strip of channelStrips.values()) strip.dispose();
|
|
6360
6582
|
if (ownsCtx) void ctx.close();
|
|
6361
6583
|
};
|
|
6362
6584
|
return {
|
|
@@ -6376,7 +6598,16 @@ var playMML = (mml, options = {}) => {
|
|
|
6376
6598
|
bpm,
|
|
6377
6599
|
metaVolume: meta.volume,
|
|
6378
6600
|
metaDrum: meta.drum,
|
|
6379
|
-
metaDrumVolume: meta.drumVolume
|
|
6601
|
+
metaDrumVolume: meta.drumVolume,
|
|
6602
|
+
metaReverb: meta.reverb,
|
|
6603
|
+
metaReverbDecay: meta.reverbDecay,
|
|
6604
|
+
metaReverbPreDelay: meta.reverbPreDelay,
|
|
6605
|
+
trackCompression: meta.trackCompression,
|
|
6606
|
+
trackWidth: meta.trackWidth,
|
|
6607
|
+
trackReverbSend: meta.trackReverbSend,
|
|
6608
|
+
trackEqLow: meta.trackEqLow,
|
|
6609
|
+
trackEqMid: meta.trackEqMid,
|
|
6610
|
+
trackEqHigh: meta.trackEqHigh
|
|
6380
6611
|
});
|
|
6381
6612
|
};
|
|
6382
6613
|
var playNote = (options) => {
|
|
@@ -9682,20 +9913,23 @@ var adjustZone = async (ctx, fontName, zone) => {
|
|
|
9682
9913
|
const newLength = attackLength + loopLengthFrame * repeatCount + releaseLength;
|
|
9683
9914
|
let totalPeak = 0;
|
|
9684
9915
|
let loopPeak = 0;
|
|
9685
|
-
|
|
9686
|
-
const
|
|
9687
|
-
for (let i2 = 0; i2 <
|
|
9688
|
-
const abs = Math.abs(
|
|
9916
|
+
for (let ch = 0; ch < oldBuf.numberOfChannels; ch++) {
|
|
9917
|
+
const chData = oldBuf.getChannelData(ch);
|
|
9918
|
+
for (let i2 = 0; i2 < chData.length; i2++) {
|
|
9919
|
+
const abs = Math.abs(chData[i2]);
|
|
9689
9920
|
if (abs > totalPeak) totalPeak = abs;
|
|
9690
9921
|
if (i2 >= loopStartFrame && i2 < loopEndFrame) {
|
|
9691
9922
|
if (abs > loopPeak) loopPeak = abs;
|
|
9692
9923
|
}
|
|
9693
9924
|
}
|
|
9694
9925
|
}
|
|
9926
|
+
const decay = isDecayInstrument(fontName);
|
|
9927
|
+
const targetRatio = decay ? 0.4 : 0.75;
|
|
9928
|
+
const maxMultiplier = decay ? 6 : 20;
|
|
9695
9929
|
let gainMultiplier = 1;
|
|
9696
|
-
if (
|
|
9697
|
-
gainMultiplier = totalPeak *
|
|
9698
|
-
if (gainMultiplier >
|
|
9930
|
+
if (loopPeak > 0 && totalPeak > 0 && loopPeak < totalPeak * 0.8) {
|
|
9931
|
+
gainMultiplier = totalPeak * targetRatio / loopPeak;
|
|
9932
|
+
if (gainMultiplier > maxMultiplier) gainMultiplier = maxMultiplier;
|
|
9699
9933
|
}
|
|
9700
9934
|
try {
|
|
9701
9935
|
const newBuf = ctx.createBuffer(
|
|
@@ -11027,14 +11261,14 @@ var DELAY_DIVISIONS = [
|
|
|
11027
11261
|
{ value: "8d", label: "\u4ED8\u70B98\u5206", beats: 0.75 },
|
|
11028
11262
|
{ value: "16", label: "16\u5206", beats: 0.25 }
|
|
11029
11263
|
];
|
|
11030
|
-
var
|
|
11264
|
+
var clamp4 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
11031
11265
|
var divisionToSeconds = (division, bpm) => {
|
|
11032
11266
|
const beats = DELAY_DIVISIONS.find((d) => d.value === division)?.beats ?? 0.5;
|
|
11033
11267
|
const safeBpm = bpm > 0 ? bpm : 120;
|
|
11034
11268
|
return 60 / safeBpm * beats;
|
|
11035
11269
|
};
|
|
11036
11270
|
var DELAY_MAX_WET = 0.45;
|
|
11037
|
-
var delayAmountToGain = (amount) =>
|
|
11271
|
+
var delayAmountToGain = (amount) => clamp4(amount, 0, 100) / 100 * DELAY_MAX_WET;
|
|
11038
11272
|
var FEEDBACK_GAIN = 0.3;
|
|
11039
11273
|
var MAX_DELAY_SEC = 2;
|
|
11040
11274
|
var createDelayBus = (ctx, destination, options = {}) => {
|
|
@@ -13109,38 +13343,6 @@ var isChordHeavyTrack = (notes, threshold = 0.6) => {
|
|
|
13109
13343
|
return chordNotes / notes.length >= threshold;
|
|
13110
13344
|
};
|
|
13111
13345
|
|
|
13112
|
-
// src/reverb.ts
|
|
13113
|
-
var DEFAULT_REVERB_DECAY_SEC = 2.2;
|
|
13114
|
-
var MIN_REVERB_DECAY_SEC = 0.3;
|
|
13115
|
-
var MAX_REVERB_DECAY_SEC = 4;
|
|
13116
|
-
var IMPULSE_DECAY_CURVE = 2.5;
|
|
13117
|
-
var DEFAULT_REVERB_PREDELAY_MS = 0;
|
|
13118
|
-
var MIN_REVERB_PREDELAY_MS = 0;
|
|
13119
|
-
var MAX_REVERB_PREDELAY_MS = 150;
|
|
13120
|
-
var createReverbImpulse = (ctx, decaySec = DEFAULT_REVERB_DECAY_SEC) => {
|
|
13121
|
-
const rate = ctx.sampleRate;
|
|
13122
|
-
const length = Math.max(
|
|
13123
|
-
1,
|
|
13124
|
-
Math.floor(
|
|
13125
|
-
rate * Math.max(
|
|
13126
|
-
MIN_REVERB_DECAY_SEC,
|
|
13127
|
-
Math.min(MAX_REVERB_DECAY_SEC, decaySec)
|
|
13128
|
-
)
|
|
13129
|
-
)
|
|
13130
|
-
);
|
|
13131
|
-
const impulse = ctx.createBuffer(2, length, rate);
|
|
13132
|
-
for (let ch = 0; ch < impulse.numberOfChannels; ch++) {
|
|
13133
|
-
const data = impulse.getChannelData(ch);
|
|
13134
|
-
for (let i2 = 0; i2 < length; i2++) {
|
|
13135
|
-
const envelope = (1 - i2 / length) ** IMPULSE_DECAY_CURVE;
|
|
13136
|
-
data[i2] = (Math.random() * 2 - 1) * envelope;
|
|
13137
|
-
}
|
|
13138
|
-
}
|
|
13139
|
-
return impulse;
|
|
13140
|
-
};
|
|
13141
|
-
var REVERB_MAX_WET = 0.6;
|
|
13142
|
-
var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100 * REVERB_MAX_WET;
|
|
13143
|
-
|
|
13144
13346
|
// src/daw.ts
|
|
13145
13347
|
var CHORD_INFO_HTML2 = `
|
|
13146
13348
|
<div class="dtm-modal-body-content">
|
|
@@ -13576,7 +13778,7 @@ var deriveCustomVocalKeyFromUrl = (url2) => {
|
|
|
13576
13778
|
if (name && /^[0-9]/.test(name)) name = `_${name}`;
|
|
13577
13779
|
return CUSTOM_VOCAL_KEY_RE.test(name) ? name : "";
|
|
13578
13780
|
};
|
|
13579
|
-
var
|
|
13781
|
+
var clamp5 = (v, min, max) => Math.min(Math.max(v, min), max);
|
|
13580
13782
|
var normalizeInstrumentName = (name) => {
|
|
13581
13783
|
if (!name) return "";
|
|
13582
13784
|
const stripped = name.replace(/\s+/g, "").toLowerCase();
|
|
@@ -13665,6 +13867,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
13665
13867
|
refs.drumFontSelect.value = currentDrumFont;
|
|
13666
13868
|
let currentInstrument = "";
|
|
13667
13869
|
let activeTrackId = options.initialActiveTrack ?? trackConfigs[0].id;
|
|
13870
|
+
let trackFxAdvancedOpen = false;
|
|
13871
|
+
let lyricAdvancedOpen = false;
|
|
13668
13872
|
let activeToolMode = "pen";
|
|
13669
13873
|
let currentInsertLength = 48;
|
|
13670
13874
|
let snapGridSteps = 12;
|
|
@@ -13961,7 +14165,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
13961
14165
|
const thumbW = Math.max(40, canvas.width / totalContentWidth * sbW);
|
|
13962
14166
|
const ratio = currentOffsetX / maxOffsetX;
|
|
13963
14167
|
refs.hScrollThumb.style.width = `${thumbW}px`;
|
|
13964
|
-
refs.hScrollThumb.style.left = `${
|
|
14168
|
+
refs.hScrollThumb.style.left = `${clamp5(ratio * (sbW - thumbW), 0, sbW - thumbW)}px`;
|
|
13965
14169
|
}
|
|
13966
14170
|
const totalHeight = renderConfig.keyCount * renderConfig.keyHeight;
|
|
13967
14171
|
const sbH = refs.vScroll.clientHeight;
|
|
@@ -14031,9 +14235,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
14031
14235
|
if (maxOffsetX <= 0) return;
|
|
14032
14236
|
const rect = refs.hScroll.getBoundingClientRect();
|
|
14033
14237
|
const thumbW = Number.parseFloat(refs.hScrollThumb.style.width) || 40;
|
|
14034
|
-
const x2 =
|
|
14238
|
+
const x2 = clamp5(clientX - rect.left - thumbW / 2, 0, rect.width - thumbW);
|
|
14035
14239
|
const ratio = x2 / (rect.width - thumbW);
|
|
14036
|
-
currentOffsetX =
|
|
14240
|
+
currentOffsetX = clamp5(ratio * maxOffsetX, 0, maxOffsetX);
|
|
14037
14241
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
14038
14242
|
redrawAll();
|
|
14039
14243
|
};
|
|
@@ -14042,9 +14246,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
14042
14246
|
if (maxOffset <= 0) return;
|
|
14043
14247
|
const rect = refs.vScroll.getBoundingClientRect();
|
|
14044
14248
|
const thumbH = Number.parseFloat(refs.vScrollThumb.style.height) || 40;
|
|
14045
|
-
const y =
|
|
14249
|
+
const y = clamp5(clientY - rect.top - thumbH / 2, 0, rect.height - thumbH);
|
|
14046
14250
|
const ratio = y / (rect.height - thumbH);
|
|
14047
|
-
currentOffsetY =
|
|
14251
|
+
currentOffsetY = clamp5(ratio * maxOffset, 0, maxOffset);
|
|
14048
14252
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
14049
14253
|
redrawAll();
|
|
14050
14254
|
};
|
|
@@ -14091,8 +14295,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14091
14295
|
if (dx !== 0 || dy !== 0) {
|
|
14092
14296
|
const maxOffsetX = getMaxOffsetX();
|
|
14093
14297
|
const maxOffsetY = getMaxOffsetY();
|
|
14094
|
-
currentOffsetX =
|
|
14095
|
-
currentOffsetY =
|
|
14298
|
+
currentOffsetX = clamp5(currentOffsetX + dx, 0, maxOffsetX);
|
|
14299
|
+
currentOffsetY = clamp5(currentOffsetY + dy, 0, maxOffsetY);
|
|
14096
14300
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
14097
14301
|
onPointerMove(lastMoveEvent);
|
|
14098
14302
|
}
|
|
@@ -14482,12 +14686,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
14482
14686
|
"wheel",
|
|
14483
14687
|
(event) => {
|
|
14484
14688
|
event.preventDefault();
|
|
14485
|
-
currentOffsetY =
|
|
14689
|
+
currentOffsetY = clamp5(
|
|
14486
14690
|
currentOffsetY + event.deltaY,
|
|
14487
14691
|
0,
|
|
14488
14692
|
getMaxOffsetY()
|
|
14489
14693
|
);
|
|
14490
|
-
currentOffsetX =
|
|
14694
|
+
currentOffsetX = clamp5(
|
|
14491
14695
|
currentOffsetX + event.deltaX,
|
|
14492
14696
|
0,
|
|
14493
14697
|
getMaxOffsetX()
|
|
@@ -14526,7 +14730,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14526
14730
|
const centerStep = (currentOffsetX + canvas.width / 2) / renderConfig.stepWidth;
|
|
14527
14731
|
renderConfig.stepWidth = BASE_STEP_WIDTH * (zoomX * 2) / 100;
|
|
14528
14732
|
refs.zoomXLabel.textContent = `${zoomX}%`;
|
|
14529
|
-
currentOffsetX =
|
|
14733
|
+
currentOffsetX = clamp5(
|
|
14530
14734
|
centerStep * renderConfig.stepWidth - canvas.width / 2,
|
|
14531
14735
|
0,
|
|
14532
14736
|
getMaxOffsetX()
|
|
@@ -14539,7 +14743,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14539
14743
|
const centerKey = (currentOffsetY + canvas.height / 2) / renderConfig.keyHeight;
|
|
14540
14744
|
renderConfig.keyHeight = BASE_KEY_HEIGHT * zoomY / 100;
|
|
14541
14745
|
refs.zoomYLabel.textContent = `${zoomY}%`;
|
|
14542
|
-
currentOffsetY =
|
|
14746
|
+
currentOffsetY = clamp5(
|
|
14543
14747
|
centerKey * renderConfig.keyHeight - canvas.height / 2,
|
|
14544
14748
|
0,
|
|
14545
14749
|
getMaxOffsetY()
|
|
@@ -14589,7 +14793,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14589
14793
|
const threshold = currentOffsetX / renderConfig.stepWidth + visibleSteps - 4;
|
|
14590
14794
|
if (currentPlayStep > threshold) {
|
|
14591
14795
|
const visibleBars = Math.round(visibleSteps / renderConfig.stepsPerBar);
|
|
14592
|
-
currentOffsetX =
|
|
14796
|
+
currentOffsetX = clamp5(
|
|
14593
14797
|
currentOffsetX + visibleBars * renderConfig.stepsPerBar * renderConfig.stepWidth,
|
|
14594
14798
|
0,
|
|
14595
14799
|
getMaxOffsetX()
|
|
@@ -14676,7 +14880,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14676
14880
|
}
|
|
14677
14881
|
if (playbackState !== "paused") {
|
|
14678
14882
|
const canvas = getGridCanvas();
|
|
14679
|
-
currentOffsetX =
|
|
14883
|
+
currentOffsetX = clamp5(
|
|
14680
14884
|
playStartStep * renderConfig.stepWidth - canvas.width * 0.5,
|
|
14681
14885
|
0,
|
|
14682
14886
|
getMaxOffsetX()
|
|
@@ -14780,7 +14984,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14780
14984
|
<input type="range" class="dtm-range dtm-grow" data-dtm="track-vol" min="0" max="127" value="${active.volume}">
|
|
14781
14985
|
<span class="dtm-label" data-dtm="track-vol-label">${active.volume}</span>
|
|
14782
14986
|
</div>
|
|
14783
|
-
<details class="dtm-advanced" data-dtm="track-fx-advanced">
|
|
14987
|
+
<details class="dtm-advanced" data-dtm="track-fx-advanced" ${trackFxAdvancedOpen ? "open" : ""}>
|
|
14784
14988
|
<summary>\u8A73\u7D30\u8A2D\u5B9A\uFF08EQ\u30FB\u97F3\u5727\u30FB\u30B9\u30C6\u30EC\u30AA\u5E45\uFF09</summary>
|
|
14785
14989
|
<div class="dtm-row">
|
|
14786
14990
|
<span class="dtm-label">EQ\u4F4E\u57DF</span>
|
|
@@ -14817,6 +15021,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
14817
15021
|
<button class="dtm-infobtn" data-dtm="track-reverb-send-info" title="\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
|
|
14818
15022
|
</div>
|
|
14819
15023
|
</details>`;
|
|
15024
|
+
refs.trackBody.querySelector(
|
|
15025
|
+
'[data-dtm="track-fx-advanced"]'
|
|
15026
|
+
).addEventListener("toggle", (e) => {
|
|
15027
|
+
trackFxAdvancedOpen = e.target.open;
|
|
15028
|
+
});
|
|
14820
15029
|
const volInput = refs.trackBody.querySelector(
|
|
14821
15030
|
'[data-dtm="track-vol"]'
|
|
14822
15031
|
);
|
|
@@ -15030,7 +15239,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15030
15239
|
<input type="range" class="dtm-range dtm-grow" data-dtm="lyric-vol" min="0" max="${MAX_VOCAL_VOLUME}" aria-label="\u6B4C\u5531\u306E\u58F0\u91CF\uFF08100=\u7B49\u500D\u3001100\u8D85\u3067\u30D6\u30FC\u30B9\u30C8\u3001\u65E2\u5B9A200\uFF09">
|
|
15031
15240
|
<span class="dtm-label" data-dtm="lyric-vol-label"></span>
|
|
15032
15241
|
</div>
|
|
15033
|
-
<details class="dtm-advanced" data-dtm="lyric-advanced">
|
|
15242
|
+
<details class="dtm-advanced" data-dtm="lyric-advanced" ${lyricAdvancedOpen ? "open" : ""}>
|
|
15034
15243
|
<summary>\u8A73\u7D30\u8A2D\u5B9A</summary>
|
|
15035
15244
|
<div class="dtm-row">
|
|
15036
15245
|
<span class="dtm-label">\u30AA\u30AF\u30BF\u30FC\u30D6</span>
|
|
@@ -15091,6 +15300,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
15091
15300
|
<textarea class="dtm-textarea" data-dtm="lyric-input" rows="2" placeholder="\u3072\u3089\u304C\u306A\u30FB\u30AB\u30BF\u30AB\u30CA\u3067\u6B4C\u8A5E\uFF08\u4F8B: \u3069\u308C\u307F\u3075\u3041\u305D\u3089\u3057\u3069\uFF09"></textarea>
|
|
15092
15301
|
</div>`;
|
|
15093
15302
|
refs.trackBody.appendChild(lyricDiv);
|
|
15303
|
+
lyricDiv.querySelector(
|
|
15304
|
+
'[data-dtm="lyric-advanced"]'
|
|
15305
|
+
).addEventListener("toggle", (e) => {
|
|
15306
|
+
lyricAdvancedOpen = e.target.open;
|
|
15307
|
+
});
|
|
15094
15308
|
const lyricModelSel = lyricDiv.querySelector(
|
|
15095
15309
|
'[data-dtm="lyric-model"]'
|
|
15096
15310
|
);
|
|
@@ -15713,7 +15927,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15713
15927
|
const canvas = getGridCanvas();
|
|
15714
15928
|
const yIndex = renderConfig.keyCount - 1 - (pitch - renderConfig.pitchRangeStart);
|
|
15715
15929
|
const logicalY = yIndex * renderConfig.keyHeight;
|
|
15716
|
-
currentOffsetY =
|
|
15930
|
+
currentOffsetY = clamp5(
|
|
15717
15931
|
logicalY - (canvas.height - renderConfig.keyHeight) / 2,
|
|
15718
15932
|
0,
|
|
15719
15933
|
getMaxOffsetY()
|
|
@@ -16288,8 +16502,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
16288
16502
|
refs.reverbAmount.value = "20";
|
|
16289
16503
|
refs.reverbAmountLabel.textContent = "20%";
|
|
16290
16504
|
options.onReverbChange?.(20);
|
|
16291
|
-
const bpmT =
|
|
16292
|
-
reverbDecay =
|
|
16505
|
+
const bpmT = clamp5((bpm - 60) / (180 - 60), 0, 1);
|
|
16506
|
+
reverbDecay = clamp5(
|
|
16293
16507
|
3 - bpmT * (3 - 0.9),
|
|
16294
16508
|
MIN_REVERB_DECAY_SEC,
|
|
16295
16509
|
MAX_REVERB_DECAY_SEC
|
|
@@ -16324,7 +16538,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
16324
16538
|
}
|
|
16325
16539
|
if (t.lyricModel) {
|
|
16326
16540
|
t.vocalVibrato = true;
|
|
16327
|
-
t.vocalReverb = isMainVocal ?
|
|
16541
|
+
t.vocalReverb = isMainVocal ? 25 : 45;
|
|
16328
16542
|
fireLyricsChange(t);
|
|
16329
16543
|
}
|
|
16330
16544
|
}
|
|
@@ -17220,7 +17434,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
17220
17434
|
currentPlayStep = step;
|
|
17221
17435
|
playbackState = "paused";
|
|
17222
17436
|
const canvas = getGridCanvas();
|
|
17223
|
-
currentOffsetX =
|
|
17437
|
+
currentOffsetX = clamp5(
|
|
17224
17438
|
step * renderConfig.stepWidth - canvas.width * 0.5,
|
|
17225
17439
|
0,
|
|
17226
17440
|
getMaxOffsetX()
|
|
@@ -17266,11 +17480,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
17266
17480
|
getViewState,
|
|
17267
17481
|
setViewState: (state) => {
|
|
17268
17482
|
if (typeof state.zoomX === "number") {
|
|
17269
|
-
zoomX =
|
|
17483
|
+
zoomX = clamp5(state.zoomX, 25, 200);
|
|
17270
17484
|
applyZoomX();
|
|
17271
17485
|
}
|
|
17272
17486
|
if (typeof state.zoomY === "number") {
|
|
17273
|
-
zoomY =
|
|
17487
|
+
zoomY = clamp5(state.zoomY, 50, 200);
|
|
17274
17488
|
applyZoomY();
|
|
17275
17489
|
}
|
|
17276
17490
|
if (typeof state.decomposeChord === "boolean") {
|
|
@@ -17292,24 +17506,24 @@ var mountDAW = (target, options = {}) => {
|
|
|
17292
17506
|
forcePauseAt,
|
|
17293
17507
|
setLoading,
|
|
17294
17508
|
setMasterVolume: (volume) => {
|
|
17295
|
-
masterVolume =
|
|
17509
|
+
masterVolume = clamp5(volume, 0, 100);
|
|
17296
17510
|
refs.masterVolume.value = String(masterVolume);
|
|
17297
17511
|
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
17298
17512
|
options.singingVoices?.setVolume(masterVolume / 100);
|
|
17299
17513
|
},
|
|
17300
17514
|
setVolume: (volume) => {
|
|
17301
|
-
masterVolume =
|
|
17515
|
+
masterVolume = clamp5(volume, 0, 100);
|
|
17302
17516
|
refs.masterVolume.value = String(masterVolume);
|
|
17303
17517
|
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
17304
17518
|
options.singingVoices?.setVolume(masterVolume / 100);
|
|
17305
17519
|
},
|
|
17306
17520
|
setDrumVolume: (volume) => {
|
|
17307
|
-
drumVolume =
|
|
17521
|
+
drumVolume = clamp5(volume, 0, 100);
|
|
17308
17522
|
refs.drumVolume.value = String(drumVolume);
|
|
17309
17523
|
refs.drumVolumeLabel.textContent = `${drumVolume}%`;
|
|
17310
17524
|
},
|
|
17311
17525
|
setReverbAmount: (amount) => {
|
|
17312
|
-
reverbAmount =
|
|
17526
|
+
reverbAmount = clamp5(amount, 0, 100);
|
|
17313
17527
|
refs.reverbAmount.value = String(reverbAmount);
|
|
17314
17528
|
refs.reverbAmountLabel.textContent = `${reverbAmount}%`;
|
|
17315
17529
|
options.onReverbChange?.(reverbAmount);
|
|
@@ -17333,7 +17547,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
17333
17547
|
options.onReverbPreDelayChange?.(reverbPreDelay);
|
|
17334
17548
|
},
|
|
17335
17549
|
setDelayAmount: (amount) => {
|
|
17336
|
-
delayAmount =
|
|
17550
|
+
delayAmount = clamp5(amount, 0, 100);
|
|
17337
17551
|
refs.delayAmount.value = String(delayAmount);
|
|
17338
17552
|
refs.delayAmountLabel.textContent = `${delayAmount}%`;
|
|
17339
17553
|
options.onDelayChange?.(delayAmount);
|
|
@@ -18112,149 +18326,6 @@ var isSupported = midiJsonParser.isSupported;
|
|
|
18112
18326
|
var parseArrayBuffer = midiJsonParser.parseArrayBuffer;
|
|
18113
18327
|
URL.revokeObjectURL(url);
|
|
18114
18328
|
|
|
18115
|
-
// src/channel-strip.ts
|
|
18116
|
-
var clamp5 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
18117
|
-
var compressionParams = (amount) => {
|
|
18118
|
-
const t = clamp5(amount, 0, 100) / 100;
|
|
18119
|
-
return {
|
|
18120
|
-
threshold: 0 + (-24 - 0) * t,
|
|
18121
|
-
ratio: 1 + (12 - 1) * t,
|
|
18122
|
-
knee: 0 + (6 - 0) * t,
|
|
18123
|
-
attack: 0.02 + (3e-3 - 0.02) * t,
|
|
18124
|
-
release: 0.25 + (0.15 - 0.25) * t
|
|
18125
|
-
};
|
|
18126
|
-
};
|
|
18127
|
-
var EQ_LOW_FREQ = 200;
|
|
18128
|
-
var EQ_MID_FREQ = 1e3;
|
|
18129
|
-
var EQ_HIGH_FREQ = 5e3;
|
|
18130
|
-
var EQ_MID_Q = 1;
|
|
18131
|
-
var EQ_MAX_DB = 12;
|
|
18132
|
-
var createChannelStrip = (ctx, destination, options = {}) => {
|
|
18133
|
-
const input = ctx.createGain();
|
|
18134
|
-
const eqLow = ctx.createBiquadFilter();
|
|
18135
|
-
eqLow.type = "lowshelf";
|
|
18136
|
-
eqLow.frequency.value = EQ_LOW_FREQ;
|
|
18137
|
-
eqLow.gain.value = clamp5(options.eqLow ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
18138
|
-
const eqMid = ctx.createBiquadFilter();
|
|
18139
|
-
eqMid.type = "peaking";
|
|
18140
|
-
eqMid.frequency.value = EQ_MID_FREQ;
|
|
18141
|
-
eqMid.Q.value = EQ_MID_Q;
|
|
18142
|
-
eqMid.gain.value = clamp5(options.eqMid ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
18143
|
-
const eqHigh = ctx.createBiquadFilter();
|
|
18144
|
-
eqHigh.type = "highshelf";
|
|
18145
|
-
eqHigh.frequency.value = EQ_HIGH_FREQ;
|
|
18146
|
-
eqHigh.gain.value = clamp5(options.eqHigh ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
18147
|
-
input.connect(eqLow);
|
|
18148
|
-
eqLow.connect(eqMid);
|
|
18149
|
-
eqMid.connect(eqHigh);
|
|
18150
|
-
const compressor = ctx.createDynamicsCompressor();
|
|
18151
|
-
const applyCompression = (amount) => {
|
|
18152
|
-
const p = compressionParams(amount);
|
|
18153
|
-
const now = ctx.currentTime;
|
|
18154
|
-
compressor.threshold.setValueAtTime(p.threshold, now);
|
|
18155
|
-
compressor.ratio.setValueAtTime(p.ratio, now);
|
|
18156
|
-
compressor.knee.setValueAtTime(p.knee, now);
|
|
18157
|
-
compressor.attack.setValueAtTime(p.attack, now);
|
|
18158
|
-
compressor.release.setValueAtTime(p.release, now);
|
|
18159
|
-
};
|
|
18160
|
-
applyCompression(options.compression ?? 0);
|
|
18161
|
-
const splitter = ctx.createChannelSplitter(2);
|
|
18162
|
-
const mid = ctx.createGain();
|
|
18163
|
-
mid.gain.value = 0.5;
|
|
18164
|
-
splitter.connect(mid, 0);
|
|
18165
|
-
splitter.connect(mid, 1);
|
|
18166
|
-
const sideSum = ctx.createGain();
|
|
18167
|
-
sideSum.gain.value = 1;
|
|
18168
|
-
const sideL = ctx.createGain();
|
|
18169
|
-
sideL.gain.value = 0.5;
|
|
18170
|
-
const sideR = ctx.createGain();
|
|
18171
|
-
sideR.gain.value = -0.5;
|
|
18172
|
-
splitter.connect(sideL, 0);
|
|
18173
|
-
splitter.connect(sideR, 1);
|
|
18174
|
-
sideL.connect(sideSum);
|
|
18175
|
-
sideR.connect(sideSum);
|
|
18176
|
-
const widthGain = ctx.createGain();
|
|
18177
|
-
sideSum.connect(widthGain);
|
|
18178
|
-
const widthInv = ctx.createGain();
|
|
18179
|
-
widthInv.gain.value = -1;
|
|
18180
|
-
widthGain.connect(widthInv);
|
|
18181
|
-
const outL = ctx.createGain();
|
|
18182
|
-
mid.connect(outL);
|
|
18183
|
-
widthGain.connect(outL);
|
|
18184
|
-
const outR = ctx.createGain();
|
|
18185
|
-
mid.connect(outR);
|
|
18186
|
-
widthInv.connect(outR);
|
|
18187
|
-
const merger = ctx.createChannelMerger(2);
|
|
18188
|
-
outL.connect(merger, 0, 0);
|
|
18189
|
-
outR.connect(merger, 0, 1);
|
|
18190
|
-
const setWidth = (width) => {
|
|
18191
|
-
widthGain.gain.setTargetAtTime(
|
|
18192
|
-
clamp5(width, 0, 200) / 100,
|
|
18193
|
-
ctx.currentTime,
|
|
18194
|
-
0.02
|
|
18195
|
-
);
|
|
18196
|
-
};
|
|
18197
|
-
setWidth(options.width ?? 100);
|
|
18198
|
-
eqHigh.connect(compressor);
|
|
18199
|
-
compressor.connect(splitter);
|
|
18200
|
-
merger.connect(destination);
|
|
18201
|
-
const reverbSendGain = ctx.createGain();
|
|
18202
|
-
reverbSendGain.gain.value = clamp5(options.reverbSend ?? 0, 0, 100) / 100;
|
|
18203
|
-
merger.connect(reverbSendGain);
|
|
18204
|
-
if (options.reverbBus) reverbSendGain.connect(options.reverbBus);
|
|
18205
|
-
return {
|
|
18206
|
-
input,
|
|
18207
|
-
setEqLow: (db) => {
|
|
18208
|
-
eqLow.gain.setTargetAtTime(
|
|
18209
|
-
clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
18210
|
-
ctx.currentTime,
|
|
18211
|
-
0.02
|
|
18212
|
-
);
|
|
18213
|
-
},
|
|
18214
|
-
setEqMid: (db) => {
|
|
18215
|
-
eqMid.gain.setTargetAtTime(
|
|
18216
|
-
clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
18217
|
-
ctx.currentTime,
|
|
18218
|
-
0.02
|
|
18219
|
-
);
|
|
18220
|
-
},
|
|
18221
|
-
setEqHigh: (db) => {
|
|
18222
|
-
eqHigh.gain.setTargetAtTime(
|
|
18223
|
-
clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
18224
|
-
ctx.currentTime,
|
|
18225
|
-
0.02
|
|
18226
|
-
);
|
|
18227
|
-
},
|
|
18228
|
-
setCompression: applyCompression,
|
|
18229
|
-
setWidth,
|
|
18230
|
-
setReverbSend: (amount) => {
|
|
18231
|
-
reverbSendGain.gain.setTargetAtTime(
|
|
18232
|
-
clamp5(amount, 0, 100) / 100,
|
|
18233
|
-
ctx.currentTime,
|
|
18234
|
-
0.02
|
|
18235
|
-
);
|
|
18236
|
-
},
|
|
18237
|
-
dispose: () => {
|
|
18238
|
-
input.disconnect();
|
|
18239
|
-
eqLow.disconnect();
|
|
18240
|
-
eqMid.disconnect();
|
|
18241
|
-
eqHigh.disconnect();
|
|
18242
|
-
compressor.disconnect();
|
|
18243
|
-
splitter.disconnect();
|
|
18244
|
-
mid.disconnect();
|
|
18245
|
-
sideSum.disconnect();
|
|
18246
|
-
sideL.disconnect();
|
|
18247
|
-
sideR.disconnect();
|
|
18248
|
-
widthGain.disconnect();
|
|
18249
|
-
widthInv.disconnect();
|
|
18250
|
-
outL.disconnect();
|
|
18251
|
-
outR.disconnect();
|
|
18252
|
-
merger.disconnect();
|
|
18253
|
-
reverbSendGain.disconnect();
|
|
18254
|
-
}
|
|
18255
|
-
};
|
|
18256
|
-
};
|
|
18257
|
-
|
|
18258
18329
|
// src/clip-meter.ts
|
|
18259
18330
|
var createClipMeter = (ctx, source, options = {}) => {
|
|
18260
18331
|
const threshold = options.threshold ?? 0.98;
|
|
@@ -18459,6 +18530,11 @@ var createDtmStudio = async (options = {}) => {
|
|
|
18459
18530
|
presetUI: true,
|
|
18460
18531
|
...options.features
|
|
18461
18532
|
};
|
|
18533
|
+
const resolveDrumPatterns = (custom) => ({
|
|
18534
|
+
...DRUM_PATTERNS,
|
|
18535
|
+
...SONG_DRUM_PATTERNS,
|
|
18536
|
+
...normalizeDrumPatterns(custom ?? {})
|
|
18537
|
+
});
|
|
18462
18538
|
const audioCtx = options.audioContext ?? new AudioContext({ sampleRate: 44100 });
|
|
18463
18539
|
const masterGain = audioCtx.createGain();
|
|
18464
18540
|
masterGain.gain.value = options.masterVolume ?? 1;
|
|
@@ -19120,7 +19196,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19120
19196
|
await loadRequiredDrums(
|
|
19121
19197
|
getDrumPatternKeys(
|
|
19122
19198
|
meta.drum,
|
|
19123
|
-
opts.drumPatterns ?? options.drumPatterns
|
|
19199
|
+
resolveDrumPatterns(opts.drumPatterns ?? options.drumPatterns)
|
|
19124
19200
|
),
|
|
19125
19201
|
meta.drumFont || "FluidR3_GM_sf2_file:0"
|
|
19126
19202
|
);
|
|
@@ -19211,7 +19287,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19211
19287
|
await loadRequiredDrums(
|
|
19212
19288
|
getDrumPatternKeys(
|
|
19213
19289
|
meta.drum,
|
|
19214
|
-
opts.drumPatterns ?? options.drumPatterns
|
|
19290
|
+
resolveDrumPatterns(opts.drumPatterns ?? options.drumPatterns)
|
|
19215
19291
|
)
|
|
19216
19292
|
);
|
|
19217
19293
|
}
|
|
@@ -19291,7 +19367,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19291
19367
|
await loadRequiredDrums(
|
|
19292
19368
|
getDrumPatternKeys(
|
|
19293
19369
|
meta.drum,
|
|
19294
|
-
opts.drumPatterns ?? options.drumPatterns
|
|
19370
|
+
resolveDrumPatterns(opts.drumPatterns ?? options.drumPatterns)
|
|
19295
19371
|
)
|
|
19296
19372
|
);
|
|
19297
19373
|
}
|