@onjmin/dtm 0.1.88 → 0.1.89
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 +317 -245
- package/dist/index.mjs +317 -245
- 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,38 @@ 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 REVERB_MAX_WET = 0.6;
|
|
3883
|
+
var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100 * REVERB_MAX_WET;
|
|
3884
|
+
|
|
3710
3885
|
// src/sequencer.ts
|
|
3711
3886
|
var STEPS_PER_BEAT = 48;
|
|
3712
3887
|
var PLAN_TIME = 0.5;
|
|
@@ -6284,9 +6459,54 @@ var playPlacements = (placements, options) => {
|
|
|
6284
6459
|
});
|
|
6285
6460
|
const ownsCtx = !options.audioContext;
|
|
6286
6461
|
const ctx = options.audioContext ?? new AudioContext();
|
|
6287
|
-
const
|
|
6462
|
+
const rawDestination = options.destination ?? ctx.destination;
|
|
6288
6463
|
const useSynth = options.synth ?? !options.onPlayNote;
|
|
6289
|
-
const
|
|
6464
|
+
const finalMix = ctx.createGain();
|
|
6465
|
+
const masterGain = ctx.createGain();
|
|
6466
|
+
masterGain.connect(finalMix);
|
|
6467
|
+
const reverbPreDelay = ctx.createDelay(MAX_REVERB_PREDELAY_MS / 1e3);
|
|
6468
|
+
reverbPreDelay.delayTime.value = (options.metaReverbPreDelay ?? DEFAULT_REVERB_PREDELAY_MS) / 1e3;
|
|
6469
|
+
const reverbConvolver = ctx.createConvolver();
|
|
6470
|
+
const reverbDecaySec = (options.metaReverbDecay ?? 22) / 10 || DEFAULT_REVERB_DECAY_SEC;
|
|
6471
|
+
reverbConvolver.buffer = createReverbImpulse(ctx, reverbDecaySec);
|
|
6472
|
+
reverbConvolver.normalize = true;
|
|
6473
|
+
const reverbWetGain = ctx.createGain();
|
|
6474
|
+
reverbWetGain.gain.value = reverbAmountToGain(options.metaReverb ?? 0);
|
|
6475
|
+
reverbPreDelay.connect(reverbConvolver);
|
|
6476
|
+
reverbConvolver.connect(reverbWetGain);
|
|
6477
|
+
reverbWetGain.connect(finalMix);
|
|
6478
|
+
finalMix.connect(rawDestination);
|
|
6479
|
+
const channelStrips = /* @__PURE__ */ new Map();
|
|
6480
|
+
const getChannelStrip = (index) => {
|
|
6481
|
+
let strip = channelStrips.get(index);
|
|
6482
|
+
if (!strip) {
|
|
6483
|
+
strip = createChannelStrip(ctx, masterGain, {
|
|
6484
|
+
compression: options.trackCompression?.[index] ?? 0,
|
|
6485
|
+
width: options.trackWidth?.[index] ?? 100,
|
|
6486
|
+
eqLow: options.trackEqLow?.[index] ?? 0,
|
|
6487
|
+
eqMid: options.trackEqMid?.[index] ?? 0,
|
|
6488
|
+
eqHigh: options.trackEqHigh?.[index] ?? 0,
|
|
6489
|
+
reverbSend: options.trackReverbSend?.[index] ?? 0,
|
|
6490
|
+
reverbBus: reverbPreDelay
|
|
6491
|
+
});
|
|
6492
|
+
channelStrips.set(index, strip);
|
|
6493
|
+
}
|
|
6494
|
+
return strip;
|
|
6495
|
+
};
|
|
6496
|
+
const synths = /* @__PURE__ */ new Map();
|
|
6497
|
+
const getSynth = (index) => {
|
|
6498
|
+
let s = synths.get(index);
|
|
6499
|
+
if (!s) {
|
|
6500
|
+
s = createSynth(ctx, getChannelStrip(index).input);
|
|
6501
|
+
synths.set(index, s);
|
|
6502
|
+
}
|
|
6503
|
+
return s;
|
|
6504
|
+
};
|
|
6505
|
+
const drumSynth = useSynth ? createSynth(ctx, masterGain) : null;
|
|
6506
|
+
const trackIndexById = /* @__PURE__ */ new Map();
|
|
6507
|
+
trackIndices.forEach((idx) => {
|
|
6508
|
+
trackIndexById.set(TRACK_ID_BY_INDEX[idx] ?? `t${idx}`, idx);
|
|
6509
|
+
});
|
|
6290
6510
|
const pauseWhenHidden = options.pauseWhenHidden ?? ownsCtx;
|
|
6291
6511
|
let playing = false;
|
|
6292
6512
|
const seq = createSequencer({
|
|
@@ -6301,12 +6521,14 @@ var playPlacements = (placements, options) => {
|
|
|
6301
6521
|
getAudioTime: () => ctx.currentTime,
|
|
6302
6522
|
onPlayNote: (e) => {
|
|
6303
6523
|
options.onPlayNote?.(e);
|
|
6304
|
-
|
|
6524
|
+
if (!useSynth) return;
|
|
6525
|
+
const index = trackIndexById.get(e.trackId);
|
|
6526
|
+
(index === void 0 ? drumSynth : getSynth(index))?.playNote(e);
|
|
6305
6527
|
},
|
|
6306
6528
|
onPlayDrum: (e) => {
|
|
6307
6529
|
const velocity = e.velocity * (drumVolume / 100) * (masterVolume / 100);
|
|
6308
6530
|
options.onPlayDrum?.({ ...e, velocity });
|
|
6309
|
-
|
|
6531
|
+
drumSynth?.playDrum({ ...e, velocity });
|
|
6310
6532
|
},
|
|
6311
6533
|
onTick: (step) => {
|
|
6312
6534
|
options.onTick?.(step);
|
|
@@ -6357,6 +6579,7 @@ var playPlacements = (placements, options) => {
|
|
|
6357
6579
|
if (pauseWhenHidden && typeof document !== "undefined") {
|
|
6358
6580
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
6359
6581
|
}
|
|
6582
|
+
for (const strip of channelStrips.values()) strip.dispose();
|
|
6360
6583
|
if (ownsCtx) void ctx.close();
|
|
6361
6584
|
};
|
|
6362
6585
|
return {
|
|
@@ -6376,7 +6599,16 @@ var playMML = (mml, options = {}) => {
|
|
|
6376
6599
|
bpm,
|
|
6377
6600
|
metaVolume: meta.volume,
|
|
6378
6601
|
metaDrum: meta.drum,
|
|
6379
|
-
metaDrumVolume: meta.drumVolume
|
|
6602
|
+
metaDrumVolume: meta.drumVolume,
|
|
6603
|
+
metaReverb: meta.reverb,
|
|
6604
|
+
metaReverbDecay: meta.reverbDecay,
|
|
6605
|
+
metaReverbPreDelay: meta.reverbPreDelay,
|
|
6606
|
+
trackCompression: meta.trackCompression,
|
|
6607
|
+
trackWidth: meta.trackWidth,
|
|
6608
|
+
trackReverbSend: meta.trackReverbSend,
|
|
6609
|
+
trackEqLow: meta.trackEqLow,
|
|
6610
|
+
trackEqMid: meta.trackEqMid,
|
|
6611
|
+
trackEqHigh: meta.trackEqHigh
|
|
6380
6612
|
});
|
|
6381
6613
|
};
|
|
6382
6614
|
var playNote = (options) => {
|
|
@@ -9682,20 +9914,23 @@ var adjustZone = async (ctx, fontName, zone) => {
|
|
|
9682
9914
|
const newLength = attackLength + loopLengthFrame * repeatCount + releaseLength;
|
|
9683
9915
|
let totalPeak = 0;
|
|
9684
9916
|
let loopPeak = 0;
|
|
9685
|
-
|
|
9686
|
-
const
|
|
9687
|
-
for (let i2 = 0; i2 <
|
|
9688
|
-
const abs = Math.abs(
|
|
9917
|
+
for (let ch = 0; ch < oldBuf.numberOfChannels; ch++) {
|
|
9918
|
+
const chData = oldBuf.getChannelData(ch);
|
|
9919
|
+
for (let i2 = 0; i2 < chData.length; i2++) {
|
|
9920
|
+
const abs = Math.abs(chData[i2]);
|
|
9689
9921
|
if (abs > totalPeak) totalPeak = abs;
|
|
9690
9922
|
if (i2 >= loopStartFrame && i2 < loopEndFrame) {
|
|
9691
9923
|
if (abs > loopPeak) loopPeak = abs;
|
|
9692
9924
|
}
|
|
9693
9925
|
}
|
|
9694
9926
|
}
|
|
9927
|
+
const decay = isDecayInstrument(fontName);
|
|
9928
|
+
const targetRatio = decay ? 0.4 : 0.75;
|
|
9929
|
+
const maxMultiplier = decay ? 6 : 20;
|
|
9695
9930
|
let gainMultiplier = 1;
|
|
9696
|
-
if (
|
|
9697
|
-
gainMultiplier = totalPeak *
|
|
9698
|
-
if (gainMultiplier >
|
|
9931
|
+
if (loopPeak > 0 && totalPeak > 0 && loopPeak < totalPeak * 0.8) {
|
|
9932
|
+
gainMultiplier = totalPeak * targetRatio / loopPeak;
|
|
9933
|
+
if (gainMultiplier > maxMultiplier) gainMultiplier = maxMultiplier;
|
|
9699
9934
|
}
|
|
9700
9935
|
try {
|
|
9701
9936
|
const newBuf = ctx.createBuffer(
|
|
@@ -11027,14 +11262,14 @@ var DELAY_DIVISIONS = [
|
|
|
11027
11262
|
{ value: "8d", label: "\u4ED8\u70B98\u5206", beats: 0.75 },
|
|
11028
11263
|
{ value: "16", label: "16\u5206", beats: 0.25 }
|
|
11029
11264
|
];
|
|
11030
|
-
var
|
|
11265
|
+
var clamp4 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
11031
11266
|
var divisionToSeconds = (division, bpm) => {
|
|
11032
11267
|
const beats = DELAY_DIVISIONS.find((d) => d.value === division)?.beats ?? 0.5;
|
|
11033
11268
|
const safeBpm = bpm > 0 ? bpm : 120;
|
|
11034
11269
|
return 60 / safeBpm * beats;
|
|
11035
11270
|
};
|
|
11036
11271
|
var DELAY_MAX_WET = 0.45;
|
|
11037
|
-
var delayAmountToGain = (amount) =>
|
|
11272
|
+
var delayAmountToGain = (amount) => clamp4(amount, 0, 100) / 100 * DELAY_MAX_WET;
|
|
11038
11273
|
var FEEDBACK_GAIN = 0.3;
|
|
11039
11274
|
var MAX_DELAY_SEC = 2;
|
|
11040
11275
|
var createDelayBus = (ctx, destination, options = {}) => {
|
|
@@ -13109,38 +13344,6 @@ var isChordHeavyTrack = (notes, threshold = 0.6) => {
|
|
|
13109
13344
|
return chordNotes / notes.length >= threshold;
|
|
13110
13345
|
};
|
|
13111
13346
|
|
|
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
13347
|
// src/daw.ts
|
|
13145
13348
|
var CHORD_INFO_HTML2 = `
|
|
13146
13349
|
<div class="dtm-modal-body-content">
|
|
@@ -13576,7 +13779,7 @@ var deriveCustomVocalKeyFromUrl = (url2) => {
|
|
|
13576
13779
|
if (name && /^[0-9]/.test(name)) name = `_${name}`;
|
|
13577
13780
|
return CUSTOM_VOCAL_KEY_RE.test(name) ? name : "";
|
|
13578
13781
|
};
|
|
13579
|
-
var
|
|
13782
|
+
var clamp5 = (v, min, max) => Math.min(Math.max(v, min), max);
|
|
13580
13783
|
var normalizeInstrumentName = (name) => {
|
|
13581
13784
|
if (!name) return "";
|
|
13582
13785
|
const stripped = name.replace(/\s+/g, "").toLowerCase();
|
|
@@ -13665,6 +13868,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
13665
13868
|
refs.drumFontSelect.value = currentDrumFont;
|
|
13666
13869
|
let currentInstrument = "";
|
|
13667
13870
|
let activeTrackId = options.initialActiveTrack ?? trackConfigs[0].id;
|
|
13871
|
+
let trackFxAdvancedOpen = false;
|
|
13872
|
+
let lyricAdvancedOpen = false;
|
|
13668
13873
|
let activeToolMode = "pen";
|
|
13669
13874
|
let currentInsertLength = 48;
|
|
13670
13875
|
let snapGridSteps = 12;
|
|
@@ -13961,7 +14166,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
13961
14166
|
const thumbW = Math.max(40, canvas.width / totalContentWidth * sbW);
|
|
13962
14167
|
const ratio = currentOffsetX / maxOffsetX;
|
|
13963
14168
|
refs.hScrollThumb.style.width = `${thumbW}px`;
|
|
13964
|
-
refs.hScrollThumb.style.left = `${
|
|
14169
|
+
refs.hScrollThumb.style.left = `${clamp5(ratio * (sbW - thumbW), 0, sbW - thumbW)}px`;
|
|
13965
14170
|
}
|
|
13966
14171
|
const totalHeight = renderConfig.keyCount * renderConfig.keyHeight;
|
|
13967
14172
|
const sbH = refs.vScroll.clientHeight;
|
|
@@ -14031,9 +14236,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
14031
14236
|
if (maxOffsetX <= 0) return;
|
|
14032
14237
|
const rect = refs.hScroll.getBoundingClientRect();
|
|
14033
14238
|
const thumbW = Number.parseFloat(refs.hScrollThumb.style.width) || 40;
|
|
14034
|
-
const x2 =
|
|
14239
|
+
const x2 = clamp5(clientX - rect.left - thumbW / 2, 0, rect.width - thumbW);
|
|
14035
14240
|
const ratio = x2 / (rect.width - thumbW);
|
|
14036
|
-
currentOffsetX =
|
|
14241
|
+
currentOffsetX = clamp5(ratio * maxOffsetX, 0, maxOffsetX);
|
|
14037
14242
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
14038
14243
|
redrawAll();
|
|
14039
14244
|
};
|
|
@@ -14042,9 +14247,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
14042
14247
|
if (maxOffset <= 0) return;
|
|
14043
14248
|
const rect = refs.vScroll.getBoundingClientRect();
|
|
14044
14249
|
const thumbH = Number.parseFloat(refs.vScrollThumb.style.height) || 40;
|
|
14045
|
-
const y =
|
|
14250
|
+
const y = clamp5(clientY - rect.top - thumbH / 2, 0, rect.height - thumbH);
|
|
14046
14251
|
const ratio = y / (rect.height - thumbH);
|
|
14047
|
-
currentOffsetY =
|
|
14252
|
+
currentOffsetY = clamp5(ratio * maxOffset, 0, maxOffset);
|
|
14048
14253
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
14049
14254
|
redrawAll();
|
|
14050
14255
|
};
|
|
@@ -14091,8 +14296,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14091
14296
|
if (dx !== 0 || dy !== 0) {
|
|
14092
14297
|
const maxOffsetX = getMaxOffsetX();
|
|
14093
14298
|
const maxOffsetY = getMaxOffsetY();
|
|
14094
|
-
currentOffsetX =
|
|
14095
|
-
currentOffsetY =
|
|
14299
|
+
currentOffsetX = clamp5(currentOffsetX + dx, 0, maxOffsetX);
|
|
14300
|
+
currentOffsetY = clamp5(currentOffsetY + dy, 0, maxOffsetY);
|
|
14096
14301
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
14097
14302
|
onPointerMove(lastMoveEvent);
|
|
14098
14303
|
}
|
|
@@ -14482,12 +14687,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
14482
14687
|
"wheel",
|
|
14483
14688
|
(event) => {
|
|
14484
14689
|
event.preventDefault();
|
|
14485
|
-
currentOffsetY =
|
|
14690
|
+
currentOffsetY = clamp5(
|
|
14486
14691
|
currentOffsetY + event.deltaY,
|
|
14487
14692
|
0,
|
|
14488
14693
|
getMaxOffsetY()
|
|
14489
14694
|
);
|
|
14490
|
-
currentOffsetX =
|
|
14695
|
+
currentOffsetX = clamp5(
|
|
14491
14696
|
currentOffsetX + event.deltaX,
|
|
14492
14697
|
0,
|
|
14493
14698
|
getMaxOffsetX()
|
|
@@ -14526,7 +14731,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14526
14731
|
const centerStep = (currentOffsetX + canvas.width / 2) / renderConfig.stepWidth;
|
|
14527
14732
|
renderConfig.stepWidth = BASE_STEP_WIDTH * (zoomX * 2) / 100;
|
|
14528
14733
|
refs.zoomXLabel.textContent = `${zoomX}%`;
|
|
14529
|
-
currentOffsetX =
|
|
14734
|
+
currentOffsetX = clamp5(
|
|
14530
14735
|
centerStep * renderConfig.stepWidth - canvas.width / 2,
|
|
14531
14736
|
0,
|
|
14532
14737
|
getMaxOffsetX()
|
|
@@ -14539,7 +14744,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14539
14744
|
const centerKey = (currentOffsetY + canvas.height / 2) / renderConfig.keyHeight;
|
|
14540
14745
|
renderConfig.keyHeight = BASE_KEY_HEIGHT * zoomY / 100;
|
|
14541
14746
|
refs.zoomYLabel.textContent = `${zoomY}%`;
|
|
14542
|
-
currentOffsetY =
|
|
14747
|
+
currentOffsetY = clamp5(
|
|
14543
14748
|
centerKey * renderConfig.keyHeight - canvas.height / 2,
|
|
14544
14749
|
0,
|
|
14545
14750
|
getMaxOffsetY()
|
|
@@ -14589,7 +14794,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14589
14794
|
const threshold = currentOffsetX / renderConfig.stepWidth + visibleSteps - 4;
|
|
14590
14795
|
if (currentPlayStep > threshold) {
|
|
14591
14796
|
const visibleBars = Math.round(visibleSteps / renderConfig.stepsPerBar);
|
|
14592
|
-
currentOffsetX =
|
|
14797
|
+
currentOffsetX = clamp5(
|
|
14593
14798
|
currentOffsetX + visibleBars * renderConfig.stepsPerBar * renderConfig.stepWidth,
|
|
14594
14799
|
0,
|
|
14595
14800
|
getMaxOffsetX()
|
|
@@ -14676,7 +14881,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14676
14881
|
}
|
|
14677
14882
|
if (playbackState !== "paused") {
|
|
14678
14883
|
const canvas = getGridCanvas();
|
|
14679
|
-
currentOffsetX =
|
|
14884
|
+
currentOffsetX = clamp5(
|
|
14680
14885
|
playStartStep * renderConfig.stepWidth - canvas.width * 0.5,
|
|
14681
14886
|
0,
|
|
14682
14887
|
getMaxOffsetX()
|
|
@@ -14780,7 +14985,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14780
14985
|
<input type="range" class="dtm-range dtm-grow" data-dtm="track-vol" min="0" max="127" value="${active.volume}">
|
|
14781
14986
|
<span class="dtm-label" data-dtm="track-vol-label">${active.volume}</span>
|
|
14782
14987
|
</div>
|
|
14783
|
-
<details class="dtm-advanced" data-dtm="track-fx-advanced">
|
|
14988
|
+
<details class="dtm-advanced" data-dtm="track-fx-advanced" ${trackFxAdvancedOpen ? "open" : ""}>
|
|
14784
14989
|
<summary>\u8A73\u7D30\u8A2D\u5B9A\uFF08EQ\u30FB\u97F3\u5727\u30FB\u30B9\u30C6\u30EC\u30AA\u5E45\uFF09</summary>
|
|
14785
14990
|
<div class="dtm-row">
|
|
14786
14991
|
<span class="dtm-label">EQ\u4F4E\u57DF</span>
|
|
@@ -14817,6 +15022,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
14817
15022
|
<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
15023
|
</div>
|
|
14819
15024
|
</details>`;
|
|
15025
|
+
refs.trackBody.querySelector(
|
|
15026
|
+
'[data-dtm="track-fx-advanced"]'
|
|
15027
|
+
).addEventListener("toggle", (e) => {
|
|
15028
|
+
trackFxAdvancedOpen = e.target.open;
|
|
15029
|
+
});
|
|
14820
15030
|
const volInput = refs.trackBody.querySelector(
|
|
14821
15031
|
'[data-dtm="track-vol"]'
|
|
14822
15032
|
);
|
|
@@ -15030,7 +15240,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15030
15240
|
<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
15241
|
<span class="dtm-label" data-dtm="lyric-vol-label"></span>
|
|
15032
15242
|
</div>
|
|
15033
|
-
<details class="dtm-advanced" data-dtm="lyric-advanced">
|
|
15243
|
+
<details class="dtm-advanced" data-dtm="lyric-advanced" ${lyricAdvancedOpen ? "open" : ""}>
|
|
15034
15244
|
<summary>\u8A73\u7D30\u8A2D\u5B9A</summary>
|
|
15035
15245
|
<div class="dtm-row">
|
|
15036
15246
|
<span class="dtm-label">\u30AA\u30AF\u30BF\u30FC\u30D6</span>
|
|
@@ -15091,6 +15301,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
15091
15301
|
<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
15302
|
</div>`;
|
|
15093
15303
|
refs.trackBody.appendChild(lyricDiv);
|
|
15304
|
+
lyricDiv.querySelector(
|
|
15305
|
+
'[data-dtm="lyric-advanced"]'
|
|
15306
|
+
).addEventListener("toggle", (e) => {
|
|
15307
|
+
lyricAdvancedOpen = e.target.open;
|
|
15308
|
+
});
|
|
15094
15309
|
const lyricModelSel = lyricDiv.querySelector(
|
|
15095
15310
|
'[data-dtm="lyric-model"]'
|
|
15096
15311
|
);
|
|
@@ -15713,7 +15928,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15713
15928
|
const canvas = getGridCanvas();
|
|
15714
15929
|
const yIndex = renderConfig.keyCount - 1 - (pitch - renderConfig.pitchRangeStart);
|
|
15715
15930
|
const logicalY = yIndex * renderConfig.keyHeight;
|
|
15716
|
-
currentOffsetY =
|
|
15931
|
+
currentOffsetY = clamp5(
|
|
15717
15932
|
logicalY - (canvas.height - renderConfig.keyHeight) / 2,
|
|
15718
15933
|
0,
|
|
15719
15934
|
getMaxOffsetY()
|
|
@@ -16288,8 +16503,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
16288
16503
|
refs.reverbAmount.value = "20";
|
|
16289
16504
|
refs.reverbAmountLabel.textContent = "20%";
|
|
16290
16505
|
options.onReverbChange?.(20);
|
|
16291
|
-
const bpmT =
|
|
16292
|
-
reverbDecay =
|
|
16506
|
+
const bpmT = clamp5((bpm - 60) / (180 - 60), 0, 1);
|
|
16507
|
+
reverbDecay = clamp5(
|
|
16293
16508
|
3 - bpmT * (3 - 0.9),
|
|
16294
16509
|
MIN_REVERB_DECAY_SEC,
|
|
16295
16510
|
MAX_REVERB_DECAY_SEC
|
|
@@ -17220,7 +17435,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
17220
17435
|
currentPlayStep = step;
|
|
17221
17436
|
playbackState = "paused";
|
|
17222
17437
|
const canvas = getGridCanvas();
|
|
17223
|
-
currentOffsetX =
|
|
17438
|
+
currentOffsetX = clamp5(
|
|
17224
17439
|
step * renderConfig.stepWidth - canvas.width * 0.5,
|
|
17225
17440
|
0,
|
|
17226
17441
|
getMaxOffsetX()
|
|
@@ -17266,11 +17481,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
17266
17481
|
getViewState,
|
|
17267
17482
|
setViewState: (state) => {
|
|
17268
17483
|
if (typeof state.zoomX === "number") {
|
|
17269
|
-
zoomX =
|
|
17484
|
+
zoomX = clamp5(state.zoomX, 25, 200);
|
|
17270
17485
|
applyZoomX();
|
|
17271
17486
|
}
|
|
17272
17487
|
if (typeof state.zoomY === "number") {
|
|
17273
|
-
zoomY =
|
|
17488
|
+
zoomY = clamp5(state.zoomY, 50, 200);
|
|
17274
17489
|
applyZoomY();
|
|
17275
17490
|
}
|
|
17276
17491
|
if (typeof state.decomposeChord === "boolean") {
|
|
@@ -17292,24 +17507,24 @@ var mountDAW = (target, options = {}) => {
|
|
|
17292
17507
|
forcePauseAt,
|
|
17293
17508
|
setLoading,
|
|
17294
17509
|
setMasterVolume: (volume) => {
|
|
17295
|
-
masterVolume =
|
|
17510
|
+
masterVolume = clamp5(volume, 0, 100);
|
|
17296
17511
|
refs.masterVolume.value = String(masterVolume);
|
|
17297
17512
|
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
17298
17513
|
options.singingVoices?.setVolume(masterVolume / 100);
|
|
17299
17514
|
},
|
|
17300
17515
|
setVolume: (volume) => {
|
|
17301
|
-
masterVolume =
|
|
17516
|
+
masterVolume = clamp5(volume, 0, 100);
|
|
17302
17517
|
refs.masterVolume.value = String(masterVolume);
|
|
17303
17518
|
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
17304
17519
|
options.singingVoices?.setVolume(masterVolume / 100);
|
|
17305
17520
|
},
|
|
17306
17521
|
setDrumVolume: (volume) => {
|
|
17307
|
-
drumVolume =
|
|
17522
|
+
drumVolume = clamp5(volume, 0, 100);
|
|
17308
17523
|
refs.drumVolume.value = String(drumVolume);
|
|
17309
17524
|
refs.drumVolumeLabel.textContent = `${drumVolume}%`;
|
|
17310
17525
|
},
|
|
17311
17526
|
setReverbAmount: (amount) => {
|
|
17312
|
-
reverbAmount =
|
|
17527
|
+
reverbAmount = clamp5(amount, 0, 100);
|
|
17313
17528
|
refs.reverbAmount.value = String(reverbAmount);
|
|
17314
17529
|
refs.reverbAmountLabel.textContent = `${reverbAmount}%`;
|
|
17315
17530
|
options.onReverbChange?.(reverbAmount);
|
|
@@ -17333,7 +17548,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
17333
17548
|
options.onReverbPreDelayChange?.(reverbPreDelay);
|
|
17334
17549
|
},
|
|
17335
17550
|
setDelayAmount: (amount) => {
|
|
17336
|
-
delayAmount =
|
|
17551
|
+
delayAmount = clamp5(amount, 0, 100);
|
|
17337
17552
|
refs.delayAmount.value = String(delayAmount);
|
|
17338
17553
|
refs.delayAmountLabel.textContent = `${delayAmount}%`;
|
|
17339
17554
|
options.onDelayChange?.(delayAmount);
|
|
@@ -18112,149 +18327,6 @@ var isSupported = midiJsonParser.isSupported;
|
|
|
18112
18327
|
var parseArrayBuffer = midiJsonParser.parseArrayBuffer;
|
|
18113
18328
|
URL.revokeObjectURL(url);
|
|
18114
18329
|
|
|
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
18330
|
// src/clip-meter.ts
|
|
18259
18331
|
var createClipMeter = (ctx, source, options = {}) => {
|
|
18260
18332
|
const threshold = options.threshold ?? 0.98;
|