@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.mjs
CHANGED
|
@@ -1335,6 +1335,149 @@ var getDrumPatternKeys = (name, dict) => {
|
|
|
1335
1335
|
return Array.from(keys);
|
|
1336
1336
|
};
|
|
1337
1337
|
|
|
1338
|
+
// src/channel-strip.ts
|
|
1339
|
+
var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
1340
|
+
var compressionParams = (amount) => {
|
|
1341
|
+
const t = clamp(amount, 0, 100) / 100;
|
|
1342
|
+
return {
|
|
1343
|
+
threshold: 0 + (-24 - 0) * t,
|
|
1344
|
+
ratio: 1 + (12 - 1) * t,
|
|
1345
|
+
knee: 0 + (6 - 0) * t,
|
|
1346
|
+
attack: 0.02 + (3e-3 - 0.02) * t,
|
|
1347
|
+
release: 0.25 + (0.15 - 0.25) * t
|
|
1348
|
+
};
|
|
1349
|
+
};
|
|
1350
|
+
var EQ_LOW_FREQ = 200;
|
|
1351
|
+
var EQ_MID_FREQ = 1e3;
|
|
1352
|
+
var EQ_HIGH_FREQ = 5e3;
|
|
1353
|
+
var EQ_MID_Q = 1;
|
|
1354
|
+
var EQ_MAX_DB = 12;
|
|
1355
|
+
var createChannelStrip = (ctx, destination, options = {}) => {
|
|
1356
|
+
const input = ctx.createGain();
|
|
1357
|
+
const eqLow = ctx.createBiquadFilter();
|
|
1358
|
+
eqLow.type = "lowshelf";
|
|
1359
|
+
eqLow.frequency.value = EQ_LOW_FREQ;
|
|
1360
|
+
eqLow.gain.value = clamp(options.eqLow ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
1361
|
+
const eqMid = ctx.createBiquadFilter();
|
|
1362
|
+
eqMid.type = "peaking";
|
|
1363
|
+
eqMid.frequency.value = EQ_MID_FREQ;
|
|
1364
|
+
eqMid.Q.value = EQ_MID_Q;
|
|
1365
|
+
eqMid.gain.value = clamp(options.eqMid ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
1366
|
+
const eqHigh = ctx.createBiquadFilter();
|
|
1367
|
+
eqHigh.type = "highshelf";
|
|
1368
|
+
eqHigh.frequency.value = EQ_HIGH_FREQ;
|
|
1369
|
+
eqHigh.gain.value = clamp(options.eqHigh ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
1370
|
+
input.connect(eqLow);
|
|
1371
|
+
eqLow.connect(eqMid);
|
|
1372
|
+
eqMid.connect(eqHigh);
|
|
1373
|
+
const compressor = ctx.createDynamicsCompressor();
|
|
1374
|
+
const applyCompression = (amount) => {
|
|
1375
|
+
const p = compressionParams(amount);
|
|
1376
|
+
const now = ctx.currentTime;
|
|
1377
|
+
compressor.threshold.setValueAtTime(p.threshold, now);
|
|
1378
|
+
compressor.ratio.setValueAtTime(p.ratio, now);
|
|
1379
|
+
compressor.knee.setValueAtTime(p.knee, now);
|
|
1380
|
+
compressor.attack.setValueAtTime(p.attack, now);
|
|
1381
|
+
compressor.release.setValueAtTime(p.release, now);
|
|
1382
|
+
};
|
|
1383
|
+
applyCompression(options.compression ?? 0);
|
|
1384
|
+
const splitter = ctx.createChannelSplitter(2);
|
|
1385
|
+
const mid = ctx.createGain();
|
|
1386
|
+
mid.gain.value = 0.5;
|
|
1387
|
+
splitter.connect(mid, 0);
|
|
1388
|
+
splitter.connect(mid, 1);
|
|
1389
|
+
const sideSum = ctx.createGain();
|
|
1390
|
+
sideSum.gain.value = 1;
|
|
1391
|
+
const sideL = ctx.createGain();
|
|
1392
|
+
sideL.gain.value = 0.5;
|
|
1393
|
+
const sideR = ctx.createGain();
|
|
1394
|
+
sideR.gain.value = -0.5;
|
|
1395
|
+
splitter.connect(sideL, 0);
|
|
1396
|
+
splitter.connect(sideR, 1);
|
|
1397
|
+
sideL.connect(sideSum);
|
|
1398
|
+
sideR.connect(sideSum);
|
|
1399
|
+
const widthGain = ctx.createGain();
|
|
1400
|
+
sideSum.connect(widthGain);
|
|
1401
|
+
const widthInv = ctx.createGain();
|
|
1402
|
+
widthInv.gain.value = -1;
|
|
1403
|
+
widthGain.connect(widthInv);
|
|
1404
|
+
const outL = ctx.createGain();
|
|
1405
|
+
mid.connect(outL);
|
|
1406
|
+
widthGain.connect(outL);
|
|
1407
|
+
const outR = ctx.createGain();
|
|
1408
|
+
mid.connect(outR);
|
|
1409
|
+
widthInv.connect(outR);
|
|
1410
|
+
const merger = ctx.createChannelMerger(2);
|
|
1411
|
+
outL.connect(merger, 0, 0);
|
|
1412
|
+
outR.connect(merger, 0, 1);
|
|
1413
|
+
const setWidth = (width) => {
|
|
1414
|
+
widthGain.gain.setTargetAtTime(
|
|
1415
|
+
clamp(width, 0, 200) / 100,
|
|
1416
|
+
ctx.currentTime,
|
|
1417
|
+
0.02
|
|
1418
|
+
);
|
|
1419
|
+
};
|
|
1420
|
+
setWidth(options.width ?? 100);
|
|
1421
|
+
eqHigh.connect(compressor);
|
|
1422
|
+
compressor.connect(splitter);
|
|
1423
|
+
merger.connect(destination);
|
|
1424
|
+
const reverbSendGain = ctx.createGain();
|
|
1425
|
+
reverbSendGain.gain.value = clamp(options.reverbSend ?? 0, 0, 100) / 100;
|
|
1426
|
+
merger.connect(reverbSendGain);
|
|
1427
|
+
if (options.reverbBus) reverbSendGain.connect(options.reverbBus);
|
|
1428
|
+
return {
|
|
1429
|
+
input,
|
|
1430
|
+
setEqLow: (db) => {
|
|
1431
|
+
eqLow.gain.setTargetAtTime(
|
|
1432
|
+
clamp(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
1433
|
+
ctx.currentTime,
|
|
1434
|
+
0.02
|
|
1435
|
+
);
|
|
1436
|
+
},
|
|
1437
|
+
setEqMid: (db) => {
|
|
1438
|
+
eqMid.gain.setTargetAtTime(
|
|
1439
|
+
clamp(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
1440
|
+
ctx.currentTime,
|
|
1441
|
+
0.02
|
|
1442
|
+
);
|
|
1443
|
+
},
|
|
1444
|
+
setEqHigh: (db) => {
|
|
1445
|
+
eqHigh.gain.setTargetAtTime(
|
|
1446
|
+
clamp(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
1447
|
+
ctx.currentTime,
|
|
1448
|
+
0.02
|
|
1449
|
+
);
|
|
1450
|
+
},
|
|
1451
|
+
setCompression: applyCompression,
|
|
1452
|
+
setWidth,
|
|
1453
|
+
setReverbSend: (amount) => {
|
|
1454
|
+
reverbSendGain.gain.setTargetAtTime(
|
|
1455
|
+
clamp(amount, 0, 100) / 100,
|
|
1456
|
+
ctx.currentTime,
|
|
1457
|
+
0.02
|
|
1458
|
+
);
|
|
1459
|
+
},
|
|
1460
|
+
dispose: () => {
|
|
1461
|
+
input.disconnect();
|
|
1462
|
+
eqLow.disconnect();
|
|
1463
|
+
eqMid.disconnect();
|
|
1464
|
+
eqHigh.disconnect();
|
|
1465
|
+
compressor.disconnect();
|
|
1466
|
+
splitter.disconnect();
|
|
1467
|
+
mid.disconnect();
|
|
1468
|
+
sideSum.disconnect();
|
|
1469
|
+
sideL.disconnect();
|
|
1470
|
+
sideR.disconnect();
|
|
1471
|
+
widthGain.disconnect();
|
|
1472
|
+
widthInv.disconnect();
|
|
1473
|
+
outL.disconnect();
|
|
1474
|
+
outR.disconnect();
|
|
1475
|
+
merger.disconnect();
|
|
1476
|
+
reverbSendGain.disconnect();
|
|
1477
|
+
}
|
|
1478
|
+
};
|
|
1479
|
+
};
|
|
1480
|
+
|
|
1338
1481
|
// src/chords.ts
|
|
1339
1482
|
var C3 = 48;
|
|
1340
1483
|
var buildChordPlacements = (options) => {
|
|
@@ -2164,7 +2307,7 @@ var normalizeLyricLines = (lines) => {
|
|
|
2164
2307
|
var LYRIC_LINE = /^@@(\d+)\s*(.*)$/;
|
|
2165
2308
|
var isLyricContinuation = (seg) => !/^[@#]/.test(seg);
|
|
2166
2309
|
var splitSegments = (mml) => mml.split(/[;\n\r]+/).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
2167
|
-
var
|
|
2310
|
+
var clamp2 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
|
|
2168
2311
|
var MAX_VOCAL_VOLUME = 400;
|
|
2169
2312
|
var VOCAL_BOOST_DB_PER_PERCENT = 0.08;
|
|
2170
2313
|
var vocalVolumeToGain = (v) => {
|
|
@@ -2198,7 +2341,7 @@ var parseLyrics = (mml) => {
|
|
|
2198
2341
|
if (modelMatch) {
|
|
2199
2342
|
model = modelMatch[1].toLowerCase();
|
|
2200
2343
|
if (modelMatch[2]) {
|
|
2201
|
-
volume =
|
|
2344
|
+
volume = clamp2(Number.parseInt(modelMatch[2], 10), 0, MAX_VOCAL_VOLUME);
|
|
2202
2345
|
}
|
|
2203
2346
|
metaTokens.push(modelMatch[0]);
|
|
2204
2347
|
rest = rest.substring(modelMatch[0].length).trim();
|
|
@@ -2206,28 +2349,28 @@ var parseLyrics = (mml) => {
|
|
|
2206
2349
|
while (true) {
|
|
2207
2350
|
const vMatch = rest.match(/^v(\d+)/i);
|
|
2208
2351
|
if (vMatch) {
|
|
2209
|
-
volume =
|
|
2352
|
+
volume = clamp2(Number.parseInt(vMatch[1], 10), 0, MAX_VOCAL_VOLUME);
|
|
2210
2353
|
metaTokens.push(vMatch[0]);
|
|
2211
2354
|
rest = rest.substring(vMatch[0].length).trim();
|
|
2212
2355
|
continue;
|
|
2213
2356
|
}
|
|
2214
2357
|
const qMatch = rest.match(/^q(\d+)/i);
|
|
2215
2358
|
if (qMatch) {
|
|
2216
|
-
gate =
|
|
2359
|
+
gate = clamp2(Number.parseInt(qMatch[1], 10), 0, 100);
|
|
2217
2360
|
metaTokens.push(qMatch[0]);
|
|
2218
2361
|
rest = rest.substring(qMatch[0].length).trim();
|
|
2219
2362
|
continue;
|
|
2220
2363
|
}
|
|
2221
2364
|
const pMatch = rest.match(/^p(\d+)/i);
|
|
2222
2365
|
if (pMatch) {
|
|
2223
|
-
pan =
|
|
2366
|
+
pan = clamp2(Number.parseInt(pMatch[1], 10), 0, 127);
|
|
2224
2367
|
metaTokens.push(pMatch[0]);
|
|
2225
2368
|
rest = rest.substring(pMatch[0].length).trim();
|
|
2226
2369
|
continue;
|
|
2227
2370
|
}
|
|
2228
2371
|
const oMatch = rest.match(/^o(-?\d+)/i);
|
|
2229
2372
|
if (oMatch) {
|
|
2230
|
-
octave =
|
|
2373
|
+
octave = clamp2(Number.parseInt(oMatch[1], 10), -2, 2);
|
|
2231
2374
|
metaTokens.push(oMatch[0]);
|
|
2232
2375
|
rest = rest.substring(oMatch[0].length).trim();
|
|
2233
2376
|
continue;
|
|
@@ -2241,28 +2384,28 @@ var parseLyrics = (mml) => {
|
|
|
2241
2384
|
}
|
|
2242
2385
|
const rMatch = rest.match(/^r(\d+)/i);
|
|
2243
2386
|
if (rMatch) {
|
|
2244
|
-
reverb =
|
|
2387
|
+
reverb = clamp2(Number.parseInt(rMatch[1], 10), 0, 100);
|
|
2245
2388
|
metaTokens.push(rMatch[0]);
|
|
2246
2389
|
rest = rest.substring(rMatch[0].length).trim();
|
|
2247
2390
|
continue;
|
|
2248
2391
|
}
|
|
2249
2392
|
const gMatch = rest.match(/^g(\d+)/i);
|
|
2250
2393
|
if (gMatch) {
|
|
2251
|
-
gender =
|
|
2394
|
+
gender = clamp2(Number.parseInt(gMatch[1], 10), 0, 100);
|
|
2252
2395
|
metaTokens.push(gMatch[0]);
|
|
2253
2396
|
rest = rest.substring(gMatch[0].length).trim();
|
|
2254
2397
|
continue;
|
|
2255
2398
|
}
|
|
2256
2399
|
const hMatch = rest.match(/^h(\d+)/i);
|
|
2257
2400
|
if (hMatch) {
|
|
2258
|
-
breathiness =
|
|
2401
|
+
breathiness = clamp2(Number.parseInt(hMatch[1], 10), 0, 100);
|
|
2259
2402
|
metaTokens.push(hMatch[0]);
|
|
2260
2403
|
rest = rest.substring(hMatch[0].length).trim();
|
|
2261
2404
|
continue;
|
|
2262
2405
|
}
|
|
2263
2406
|
const eMatch = rest.match(/^e(\d+)/i);
|
|
2264
2407
|
if (eMatch) {
|
|
2265
|
-
delay =
|
|
2408
|
+
delay = clamp2(Number.parseInt(eMatch[1], 10), 0, 100);
|
|
2266
2409
|
metaTokens.push(eMatch[0]);
|
|
2267
2410
|
rest = rest.substring(eMatch[0].length).trim();
|
|
2268
2411
|
continue;
|
|
@@ -3178,7 +3321,7 @@ var PITCH_MAP = {
|
|
|
3178
3321
|
a: 9,
|
|
3179
3322
|
b: 11
|
|
3180
3323
|
};
|
|
3181
|
-
var
|
|
3324
|
+
var clamp3 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
|
|
3182
3325
|
var META_DIRECTIVE = /#(inst|drum|drumfont|volume|drumvolume|reverb|reverbdecay|reverbpredelay|delay|delaydiv|fadein|fadeout|mode)=([\w-]+)/gi;
|
|
3183
3326
|
var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
|
|
3184
3327
|
var TRACK_COMP_DIRECTIVE = /#t(\d+)comp=(\d+)/gi;
|
|
@@ -3202,24 +3345,24 @@ var parseMmlMeta = (mml) => {
|
|
|
3202
3345
|
if (!Number.isNaN(dv)) meta.drumVolume = dv;
|
|
3203
3346
|
} else if (key === "reverb") {
|
|
3204
3347
|
const rv = Number.parseInt(m[2], 10);
|
|
3205
|
-
if (!Number.isNaN(rv)) meta.reverb =
|
|
3348
|
+
if (!Number.isNaN(rv)) meta.reverb = clamp3(rv, 0, 100);
|
|
3206
3349
|
} else if (key === "reverbdecay") {
|
|
3207
3350
|
const rd = Number.parseInt(m[2], 10);
|
|
3208
|
-
if (!Number.isNaN(rd)) meta.reverbDecay =
|
|
3351
|
+
if (!Number.isNaN(rd)) meta.reverbDecay = clamp3(rd, 3, 40);
|
|
3209
3352
|
} else if (key === "reverbpredelay") {
|
|
3210
3353
|
const rp = Number.parseInt(m[2], 10);
|
|
3211
|
-
if (!Number.isNaN(rp)) meta.reverbPreDelay =
|
|
3354
|
+
if (!Number.isNaN(rp)) meta.reverbPreDelay = clamp3(rp, 0, 150);
|
|
3212
3355
|
} else if (key === "delay") {
|
|
3213
3356
|
const dv = Number.parseInt(m[2], 10);
|
|
3214
|
-
if (!Number.isNaN(dv)) meta.delay =
|
|
3357
|
+
if (!Number.isNaN(dv)) meta.delay = clamp3(dv, 0, 100);
|
|
3215
3358
|
} else if (key === "delaydiv") {
|
|
3216
3359
|
if (["4", "8", "8d", "16"].includes(m[2])) meta.delayDivision = m[2];
|
|
3217
3360
|
} else if (key === "fadein") {
|
|
3218
3361
|
const fi = Number.parseInt(m[2], 10);
|
|
3219
|
-
if (!Number.isNaN(fi)) meta.fadeIn =
|
|
3362
|
+
if (!Number.isNaN(fi)) meta.fadeIn = clamp3(fi, 0, 100);
|
|
3220
3363
|
} else if (key === "fadeout") {
|
|
3221
3364
|
const fo = Number.parseInt(m[2], 10);
|
|
3222
|
-
if (!Number.isNaN(fo)) meta.fadeOut =
|
|
3365
|
+
if (!Number.isNaN(fo)) meta.fadeOut = clamp3(fo, 0, 100);
|
|
3223
3366
|
} else if (key === "mode") {
|
|
3224
3367
|
if (m[2] === "simple" || m[2] === "advanced") {
|
|
3225
3368
|
meta.mode = m[2];
|
|
@@ -3236,7 +3379,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3236
3379
|
}
|
|
3237
3380
|
for (const m of mml.matchAll(TRACK_COMP_DIRECTIVE)) {
|
|
3238
3381
|
const idx = Number.parseInt(m[1], 10);
|
|
3239
|
-
const val =
|
|
3382
|
+
const val = clamp3(Number.parseInt(m[2], 10), 0, 100);
|
|
3240
3383
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3241
3384
|
meta.trackCompression ??= {};
|
|
3242
3385
|
meta.trackCompression[idx] = val;
|
|
@@ -3244,7 +3387,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3244
3387
|
}
|
|
3245
3388
|
for (const m of mml.matchAll(TRACK_WIDTH_DIRECTIVE)) {
|
|
3246
3389
|
const idx = Number.parseInt(m[1], 10);
|
|
3247
|
-
const val =
|
|
3390
|
+
const val = clamp3(Number.parseInt(m[2], 10), 0, 200);
|
|
3248
3391
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3249
3392
|
meta.trackWidth ??= {};
|
|
3250
3393
|
meta.trackWidth[idx] = val;
|
|
@@ -3252,7 +3395,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3252
3395
|
}
|
|
3253
3396
|
for (const m of mml.matchAll(TRACK_REVERBSEND_DIRECTIVE)) {
|
|
3254
3397
|
const idx = Number.parseInt(m[1], 10);
|
|
3255
|
-
const val =
|
|
3398
|
+
const val = clamp3(Number.parseInt(m[2], 10), 0, 100);
|
|
3256
3399
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3257
3400
|
meta.trackReverbSend ??= {};
|
|
3258
3401
|
meta.trackReverbSend[idx] = val;
|
|
@@ -3260,7 +3403,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3260
3403
|
}
|
|
3261
3404
|
for (const m of mml.matchAll(TRACK_EQLOW_DIRECTIVE)) {
|
|
3262
3405
|
const idx = Number.parseInt(m[1], 10);
|
|
3263
|
-
const val =
|
|
3406
|
+
const val = clamp3(Number.parseInt(m[2], 10), -12, 12);
|
|
3264
3407
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3265
3408
|
meta.trackEqLow ??= {};
|
|
3266
3409
|
meta.trackEqLow[idx] = val;
|
|
@@ -3268,7 +3411,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3268
3411
|
}
|
|
3269
3412
|
for (const m of mml.matchAll(TRACK_EQMID_DIRECTIVE)) {
|
|
3270
3413
|
const idx = Number.parseInt(m[1], 10);
|
|
3271
|
-
const val =
|
|
3414
|
+
const val = clamp3(Number.parseInt(m[2], 10), -12, 12);
|
|
3272
3415
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3273
3416
|
meta.trackEqMid ??= {};
|
|
3274
3417
|
meta.trackEqMid[idx] = val;
|
|
@@ -3276,7 +3419,7 @@ var parseMmlMeta = (mml) => {
|
|
|
3276
3419
|
}
|
|
3277
3420
|
for (const m of mml.matchAll(TRACK_EQHIGH_DIRECTIVE)) {
|
|
3278
3421
|
const idx = Number.parseInt(m[1], 10);
|
|
3279
|
-
const val =
|
|
3422
|
+
const val = clamp3(Number.parseInt(m[2], 10), -12, 12);
|
|
3280
3423
|
if (!Number.isNaN(idx) && !Number.isNaN(val)) {
|
|
3281
3424
|
meta.trackEqHigh ??= {};
|
|
3282
3425
|
meta.trackEqHigh[idx] = val;
|
|
@@ -3425,7 +3568,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
3425
3568
|
numStr += body[j];
|
|
3426
3569
|
j++;
|
|
3427
3570
|
}
|
|
3428
|
-
const len = numStr ?
|
|
3571
|
+
const len = numStr ? clamp3(Number.parseInt(numStr, 10), 1, 64) : baseLength;
|
|
3429
3572
|
let steps = Math.round(stepsPerBar / len);
|
|
3430
3573
|
while (j < body.length && body[j] === ".") {
|
|
3431
3574
|
steps = Math.round(steps * 1.5);
|
|
@@ -3443,7 +3586,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
3443
3586
|
numStr += body[j];
|
|
3444
3587
|
j++;
|
|
3445
3588
|
}
|
|
3446
|
-
octave = numStr ?
|
|
3589
|
+
octave = numStr ? clamp3(Number.parseInt(numStr, 10), 0, 8) : 4;
|
|
3447
3590
|
pushTok("octave", currentStep, 0, tokStart);
|
|
3448
3591
|
} else if (ch === ">") {
|
|
3449
3592
|
octave = Math.min(8, octave + 1);
|
|
@@ -3460,7 +3603,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
3460
3603
|
numStr += body[j];
|
|
3461
3604
|
j++;
|
|
3462
3605
|
}
|
|
3463
|
-
baseLength =
|
|
3606
|
+
baseLength = clamp3(Number.parseInt(numStr, 10) || 16, 1, 64);
|
|
3464
3607
|
pushTok("length", currentStep, 0, tokStart);
|
|
3465
3608
|
} else if (ch === "r") {
|
|
3466
3609
|
j++;
|
|
@@ -3477,10 +3620,10 @@ var parseMML = (mml, options = {}) => {
|
|
|
3477
3620
|
}
|
|
3478
3621
|
if (ch === "t" && numStr) {
|
|
3479
3622
|
if (bpm === null) {
|
|
3480
|
-
bpm =
|
|
3623
|
+
bpm = clamp3(Number.parseInt(numStr, 10), 1, 255);
|
|
3481
3624
|
}
|
|
3482
3625
|
} else if (ch === "v" && numStr) {
|
|
3483
|
-
velocity =
|
|
3626
|
+
velocity = clamp3(Number.parseInt(numStr, 10), 0, 127);
|
|
3484
3627
|
trackVelocity.set(trackIndex, velocity);
|
|
3485
3628
|
}
|
|
3486
3629
|
pushTok("ctrl", currentStep, 0, tokStart);
|
|
@@ -3514,7 +3657,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
3514
3657
|
numStr += body[j];
|
|
3515
3658
|
j++;
|
|
3516
3659
|
}
|
|
3517
|
-
octave = numStr ?
|
|
3660
|
+
octave = numStr ? clamp3(Number.parseInt(numStr, 10), 0, 8) : 4;
|
|
3518
3661
|
} else {
|
|
3519
3662
|
j++;
|
|
3520
3663
|
}
|
|
@@ -3576,6 +3719,38 @@ var parseMML = (mml, options = {}) => {
|
|
|
3576
3719
|
};
|
|
3577
3720
|
};
|
|
3578
3721
|
|
|
3722
|
+
// src/reverb.ts
|
|
3723
|
+
var DEFAULT_REVERB_DECAY_SEC = 2.2;
|
|
3724
|
+
var MIN_REVERB_DECAY_SEC = 0.3;
|
|
3725
|
+
var MAX_REVERB_DECAY_SEC = 4;
|
|
3726
|
+
var IMPULSE_DECAY_CURVE = 2.5;
|
|
3727
|
+
var DEFAULT_REVERB_PREDELAY_MS = 0;
|
|
3728
|
+
var MIN_REVERB_PREDELAY_MS = 0;
|
|
3729
|
+
var MAX_REVERB_PREDELAY_MS = 150;
|
|
3730
|
+
var createReverbImpulse = (ctx, decaySec = DEFAULT_REVERB_DECAY_SEC) => {
|
|
3731
|
+
const rate = ctx.sampleRate;
|
|
3732
|
+
const length = Math.max(
|
|
3733
|
+
1,
|
|
3734
|
+
Math.floor(
|
|
3735
|
+
rate * Math.max(
|
|
3736
|
+
MIN_REVERB_DECAY_SEC,
|
|
3737
|
+
Math.min(MAX_REVERB_DECAY_SEC, decaySec)
|
|
3738
|
+
)
|
|
3739
|
+
)
|
|
3740
|
+
);
|
|
3741
|
+
const impulse = ctx.createBuffer(2, length, rate);
|
|
3742
|
+
for (let ch = 0; ch < impulse.numberOfChannels; ch++) {
|
|
3743
|
+
const data = impulse.getChannelData(ch);
|
|
3744
|
+
for (let i2 = 0; i2 < length; i2++) {
|
|
3745
|
+
const envelope = (1 - i2 / length) ** IMPULSE_DECAY_CURVE;
|
|
3746
|
+
data[i2] = (Math.random() * 2 - 1) * envelope;
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
return impulse;
|
|
3750
|
+
};
|
|
3751
|
+
var REVERB_MAX_WET = 0.6;
|
|
3752
|
+
var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100 * REVERB_MAX_WET;
|
|
3753
|
+
|
|
3579
3754
|
// src/sequencer.ts
|
|
3580
3755
|
var STEPS_PER_BEAT = 48;
|
|
3581
3756
|
var PLAN_TIME = 0.5;
|
|
@@ -6153,9 +6328,54 @@ var playPlacements = (placements, options) => {
|
|
|
6153
6328
|
});
|
|
6154
6329
|
const ownsCtx = !options.audioContext;
|
|
6155
6330
|
const ctx = options.audioContext ?? new AudioContext();
|
|
6156
|
-
const
|
|
6331
|
+
const rawDestination = options.destination ?? ctx.destination;
|
|
6157
6332
|
const useSynth = options.synth ?? !options.onPlayNote;
|
|
6158
|
-
const
|
|
6333
|
+
const finalMix = ctx.createGain();
|
|
6334
|
+
const masterGain = ctx.createGain();
|
|
6335
|
+
masterGain.connect(finalMix);
|
|
6336
|
+
const reverbPreDelay = ctx.createDelay(MAX_REVERB_PREDELAY_MS / 1e3);
|
|
6337
|
+
reverbPreDelay.delayTime.value = (options.metaReverbPreDelay ?? DEFAULT_REVERB_PREDELAY_MS) / 1e3;
|
|
6338
|
+
const reverbConvolver = ctx.createConvolver();
|
|
6339
|
+
const reverbDecaySec = (options.metaReverbDecay ?? 22) / 10 || DEFAULT_REVERB_DECAY_SEC;
|
|
6340
|
+
reverbConvolver.buffer = createReverbImpulse(ctx, reverbDecaySec);
|
|
6341
|
+
reverbConvolver.normalize = true;
|
|
6342
|
+
const reverbWetGain = ctx.createGain();
|
|
6343
|
+
reverbWetGain.gain.value = reverbAmountToGain(options.metaReverb ?? 0);
|
|
6344
|
+
reverbPreDelay.connect(reverbConvolver);
|
|
6345
|
+
reverbConvolver.connect(reverbWetGain);
|
|
6346
|
+
reverbWetGain.connect(finalMix);
|
|
6347
|
+
finalMix.connect(rawDestination);
|
|
6348
|
+
const channelStrips = /* @__PURE__ */ new Map();
|
|
6349
|
+
const getChannelStrip = (index) => {
|
|
6350
|
+
let strip = channelStrips.get(index);
|
|
6351
|
+
if (!strip) {
|
|
6352
|
+
strip = createChannelStrip(ctx, masterGain, {
|
|
6353
|
+
compression: options.trackCompression?.[index] ?? 0,
|
|
6354
|
+
width: options.trackWidth?.[index] ?? 100,
|
|
6355
|
+
eqLow: options.trackEqLow?.[index] ?? 0,
|
|
6356
|
+
eqMid: options.trackEqMid?.[index] ?? 0,
|
|
6357
|
+
eqHigh: options.trackEqHigh?.[index] ?? 0,
|
|
6358
|
+
reverbSend: options.trackReverbSend?.[index] ?? 0,
|
|
6359
|
+
reverbBus: reverbPreDelay
|
|
6360
|
+
});
|
|
6361
|
+
channelStrips.set(index, strip);
|
|
6362
|
+
}
|
|
6363
|
+
return strip;
|
|
6364
|
+
};
|
|
6365
|
+
const synths = /* @__PURE__ */ new Map();
|
|
6366
|
+
const getSynth = (index) => {
|
|
6367
|
+
let s = synths.get(index);
|
|
6368
|
+
if (!s) {
|
|
6369
|
+
s = createSynth(ctx, getChannelStrip(index).input);
|
|
6370
|
+
synths.set(index, s);
|
|
6371
|
+
}
|
|
6372
|
+
return s;
|
|
6373
|
+
};
|
|
6374
|
+
const drumSynth = useSynth ? createSynth(ctx, masterGain) : null;
|
|
6375
|
+
const trackIndexById = /* @__PURE__ */ new Map();
|
|
6376
|
+
trackIndices.forEach((idx) => {
|
|
6377
|
+
trackIndexById.set(TRACK_ID_BY_INDEX[idx] ?? `t${idx}`, idx);
|
|
6378
|
+
});
|
|
6159
6379
|
const pauseWhenHidden = options.pauseWhenHidden ?? ownsCtx;
|
|
6160
6380
|
let playing = false;
|
|
6161
6381
|
const seq = createSequencer({
|
|
@@ -6170,12 +6390,14 @@ var playPlacements = (placements, options) => {
|
|
|
6170
6390
|
getAudioTime: () => ctx.currentTime,
|
|
6171
6391
|
onPlayNote: (e) => {
|
|
6172
6392
|
options.onPlayNote?.(e);
|
|
6173
|
-
|
|
6393
|
+
if (!useSynth) return;
|
|
6394
|
+
const index = trackIndexById.get(e.trackId);
|
|
6395
|
+
(index === void 0 ? drumSynth : getSynth(index))?.playNote(e);
|
|
6174
6396
|
},
|
|
6175
6397
|
onPlayDrum: (e) => {
|
|
6176
6398
|
const velocity = e.velocity * (drumVolume / 100) * (masterVolume / 100);
|
|
6177
6399
|
options.onPlayDrum?.({ ...e, velocity });
|
|
6178
|
-
|
|
6400
|
+
drumSynth?.playDrum({ ...e, velocity });
|
|
6179
6401
|
},
|
|
6180
6402
|
onTick: (step) => {
|
|
6181
6403
|
options.onTick?.(step);
|
|
@@ -6226,6 +6448,7 @@ var playPlacements = (placements, options) => {
|
|
|
6226
6448
|
if (pauseWhenHidden && typeof document !== "undefined") {
|
|
6227
6449
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
6228
6450
|
}
|
|
6451
|
+
for (const strip of channelStrips.values()) strip.dispose();
|
|
6229
6452
|
if (ownsCtx) void ctx.close();
|
|
6230
6453
|
};
|
|
6231
6454
|
return {
|
|
@@ -6245,7 +6468,16 @@ var playMML = (mml, options = {}) => {
|
|
|
6245
6468
|
bpm,
|
|
6246
6469
|
metaVolume: meta.volume,
|
|
6247
6470
|
metaDrum: meta.drum,
|
|
6248
|
-
metaDrumVolume: meta.drumVolume
|
|
6471
|
+
metaDrumVolume: meta.drumVolume,
|
|
6472
|
+
metaReverb: meta.reverb,
|
|
6473
|
+
metaReverbDecay: meta.reverbDecay,
|
|
6474
|
+
metaReverbPreDelay: meta.reverbPreDelay,
|
|
6475
|
+
trackCompression: meta.trackCompression,
|
|
6476
|
+
trackWidth: meta.trackWidth,
|
|
6477
|
+
trackReverbSend: meta.trackReverbSend,
|
|
6478
|
+
trackEqLow: meta.trackEqLow,
|
|
6479
|
+
trackEqMid: meta.trackEqMid,
|
|
6480
|
+
trackEqHigh: meta.trackEqHigh
|
|
6249
6481
|
});
|
|
6250
6482
|
};
|
|
6251
6483
|
var playNote = (options) => {
|
|
@@ -9551,20 +9783,23 @@ var adjustZone = async (ctx, fontName, zone) => {
|
|
|
9551
9783
|
const newLength = attackLength + loopLengthFrame * repeatCount + releaseLength;
|
|
9552
9784
|
let totalPeak = 0;
|
|
9553
9785
|
let loopPeak = 0;
|
|
9554
|
-
|
|
9555
|
-
const
|
|
9556
|
-
for (let i2 = 0; i2 <
|
|
9557
|
-
const abs = Math.abs(
|
|
9786
|
+
for (let ch = 0; ch < oldBuf.numberOfChannels; ch++) {
|
|
9787
|
+
const chData = oldBuf.getChannelData(ch);
|
|
9788
|
+
for (let i2 = 0; i2 < chData.length; i2++) {
|
|
9789
|
+
const abs = Math.abs(chData[i2]);
|
|
9558
9790
|
if (abs > totalPeak) totalPeak = abs;
|
|
9559
9791
|
if (i2 >= loopStartFrame && i2 < loopEndFrame) {
|
|
9560
9792
|
if (abs > loopPeak) loopPeak = abs;
|
|
9561
9793
|
}
|
|
9562
9794
|
}
|
|
9563
9795
|
}
|
|
9796
|
+
const decay = isDecayInstrument(fontName);
|
|
9797
|
+
const targetRatio = decay ? 0.4 : 0.75;
|
|
9798
|
+
const maxMultiplier = decay ? 6 : 20;
|
|
9564
9799
|
let gainMultiplier = 1;
|
|
9565
|
-
if (
|
|
9566
|
-
gainMultiplier = totalPeak *
|
|
9567
|
-
if (gainMultiplier >
|
|
9800
|
+
if (loopPeak > 0 && totalPeak > 0 && loopPeak < totalPeak * 0.8) {
|
|
9801
|
+
gainMultiplier = totalPeak * targetRatio / loopPeak;
|
|
9802
|
+
if (gainMultiplier > maxMultiplier) gainMultiplier = maxMultiplier;
|
|
9568
9803
|
}
|
|
9569
9804
|
try {
|
|
9570
9805
|
const newBuf = ctx.createBuffer(
|
|
@@ -10896,14 +11131,14 @@ var DELAY_DIVISIONS = [
|
|
|
10896
11131
|
{ value: "8d", label: "\u4ED8\u70B98\u5206", beats: 0.75 },
|
|
10897
11132
|
{ value: "16", label: "16\u5206", beats: 0.25 }
|
|
10898
11133
|
];
|
|
10899
|
-
var
|
|
11134
|
+
var clamp4 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
10900
11135
|
var divisionToSeconds = (division, bpm) => {
|
|
10901
11136
|
const beats = DELAY_DIVISIONS.find((d) => d.value === division)?.beats ?? 0.5;
|
|
10902
11137
|
const safeBpm = bpm > 0 ? bpm : 120;
|
|
10903
11138
|
return 60 / safeBpm * beats;
|
|
10904
11139
|
};
|
|
10905
11140
|
var DELAY_MAX_WET = 0.45;
|
|
10906
|
-
var delayAmountToGain = (amount) =>
|
|
11141
|
+
var delayAmountToGain = (amount) => clamp4(amount, 0, 100) / 100 * DELAY_MAX_WET;
|
|
10907
11142
|
var FEEDBACK_GAIN = 0.3;
|
|
10908
11143
|
var MAX_DELAY_SEC = 2;
|
|
10909
11144
|
var createDelayBus = (ctx, destination, options = {}) => {
|
|
@@ -12978,38 +13213,6 @@ var isChordHeavyTrack = (notes, threshold = 0.6) => {
|
|
|
12978
13213
|
return chordNotes / notes.length >= threshold;
|
|
12979
13214
|
};
|
|
12980
13215
|
|
|
12981
|
-
// src/reverb.ts
|
|
12982
|
-
var DEFAULT_REVERB_DECAY_SEC = 2.2;
|
|
12983
|
-
var MIN_REVERB_DECAY_SEC = 0.3;
|
|
12984
|
-
var MAX_REVERB_DECAY_SEC = 4;
|
|
12985
|
-
var IMPULSE_DECAY_CURVE = 2.5;
|
|
12986
|
-
var DEFAULT_REVERB_PREDELAY_MS = 0;
|
|
12987
|
-
var MIN_REVERB_PREDELAY_MS = 0;
|
|
12988
|
-
var MAX_REVERB_PREDELAY_MS = 150;
|
|
12989
|
-
var createReverbImpulse = (ctx, decaySec = DEFAULT_REVERB_DECAY_SEC) => {
|
|
12990
|
-
const rate = ctx.sampleRate;
|
|
12991
|
-
const length = Math.max(
|
|
12992
|
-
1,
|
|
12993
|
-
Math.floor(
|
|
12994
|
-
rate * Math.max(
|
|
12995
|
-
MIN_REVERB_DECAY_SEC,
|
|
12996
|
-
Math.min(MAX_REVERB_DECAY_SEC, decaySec)
|
|
12997
|
-
)
|
|
12998
|
-
)
|
|
12999
|
-
);
|
|
13000
|
-
const impulse = ctx.createBuffer(2, length, rate);
|
|
13001
|
-
for (let ch = 0; ch < impulse.numberOfChannels; ch++) {
|
|
13002
|
-
const data = impulse.getChannelData(ch);
|
|
13003
|
-
for (let i2 = 0; i2 < length; i2++) {
|
|
13004
|
-
const envelope = (1 - i2 / length) ** IMPULSE_DECAY_CURVE;
|
|
13005
|
-
data[i2] = (Math.random() * 2 - 1) * envelope;
|
|
13006
|
-
}
|
|
13007
|
-
}
|
|
13008
|
-
return impulse;
|
|
13009
|
-
};
|
|
13010
|
-
var REVERB_MAX_WET = 0.6;
|
|
13011
|
-
var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100 * REVERB_MAX_WET;
|
|
13012
|
-
|
|
13013
13216
|
// src/daw.ts
|
|
13014
13217
|
var CHORD_INFO_HTML2 = `
|
|
13015
13218
|
<div class="dtm-modal-body-content">
|
|
@@ -13445,7 +13648,7 @@ var deriveCustomVocalKeyFromUrl = (url2) => {
|
|
|
13445
13648
|
if (name && /^[0-9]/.test(name)) name = `_${name}`;
|
|
13446
13649
|
return CUSTOM_VOCAL_KEY_RE.test(name) ? name : "";
|
|
13447
13650
|
};
|
|
13448
|
-
var
|
|
13651
|
+
var clamp5 = (v, min, max) => Math.min(Math.max(v, min), max);
|
|
13449
13652
|
var normalizeInstrumentName = (name) => {
|
|
13450
13653
|
if (!name) return "";
|
|
13451
13654
|
const stripped = name.replace(/\s+/g, "").toLowerCase();
|
|
@@ -13534,6 +13737,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
13534
13737
|
refs.drumFontSelect.value = currentDrumFont;
|
|
13535
13738
|
let currentInstrument = "";
|
|
13536
13739
|
let activeTrackId = options.initialActiveTrack ?? trackConfigs[0].id;
|
|
13740
|
+
let trackFxAdvancedOpen = false;
|
|
13741
|
+
let lyricAdvancedOpen = false;
|
|
13537
13742
|
let activeToolMode = "pen";
|
|
13538
13743
|
let currentInsertLength = 48;
|
|
13539
13744
|
let snapGridSteps = 12;
|
|
@@ -13830,7 +14035,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
13830
14035
|
const thumbW = Math.max(40, canvas.width / totalContentWidth * sbW);
|
|
13831
14036
|
const ratio = currentOffsetX / maxOffsetX;
|
|
13832
14037
|
refs.hScrollThumb.style.width = `${thumbW}px`;
|
|
13833
|
-
refs.hScrollThumb.style.left = `${
|
|
14038
|
+
refs.hScrollThumb.style.left = `${clamp5(ratio * (sbW - thumbW), 0, sbW - thumbW)}px`;
|
|
13834
14039
|
}
|
|
13835
14040
|
const totalHeight = renderConfig.keyCount * renderConfig.keyHeight;
|
|
13836
14041
|
const sbH = refs.vScroll.clientHeight;
|
|
@@ -13900,9 +14105,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
13900
14105
|
if (maxOffsetX <= 0) return;
|
|
13901
14106
|
const rect = refs.hScroll.getBoundingClientRect();
|
|
13902
14107
|
const thumbW = Number.parseFloat(refs.hScrollThumb.style.width) || 40;
|
|
13903
|
-
const x2 =
|
|
14108
|
+
const x2 = clamp5(clientX - rect.left - thumbW / 2, 0, rect.width - thumbW);
|
|
13904
14109
|
const ratio = x2 / (rect.width - thumbW);
|
|
13905
|
-
currentOffsetX =
|
|
14110
|
+
currentOffsetX = clamp5(ratio * maxOffsetX, 0, maxOffsetX);
|
|
13906
14111
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
13907
14112
|
redrawAll();
|
|
13908
14113
|
};
|
|
@@ -13911,9 +14116,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
13911
14116
|
if (maxOffset <= 0) return;
|
|
13912
14117
|
const rect = refs.vScroll.getBoundingClientRect();
|
|
13913
14118
|
const thumbH = Number.parseFloat(refs.vScrollThumb.style.height) || 40;
|
|
13914
|
-
const y =
|
|
14119
|
+
const y = clamp5(clientY - rect.top - thumbH / 2, 0, rect.height - thumbH);
|
|
13915
14120
|
const ratio = y / (rect.height - thumbH);
|
|
13916
|
-
currentOffsetY =
|
|
14121
|
+
currentOffsetY = clamp5(ratio * maxOffset, 0, maxOffset);
|
|
13917
14122
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
13918
14123
|
redrawAll();
|
|
13919
14124
|
};
|
|
@@ -13960,8 +14165,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
13960
14165
|
if (dx !== 0 || dy !== 0) {
|
|
13961
14166
|
const maxOffsetX = getMaxOffsetX();
|
|
13962
14167
|
const maxOffsetY = getMaxOffsetY();
|
|
13963
|
-
currentOffsetX =
|
|
13964
|
-
currentOffsetY =
|
|
14168
|
+
currentOffsetX = clamp5(currentOffsetX + dx, 0, maxOffsetX);
|
|
14169
|
+
currentOffsetY = clamp5(currentOffsetY + dy, 0, maxOffsetY);
|
|
13965
14170
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
13966
14171
|
onPointerMove(lastMoveEvent);
|
|
13967
14172
|
}
|
|
@@ -14351,12 +14556,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
14351
14556
|
"wheel",
|
|
14352
14557
|
(event) => {
|
|
14353
14558
|
event.preventDefault();
|
|
14354
|
-
currentOffsetY =
|
|
14559
|
+
currentOffsetY = clamp5(
|
|
14355
14560
|
currentOffsetY + event.deltaY,
|
|
14356
14561
|
0,
|
|
14357
14562
|
getMaxOffsetY()
|
|
14358
14563
|
);
|
|
14359
|
-
currentOffsetX =
|
|
14564
|
+
currentOffsetX = clamp5(
|
|
14360
14565
|
currentOffsetX + event.deltaX,
|
|
14361
14566
|
0,
|
|
14362
14567
|
getMaxOffsetX()
|
|
@@ -14395,7 +14600,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14395
14600
|
const centerStep = (currentOffsetX + canvas.width / 2) / renderConfig.stepWidth;
|
|
14396
14601
|
renderConfig.stepWidth = BASE_STEP_WIDTH * (zoomX * 2) / 100;
|
|
14397
14602
|
refs.zoomXLabel.textContent = `${zoomX}%`;
|
|
14398
|
-
currentOffsetX =
|
|
14603
|
+
currentOffsetX = clamp5(
|
|
14399
14604
|
centerStep * renderConfig.stepWidth - canvas.width / 2,
|
|
14400
14605
|
0,
|
|
14401
14606
|
getMaxOffsetX()
|
|
@@ -14408,7 +14613,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14408
14613
|
const centerKey = (currentOffsetY + canvas.height / 2) / renderConfig.keyHeight;
|
|
14409
14614
|
renderConfig.keyHeight = BASE_KEY_HEIGHT * zoomY / 100;
|
|
14410
14615
|
refs.zoomYLabel.textContent = `${zoomY}%`;
|
|
14411
|
-
currentOffsetY =
|
|
14616
|
+
currentOffsetY = clamp5(
|
|
14412
14617
|
centerKey * renderConfig.keyHeight - canvas.height / 2,
|
|
14413
14618
|
0,
|
|
14414
14619
|
getMaxOffsetY()
|
|
@@ -14458,7 +14663,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14458
14663
|
const threshold = currentOffsetX / renderConfig.stepWidth + visibleSteps - 4;
|
|
14459
14664
|
if (currentPlayStep > threshold) {
|
|
14460
14665
|
const visibleBars = Math.round(visibleSteps / renderConfig.stepsPerBar);
|
|
14461
|
-
currentOffsetX =
|
|
14666
|
+
currentOffsetX = clamp5(
|
|
14462
14667
|
currentOffsetX + visibleBars * renderConfig.stepsPerBar * renderConfig.stepWidth,
|
|
14463
14668
|
0,
|
|
14464
14669
|
getMaxOffsetX()
|
|
@@ -14545,7 +14750,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14545
14750
|
}
|
|
14546
14751
|
if (playbackState !== "paused") {
|
|
14547
14752
|
const canvas = getGridCanvas();
|
|
14548
|
-
currentOffsetX =
|
|
14753
|
+
currentOffsetX = clamp5(
|
|
14549
14754
|
playStartStep * renderConfig.stepWidth - canvas.width * 0.5,
|
|
14550
14755
|
0,
|
|
14551
14756
|
getMaxOffsetX()
|
|
@@ -14649,7 +14854,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14649
14854
|
<input type="range" class="dtm-range dtm-grow" data-dtm="track-vol" min="0" max="127" value="${active.volume}">
|
|
14650
14855
|
<span class="dtm-label" data-dtm="track-vol-label">${active.volume}</span>
|
|
14651
14856
|
</div>
|
|
14652
|
-
<details class="dtm-advanced" data-dtm="track-fx-advanced">
|
|
14857
|
+
<details class="dtm-advanced" data-dtm="track-fx-advanced" ${trackFxAdvancedOpen ? "open" : ""}>
|
|
14653
14858
|
<summary>\u8A73\u7D30\u8A2D\u5B9A\uFF08EQ\u30FB\u97F3\u5727\u30FB\u30B9\u30C6\u30EC\u30AA\u5E45\uFF09</summary>
|
|
14654
14859
|
<div class="dtm-row">
|
|
14655
14860
|
<span class="dtm-label">EQ\u4F4E\u57DF</span>
|
|
@@ -14686,6 +14891,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
14686
14891
|
<button class="dtm-infobtn" data-dtm="track-reverb-send-info" title="\u30EA\u30D0\u30FC\u30D6\u9001\u308A\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
|
|
14687
14892
|
</div>
|
|
14688
14893
|
</details>`;
|
|
14894
|
+
refs.trackBody.querySelector(
|
|
14895
|
+
'[data-dtm="track-fx-advanced"]'
|
|
14896
|
+
).addEventListener("toggle", (e) => {
|
|
14897
|
+
trackFxAdvancedOpen = e.target.open;
|
|
14898
|
+
});
|
|
14689
14899
|
const volInput = refs.trackBody.querySelector(
|
|
14690
14900
|
'[data-dtm="track-vol"]'
|
|
14691
14901
|
);
|
|
@@ -14899,7 +15109,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14899
15109
|
<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">
|
|
14900
15110
|
<span class="dtm-label" data-dtm="lyric-vol-label"></span>
|
|
14901
15111
|
</div>
|
|
14902
|
-
<details class="dtm-advanced" data-dtm="lyric-advanced">
|
|
15112
|
+
<details class="dtm-advanced" data-dtm="lyric-advanced" ${lyricAdvancedOpen ? "open" : ""}>
|
|
14903
15113
|
<summary>\u8A73\u7D30\u8A2D\u5B9A</summary>
|
|
14904
15114
|
<div class="dtm-row">
|
|
14905
15115
|
<span class="dtm-label">\u30AA\u30AF\u30BF\u30FC\u30D6</span>
|
|
@@ -14960,6 +15170,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
14960
15170
|
<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>
|
|
14961
15171
|
</div>`;
|
|
14962
15172
|
refs.trackBody.appendChild(lyricDiv);
|
|
15173
|
+
lyricDiv.querySelector(
|
|
15174
|
+
'[data-dtm="lyric-advanced"]'
|
|
15175
|
+
).addEventListener("toggle", (e) => {
|
|
15176
|
+
lyricAdvancedOpen = e.target.open;
|
|
15177
|
+
});
|
|
14963
15178
|
const lyricModelSel = lyricDiv.querySelector(
|
|
14964
15179
|
'[data-dtm="lyric-model"]'
|
|
14965
15180
|
);
|
|
@@ -15582,7 +15797,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15582
15797
|
const canvas = getGridCanvas();
|
|
15583
15798
|
const yIndex = renderConfig.keyCount - 1 - (pitch - renderConfig.pitchRangeStart);
|
|
15584
15799
|
const logicalY = yIndex * renderConfig.keyHeight;
|
|
15585
|
-
currentOffsetY =
|
|
15800
|
+
currentOffsetY = clamp5(
|
|
15586
15801
|
logicalY - (canvas.height - renderConfig.keyHeight) / 2,
|
|
15587
15802
|
0,
|
|
15588
15803
|
getMaxOffsetY()
|
|
@@ -16157,8 +16372,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
16157
16372
|
refs.reverbAmount.value = "20";
|
|
16158
16373
|
refs.reverbAmountLabel.textContent = "20%";
|
|
16159
16374
|
options.onReverbChange?.(20);
|
|
16160
|
-
const bpmT =
|
|
16161
|
-
reverbDecay =
|
|
16375
|
+
const bpmT = clamp5((bpm - 60) / (180 - 60), 0, 1);
|
|
16376
|
+
reverbDecay = clamp5(
|
|
16162
16377
|
3 - bpmT * (3 - 0.9),
|
|
16163
16378
|
MIN_REVERB_DECAY_SEC,
|
|
16164
16379
|
MAX_REVERB_DECAY_SEC
|
|
@@ -17089,7 +17304,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
17089
17304
|
currentPlayStep = step;
|
|
17090
17305
|
playbackState = "paused";
|
|
17091
17306
|
const canvas = getGridCanvas();
|
|
17092
|
-
currentOffsetX =
|
|
17307
|
+
currentOffsetX = clamp5(
|
|
17093
17308
|
step * renderConfig.stepWidth - canvas.width * 0.5,
|
|
17094
17309
|
0,
|
|
17095
17310
|
getMaxOffsetX()
|
|
@@ -17135,11 +17350,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
17135
17350
|
getViewState,
|
|
17136
17351
|
setViewState: (state) => {
|
|
17137
17352
|
if (typeof state.zoomX === "number") {
|
|
17138
|
-
zoomX =
|
|
17353
|
+
zoomX = clamp5(state.zoomX, 25, 200);
|
|
17139
17354
|
applyZoomX();
|
|
17140
17355
|
}
|
|
17141
17356
|
if (typeof state.zoomY === "number") {
|
|
17142
|
-
zoomY =
|
|
17357
|
+
zoomY = clamp5(state.zoomY, 50, 200);
|
|
17143
17358
|
applyZoomY();
|
|
17144
17359
|
}
|
|
17145
17360
|
if (typeof state.decomposeChord === "boolean") {
|
|
@@ -17161,24 +17376,24 @@ var mountDAW = (target, options = {}) => {
|
|
|
17161
17376
|
forcePauseAt,
|
|
17162
17377
|
setLoading,
|
|
17163
17378
|
setMasterVolume: (volume) => {
|
|
17164
|
-
masterVolume =
|
|
17379
|
+
masterVolume = clamp5(volume, 0, 100);
|
|
17165
17380
|
refs.masterVolume.value = String(masterVolume);
|
|
17166
17381
|
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
17167
17382
|
options.singingVoices?.setVolume(masterVolume / 100);
|
|
17168
17383
|
},
|
|
17169
17384
|
setVolume: (volume) => {
|
|
17170
|
-
masterVolume =
|
|
17385
|
+
masterVolume = clamp5(volume, 0, 100);
|
|
17171
17386
|
refs.masterVolume.value = String(masterVolume);
|
|
17172
17387
|
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
17173
17388
|
options.singingVoices?.setVolume(masterVolume / 100);
|
|
17174
17389
|
},
|
|
17175
17390
|
setDrumVolume: (volume) => {
|
|
17176
|
-
drumVolume =
|
|
17391
|
+
drumVolume = clamp5(volume, 0, 100);
|
|
17177
17392
|
refs.drumVolume.value = String(drumVolume);
|
|
17178
17393
|
refs.drumVolumeLabel.textContent = `${drumVolume}%`;
|
|
17179
17394
|
},
|
|
17180
17395
|
setReverbAmount: (amount) => {
|
|
17181
|
-
reverbAmount =
|
|
17396
|
+
reverbAmount = clamp5(amount, 0, 100);
|
|
17182
17397
|
refs.reverbAmount.value = String(reverbAmount);
|
|
17183
17398
|
refs.reverbAmountLabel.textContent = `${reverbAmount}%`;
|
|
17184
17399
|
options.onReverbChange?.(reverbAmount);
|
|
@@ -17202,7 +17417,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
17202
17417
|
options.onReverbPreDelayChange?.(reverbPreDelay);
|
|
17203
17418
|
},
|
|
17204
17419
|
setDelayAmount: (amount) => {
|
|
17205
|
-
delayAmount =
|
|
17420
|
+
delayAmount = clamp5(amount, 0, 100);
|
|
17206
17421
|
refs.delayAmount.value = String(delayAmount);
|
|
17207
17422
|
refs.delayAmountLabel.textContent = `${delayAmount}%`;
|
|
17208
17423
|
options.onDelayChange?.(delayAmount);
|
|
@@ -17981,149 +18196,6 @@ var isSupported = midiJsonParser.isSupported;
|
|
|
17981
18196
|
var parseArrayBuffer = midiJsonParser.parseArrayBuffer;
|
|
17982
18197
|
URL.revokeObjectURL(url);
|
|
17983
18198
|
|
|
17984
|
-
// src/channel-strip.ts
|
|
17985
|
-
var clamp5 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
17986
|
-
var compressionParams = (amount) => {
|
|
17987
|
-
const t = clamp5(amount, 0, 100) / 100;
|
|
17988
|
-
return {
|
|
17989
|
-
threshold: 0 + (-24 - 0) * t,
|
|
17990
|
-
ratio: 1 + (12 - 1) * t,
|
|
17991
|
-
knee: 0 + (6 - 0) * t,
|
|
17992
|
-
attack: 0.02 + (3e-3 - 0.02) * t,
|
|
17993
|
-
release: 0.25 + (0.15 - 0.25) * t
|
|
17994
|
-
};
|
|
17995
|
-
};
|
|
17996
|
-
var EQ_LOW_FREQ = 200;
|
|
17997
|
-
var EQ_MID_FREQ = 1e3;
|
|
17998
|
-
var EQ_HIGH_FREQ = 5e3;
|
|
17999
|
-
var EQ_MID_Q = 1;
|
|
18000
|
-
var EQ_MAX_DB = 12;
|
|
18001
|
-
var createChannelStrip = (ctx, destination, options = {}) => {
|
|
18002
|
-
const input = ctx.createGain();
|
|
18003
|
-
const eqLow = ctx.createBiquadFilter();
|
|
18004
|
-
eqLow.type = "lowshelf";
|
|
18005
|
-
eqLow.frequency.value = EQ_LOW_FREQ;
|
|
18006
|
-
eqLow.gain.value = clamp5(options.eqLow ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
18007
|
-
const eqMid = ctx.createBiquadFilter();
|
|
18008
|
-
eqMid.type = "peaking";
|
|
18009
|
-
eqMid.frequency.value = EQ_MID_FREQ;
|
|
18010
|
-
eqMid.Q.value = EQ_MID_Q;
|
|
18011
|
-
eqMid.gain.value = clamp5(options.eqMid ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
18012
|
-
const eqHigh = ctx.createBiquadFilter();
|
|
18013
|
-
eqHigh.type = "highshelf";
|
|
18014
|
-
eqHigh.frequency.value = EQ_HIGH_FREQ;
|
|
18015
|
-
eqHigh.gain.value = clamp5(options.eqHigh ?? 0, -EQ_MAX_DB, EQ_MAX_DB);
|
|
18016
|
-
input.connect(eqLow);
|
|
18017
|
-
eqLow.connect(eqMid);
|
|
18018
|
-
eqMid.connect(eqHigh);
|
|
18019
|
-
const compressor = ctx.createDynamicsCompressor();
|
|
18020
|
-
const applyCompression = (amount) => {
|
|
18021
|
-
const p = compressionParams(amount);
|
|
18022
|
-
const now = ctx.currentTime;
|
|
18023
|
-
compressor.threshold.setValueAtTime(p.threshold, now);
|
|
18024
|
-
compressor.ratio.setValueAtTime(p.ratio, now);
|
|
18025
|
-
compressor.knee.setValueAtTime(p.knee, now);
|
|
18026
|
-
compressor.attack.setValueAtTime(p.attack, now);
|
|
18027
|
-
compressor.release.setValueAtTime(p.release, now);
|
|
18028
|
-
};
|
|
18029
|
-
applyCompression(options.compression ?? 0);
|
|
18030
|
-
const splitter = ctx.createChannelSplitter(2);
|
|
18031
|
-
const mid = ctx.createGain();
|
|
18032
|
-
mid.gain.value = 0.5;
|
|
18033
|
-
splitter.connect(mid, 0);
|
|
18034
|
-
splitter.connect(mid, 1);
|
|
18035
|
-
const sideSum = ctx.createGain();
|
|
18036
|
-
sideSum.gain.value = 1;
|
|
18037
|
-
const sideL = ctx.createGain();
|
|
18038
|
-
sideL.gain.value = 0.5;
|
|
18039
|
-
const sideR = ctx.createGain();
|
|
18040
|
-
sideR.gain.value = -0.5;
|
|
18041
|
-
splitter.connect(sideL, 0);
|
|
18042
|
-
splitter.connect(sideR, 1);
|
|
18043
|
-
sideL.connect(sideSum);
|
|
18044
|
-
sideR.connect(sideSum);
|
|
18045
|
-
const widthGain = ctx.createGain();
|
|
18046
|
-
sideSum.connect(widthGain);
|
|
18047
|
-
const widthInv = ctx.createGain();
|
|
18048
|
-
widthInv.gain.value = -1;
|
|
18049
|
-
widthGain.connect(widthInv);
|
|
18050
|
-
const outL = ctx.createGain();
|
|
18051
|
-
mid.connect(outL);
|
|
18052
|
-
widthGain.connect(outL);
|
|
18053
|
-
const outR = ctx.createGain();
|
|
18054
|
-
mid.connect(outR);
|
|
18055
|
-
widthInv.connect(outR);
|
|
18056
|
-
const merger = ctx.createChannelMerger(2);
|
|
18057
|
-
outL.connect(merger, 0, 0);
|
|
18058
|
-
outR.connect(merger, 0, 1);
|
|
18059
|
-
const setWidth = (width) => {
|
|
18060
|
-
widthGain.gain.setTargetAtTime(
|
|
18061
|
-
clamp5(width, 0, 200) / 100,
|
|
18062
|
-
ctx.currentTime,
|
|
18063
|
-
0.02
|
|
18064
|
-
);
|
|
18065
|
-
};
|
|
18066
|
-
setWidth(options.width ?? 100);
|
|
18067
|
-
eqHigh.connect(compressor);
|
|
18068
|
-
compressor.connect(splitter);
|
|
18069
|
-
merger.connect(destination);
|
|
18070
|
-
const reverbSendGain = ctx.createGain();
|
|
18071
|
-
reverbSendGain.gain.value = clamp5(options.reverbSend ?? 0, 0, 100) / 100;
|
|
18072
|
-
merger.connect(reverbSendGain);
|
|
18073
|
-
if (options.reverbBus) reverbSendGain.connect(options.reverbBus);
|
|
18074
|
-
return {
|
|
18075
|
-
input,
|
|
18076
|
-
setEqLow: (db) => {
|
|
18077
|
-
eqLow.gain.setTargetAtTime(
|
|
18078
|
-
clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
18079
|
-
ctx.currentTime,
|
|
18080
|
-
0.02
|
|
18081
|
-
);
|
|
18082
|
-
},
|
|
18083
|
-
setEqMid: (db) => {
|
|
18084
|
-
eqMid.gain.setTargetAtTime(
|
|
18085
|
-
clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
18086
|
-
ctx.currentTime,
|
|
18087
|
-
0.02
|
|
18088
|
-
);
|
|
18089
|
-
},
|
|
18090
|
-
setEqHigh: (db) => {
|
|
18091
|
-
eqHigh.gain.setTargetAtTime(
|
|
18092
|
-
clamp5(db, -EQ_MAX_DB, EQ_MAX_DB),
|
|
18093
|
-
ctx.currentTime,
|
|
18094
|
-
0.02
|
|
18095
|
-
);
|
|
18096
|
-
},
|
|
18097
|
-
setCompression: applyCompression,
|
|
18098
|
-
setWidth,
|
|
18099
|
-
setReverbSend: (amount) => {
|
|
18100
|
-
reverbSendGain.gain.setTargetAtTime(
|
|
18101
|
-
clamp5(amount, 0, 100) / 100,
|
|
18102
|
-
ctx.currentTime,
|
|
18103
|
-
0.02
|
|
18104
|
-
);
|
|
18105
|
-
},
|
|
18106
|
-
dispose: () => {
|
|
18107
|
-
input.disconnect();
|
|
18108
|
-
eqLow.disconnect();
|
|
18109
|
-
eqMid.disconnect();
|
|
18110
|
-
eqHigh.disconnect();
|
|
18111
|
-
compressor.disconnect();
|
|
18112
|
-
splitter.disconnect();
|
|
18113
|
-
mid.disconnect();
|
|
18114
|
-
sideSum.disconnect();
|
|
18115
|
-
sideL.disconnect();
|
|
18116
|
-
sideR.disconnect();
|
|
18117
|
-
widthGain.disconnect();
|
|
18118
|
-
widthInv.disconnect();
|
|
18119
|
-
outL.disconnect();
|
|
18120
|
-
outR.disconnect();
|
|
18121
|
-
merger.disconnect();
|
|
18122
|
-
reverbSendGain.disconnect();
|
|
18123
|
-
}
|
|
18124
|
-
};
|
|
18125
|
-
};
|
|
18126
|
-
|
|
18127
18199
|
// src/clip-meter.ts
|
|
18128
18200
|
var createClipMeter = (ctx, source, options = {}) => {
|
|
18129
18201
|
const threshold = options.threshold ?? 0.98;
|