@onjmin/dtm 0.1.88 → 0.1.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +325 -249
- package/dist/index.mjs +325 -249
- package/package.json +1 -1
package/dist/index.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,37 @@ 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 reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100;
|
|
3752
|
+
|
|
3579
3753
|
// src/sequencer.ts
|
|
3580
3754
|
var STEPS_PER_BEAT = 48;
|
|
3581
3755
|
var PLAN_TIME = 0.5;
|
|
@@ -6153,9 +6327,54 @@ var playPlacements = (placements, options) => {
|
|
|
6153
6327
|
});
|
|
6154
6328
|
const ownsCtx = !options.audioContext;
|
|
6155
6329
|
const ctx = options.audioContext ?? new AudioContext();
|
|
6156
|
-
const
|
|
6330
|
+
const rawDestination = options.destination ?? ctx.destination;
|
|
6157
6331
|
const useSynth = options.synth ?? !options.onPlayNote;
|
|
6158
|
-
const
|
|
6332
|
+
const finalMix = ctx.createGain();
|
|
6333
|
+
const masterGain = ctx.createGain();
|
|
6334
|
+
masterGain.connect(finalMix);
|
|
6335
|
+
const reverbPreDelay = ctx.createDelay(MAX_REVERB_PREDELAY_MS / 1e3);
|
|
6336
|
+
reverbPreDelay.delayTime.value = (options.metaReverbPreDelay ?? DEFAULT_REVERB_PREDELAY_MS) / 1e3;
|
|
6337
|
+
const reverbConvolver = ctx.createConvolver();
|
|
6338
|
+
const reverbDecaySec = (options.metaReverbDecay ?? 22) / 10 || DEFAULT_REVERB_DECAY_SEC;
|
|
6339
|
+
reverbConvolver.buffer = createReverbImpulse(ctx, reverbDecaySec);
|
|
6340
|
+
reverbConvolver.normalize = true;
|
|
6341
|
+
const reverbWetGain = ctx.createGain();
|
|
6342
|
+
reverbWetGain.gain.value = reverbAmountToGain(options.metaReverb ?? 0);
|
|
6343
|
+
reverbPreDelay.connect(reverbConvolver);
|
|
6344
|
+
reverbConvolver.connect(reverbWetGain);
|
|
6345
|
+
reverbWetGain.connect(finalMix);
|
|
6346
|
+
finalMix.connect(rawDestination);
|
|
6347
|
+
const channelStrips = /* @__PURE__ */ new Map();
|
|
6348
|
+
const getChannelStrip = (index) => {
|
|
6349
|
+
let strip = channelStrips.get(index);
|
|
6350
|
+
if (!strip) {
|
|
6351
|
+
strip = createChannelStrip(ctx, masterGain, {
|
|
6352
|
+
compression: options.trackCompression?.[index] ?? 0,
|
|
6353
|
+
width: options.trackWidth?.[index] ?? 100,
|
|
6354
|
+
eqLow: options.trackEqLow?.[index] ?? 0,
|
|
6355
|
+
eqMid: options.trackEqMid?.[index] ?? 0,
|
|
6356
|
+
eqHigh: options.trackEqHigh?.[index] ?? 0,
|
|
6357
|
+
reverbSend: options.trackReverbSend?.[index] ?? 0,
|
|
6358
|
+
reverbBus: reverbPreDelay
|
|
6359
|
+
});
|
|
6360
|
+
channelStrips.set(index, strip);
|
|
6361
|
+
}
|
|
6362
|
+
return strip;
|
|
6363
|
+
};
|
|
6364
|
+
const synths = /* @__PURE__ */ new Map();
|
|
6365
|
+
const getSynth = (index) => {
|
|
6366
|
+
let s = synths.get(index);
|
|
6367
|
+
if (!s) {
|
|
6368
|
+
s = createSynth(ctx, getChannelStrip(index).input);
|
|
6369
|
+
synths.set(index, s);
|
|
6370
|
+
}
|
|
6371
|
+
return s;
|
|
6372
|
+
};
|
|
6373
|
+
const drumSynth = useSynth ? createSynth(ctx, masterGain) : null;
|
|
6374
|
+
const trackIndexById = /* @__PURE__ */ new Map();
|
|
6375
|
+
trackIndices.forEach((idx) => {
|
|
6376
|
+
trackIndexById.set(TRACK_ID_BY_INDEX[idx] ?? `t${idx}`, idx);
|
|
6377
|
+
});
|
|
6159
6378
|
const pauseWhenHidden = options.pauseWhenHidden ?? ownsCtx;
|
|
6160
6379
|
let playing = false;
|
|
6161
6380
|
const seq = createSequencer({
|
|
@@ -6170,12 +6389,14 @@ var playPlacements = (placements, options) => {
|
|
|
6170
6389
|
getAudioTime: () => ctx.currentTime,
|
|
6171
6390
|
onPlayNote: (e) => {
|
|
6172
6391
|
options.onPlayNote?.(e);
|
|
6173
|
-
|
|
6392
|
+
if (!useSynth) return;
|
|
6393
|
+
const index = trackIndexById.get(e.trackId);
|
|
6394
|
+
(index === void 0 ? drumSynth : getSynth(index))?.playNote(e);
|
|
6174
6395
|
},
|
|
6175
6396
|
onPlayDrum: (e) => {
|
|
6176
6397
|
const velocity = e.velocity * (drumVolume / 100) * (masterVolume / 100);
|
|
6177
6398
|
options.onPlayDrum?.({ ...e, velocity });
|
|
6178
|
-
|
|
6399
|
+
drumSynth?.playDrum({ ...e, velocity });
|
|
6179
6400
|
},
|
|
6180
6401
|
onTick: (step) => {
|
|
6181
6402
|
options.onTick?.(step);
|
|
@@ -6226,6 +6447,7 @@ var playPlacements = (placements, options) => {
|
|
|
6226
6447
|
if (pauseWhenHidden && typeof document !== "undefined") {
|
|
6227
6448
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
6228
6449
|
}
|
|
6450
|
+
for (const strip of channelStrips.values()) strip.dispose();
|
|
6229
6451
|
if (ownsCtx) void ctx.close();
|
|
6230
6452
|
};
|
|
6231
6453
|
return {
|
|
@@ -6245,7 +6467,16 @@ var playMML = (mml, options = {}) => {
|
|
|
6245
6467
|
bpm,
|
|
6246
6468
|
metaVolume: meta.volume,
|
|
6247
6469
|
metaDrum: meta.drum,
|
|
6248
|
-
metaDrumVolume: meta.drumVolume
|
|
6470
|
+
metaDrumVolume: meta.drumVolume,
|
|
6471
|
+
metaReverb: meta.reverb,
|
|
6472
|
+
metaReverbDecay: meta.reverbDecay,
|
|
6473
|
+
metaReverbPreDelay: meta.reverbPreDelay,
|
|
6474
|
+
trackCompression: meta.trackCompression,
|
|
6475
|
+
trackWidth: meta.trackWidth,
|
|
6476
|
+
trackReverbSend: meta.trackReverbSend,
|
|
6477
|
+
trackEqLow: meta.trackEqLow,
|
|
6478
|
+
trackEqMid: meta.trackEqMid,
|
|
6479
|
+
trackEqHigh: meta.trackEqHigh
|
|
6249
6480
|
});
|
|
6250
6481
|
};
|
|
6251
6482
|
var playNote = (options) => {
|
|
@@ -9551,20 +9782,23 @@ var adjustZone = async (ctx, fontName, zone) => {
|
|
|
9551
9782
|
const newLength = attackLength + loopLengthFrame * repeatCount + releaseLength;
|
|
9552
9783
|
let totalPeak = 0;
|
|
9553
9784
|
let loopPeak = 0;
|
|
9554
|
-
|
|
9555
|
-
const
|
|
9556
|
-
for (let i2 = 0; i2 <
|
|
9557
|
-
const abs = Math.abs(
|
|
9785
|
+
for (let ch = 0; ch < oldBuf.numberOfChannels; ch++) {
|
|
9786
|
+
const chData = oldBuf.getChannelData(ch);
|
|
9787
|
+
for (let i2 = 0; i2 < chData.length; i2++) {
|
|
9788
|
+
const abs = Math.abs(chData[i2]);
|
|
9558
9789
|
if (abs > totalPeak) totalPeak = abs;
|
|
9559
9790
|
if (i2 >= loopStartFrame && i2 < loopEndFrame) {
|
|
9560
9791
|
if (abs > loopPeak) loopPeak = abs;
|
|
9561
9792
|
}
|
|
9562
9793
|
}
|
|
9563
9794
|
}
|
|
9795
|
+
const decay = isDecayInstrument(fontName);
|
|
9796
|
+
const targetRatio = decay ? 0.4 : 0.75;
|
|
9797
|
+
const maxMultiplier = decay ? 6 : 20;
|
|
9564
9798
|
let gainMultiplier = 1;
|
|
9565
|
-
if (
|
|
9566
|
-
gainMultiplier = totalPeak *
|
|
9567
|
-
if (gainMultiplier >
|
|
9799
|
+
if (loopPeak > 0 && totalPeak > 0 && loopPeak < totalPeak * 0.8) {
|
|
9800
|
+
gainMultiplier = totalPeak * targetRatio / loopPeak;
|
|
9801
|
+
if (gainMultiplier > maxMultiplier) gainMultiplier = maxMultiplier;
|
|
9568
9802
|
}
|
|
9569
9803
|
try {
|
|
9570
9804
|
const newBuf = ctx.createBuffer(
|
|
@@ -10896,14 +11130,14 @@ var DELAY_DIVISIONS = [
|
|
|
10896
11130
|
{ value: "8d", label: "\u4ED8\u70B98\u5206", beats: 0.75 },
|
|
10897
11131
|
{ value: "16", label: "16\u5206", beats: 0.25 }
|
|
10898
11132
|
];
|
|
10899
|
-
var
|
|
11133
|
+
var clamp4 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
10900
11134
|
var divisionToSeconds = (division, bpm) => {
|
|
10901
11135
|
const beats = DELAY_DIVISIONS.find((d) => d.value === division)?.beats ?? 0.5;
|
|
10902
11136
|
const safeBpm = bpm > 0 ? bpm : 120;
|
|
10903
11137
|
return 60 / safeBpm * beats;
|
|
10904
11138
|
};
|
|
10905
11139
|
var DELAY_MAX_WET = 0.45;
|
|
10906
|
-
var delayAmountToGain = (amount) =>
|
|
11140
|
+
var delayAmountToGain = (amount) => clamp4(amount, 0, 100) / 100 * DELAY_MAX_WET;
|
|
10907
11141
|
var FEEDBACK_GAIN = 0.3;
|
|
10908
11142
|
var MAX_DELAY_SEC = 2;
|
|
10909
11143
|
var createDelayBus = (ctx, destination, options = {}) => {
|
|
@@ -12978,38 +13212,6 @@ var isChordHeavyTrack = (notes, threshold = 0.6) => {
|
|
|
12978
13212
|
return chordNotes / notes.length >= threshold;
|
|
12979
13213
|
};
|
|
12980
13214
|
|
|
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
13215
|
// src/daw.ts
|
|
13014
13216
|
var CHORD_INFO_HTML2 = `
|
|
13015
13217
|
<div class="dtm-modal-body-content">
|
|
@@ -13445,7 +13647,7 @@ var deriveCustomVocalKeyFromUrl = (url2) => {
|
|
|
13445
13647
|
if (name && /^[0-9]/.test(name)) name = `_${name}`;
|
|
13446
13648
|
return CUSTOM_VOCAL_KEY_RE.test(name) ? name : "";
|
|
13447
13649
|
};
|
|
13448
|
-
var
|
|
13650
|
+
var clamp5 = (v, min, max) => Math.min(Math.max(v, min), max);
|
|
13449
13651
|
var normalizeInstrumentName = (name) => {
|
|
13450
13652
|
if (!name) return "";
|
|
13451
13653
|
const stripped = name.replace(/\s+/g, "").toLowerCase();
|
|
@@ -13534,6 +13736,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
13534
13736
|
refs.drumFontSelect.value = currentDrumFont;
|
|
13535
13737
|
let currentInstrument = "";
|
|
13536
13738
|
let activeTrackId = options.initialActiveTrack ?? trackConfigs[0].id;
|
|
13739
|
+
let trackFxAdvancedOpen = false;
|
|
13740
|
+
let lyricAdvancedOpen = false;
|
|
13537
13741
|
let activeToolMode = "pen";
|
|
13538
13742
|
let currentInsertLength = 48;
|
|
13539
13743
|
let snapGridSteps = 12;
|
|
@@ -13830,7 +14034,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
13830
14034
|
const thumbW = Math.max(40, canvas.width / totalContentWidth * sbW);
|
|
13831
14035
|
const ratio = currentOffsetX / maxOffsetX;
|
|
13832
14036
|
refs.hScrollThumb.style.width = `${thumbW}px`;
|
|
13833
|
-
refs.hScrollThumb.style.left = `${
|
|
14037
|
+
refs.hScrollThumb.style.left = `${clamp5(ratio * (sbW - thumbW), 0, sbW - thumbW)}px`;
|
|
13834
14038
|
}
|
|
13835
14039
|
const totalHeight = renderConfig.keyCount * renderConfig.keyHeight;
|
|
13836
14040
|
const sbH = refs.vScroll.clientHeight;
|
|
@@ -13900,9 +14104,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
13900
14104
|
if (maxOffsetX <= 0) return;
|
|
13901
14105
|
const rect = refs.hScroll.getBoundingClientRect();
|
|
13902
14106
|
const thumbW = Number.parseFloat(refs.hScrollThumb.style.width) || 40;
|
|
13903
|
-
const x2 =
|
|
14107
|
+
const x2 = clamp5(clientX - rect.left - thumbW / 2, 0, rect.width - thumbW);
|
|
13904
14108
|
const ratio = x2 / (rect.width - thumbW);
|
|
13905
|
-
currentOffsetX =
|
|
14109
|
+
currentOffsetX = clamp5(ratio * maxOffsetX, 0, maxOffsetX);
|
|
13906
14110
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
13907
14111
|
redrawAll();
|
|
13908
14112
|
};
|
|
@@ -13911,9 +14115,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
13911
14115
|
if (maxOffset <= 0) return;
|
|
13912
14116
|
const rect = refs.vScroll.getBoundingClientRect();
|
|
13913
14117
|
const thumbH = Number.parseFloat(refs.vScrollThumb.style.height) || 40;
|
|
13914
|
-
const y =
|
|
14118
|
+
const y = clamp5(clientY - rect.top - thumbH / 2, 0, rect.height - thumbH);
|
|
13915
14119
|
const ratio = y / (rect.height - thumbH);
|
|
13916
|
-
currentOffsetY =
|
|
14120
|
+
currentOffsetY = clamp5(ratio * maxOffset, 0, maxOffset);
|
|
13917
14121
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
13918
14122
|
redrawAll();
|
|
13919
14123
|
};
|
|
@@ -13960,8 +14164,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
13960
14164
|
if (dx !== 0 || dy !== 0) {
|
|
13961
14165
|
const maxOffsetX = getMaxOffsetX();
|
|
13962
14166
|
const maxOffsetY = getMaxOffsetY();
|
|
13963
|
-
currentOffsetX =
|
|
13964
|
-
currentOffsetY =
|
|
14167
|
+
currentOffsetX = clamp5(currentOffsetX + dx, 0, maxOffsetX);
|
|
14168
|
+
currentOffsetY = clamp5(currentOffsetY + dy, 0, maxOffsetY);
|
|
13965
14169
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
13966
14170
|
onPointerMove(lastMoveEvent);
|
|
13967
14171
|
}
|
|
@@ -14351,12 +14555,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
14351
14555
|
"wheel",
|
|
14352
14556
|
(event) => {
|
|
14353
14557
|
event.preventDefault();
|
|
14354
|
-
currentOffsetY =
|
|
14558
|
+
currentOffsetY = clamp5(
|
|
14355
14559
|
currentOffsetY + event.deltaY,
|
|
14356
14560
|
0,
|
|
14357
14561
|
getMaxOffsetY()
|
|
14358
14562
|
);
|
|
14359
|
-
currentOffsetX =
|
|
14563
|
+
currentOffsetX = clamp5(
|
|
14360
14564
|
currentOffsetX + event.deltaX,
|
|
14361
14565
|
0,
|
|
14362
14566
|
getMaxOffsetX()
|
|
@@ -14395,7 +14599,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14395
14599
|
const centerStep = (currentOffsetX + canvas.width / 2) / renderConfig.stepWidth;
|
|
14396
14600
|
renderConfig.stepWidth = BASE_STEP_WIDTH * (zoomX * 2) / 100;
|
|
14397
14601
|
refs.zoomXLabel.textContent = `${zoomX}%`;
|
|
14398
|
-
currentOffsetX =
|
|
14602
|
+
currentOffsetX = clamp5(
|
|
14399
14603
|
centerStep * renderConfig.stepWidth - canvas.width / 2,
|
|
14400
14604
|
0,
|
|
14401
14605
|
getMaxOffsetX()
|
|
@@ -14408,7 +14612,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14408
14612
|
const centerKey = (currentOffsetY + canvas.height / 2) / renderConfig.keyHeight;
|
|
14409
14613
|
renderConfig.keyHeight = BASE_KEY_HEIGHT * zoomY / 100;
|
|
14410
14614
|
refs.zoomYLabel.textContent = `${zoomY}%`;
|
|
14411
|
-
currentOffsetY =
|
|
14615
|
+
currentOffsetY = clamp5(
|
|
14412
14616
|
centerKey * renderConfig.keyHeight - canvas.height / 2,
|
|
14413
14617
|
0,
|
|
14414
14618
|
getMaxOffsetY()
|
|
@@ -14458,7 +14662,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14458
14662
|
const threshold = currentOffsetX / renderConfig.stepWidth + visibleSteps - 4;
|
|
14459
14663
|
if (currentPlayStep > threshold) {
|
|
14460
14664
|
const visibleBars = Math.round(visibleSteps / renderConfig.stepsPerBar);
|
|
14461
|
-
currentOffsetX =
|
|
14665
|
+
currentOffsetX = clamp5(
|
|
14462
14666
|
currentOffsetX + visibleBars * renderConfig.stepsPerBar * renderConfig.stepWidth,
|
|
14463
14667
|
0,
|
|
14464
14668
|
getMaxOffsetX()
|
|
@@ -14545,7 +14749,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14545
14749
|
}
|
|
14546
14750
|
if (playbackState !== "paused") {
|
|
14547
14751
|
const canvas = getGridCanvas();
|
|
14548
|
-
currentOffsetX =
|
|
14752
|
+
currentOffsetX = clamp5(
|
|
14549
14753
|
playStartStep * renderConfig.stepWidth - canvas.width * 0.5,
|
|
14550
14754
|
0,
|
|
14551
14755
|
getMaxOffsetX()
|
|
@@ -14649,7 +14853,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14649
14853
|
<input type="range" class="dtm-range dtm-grow" data-dtm="track-vol" min="0" max="127" value="${active.volume}">
|
|
14650
14854
|
<span class="dtm-label" data-dtm="track-vol-label">${active.volume}</span>
|
|
14651
14855
|
</div>
|
|
14652
|
-
<details class="dtm-advanced" data-dtm="track-fx-advanced">
|
|
14856
|
+
<details class="dtm-advanced" data-dtm="track-fx-advanced" ${trackFxAdvancedOpen ? "open" : ""}>
|
|
14653
14857
|
<summary>\u8A73\u7D30\u8A2D\u5B9A\uFF08EQ\u30FB\u97F3\u5727\u30FB\u30B9\u30C6\u30EC\u30AA\u5E45\uFF09</summary>
|
|
14654
14858
|
<div class="dtm-row">
|
|
14655
14859
|
<span class="dtm-label">EQ\u4F4E\u57DF</span>
|
|
@@ -14686,6 +14890,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
14686
14890
|
<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
14891
|
</div>
|
|
14688
14892
|
</details>`;
|
|
14893
|
+
refs.trackBody.querySelector(
|
|
14894
|
+
'[data-dtm="track-fx-advanced"]'
|
|
14895
|
+
).addEventListener("toggle", (e) => {
|
|
14896
|
+
trackFxAdvancedOpen = e.target.open;
|
|
14897
|
+
});
|
|
14689
14898
|
const volInput = refs.trackBody.querySelector(
|
|
14690
14899
|
'[data-dtm="track-vol"]'
|
|
14691
14900
|
);
|
|
@@ -14899,7 +15108,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14899
15108
|
<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
15109
|
<span class="dtm-label" data-dtm="lyric-vol-label"></span>
|
|
14901
15110
|
</div>
|
|
14902
|
-
<details class="dtm-advanced" data-dtm="lyric-advanced">
|
|
15111
|
+
<details class="dtm-advanced" data-dtm="lyric-advanced" ${lyricAdvancedOpen ? "open" : ""}>
|
|
14903
15112
|
<summary>\u8A73\u7D30\u8A2D\u5B9A</summary>
|
|
14904
15113
|
<div class="dtm-row">
|
|
14905
15114
|
<span class="dtm-label">\u30AA\u30AF\u30BF\u30FC\u30D6</span>
|
|
@@ -14960,6 +15169,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
14960
15169
|
<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
15170
|
</div>`;
|
|
14962
15171
|
refs.trackBody.appendChild(lyricDiv);
|
|
15172
|
+
lyricDiv.querySelector(
|
|
15173
|
+
'[data-dtm="lyric-advanced"]'
|
|
15174
|
+
).addEventListener("toggle", (e) => {
|
|
15175
|
+
lyricAdvancedOpen = e.target.open;
|
|
15176
|
+
});
|
|
14963
15177
|
const lyricModelSel = lyricDiv.querySelector(
|
|
14964
15178
|
'[data-dtm="lyric-model"]'
|
|
14965
15179
|
);
|
|
@@ -15582,7 +15796,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15582
15796
|
const canvas = getGridCanvas();
|
|
15583
15797
|
const yIndex = renderConfig.keyCount - 1 - (pitch - renderConfig.pitchRangeStart);
|
|
15584
15798
|
const logicalY = yIndex * renderConfig.keyHeight;
|
|
15585
|
-
currentOffsetY =
|
|
15799
|
+
currentOffsetY = clamp5(
|
|
15586
15800
|
logicalY - (canvas.height - renderConfig.keyHeight) / 2,
|
|
15587
15801
|
0,
|
|
15588
15802
|
getMaxOffsetY()
|
|
@@ -16157,8 +16371,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
16157
16371
|
refs.reverbAmount.value = "20";
|
|
16158
16372
|
refs.reverbAmountLabel.textContent = "20%";
|
|
16159
16373
|
options.onReverbChange?.(20);
|
|
16160
|
-
const bpmT =
|
|
16161
|
-
reverbDecay =
|
|
16374
|
+
const bpmT = clamp5((bpm - 60) / (180 - 60), 0, 1);
|
|
16375
|
+
reverbDecay = clamp5(
|
|
16162
16376
|
3 - bpmT * (3 - 0.9),
|
|
16163
16377
|
MIN_REVERB_DECAY_SEC,
|
|
16164
16378
|
MAX_REVERB_DECAY_SEC
|
|
@@ -16193,7 +16407,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
16193
16407
|
}
|
|
16194
16408
|
if (t.lyricModel) {
|
|
16195
16409
|
t.vocalVibrato = true;
|
|
16196
|
-
t.vocalReverb = isMainVocal ?
|
|
16410
|
+
t.vocalReverb = isMainVocal ? 25 : 45;
|
|
16197
16411
|
fireLyricsChange(t);
|
|
16198
16412
|
}
|
|
16199
16413
|
}
|
|
@@ -17089,7 +17303,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
17089
17303
|
currentPlayStep = step;
|
|
17090
17304
|
playbackState = "paused";
|
|
17091
17305
|
const canvas = getGridCanvas();
|
|
17092
|
-
currentOffsetX =
|
|
17306
|
+
currentOffsetX = clamp5(
|
|
17093
17307
|
step * renderConfig.stepWidth - canvas.width * 0.5,
|
|
17094
17308
|
0,
|
|
17095
17309
|
getMaxOffsetX()
|
|
@@ -17135,11 +17349,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
17135
17349
|
getViewState,
|
|
17136
17350
|
setViewState: (state) => {
|
|
17137
17351
|
if (typeof state.zoomX === "number") {
|
|
17138
|
-
zoomX =
|
|
17352
|
+
zoomX = clamp5(state.zoomX, 25, 200);
|
|
17139
17353
|
applyZoomX();
|
|
17140
17354
|
}
|
|
17141
17355
|
if (typeof state.zoomY === "number") {
|
|
17142
|
-
zoomY =
|
|
17356
|
+
zoomY = clamp5(state.zoomY, 50, 200);
|
|
17143
17357
|
applyZoomY();
|
|
17144
17358
|
}
|
|
17145
17359
|
if (typeof state.decomposeChord === "boolean") {
|
|
@@ -17161,24 +17375,24 @@ var mountDAW = (target, options = {}) => {
|
|
|
17161
17375
|
forcePauseAt,
|
|
17162
17376
|
setLoading,
|
|
17163
17377
|
setMasterVolume: (volume) => {
|
|
17164
|
-
masterVolume =
|
|
17378
|
+
masterVolume = clamp5(volume, 0, 100);
|
|
17165
17379
|
refs.masterVolume.value = String(masterVolume);
|
|
17166
17380
|
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
17167
17381
|
options.singingVoices?.setVolume(masterVolume / 100);
|
|
17168
17382
|
},
|
|
17169
17383
|
setVolume: (volume) => {
|
|
17170
|
-
masterVolume =
|
|
17384
|
+
masterVolume = clamp5(volume, 0, 100);
|
|
17171
17385
|
refs.masterVolume.value = String(masterVolume);
|
|
17172
17386
|
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
17173
17387
|
options.singingVoices?.setVolume(masterVolume / 100);
|
|
17174
17388
|
},
|
|
17175
17389
|
setDrumVolume: (volume) => {
|
|
17176
|
-
drumVolume =
|
|
17390
|
+
drumVolume = clamp5(volume, 0, 100);
|
|
17177
17391
|
refs.drumVolume.value = String(drumVolume);
|
|
17178
17392
|
refs.drumVolumeLabel.textContent = `${drumVolume}%`;
|
|
17179
17393
|
},
|
|
17180
17394
|
setReverbAmount: (amount) => {
|
|
17181
|
-
reverbAmount =
|
|
17395
|
+
reverbAmount = clamp5(amount, 0, 100);
|
|
17182
17396
|
refs.reverbAmount.value = String(reverbAmount);
|
|
17183
17397
|
refs.reverbAmountLabel.textContent = `${reverbAmount}%`;
|
|
17184
17398
|
options.onReverbChange?.(reverbAmount);
|
|
@@ -17202,7 +17416,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
17202
17416
|
options.onReverbPreDelayChange?.(reverbPreDelay);
|
|
17203
17417
|
},
|
|
17204
17418
|
setDelayAmount: (amount) => {
|
|
17205
|
-
delayAmount =
|
|
17419
|
+
delayAmount = clamp5(amount, 0, 100);
|
|
17206
17420
|
refs.delayAmount.value = String(delayAmount);
|
|
17207
17421
|
refs.delayAmountLabel.textContent = `${delayAmount}%`;
|
|
17208
17422
|
options.onDelayChange?.(delayAmount);
|
|
@@ -17981,149 +18195,6 @@ var isSupported = midiJsonParser.isSupported;
|
|
|
17981
18195
|
var parseArrayBuffer = midiJsonParser.parseArrayBuffer;
|
|
17982
18196
|
URL.revokeObjectURL(url);
|
|
17983
18197
|
|
|
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
18198
|
// src/clip-meter.ts
|
|
18128
18199
|
var createClipMeter = (ctx, source, options = {}) => {
|
|
18129
18200
|
const threshold = options.threshold ?? 0.98;
|
|
@@ -18327,6 +18398,11 @@ var createDtmStudio = async (options = {}) => {
|
|
|
18327
18398
|
presetUI: true,
|
|
18328
18399
|
...options.features
|
|
18329
18400
|
};
|
|
18401
|
+
const resolveDrumPatterns = (custom) => ({
|
|
18402
|
+
...DRUM_PATTERNS,
|
|
18403
|
+
...SONG_DRUM_PATTERNS,
|
|
18404
|
+
...normalizeDrumPatterns(custom ?? {})
|
|
18405
|
+
});
|
|
18330
18406
|
const audioCtx = options.audioContext ?? new AudioContext({ sampleRate: 44100 });
|
|
18331
18407
|
const masterGain = audioCtx.createGain();
|
|
18332
18408
|
masterGain.gain.value = options.masterVolume ?? 1;
|
|
@@ -18988,7 +19064,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
18988
19064
|
await loadRequiredDrums(
|
|
18989
19065
|
getDrumPatternKeys(
|
|
18990
19066
|
meta.drum,
|
|
18991
|
-
opts.drumPatterns ?? options.drumPatterns
|
|
19067
|
+
resolveDrumPatterns(opts.drumPatterns ?? options.drumPatterns)
|
|
18992
19068
|
),
|
|
18993
19069
|
meta.drumFont || "FluidR3_GM_sf2_file:0"
|
|
18994
19070
|
);
|
|
@@ -19079,7 +19155,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19079
19155
|
await loadRequiredDrums(
|
|
19080
19156
|
getDrumPatternKeys(
|
|
19081
19157
|
meta.drum,
|
|
19082
|
-
opts.drumPatterns ?? options.drumPatterns
|
|
19158
|
+
resolveDrumPatterns(opts.drumPatterns ?? options.drumPatterns)
|
|
19083
19159
|
)
|
|
19084
19160
|
);
|
|
19085
19161
|
}
|
|
@@ -19159,7 +19235,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19159
19235
|
await loadRequiredDrums(
|
|
19160
19236
|
getDrumPatternKeys(
|
|
19161
19237
|
meta.drum,
|
|
19162
|
-
opts.drumPatterns ?? options.drumPatterns
|
|
19238
|
+
resolveDrumPatterns(opts.drumPatterns ?? options.drumPatterns)
|
|
19163
19239
|
)
|
|
19164
19240
|
);
|
|
19165
19241
|
}
|