@onjmin/dtm 0.1.83 → 0.1.84
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/README.md +1 -1
- package/dist/index.d.mts +80 -4
- package/dist/index.d.ts +80 -4
- package/dist/index.js +696 -296
- package/dist/index.mjs +695 -296
- package/dist/voice-worker.js +175 -13
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -46,6 +46,7 @@ __export(index_exports, {
|
|
|
46
46
|
PREWARM_NOTES: () => PREWARM_NOTES,
|
|
47
47
|
TRACKS_ADVANCED: () => TRACKS_ADVANCED,
|
|
48
48
|
TRACKS_SIMPLE: () => TRACKS_SIMPLE,
|
|
49
|
+
VIBRATO_MIN_SEC: () => VIBRATO_MIN_SEC,
|
|
49
50
|
VOICE_IMAGES: () => VOICE_IMAGES,
|
|
50
51
|
VOICE_IMAGE_KEY: () => VOICE_IMAGE_KEY,
|
|
51
52
|
analyzeMidiTracks: () => analyzeMidiTracks,
|
|
@@ -348,8 +349,8 @@ var Input = class _Input {
|
|
|
348
349
|
}
|
|
349
350
|
return str.length ? Number(str) : null;
|
|
350
351
|
}
|
|
351
|
-
slice(
|
|
352
|
-
return this.str.slice(this.idx, this.idx +
|
|
352
|
+
slice(i2) {
|
|
353
|
+
return this.str.slice(this.idx, this.idx + i2);
|
|
353
354
|
}
|
|
354
355
|
};
|
|
355
356
|
var Output = class {
|
|
@@ -385,8 +386,8 @@ var Matcher = class {
|
|
|
385
386
|
else this._set(key, value);
|
|
386
387
|
}
|
|
387
388
|
parse(input) {
|
|
388
|
-
for (const
|
|
389
|
-
const s = input.slice(
|
|
389
|
+
for (const i2 of this.lengths) {
|
|
390
|
+
const s = input.slice(i2);
|
|
390
391
|
if (this.map.has(s)) {
|
|
391
392
|
input.idx += s.length;
|
|
392
393
|
return this.map.get(s);
|
|
@@ -440,7 +441,7 @@ var parseFormula = (input, output = new Output(), nest = 0) => {
|
|
|
440
441
|
if (o.isChord) {
|
|
441
442
|
output.value = [...o.value].concat(v);
|
|
442
443
|
} else {
|
|
443
|
-
const a = v.sort((
|
|
444
|
+
const a = v.sort((x2, y) => x2 - y);
|
|
444
445
|
const pitch = (o.pitch + 3) % 12 - 3;
|
|
445
446
|
if (a[0] < pitch) {
|
|
446
447
|
while (a[0] < pitch) a.push(a.shift() + 12);
|
|
@@ -477,16 +478,16 @@ halfMatcher.set("+", 1);
|
|
|
477
478
|
halfMatcher.set("-", -1);
|
|
478
479
|
var parseHalf = (input, isPitch = false) => (isPitch ? halfMatcherStrict : halfMatcher).parse(input);
|
|
479
480
|
var idx2pitch = [0, 2, 4, 5, 7, 9, 11];
|
|
480
|
-
for (const
|
|
481
|
+
for (const i2 of [...idx2pitch.keys()]) idx2pitch.push(idx2pitch[i2] + 12);
|
|
481
482
|
var deg2pitch = (deg) => idx2pitch[deg - 1];
|
|
482
483
|
var pitchMatcher = new Matcher();
|
|
483
|
-
for (const [
|
|
484
|
-
pitchMatcher.set(v, idx2pitch[
|
|
484
|
+
for (const [i2, v] of [..."CDEFGAB"].entries())
|
|
485
|
+
pitchMatcher.set(v, idx2pitch[i2]);
|
|
485
486
|
var parsePitch = (input, output) => {
|
|
486
487
|
const pitch = pitchMatcher.parse(input);
|
|
487
488
|
if (pitch === null) err(input, "Not found pitch");
|
|
488
489
|
output.pitch = pitch;
|
|
489
|
-
for (let
|
|
490
|
+
for (let i2 = 0; i2 < 2; i2++) {
|
|
490
491
|
const half = parseHalf(input, true);
|
|
491
492
|
if (half === null) break;
|
|
492
493
|
output.pitch += half;
|
|
@@ -692,11 +693,11 @@ var pearson = (a, b) => {
|
|
|
692
693
|
let num = 0;
|
|
693
694
|
let da = 0;
|
|
694
695
|
let db = 0;
|
|
695
|
-
for (let
|
|
696
|
-
const
|
|
697
|
-
const y = b[
|
|
698
|
-
num +=
|
|
699
|
-
da +=
|
|
696
|
+
for (let i2 = 0; i2 < a.length; i2++) {
|
|
697
|
+
const x2 = a[i2] - ma;
|
|
698
|
+
const y = b[i2] - mb;
|
|
699
|
+
num += x2 * y;
|
|
700
|
+
da += x2 * x2;
|
|
700
701
|
db += y * y;
|
|
701
702
|
}
|
|
702
703
|
const den = Math.sqrt(da * db);
|
|
@@ -772,19 +773,19 @@ var coalesce = (segments) => {
|
|
|
772
773
|
var mergeShortSegments = (segments, min) => {
|
|
773
774
|
if (min <= 0) return segments;
|
|
774
775
|
const result = segments.map((s) => ({ ...s }));
|
|
775
|
-
let
|
|
776
|
-
while (
|
|
777
|
-
if (result[
|
|
778
|
-
|
|
776
|
+
let i2 = 0;
|
|
777
|
+
while (i2 < result.length && result.length > 1) {
|
|
778
|
+
if (result[i2].duration >= min) {
|
|
779
|
+
i2++;
|
|
779
780
|
continue;
|
|
780
781
|
}
|
|
781
|
-
if (
|
|
782
|
-
result[
|
|
783
|
-
result.splice(
|
|
782
|
+
if (i2 > 0) {
|
|
783
|
+
result[i2 - 1].duration += result[i2].duration;
|
|
784
|
+
result.splice(i2, 1);
|
|
784
785
|
} else {
|
|
785
|
-
result[
|
|
786
|
-
result[
|
|
787
|
-
result.splice(
|
|
786
|
+
result[i2 + 1].when = result[i2].when;
|
|
787
|
+
result[i2 + 1].duration += result[i2].duration;
|
|
788
|
+
result.splice(i2, 1);
|
|
788
789
|
}
|
|
789
790
|
}
|
|
790
791
|
return coalesce(result);
|
|
@@ -1003,19 +1004,19 @@ var viterbi = (emissions, changePenalty) => {
|
|
|
1003
1004
|
const curr = new Array(N).fill(0);
|
|
1004
1005
|
const em = emissions[t];
|
|
1005
1006
|
const switchVal = bestPrevVal - changePenalty;
|
|
1006
|
-
for (let
|
|
1007
|
-
if (prev[
|
|
1008
|
-
curr[
|
|
1009
|
-
back[t][
|
|
1007
|
+
for (let i2 = 0; i2 < N; i2++) {
|
|
1008
|
+
if (prev[i2] >= switchVal) {
|
|
1009
|
+
curr[i2] = em[i2] + prev[i2];
|
|
1010
|
+
back[t][i2] = i2;
|
|
1010
1011
|
} else {
|
|
1011
|
-
curr[
|
|
1012
|
-
back[t][
|
|
1012
|
+
curr[i2] = em[i2] + switchVal;
|
|
1013
|
+
back[t][i2] = bestPrevIdx;
|
|
1013
1014
|
}
|
|
1014
1015
|
}
|
|
1015
1016
|
prev = curr;
|
|
1016
1017
|
}
|
|
1017
1018
|
let bestIdx = 0;
|
|
1018
|
-
for (let
|
|
1019
|
+
for (let i2 = 1; i2 < N; i2++) if (prev[i2] > prev[bestIdx]) bestIdx = i2;
|
|
1019
1020
|
const path = new Array(T).fill(0);
|
|
1020
1021
|
path[T - 1] = bestIdx;
|
|
1021
1022
|
for (let t = T - 1; t > 0; t--) path[t - 1] = back[t][path[t]];
|
|
@@ -1112,20 +1113,20 @@ var parseChords = (str, bpm = 120) => {
|
|
|
1112
1113
|
if (!str2.length) continue;
|
|
1113
1114
|
const when = idx++ * secBar;
|
|
1114
1115
|
const a = [];
|
|
1115
|
-
for (let
|
|
1116
|
-
const char = str2[
|
|
1117
|
-
const prev = str2[
|
|
1118
|
-
const prev2 = str2.slice(
|
|
1116
|
+
for (let i2 = 0; i2 < str2.length; i2++) {
|
|
1117
|
+
const char = str2[i2];
|
|
1118
|
+
const prev = str2[i2 - 1];
|
|
1119
|
+
const prev2 = str2.slice(i2 - 2, i2);
|
|
1119
1120
|
if (!frontChars.has(char)) continue;
|
|
1120
1121
|
if (prev === "/" || prev2 === "on") continue;
|
|
1121
1122
|
if (prev2 === "N." && char === "C") continue;
|
|
1122
|
-
a.push(
|
|
1123
|
+
a.push(i2);
|
|
1123
1124
|
}
|
|
1124
1125
|
if (!a.length) continue;
|
|
1125
1126
|
const divide = 2 ** Math.ceil(Math.log2(a.length));
|
|
1126
1127
|
const unitTime = secBar / divide;
|
|
1127
|
-
for (const [
|
|
1128
|
-
const s = str2.slice(v,
|
|
1128
|
+
for (const [i2, v] of a.entries()) {
|
|
1129
|
+
const s = str2.slice(v, i2 === a.length - 1 ? str2.length : a[i2 + 1]).replace(/\s+/g, "");
|
|
1129
1130
|
const c = s[0];
|
|
1130
1131
|
if (c === "_" || c === "N") {
|
|
1131
1132
|
last = null;
|
|
@@ -1135,7 +1136,7 @@ var parseChords = (str, bpm = 120) => {
|
|
|
1135
1136
|
if (last) last.duration += unitTime;
|
|
1136
1137
|
continue;
|
|
1137
1138
|
}
|
|
1138
|
-
const _when = when +
|
|
1139
|
+
const _when = when + i2 * unitTime;
|
|
1139
1140
|
if (c === "%") {
|
|
1140
1141
|
if (last === null) continue;
|
|
1141
1142
|
const base = last;
|
|
@@ -1300,19 +1301,19 @@ var resolveDrumPattern = (name, dict, currentBar) => {
|
|
|
1300
1301
|
if (Array.isArray(patternObj) && patternObj.length > 0 && "ranges" in patternObj[0]) {
|
|
1301
1302
|
const songDef = patternObj;
|
|
1302
1303
|
const instructions = songDef.filter(
|
|
1303
|
-
(
|
|
1304
|
+
(i2) => i2.ranges.some(([s, e]) => currentBar >= s && currentBar <= e)
|
|
1304
1305
|
);
|
|
1305
1306
|
if (instructions.length > 0) {
|
|
1306
|
-
return instructions.flatMap((
|
|
1307
|
-
const maxStep = Math.max(...
|
|
1308
|
-
const loopLengthBars =
|
|
1309
|
-
const range =
|
|
1307
|
+
return instructions.flatMap((i2) => {
|
|
1308
|
+
const maxStep = Math.max(...i2.pattern.map((p) => p.step), 0);
|
|
1309
|
+
const loopLengthBars = i2.patternBars ?? Math.max(1, Math.ceil((maxStep + 1) / 192));
|
|
1310
|
+
const range = i2.ranges.find(
|
|
1310
1311
|
([s, e]) => currentBar >= s && currentBar <= e
|
|
1311
1312
|
);
|
|
1312
1313
|
const startBar = range ? range[0] : 1;
|
|
1313
1314
|
const barInLoop = (currentBar - startBar) % loopLengthBars;
|
|
1314
1315
|
const stepOffset = barInLoop * 192;
|
|
1315
|
-
return
|
|
1316
|
+
return i2.pattern.filter((p) => p.step >= stepOffset && p.step < stepOffset + 192).map((p) => ({ ...p, step: p.step - stepOffset }));
|
|
1316
1317
|
});
|
|
1317
1318
|
}
|
|
1318
1319
|
}
|
|
@@ -1455,8 +1456,8 @@ var getDrumPatternKeys = (name, dict) => {
|
|
|
1455
1456
|
const keys = /* @__PURE__ */ new Set();
|
|
1456
1457
|
if (Array.isArray(patternObj) && patternObj.length > 0 && "ranges" in patternObj[0]) {
|
|
1457
1458
|
const songDef = patternObj;
|
|
1458
|
-
for (const
|
|
1459
|
-
for (const p of
|
|
1459
|
+
for (const i2 of songDef) {
|
|
1460
|
+
for (const p of i2.pattern) keys.add(p.pitch);
|
|
1460
1461
|
}
|
|
1461
1462
|
} else if (Array.isArray(patternObj)) {
|
|
1462
1463
|
for (const p of patternObj) keys.add(p.pitch);
|
|
@@ -1513,21 +1514,21 @@ var buildChordPlacements = (options) => {
|
|
|
1513
1514
|
}
|
|
1514
1515
|
} else if (patternType === "arpeggio") {
|
|
1515
1516
|
const arpInterval = Math.floor(noteLength / notes.length);
|
|
1516
|
-
notes.forEach((noteOffset,
|
|
1517
|
+
notes.forEach((noteOffset, i2) => {
|
|
1517
1518
|
placements.push({
|
|
1518
|
-
startStep: chord.whenStep +
|
|
1519
|
+
startStep: chord.whenStep + i2 * arpInterval,
|
|
1519
1520
|
pitch: C3 + noteOffset + offset,
|
|
1520
|
-
durationSteps: noteLength -
|
|
1521
|
+
durationSteps: noteLength - i2 * arpInterval,
|
|
1521
1522
|
velocity: 100
|
|
1522
1523
|
});
|
|
1523
1524
|
});
|
|
1524
1525
|
} else if (patternType === "arpeggio-fast") {
|
|
1525
1526
|
const arpInterval = 6;
|
|
1526
|
-
notes.forEach((noteOffset,
|
|
1527
|
+
notes.forEach((noteOffset, i2) => {
|
|
1527
1528
|
placements.push({
|
|
1528
|
-
startStep: chord.whenStep +
|
|
1529
|
+
startStep: chord.whenStep + i2 * arpInterval,
|
|
1529
1530
|
pitch: C3 + noteOffset + offset,
|
|
1530
|
-
durationSteps: Math.max(12, noteLength -
|
|
1531
|
+
durationSteps: Math.max(12, noteLength - i2 * arpInterval),
|
|
1531
1532
|
velocity: 100
|
|
1532
1533
|
});
|
|
1533
1534
|
});
|
|
@@ -1567,8 +1568,8 @@ var buildChordPlacements = (options) => {
|
|
|
1567
1568
|
}
|
|
1568
1569
|
}
|
|
1569
1570
|
} else if (patternType === "alternating") {
|
|
1570
|
-
notes.forEach((noteOffset,
|
|
1571
|
-
const stepOffset =
|
|
1571
|
+
notes.forEach((noteOffset, i2) => {
|
|
1572
|
+
const stepOffset = i2 * Math.floor(stepsPerBar / 4);
|
|
1572
1573
|
placements.push({
|
|
1573
1574
|
startStep: chord.whenStep + stepOffset,
|
|
1574
1575
|
pitch: C3 + noteOffset + offset,
|
|
@@ -1590,8 +1591,8 @@ var buildChordPlacements = (options) => {
|
|
|
1590
1591
|
}
|
|
1591
1592
|
if (notes.length === 0) return;
|
|
1592
1593
|
const startStep = barIndex * chordLength;
|
|
1593
|
-
notes.forEach((noteOffset,
|
|
1594
|
-
const stepOffset =
|
|
1594
|
+
notes.forEach((noteOffset, i2) => {
|
|
1595
|
+
const stepOffset = i2 * 3;
|
|
1595
1596
|
placements.push({
|
|
1596
1597
|
startStep: startStep + stepOffset,
|
|
1597
1598
|
pitch: C3 + noteOffset + offset,
|
|
@@ -1604,7 +1605,136 @@ var buildChordPlacements = (options) => {
|
|
|
1604
1605
|
return placements;
|
|
1605
1606
|
};
|
|
1606
1607
|
|
|
1607
|
-
// node_modules/.pnpm/@onjmin+koe@1.0.
|
|
1608
|
+
// node_modules/.pnpm/@onjmin+koe@1.0.5/node_modules/@onjmin/koe/dist/index.js
|
|
1609
|
+
var u8 = Uint8Array;
|
|
1610
|
+
var u16 = Uint16Array;
|
|
1611
|
+
var i32 = Int32Array;
|
|
1612
|
+
var fleb = new u8([
|
|
1613
|
+
0,
|
|
1614
|
+
0,
|
|
1615
|
+
0,
|
|
1616
|
+
0,
|
|
1617
|
+
0,
|
|
1618
|
+
0,
|
|
1619
|
+
0,
|
|
1620
|
+
0,
|
|
1621
|
+
1,
|
|
1622
|
+
1,
|
|
1623
|
+
1,
|
|
1624
|
+
1,
|
|
1625
|
+
2,
|
|
1626
|
+
2,
|
|
1627
|
+
2,
|
|
1628
|
+
2,
|
|
1629
|
+
3,
|
|
1630
|
+
3,
|
|
1631
|
+
3,
|
|
1632
|
+
3,
|
|
1633
|
+
4,
|
|
1634
|
+
4,
|
|
1635
|
+
4,
|
|
1636
|
+
4,
|
|
1637
|
+
5,
|
|
1638
|
+
5,
|
|
1639
|
+
5,
|
|
1640
|
+
5,
|
|
1641
|
+
0,
|
|
1642
|
+
/* unused */
|
|
1643
|
+
0,
|
|
1644
|
+
0,
|
|
1645
|
+
/* impossible */
|
|
1646
|
+
0
|
|
1647
|
+
]);
|
|
1648
|
+
var fdeb = new u8([
|
|
1649
|
+
0,
|
|
1650
|
+
0,
|
|
1651
|
+
0,
|
|
1652
|
+
0,
|
|
1653
|
+
1,
|
|
1654
|
+
1,
|
|
1655
|
+
2,
|
|
1656
|
+
2,
|
|
1657
|
+
3,
|
|
1658
|
+
3,
|
|
1659
|
+
4,
|
|
1660
|
+
4,
|
|
1661
|
+
5,
|
|
1662
|
+
5,
|
|
1663
|
+
6,
|
|
1664
|
+
6,
|
|
1665
|
+
7,
|
|
1666
|
+
7,
|
|
1667
|
+
8,
|
|
1668
|
+
8,
|
|
1669
|
+
9,
|
|
1670
|
+
9,
|
|
1671
|
+
10,
|
|
1672
|
+
10,
|
|
1673
|
+
11,
|
|
1674
|
+
11,
|
|
1675
|
+
12,
|
|
1676
|
+
12,
|
|
1677
|
+
13,
|
|
1678
|
+
13,
|
|
1679
|
+
/* unused */
|
|
1680
|
+
0,
|
|
1681
|
+
0
|
|
1682
|
+
]);
|
|
1683
|
+
var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
|
1684
|
+
var freb = function(eb, start) {
|
|
1685
|
+
var b = new u16(31);
|
|
1686
|
+
for (var i2 = 0; i2 < 31; ++i2) {
|
|
1687
|
+
b[i2] = start += 1 << eb[i2 - 1];
|
|
1688
|
+
}
|
|
1689
|
+
var r = new i32(b[30]);
|
|
1690
|
+
for (var i2 = 1; i2 < 30; ++i2) {
|
|
1691
|
+
for (var j = b[i2]; j < b[i2 + 1]; ++j) {
|
|
1692
|
+
r[j] = j - b[i2] << 5 | i2;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
return { b, r };
|
|
1696
|
+
};
|
|
1697
|
+
var _a = freb(fleb, 2);
|
|
1698
|
+
var fl = _a.b;
|
|
1699
|
+
var revfl = _a.r;
|
|
1700
|
+
fl[28] = 258, revfl[258] = 28;
|
|
1701
|
+
var _b = freb(fdeb, 0);
|
|
1702
|
+
var fd = _b.b;
|
|
1703
|
+
var revfd = _b.r;
|
|
1704
|
+
var rev = new u16(32768);
|
|
1705
|
+
for (i = 0; i < 32768; ++i) {
|
|
1706
|
+
x = (i & 43690) >> 1 | (i & 21845) << 1;
|
|
1707
|
+
x = (x & 52428) >> 2 | (x & 13107) << 2;
|
|
1708
|
+
x = (x & 61680) >> 4 | (x & 3855) << 4;
|
|
1709
|
+
rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
|
|
1710
|
+
}
|
|
1711
|
+
var x;
|
|
1712
|
+
var i;
|
|
1713
|
+
var flt = new u8(288);
|
|
1714
|
+
for (i = 0; i < 144; ++i)
|
|
1715
|
+
flt[i] = 8;
|
|
1716
|
+
var i;
|
|
1717
|
+
for (i = 144; i < 256; ++i)
|
|
1718
|
+
flt[i] = 9;
|
|
1719
|
+
var i;
|
|
1720
|
+
for (i = 256; i < 280; ++i)
|
|
1721
|
+
flt[i] = 7;
|
|
1722
|
+
var i;
|
|
1723
|
+
for (i = 280; i < 288; ++i)
|
|
1724
|
+
flt[i] = 8;
|
|
1725
|
+
var i;
|
|
1726
|
+
var fdt = new u8(32);
|
|
1727
|
+
for (i = 0; i < 32; ++i)
|
|
1728
|
+
fdt[i] = 5;
|
|
1729
|
+
var i;
|
|
1730
|
+
var et = /* @__PURE__ */ new u8(0);
|
|
1731
|
+
var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder();
|
|
1732
|
+
var tds = 0;
|
|
1733
|
+
try {
|
|
1734
|
+
td.decode(et, { stream: true });
|
|
1735
|
+
tds = 1;
|
|
1736
|
+
} catch (e) {
|
|
1737
|
+
}
|
|
1608
1738
|
var MAGIC = 1263486208;
|
|
1609
1739
|
function parseKoeHeader(headerBytes) {
|
|
1610
1740
|
const view = new DataView(headerBytes);
|
|
@@ -1770,7 +1900,7 @@ var VoiceBank = class _VoiceBank {
|
|
|
1770
1900
|
if (!buf) return null;
|
|
1771
1901
|
const int16 = new Int16Array(buf, 0, Math.floor(buf.byteLength / 2));
|
|
1772
1902
|
const f64 = new Float64Array(int16.length);
|
|
1773
|
-
for (let
|
|
1903
|
+
for (let i2 = 0; i2 < int16.length; i2++) f64[i2] = int16[i2] / 32768;
|
|
1774
1904
|
return f64;
|
|
1775
1905
|
}
|
|
1776
1906
|
};
|
|
@@ -1778,6 +1908,7 @@ var WORLDLINE_SAMPLE_RATE = 48e3;
|
|
|
1778
1908
|
var MIN_WORLDLINE_SAMPLES = 4096;
|
|
1779
1909
|
var SYNTH_REQ_SIZE = 120;
|
|
1780
1910
|
var WL_FRAME_MS = 10;
|
|
1911
|
+
var sampleCurve = (input, curve, totalMs) => typeof curve === "function" ? curve(input, totalMs) : curve;
|
|
1781
1912
|
var samplesToMs = (samples) => samples / WORLDLINE_SAMPLE_RATE * 1e3;
|
|
1782
1913
|
function leadInFromEntry(entry) {
|
|
1783
1914
|
return {
|
|
@@ -1863,11 +1994,23 @@ var Worldline = class _Worldline {
|
|
|
1863
1994
|
* {@link MIN_WORLDLINE_SAMPLES} (too short for stable F0 analysis).
|
|
1864
1995
|
*/
|
|
1865
1996
|
renderNote(params) {
|
|
1866
|
-
const {
|
|
1997
|
+
const {
|
|
1998
|
+
pcm,
|
|
1999
|
+
pitch,
|
|
2000
|
+
durationMs,
|
|
2001
|
+
preMs,
|
|
2002
|
+
consonantMs,
|
|
2003
|
+
tempo = 120,
|
|
2004
|
+
gender = 0.5,
|
|
2005
|
+
tension = 0.5,
|
|
2006
|
+
breathiness = 0.5,
|
|
2007
|
+
voicing = 1
|
|
2008
|
+
} = params;
|
|
1867
2009
|
if (!pcm || pcm.length < MIN_WORLDLINE_SAMPLES) return null;
|
|
1868
2010
|
const WL = this.wasm;
|
|
1869
2011
|
const FS = WORLDLINE_SAMPLE_RATE;
|
|
1870
|
-
const
|
|
2012
|
+
const basePitch = sampleCurve(preMs + durationMs / 2, pitch, preMs + durationMs);
|
|
2013
|
+
const midiNote = Math.round(69 + 12 * Math.log2(basePitch / 440));
|
|
1871
2014
|
const posMs = 0;
|
|
1872
2015
|
const reqLen = preMs + durationMs;
|
|
1873
2016
|
const cutMs = WL_FRAME_MS * 2;
|
|
@@ -1913,11 +2056,19 @@ var Worldline = class _Worldline {
|
|
|
1913
2056
|
WL._free(reqPtr);
|
|
1914
2057
|
const totalMs = posMs + reqLen + WL_FRAME_MS * 2;
|
|
1915
2058
|
const nFrames = Math.ceil(totalMs / WL_FRAME_MS) + 4;
|
|
1916
|
-
const f0Arr = new Float64Array(nFrames)
|
|
1917
|
-
const gArr = new Float64Array(nFrames)
|
|
1918
|
-
const tArr = new Float64Array(nFrames)
|
|
1919
|
-
const bArr = new Float64Array(nFrames)
|
|
1920
|
-
const vArr = new Float64Array(nFrames)
|
|
2059
|
+
const f0Arr = new Float64Array(nFrames);
|
|
2060
|
+
const gArr = new Float64Array(nFrames);
|
|
2061
|
+
const tArr = new Float64Array(nFrames);
|
|
2062
|
+
const bArr = new Float64Array(nFrames);
|
|
2063
|
+
const vArr = new Float64Array(nFrames);
|
|
2064
|
+
for (let i2 = 0; i2 < nFrames; i2++) {
|
|
2065
|
+
const tMs = i2 * WL_FRAME_MS;
|
|
2066
|
+
f0Arr[i2] = sampleCurve(tMs, pitch, totalMs);
|
|
2067
|
+
gArr[i2] = sampleCurve(tMs, gender, totalMs);
|
|
2068
|
+
tArr[i2] = sampleCurve(tMs, tension, totalMs);
|
|
2069
|
+
bArr[i2] = sampleCurve(tMs, breathiness, totalMs);
|
|
2070
|
+
vArr[i2] = sampleCurve(tMs, voicing, totalMs);
|
|
2071
|
+
}
|
|
1921
2072
|
const f0Ptr = WL._malloc(nFrames * 8);
|
|
1922
2073
|
const gPtr = WL._malloc(nFrames * 8);
|
|
1923
2074
|
const tPtr = WL._malloc(nFrames * 8);
|
|
@@ -1977,6 +2128,19 @@ var DEFAULT_PLAYBACK_VELOCITY = 127;
|
|
|
1977
2128
|
var DEFAULT_STEPS_PER_BAR = 192;
|
|
1978
2129
|
var MML_END_MARKER = "#end;";
|
|
1979
2130
|
|
|
2131
|
+
// src/vibrato.ts
|
|
2132
|
+
var VIBRATO_MIN_SEC = 0.35;
|
|
2133
|
+
var VIBRATO_RATE_HZ = 5.5;
|
|
2134
|
+
var VIBRATO_DEPTH_CENTS = 35;
|
|
2135
|
+
var VIBRATO_FADE_MS = 150;
|
|
2136
|
+
var vibratoPitchCurve = (baseHz, preMs) => (tMs) => {
|
|
2137
|
+
const sinceOnset = tMs - preMs;
|
|
2138
|
+
if (sinceOnset <= 0) return baseHz;
|
|
2139
|
+
const depth = VIBRATO_DEPTH_CENTS * Math.min(1, sinceOnset / VIBRATO_FADE_MS);
|
|
2140
|
+
const cents = depth * Math.sin(2 * Math.PI * VIBRATO_RATE_HZ * sinceOnset / 1e3);
|
|
2141
|
+
return baseHz * 2 ** (cents / 1200);
|
|
2142
|
+
};
|
|
2143
|
+
|
|
1980
2144
|
// src/lyrics.ts
|
|
1981
2145
|
var kanaTable = {
|
|
1982
2146
|
\u3042: ["", "a"],
|
|
@@ -2141,8 +2305,8 @@ var vocalVolumeToGain = (v) => {
|
|
|
2141
2305
|
var parseLyrics = (mml) => {
|
|
2142
2306
|
const tracks = /* @__PURE__ */ new Map();
|
|
2143
2307
|
const segments = splitSegments(mml);
|
|
2144
|
-
for (let
|
|
2145
|
-
const m = segments[
|
|
2308
|
+
for (let i2 = 0; i2 < segments.length; i2++) {
|
|
2309
|
+
const m = segments[i2].match(LYRIC_LINE);
|
|
2146
2310
|
if (!m) continue;
|
|
2147
2311
|
const trackId = Number.parseInt(m[1], 10);
|
|
2148
2312
|
let rest = m[2].trim();
|
|
@@ -2150,8 +2314,10 @@ var parseLyrics = (mml) => {
|
|
|
2150
2314
|
let gate = 100;
|
|
2151
2315
|
let pan = 64;
|
|
2152
2316
|
let octave = 0;
|
|
2317
|
+
let vibrato = false;
|
|
2318
|
+
let reverb = 0;
|
|
2153
2319
|
const modelMatch = rest.match(
|
|
2154
|
-
/^([a-z_][a-z0-9_]*?)(?=(?:[
|
|
2320
|
+
/^([a-z_][a-z0-9_]*?)(?=(?:[vqpobr]-?\d)|[^a-z0-9_]|$)(?::(\d+))?/i
|
|
2155
2321
|
);
|
|
2156
2322
|
let model = "";
|
|
2157
2323
|
const metaTokens = [];
|
|
@@ -2192,11 +2358,25 @@ var parseLyrics = (mml) => {
|
|
|
2192
2358
|
rest = rest.substring(oMatch[0].length).trim();
|
|
2193
2359
|
continue;
|
|
2194
2360
|
}
|
|
2361
|
+
const bMatch = rest.match(/^b([01])/i);
|
|
2362
|
+
if (bMatch) {
|
|
2363
|
+
vibrato = bMatch[1] === "1";
|
|
2364
|
+
metaTokens.push(bMatch[0]);
|
|
2365
|
+
rest = rest.substring(bMatch[0].length).trim();
|
|
2366
|
+
continue;
|
|
2367
|
+
}
|
|
2368
|
+
const rMatch = rest.match(/^r(\d+)/i);
|
|
2369
|
+
if (rMatch) {
|
|
2370
|
+
reverb = clamp(Number.parseInt(rMatch[1], 10), 0, 100);
|
|
2371
|
+
metaTokens.push(rMatch[0]);
|
|
2372
|
+
rest = rest.substring(rMatch[0].length).trim();
|
|
2373
|
+
continue;
|
|
2374
|
+
}
|
|
2195
2375
|
break;
|
|
2196
2376
|
}
|
|
2197
2377
|
const lyricLines = [rest];
|
|
2198
|
-
while (
|
|
2199
|
-
lyricLines.push(segments[++
|
|
2378
|
+
while (i2 + 1 < segments.length && isLyricContinuation(segments[i2 + 1])) {
|
|
2379
|
+
lyricLines.push(segments[++i2]);
|
|
2200
2380
|
}
|
|
2201
2381
|
const { syllables, lineBreaks } = normalizeLyricLines(lyricLines);
|
|
2202
2382
|
tracks.set(trackId, {
|
|
@@ -2206,6 +2386,8 @@ var parseLyrics = (mml) => {
|
|
|
2206
2386
|
gate,
|
|
2207
2387
|
pan,
|
|
2208
2388
|
octave,
|
|
2389
|
+
vibrato,
|
|
2390
|
+
reverb,
|
|
2209
2391
|
syllables,
|
|
2210
2392
|
metaText: metaTokens.join(" "),
|
|
2211
2393
|
...lineBreaks.length > 0 ? { lineBreaks } : {}
|
|
@@ -2216,13 +2398,13 @@ var parseLyrics = (mml) => {
|
|
|
2216
2398
|
var stripLyrics = (mml) => {
|
|
2217
2399
|
const segments = splitSegments(mml);
|
|
2218
2400
|
const kept = [];
|
|
2219
|
-
for (let
|
|
2220
|
-
if (LYRIC_LINE.test(segments[
|
|
2221
|
-
while (
|
|
2222
|
-
|
|
2401
|
+
for (let i2 = 0; i2 < segments.length; i2++) {
|
|
2402
|
+
if (LYRIC_LINE.test(segments[i2])) {
|
|
2403
|
+
while (i2 + 1 < segments.length && isLyricContinuation(segments[i2 + 1]))
|
|
2404
|
+
i2++;
|
|
2223
2405
|
continue;
|
|
2224
2406
|
}
|
|
2225
|
-
kept.push(segments[
|
|
2407
|
+
kept.push(segments[i2]);
|
|
2226
2408
|
}
|
|
2227
2409
|
return kept.join("\n");
|
|
2228
2410
|
};
|
|
@@ -2291,7 +2473,7 @@ var FORMANTS = {
|
|
|
2291
2473
|
N: [250, 1e3]
|
|
2292
2474
|
};
|
|
2293
2475
|
var midiToFreq = (m) => 440 * 2 ** ((m - 69) / 12);
|
|
2294
|
-
var createKlattVoice = (ctx, destination) => {
|
|
2476
|
+
var createKlattVoice = (ctx, destination, reverbBus) => {
|
|
2295
2477
|
const active = /* @__PURE__ */ new Set();
|
|
2296
2478
|
const voice = (syllable, e) => {
|
|
2297
2479
|
const t0 = ctx.currentTime + e.when;
|
|
@@ -2309,6 +2491,12 @@ var createKlattVoice = (ctx, destination) => {
|
|
|
2309
2491
|
panner.connect(destination);
|
|
2310
2492
|
out = panner;
|
|
2311
2493
|
}
|
|
2494
|
+
let sendGain = null;
|
|
2495
|
+
if (reverbBus && e.reverbSend && e.reverbSend > 0 && panner) {
|
|
2496
|
+
sendGain = ctx.createGain();
|
|
2497
|
+
sendGain.gain.value = Math.max(0, Math.min(1, e.reverbSend));
|
|
2498
|
+
panner.connect(sendGain).connect(reverbBus);
|
|
2499
|
+
}
|
|
2312
2500
|
const osc = ctx.createOscillator();
|
|
2313
2501
|
osc.type = "sawtooth";
|
|
2314
2502
|
osc.frequency.value = midiToFreq(e.pitch);
|
|
@@ -2337,7 +2525,7 @@ var createKlattVoice = (ctx, destination) => {
|
|
|
2337
2525
|
const length = Math.max(1, Math.floor(ctx.sampleRate * dur));
|
|
2338
2526
|
const buffer = ctx.createBuffer(1, length, ctx.sampleRate);
|
|
2339
2527
|
const data = buffer.getChannelData(0);
|
|
2340
|
-
for (let
|
|
2528
|
+
for (let i2 = 0; i2 < length; i2++) data[i2] = Math.random() * 2 - 1;
|
|
2341
2529
|
const src = ctx.createBufferSource();
|
|
2342
2530
|
src.buffer = buffer;
|
|
2343
2531
|
const hp = ctx.createBiquadFilter();
|
|
@@ -2364,6 +2552,7 @@ var createKlattVoice = (ctx, destination) => {
|
|
|
2364
2552
|
active.delete(osc);
|
|
2365
2553
|
osc.disconnect();
|
|
2366
2554
|
panner?.disconnect();
|
|
2555
|
+
sendGain?.disconnect();
|
|
2367
2556
|
};
|
|
2368
2557
|
};
|
|
2369
2558
|
voice.stopAll = () => {
|
|
@@ -2382,6 +2571,7 @@ var KOE_BASE_URL = "https://pub-12482a6b5cbc4c9e906b2e1904cabae5.r2.dev";
|
|
|
2382
2571
|
var KOE_VOICEBANKS = {
|
|
2383
2572
|
tsukuyomi: "\u3064\u304F\u3088\u307F\u3061\u3083\u3093.koe",
|
|
2384
2573
|
rino: "\u6625\u97F3\u30EA\u30CEver0.3.koe",
|
|
2574
|
+
rino121: "\u6625\u97F3\u30EA\u30CEver.1.1(226).koe",
|
|
2385
2575
|
roze: "\u675F\u97F3\u30ED\u30BCver0.\uFF151(\u591A\u97F3\u968E).koe",
|
|
2386
2576
|
ruko_male: "\u6B32\u97F3\u30EB\u30B3\u2642\u9023\u7D9A\u97F3Ver.1.03.koe",
|
|
2387
2577
|
ruko_female: "\u6B32\u97F3\u30EB\u30B3\u2640\u6B4C\u9023\u7D9A\u97F3\u666E1.00.koe",
|
|
@@ -2390,11 +2580,13 @@ var KOE_VOICEBANKS = {
|
|
|
2390
2580
|
rei: "\u8DB3\u7ACB\u30EC\u30A4ver3.5.0.koe",
|
|
2391
2581
|
mgroid: "MGRoid_\u539F\u97F3\u8A2D\u5B9A\u6E08\u307F.koe",
|
|
2392
2582
|
motroid: "MOTRoid\u5B8C\u5168\u7248V2.koe",
|
|
2393
|
-
nynroid: "NYNRoidver1.4.koe"
|
|
2583
|
+
nynroid: "NYNRoidver1.4.koe",
|
|
2584
|
+
uc: "\u84C4\u97F3\u30AD\u30EA\u30B3\u97F3\u6E90.koe"
|
|
2394
2585
|
};
|
|
2395
2586
|
var KOE_VOICEBANK_LABELS = {
|
|
2396
2587
|
tsukuyomi: "\u3064\u304F\u3088\u307F\u3061\u3083\u3093",
|
|
2397
2588
|
rino: "\u6625\u97F3\u30EA\u30CE",
|
|
2589
|
+
rino121: "\u6625\u97F3\u30EA\u30CEv1.2.1",
|
|
2398
2590
|
roze: "\u675F\u97F3\u30ED\u30BC",
|
|
2399
2591
|
ruko_male: "\u6B32\u97F3\u30EB\u30B3\u2642",
|
|
2400
2592
|
ruko_female: "\u6B32\u97F3\u30EB\u30B3\u2640",
|
|
@@ -2403,12 +2595,14 @@ var KOE_VOICEBANK_LABELS = {
|
|
|
2403
2595
|
rei: "\u8DB3\u7ACB\u30EC\u30A4",
|
|
2404
2596
|
mgroid: "MGRoid",
|
|
2405
2597
|
motroid: "MOTRoid",
|
|
2406
|
-
nynroid: "NYNRoid"
|
|
2598
|
+
nynroid: "NYNRoid",
|
|
2599
|
+
uc: "\u84C4\u97F3\u30AD\u30EA\u30B3"
|
|
2407
2600
|
};
|
|
2408
2601
|
var VOICE_IMAGE_KEY = {
|
|
2409
2602
|
klatt: "puyuyu",
|
|
2410
2603
|
tsukuyomi: "tsukuyomi",
|
|
2411
2604
|
rino: "rino",
|
|
2605
|
+
rino121: "rino",
|
|
2412
2606
|
roze: "roze",
|
|
2413
2607
|
ruko_male: "ruko",
|
|
2414
2608
|
ruko_female: "ruko",
|
|
@@ -2417,11 +2611,13 @@ var VOICE_IMAGE_KEY = {
|
|
|
2417
2611
|
rei: "rei",
|
|
2418
2612
|
mgroid: "MGRoid",
|
|
2419
2613
|
motroid: "MOTRoid",
|
|
2420
|
-
nynroid: "NYNRoid"
|
|
2614
|
+
nynroid: "NYNRoid",
|
|
2615
|
+
uc: "uc"
|
|
2421
2616
|
};
|
|
2422
2617
|
var KOE_VOICEBANK_TERMS = {
|
|
2423
2618
|
tsukuyomi: "https://tyc.rei-yumesaki.net/material/utau/terms/",
|
|
2424
2619
|
rino: "https://hatenakun1.github.io/halunelino/",
|
|
2620
|
+
rino121: "https://harunerino.vercel.app/",
|
|
2425
2621
|
roze: "https://tabaneroze.ninja-web.net/terms-of-use.html",
|
|
2426
2622
|
ruko_male: "https://long-sleeper.net/index.php?id=22",
|
|
2427
2623
|
ruko_female: "https://long-sleeper.net/index.php?id=22",
|
|
@@ -2430,7 +2626,8 @@ var KOE_VOICEBANK_TERMS = {
|
|
|
2430
2626
|
rei: "https://mechanicalgirl.jp/guidelines/",
|
|
2431
2627
|
mgroid: "https://x.com/nisusansu/status/1048825378188353536",
|
|
2432
2628
|
motroid: "https://www.nicovideo.jp/watch/sm40031282",
|
|
2433
|
-
nynroid: "https://www.bilibili.com/video/BV1V24y1a7qs"
|
|
2629
|
+
nynroid: "https://www.bilibili.com/video/BV1V24y1a7qs",
|
|
2630
|
+
uc: "https://chi9nekiriko.wixsite.com/home/%E5%88%A9%E7%94%A8%E8%A6%8F%E7%B4%84"
|
|
2434
2631
|
};
|
|
2435
2632
|
var koeUrl = (name, base = KOE_BASE_URL) => `${base}/${encodeURIComponent(name)}`;
|
|
2436
2633
|
var DEFAULT_WORLDLINE_SCRIPT = "https://onjmin.github.io/koe/demo/world/worldline.js";
|
|
@@ -2519,7 +2716,7 @@ var createLocalBackend = async (options) => {
|
|
|
2519
2716
|
}
|
|
2520
2717
|
return p;
|
|
2521
2718
|
};
|
|
2522
|
-
const renderAlias = async (alias, pitch, durationMs) => {
|
|
2719
|
+
const renderAlias = async (alias, pitch, durationMs, vibrato) => {
|
|
2523
2720
|
const pcm = await getPcm(alias);
|
|
2524
2721
|
if (!pcm || pcm.length === 0) return null;
|
|
2525
2722
|
const entry = bank.manifest.phonemes[alias];
|
|
@@ -2528,7 +2725,7 @@ var createLocalBackend = async (options) => {
|
|
|
2528
2725
|
if (worldline) {
|
|
2529
2726
|
const audio = worldline.renderNote({
|
|
2530
2727
|
pcm,
|
|
2531
|
-
pitch: targetHz,
|
|
2728
|
+
pitch: vibrato ? vibratoPitchCurve(targetHz, lead.preMs) : targetHz,
|
|
2532
2729
|
durationMs,
|
|
2533
2730
|
...lead
|
|
2534
2731
|
});
|
|
@@ -2595,7 +2792,7 @@ var createWorkerBackend = async (workerUrl, options) => {
|
|
|
2595
2792
|
});
|
|
2596
2793
|
onReady = null;
|
|
2597
2794
|
onFail = null;
|
|
2598
|
-
const renderAlias = (alias, pitch, durationMs) => new Promise((resolve) => {
|
|
2795
|
+
const renderAlias = (alias, pitch, durationMs, vibrato) => new Promise((resolve) => {
|
|
2599
2796
|
const id = ++reqId;
|
|
2600
2797
|
pending.set(
|
|
2601
2798
|
id,
|
|
@@ -2608,7 +2805,8 @@ var createWorkerBackend = async (workerUrl, options) => {
|
|
|
2608
2805
|
id,
|
|
2609
2806
|
alias,
|
|
2610
2807
|
pitch,
|
|
2611
|
-
durationMs
|
|
2808
|
+
durationMs,
|
|
2809
|
+
vibrato
|
|
2612
2810
|
});
|
|
2613
2811
|
});
|
|
2614
2812
|
return {
|
|
@@ -2637,15 +2835,15 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
2637
2835
|
const inflight = /* @__PURE__ */ new Map();
|
|
2638
2836
|
const active = /* @__PURE__ */ new Set();
|
|
2639
2837
|
let prevVowel = "";
|
|
2640
|
-
const keyOf = (alias, pitch, durationMs) => `${alias}|${pitch}|${Math.round(durationMs / 10) * 10}`;
|
|
2641
|
-
const renderInto = (alias, pitch, durationMs) => {
|
|
2642
|
-
const key = keyOf(alias, pitch, durationMs);
|
|
2838
|
+
const keyOf = (alias, pitch, durationMs, vibrato) => `${alias}|${pitch}|${Math.round(durationMs / 10) * 10}${vibrato ? "|vib" : ""}`;
|
|
2839
|
+
const renderInto = (alias, pitch, durationMs, vibrato) => {
|
|
2840
|
+
const key = keyOf(alias, pitch, durationMs, vibrato);
|
|
2643
2841
|
const existing = renderCache.get(key);
|
|
2644
2842
|
if (existing !== void 0) return Promise.resolve(existing);
|
|
2645
2843
|
const flying = inflight.get(key);
|
|
2646
2844
|
if (flying) return flying;
|
|
2647
2845
|
const p = (async () => {
|
|
2648
|
-
const out = await backend.renderAlias(alias, pitch, durationMs);
|
|
2846
|
+
const out = await backend.renderAlias(alias, pitch, durationMs, vibrato);
|
|
2649
2847
|
let rendered = null;
|
|
2650
2848
|
if (out) {
|
|
2651
2849
|
const buf = ctx.createBuffer(1, out.pcm.length, KOE_SAMPLE_RATE);
|
|
@@ -2660,7 +2858,7 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
2660
2858
|
return p;
|
|
2661
2859
|
};
|
|
2662
2860
|
const LEADCAP_S = 0.09;
|
|
2663
|
-
const schedule = (r, t0, peak, pan) => {
|
|
2861
|
+
const schedule = (r, t0, peak, pan, reverbSend = 0) => {
|
|
2664
2862
|
let out = destination;
|
|
2665
2863
|
let panner = null;
|
|
2666
2864
|
if (typeof ctx.createStereoPanner === "function") {
|
|
@@ -2669,6 +2867,12 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
2669
2867
|
panner.connect(destination);
|
|
2670
2868
|
out = panner;
|
|
2671
2869
|
}
|
|
2870
|
+
let sendGain = null;
|
|
2871
|
+
if (options.reverbBus && reverbSend > 0 && panner) {
|
|
2872
|
+
sendGain = ctx.createGain();
|
|
2873
|
+
sendGain.gain.value = Math.max(0, Math.min(1, reverbSend));
|
|
2874
|
+
panner.connect(sendGain).connect(options.reverbBus);
|
|
2875
|
+
}
|
|
2672
2876
|
const src = ctx.createBufferSource();
|
|
2673
2877
|
src.buffer = r.audio;
|
|
2674
2878
|
src.playbackRate.value = r.rate;
|
|
@@ -2694,6 +2898,7 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
2694
2898
|
src.disconnect();
|
|
2695
2899
|
env.disconnect();
|
|
2696
2900
|
panner?.disconnect();
|
|
2901
|
+
sendGain?.disconnect();
|
|
2697
2902
|
};
|
|
2698
2903
|
};
|
|
2699
2904
|
const model = (syllable, e) => {
|
|
@@ -2712,10 +2917,10 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
2712
2917
|
const pan = e.pan ?? 0;
|
|
2713
2918
|
const durationMs = Math.max(60, e.duration * 1e3);
|
|
2714
2919
|
void renderInto(alias, e.pitch, durationMs).then((r) => {
|
|
2715
|
-
if (r) schedule(r, t0, peak, pan);
|
|
2920
|
+
if (r) schedule(r, t0, peak, pan, e.reverbSend);
|
|
2716
2921
|
});
|
|
2717
2922
|
};
|
|
2718
|
-
model.renderToCache = async (syllable, prevVowelArg, pitch, durationMs) => {
|
|
2923
|
+
model.renderToCache = async (syllable, prevVowelArg, pitch, durationMs, vibrato) => {
|
|
2719
2924
|
if (syllable.consonant === "Q" || syllable.vowel === "") return null;
|
|
2720
2925
|
const alias = resolveKoeAlias(
|
|
2721
2926
|
backend.hasAlias,
|
|
@@ -2726,12 +2931,13 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
2726
2931
|
);
|
|
2727
2932
|
if (!alias) return null;
|
|
2728
2933
|
const dMs = Math.max(60, durationMs);
|
|
2729
|
-
const
|
|
2730
|
-
|
|
2934
|
+
const vib = !!vibrato && dMs / 1e3 >= VIBRATO_MIN_SEC;
|
|
2935
|
+
const r = await renderInto(alias, pitch, dMs, vib);
|
|
2936
|
+
return r ? keyOf(alias, pitch, dMs, vib) : null;
|
|
2731
2937
|
};
|
|
2732
|
-
model.scheduleCached = (key, t0, peak, pan) => {
|
|
2938
|
+
model.scheduleCached = (key, t0, peak, pan, reverbSend) => {
|
|
2733
2939
|
const r = renderCache.get(key);
|
|
2734
|
-
if (r) schedule(r, t0, peak, pan);
|
|
2940
|
+
if (r) schedule(r, t0, peak, pan, reverbSend);
|
|
2735
2941
|
};
|
|
2736
2942
|
model.stopAll = () => {
|
|
2737
2943
|
for (const src of active) {
|
|
@@ -2762,7 +2968,7 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2762
2968
|
const masterGain = ctx.createGain();
|
|
2763
2969
|
masterGain.connect(destination);
|
|
2764
2970
|
const loaded = /* @__PURE__ */ new Map([
|
|
2765
|
-
[FALLBACK_MODEL, createKlattVoice(ctx, masterGain)]
|
|
2971
|
+
[FALLBACK_MODEL, createKlattVoice(ctx, masterGain, options.reverbBus)]
|
|
2766
2972
|
]);
|
|
2767
2973
|
const loading = /* @__PURE__ */ new Map();
|
|
2768
2974
|
const load2 = (model) => {
|
|
@@ -2782,7 +2988,8 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2782
2988
|
koe,
|
|
2783
2989
|
worldlineScriptUrl: options.worldlineScriptUrl,
|
|
2784
2990
|
lightweight: options.lightweight,
|
|
2785
|
-
voiceWorkerUrl: options.voiceWorkerUrl
|
|
2991
|
+
voiceWorkerUrl: options.voiceWorkerUrl,
|
|
2992
|
+
reverbBus: options.reverbBus
|
|
2786
2993
|
})
|
|
2787
2994
|
))().then((v) => {
|
|
2788
2995
|
loaded.set(m, v);
|
|
@@ -2827,7 +3034,7 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2827
3034
|
forEachSungNote(track, (note, prevVowel) => {
|
|
2828
3035
|
if (n >= count && note.startSec >= STREAM_LOOKAHEAD_SEC) return;
|
|
2829
3036
|
n++;
|
|
2830
|
-
tasks.push({ model: m, note, prevVowel });
|
|
3037
|
+
tasks.push({ model: m, note, prevVowel, vibrato: track.vibrato });
|
|
2831
3038
|
});
|
|
2832
3039
|
}
|
|
2833
3040
|
const total = tasks.length;
|
|
@@ -2842,7 +3049,8 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2842
3049
|
task.note.syllable,
|
|
2843
3050
|
task.prevVowel,
|
|
2844
3051
|
task.note.pitch,
|
|
2845
|
-
task.note.durationSec * 1e3
|
|
3052
|
+
task.note.durationSec * 1e3,
|
|
3053
|
+
task.vibrato
|
|
2846
3054
|
) ?? Promise.resolve(null));
|
|
2847
3055
|
done++;
|
|
2848
3056
|
onProgress?.(done, total);
|
|
@@ -2887,13 +3095,14 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2887
3095
|
note.syllable,
|
|
2888
3096
|
prevVowel,
|
|
2889
3097
|
note.pitch,
|
|
2890
|
-
note.durationSec * 1e3
|
|
3098
|
+
note.durationSec * 1e3,
|
|
3099
|
+
track.vibrato
|
|
2891
3100
|
);
|
|
2892
3101
|
if (session !== streamSession) return;
|
|
2893
3102
|
if (key) {
|
|
2894
3103
|
const delay = ctx.currentTime - t0;
|
|
2895
3104
|
if (delay < 0.05) {
|
|
2896
|
-
scheduleCached(key, t0, peak, track.pan);
|
|
3105
|
+
scheduleCached(key, t0, peak, track.pan, track.reverbSend);
|
|
2897
3106
|
opts?.onScheduled?.(track, note, t0);
|
|
2898
3107
|
} else {
|
|
2899
3108
|
console.warn(
|
|
@@ -2912,7 +3121,8 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
2912
3121
|
volume: peak,
|
|
2913
3122
|
when,
|
|
2914
3123
|
duration: note.durationSec,
|
|
2915
|
-
pan: track.pan
|
|
3124
|
+
pan: track.pan,
|
|
3125
|
+
reverbSend: track.reverbSend
|
|
2916
3126
|
});
|
|
2917
3127
|
opts?.onScheduled?.(track, note, t0);
|
|
2918
3128
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -5773,7 +5983,7 @@ var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
|
5773
5983
|
const length = Math.max(1, Math.floor(ctx.sampleRate * dur));
|
|
5774
5984
|
const buffer = ctx.createBuffer(1, length, ctx.sampleRate);
|
|
5775
5985
|
const data = buffer.getChannelData(0);
|
|
5776
|
-
for (let
|
|
5986
|
+
for (let i2 = 0; i2 < length; i2++) data[i2] = Math.random() * 2 - 1;
|
|
5777
5987
|
const src = ctx.createBufferSource();
|
|
5778
5988
|
src.buffer = buffer;
|
|
5779
5989
|
const filter = ctx.createBiquadFilter();
|
|
@@ -7694,6 +7904,9 @@ var teto_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAA
|
|
|
7694
7904
|
// assets/tsukuyomi.png
|
|
7695
7905
|
var tsukuyomi_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAB3UlEQVR42tWXPUsDQRCGnwuijYIoFqKmEDSCxIgIgqnsogbsrC3ERiwN4g8Ikk6sFH+BnYofvcTKxoigBCzEYGGjWIiCOYvNBHe5TdQUt051czc3e++zO7tzHnUst7vn04BlFue9Ws8jhGxN9ZTPzU43NkIlj41E6AQ8m/JYfByAoWgXAPtHJzRCRN43SbizBkzlv1X2VzLuVoHYzf0TAMnys+aLCTGThMTJGnKfQOpyWF1cvgHQsh4FYPDzRd3vmQHgoPShJbi9uqhJyPk1UK3Jwsq1/115rLst8AX/4UFP0NsbGHf7+Kr579l7zR/ZGvbcrAJR7vvqEOwcTdZMYIuTPCYJUS7Ewydgzn2Bay2gdWzqR4nMuMPlhcC5l/HcWQOnCaU4lVU3xI9tnwFQnOgDoDmdDs5QKmlxYhdGmBA2ibhTBfKFHCkCm00dAKymJn+USAidHx8DkI+0q7PBUC7j5BJ7jlSB7RyXziVT8fM7Oz7AeLmsz3VEaWi7UvtAcmvJC+qmq4T/XT8gdTsw2692to07AF7jeU35wFq/7COqzhdVnVOnu3aHgNm1VpVXlImJX9zQffO5kDg1dlb3CNj6dZsVK2vAVP7Xf8bwCfxWuc23Eanmt/xlh07gC6oBviFE8rZHAAAAAElFTkSuQmCC";
|
|
7696
7906
|
|
|
7907
|
+
// assets/uc.png
|
|
7908
|
+
var uc_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAB/ElEQVR42s2WPUhbURTHfxHniEEIVdvFmDaTiIJjBpcuGQrFr2JDaoeCkxRdAg6WVPAD20FwqEoUQUs7hdAuDaSbYNpGF6VxiaLooCCdREyHlxs4z/deEhG8Zwn/m8d57/+759x7XEAB53BxuyhU8lAN9xyusLfe8UvTrT4ALhr3q0ocSjsDiJ+c60Gg9q6dq1jYbATgTdeR3jVQW63z5t6nABxufBd6syMLQFemDYDA0jEA829nAPg8O6ApAVWNYW99RU7tQjkvkcDQw9kpAILa14B579203iqhmURqv1gDDIj+Vyesfl1Qbq/NNRFJ7gDwsblBPthh/AxuvQDgzORc33PgImv0r7vtgeV6icTkFQDvCBgL26ZEr04r+oD7J6D6P21DoqR3s0LnIntC+5YfC900ViT5xOiKkPeP6oKC3l1A6p+QblqE/tV/aZnoBpG09QsVcUVCHwLBvznxRyJotGsu+FCsj0zkLRPNjT8qOj9wrB39ZsJEIiGGt/Bq2HqPy5CwI2CeDZ9/WjHWQyFNCJjn9+TUN3GGr3auAXAdME6+TCYjEtTlv8rqf/Ze6C+vXwrnJTLaEfD0eBxvRXUL+vN+x4TRaFTon2sfAJhe/6H5bThU0w7A4vXvqmZBO+fmGO3rViQ0vwvsSMQH4wDEYrGKnKu9134e+A/zObZWSEHtdQAAAABJRU5ErkJggg==";
|
|
7909
|
+
|
|
7697
7910
|
// src/voice-images.ts
|
|
7698
7911
|
var VOICE_IMAGES = {
|
|
7699
7912
|
puyuyu: puyuyu_default,
|
|
@@ -7706,7 +7919,8 @@ var VOICE_IMAGES = {
|
|
|
7706
7919
|
rei: rei_default,
|
|
7707
7920
|
MGRoid: MGRoid_default,
|
|
7708
7921
|
MOTRoid: MOTRoid_default,
|
|
7709
|
-
NYNRoid: NYNRoid_default
|
|
7922
|
+
NYNRoid: NYNRoid_default,
|
|
7923
|
+
uc: uc_default
|
|
7710
7924
|
};
|
|
7711
7925
|
var FALLBACK_VOCAL_ICON = Chip_default;
|
|
7712
7926
|
|
|
@@ -7768,8 +7982,8 @@ var copyToClipboard = async (doc, text) => {
|
|
|
7768
7982
|
};
|
|
7769
7983
|
var toBase64Url = (bytes) => {
|
|
7770
7984
|
let bin = "";
|
|
7771
|
-
for (let
|
|
7772
|
-
bin += String.fromCharCode(bytes[
|
|
7985
|
+
for (let i2 = 0; i2 < bytes.length; i2++) {
|
|
7986
|
+
bin += String.fromCharCode(bytes[i2]);
|
|
7773
7987
|
}
|
|
7774
7988
|
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
7775
7989
|
};
|
|
@@ -7782,8 +7996,8 @@ var SHIFT_KATAKANA = 255;
|
|
|
7782
7996
|
var VALUE_PROLONGED = 223;
|
|
7783
7997
|
var customEncode = (str) => {
|
|
7784
7998
|
const bytes = [];
|
|
7785
|
-
for (let
|
|
7786
|
-
const code = str.charCodeAt(
|
|
7999
|
+
for (let i2 = 0; i2 < str.length; i2++) {
|
|
8000
|
+
const code = str.charCodeAt(i2);
|
|
7787
8001
|
if (code === 32) {
|
|
7788
8002
|
continue;
|
|
7789
8003
|
}
|
|
@@ -7825,37 +8039,37 @@ var fromBase64Url = (s) => {
|
|
|
7825
8039
|
}
|
|
7826
8040
|
const bin = atob(normalized);
|
|
7827
8041
|
const bytes = new Uint8Array(bin.length);
|
|
7828
|
-
for (let
|
|
7829
|
-
bytes[
|
|
8042
|
+
for (let i2 = 0; i2 < bin.length; i2++) {
|
|
8043
|
+
bytes[i2] = bin.charCodeAt(i2);
|
|
7830
8044
|
}
|
|
7831
8045
|
return bytes;
|
|
7832
8046
|
};
|
|
7833
8047
|
var customDecode = (bytes) => {
|
|
7834
8048
|
let str = "";
|
|
7835
|
-
let
|
|
7836
|
-
while (
|
|
7837
|
-
const byte = bytes[
|
|
8049
|
+
let i2 = 0;
|
|
8050
|
+
while (i2 < bytes.length) {
|
|
8051
|
+
const byte = bytes[i2];
|
|
7838
8052
|
if (byte <= 127) {
|
|
7839
8053
|
str += String.fromCharCode(byte);
|
|
7840
|
-
|
|
8054
|
+
i2++;
|
|
7841
8055
|
} else if (byte === VALUE_PROLONGED) {
|
|
7842
8056
|
str += String.fromCharCode(PROLONGED_MARK);
|
|
7843
|
-
|
|
8057
|
+
i2++;
|
|
7844
8058
|
} else if (byte >= 128 && byte <= 222) {
|
|
7845
8059
|
str += String.fromCharCode(HIRAGANA_START + (byte - 128));
|
|
7846
|
-
|
|
8060
|
+
i2++;
|
|
7847
8061
|
} else if (byte === SHIFT_KATAKANA) {
|
|
7848
|
-
if (
|
|
7849
|
-
const nextByte = bytes[
|
|
8062
|
+
if (i2 + 1 < bytes.length) {
|
|
8063
|
+
const nextByte = bytes[i2 + 1];
|
|
7850
8064
|
if (nextByte >= 128 && nextByte <= 222) {
|
|
7851
8065
|
str += String.fromCharCode(HIRAGANA_START + 96 + (nextByte - 128));
|
|
7852
8066
|
}
|
|
7853
|
-
|
|
8067
|
+
i2 += 2;
|
|
7854
8068
|
} else {
|
|
7855
|
-
|
|
8069
|
+
i2++;
|
|
7856
8070
|
}
|
|
7857
8071
|
} else {
|
|
7858
|
-
|
|
8072
|
+
i2++;
|
|
7859
8073
|
}
|
|
7860
8074
|
}
|
|
7861
8075
|
return str;
|
|
@@ -8420,7 +8634,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
8420
8634
|
const beatRow = doc.createElement("div");
|
|
8421
8635
|
beatRow.className = "dtm-player-beat-row";
|
|
8422
8636
|
const beatDots = [];
|
|
8423
|
-
for (let
|
|
8637
|
+
for (let i2 = 0; i2 < 4; i2++) {
|
|
8424
8638
|
const d = doc.createElement("span");
|
|
8425
8639
|
d.className = "dtm-player-beat-dot";
|
|
8426
8640
|
beatRow.appendChild(d);
|
|
@@ -8524,9 +8738,9 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
8524
8738
|
lane.appendChild(metaEl);
|
|
8525
8739
|
}
|
|
8526
8740
|
const count = Math.min(notes.length, lyricTrack.syllables.length);
|
|
8527
|
-
for (let
|
|
8528
|
-
const note = notes[
|
|
8529
|
-
if (breaks.has(
|
|
8741
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
8742
|
+
const note = notes[i2];
|
|
8743
|
+
if (breaks.has(i2)) {
|
|
8530
8744
|
const br = doc.createElement("span");
|
|
8531
8745
|
br.className = "dtm-tk dtm-tk--break";
|
|
8532
8746
|
br.textContent = "\\n";
|
|
@@ -8534,7 +8748,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
8534
8748
|
}
|
|
8535
8749
|
const span = doc.createElement("span");
|
|
8536
8750
|
span.className = "dtm-tk dtm-tk--lyric";
|
|
8537
|
-
span.textContent = lyricTrack.syllables[
|
|
8751
|
+
span.textContent = lyricTrack.syllables[i2].kana;
|
|
8538
8752
|
lane.appendChild(span);
|
|
8539
8753
|
laneTokens.push({
|
|
8540
8754
|
el: span,
|
|
@@ -8687,8 +8901,8 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
8687
8901
|
const intStep = Math.floor(step);
|
|
8688
8902
|
playStep = intStep;
|
|
8689
8903
|
const beatIndex = Math.floor(step / STEPS_PER_BEAT2) % 4;
|
|
8690
|
-
for (let
|
|
8691
|
-
beatDots[
|
|
8904
|
+
for (let i2 = 0; i2 < 4; i2++)
|
|
8905
|
+
beatDots[i2].classList.toggle("dtm-player-beat-dot--on", i2 === beatIndex);
|
|
8692
8906
|
barEl.textContent = String(Math.floor(step / STEPS_PER_BAR2) + 1);
|
|
8693
8907
|
if (!isSeeking) {
|
|
8694
8908
|
seekInput.value = String(Math.min(maxStep, Math.max(0, intStep)));
|
|
@@ -8783,11 +8997,11 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
8783
8997
|
const semis = (lt.octave ?? 0) * 12;
|
|
8784
8998
|
const count = Math.min(sorted.length, lt.syllables.length);
|
|
8785
8999
|
const notes = [];
|
|
8786
|
-
for (let
|
|
8787
|
-
const n = sorted[
|
|
9000
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
9001
|
+
const n = sorted[i2];
|
|
8788
9002
|
if (n.startStep < fromStep) continue;
|
|
8789
9003
|
notes.push({
|
|
8790
|
-
syllable: lt.syllables[
|
|
9004
|
+
syllable: lt.syllables[i2],
|
|
8791
9005
|
pitch: n.pitch + semis,
|
|
8792
9006
|
startSec: (n.startStep - fromStep) * secondsPerStep,
|
|
8793
9007
|
durationSec: n.durationSteps * secondsPerStep * gate
|
|
@@ -8798,6 +9012,8 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
8798
9012
|
model: lt.model,
|
|
8799
9013
|
volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME),
|
|
8800
9014
|
pan: panToStereo(lt.pan ?? DEFAULT_PAN),
|
|
9015
|
+
vibrato: lt.vibrato,
|
|
9016
|
+
reverbSend: (lt.reverb ?? 0) / 100,
|
|
8801
9017
|
notes
|
|
8802
9018
|
};
|
|
8803
9019
|
});
|
|
@@ -9057,13 +9273,13 @@ var findZone = (ctx, fontName, zones, pitchs = []) => {
|
|
|
9057
9273
|
for (const zone of zones) {
|
|
9058
9274
|
const low = zone.keyRangeLow | 0, high = zone.keyRangeHigh | 0;
|
|
9059
9275
|
if (low > high) continue;
|
|
9060
|
-
for (let
|
|
9276
|
+
for (let i2 = low; i2 <= high; i2++) pitchs.push(i2);
|
|
9061
9277
|
}
|
|
9062
9278
|
const set = new Set(pitchs);
|
|
9063
9279
|
const map = new Map(pitchs.map((v) => [v, zones[0]]));
|
|
9064
|
-
for (let
|
|
9280
|
+
for (let i2 = zones.length - 1; i2 >= 0; i2--)
|
|
9065
9281
|
for (const v of set) {
|
|
9066
|
-
const zone = zones[
|
|
9282
|
+
const zone = zones[i2];
|
|
9067
9283
|
if (v < zone.keyRangeLow || v > zone.keyRangeHigh) continue;
|
|
9068
9284
|
set.delete(v);
|
|
9069
9285
|
map.set(v, { ...zone });
|
|
@@ -9093,13 +9309,13 @@ var adjustZone = async (ctx, fontName, zone) => {
|
|
|
9093
9309
|
const decoded = atob(zone.sample);
|
|
9094
9310
|
zone.buffer = ctx.createBuffer(1, decoded.length / 2, zone.sampleRate);
|
|
9095
9311
|
const a = zone.buffer.getChannelData(0);
|
|
9096
|
-
for (let
|
|
9097
|
-
let b1 = decoded.charCodeAt(
|
|
9312
|
+
for (let i2 = 0; i2 < decoded.length / 2; i2++) {
|
|
9313
|
+
let b1 = decoded.charCodeAt(i2 * 2), b2 = decoded.charCodeAt(i2 * 2 + 1);
|
|
9098
9314
|
if (b1 < 0) b1 = 256 + b1;
|
|
9099
9315
|
if (b2 < 0) b2 = 256 + b2;
|
|
9100
9316
|
let n = b2 * 256 + b1;
|
|
9101
9317
|
if (n >= 65536 / 2) n = n - 65536;
|
|
9102
|
-
a[
|
|
9318
|
+
a[i2] = n / 65536;
|
|
9103
9319
|
}
|
|
9104
9320
|
} else if (zone.file) {
|
|
9105
9321
|
const bytes = Uint8Array.from(atob(zone.file), (c) => c.charCodeAt(0));
|
|
@@ -9139,10 +9355,10 @@ var adjustZone = async (ctx, fontName, zone) => {
|
|
|
9139
9355
|
let loopPeak = 0;
|
|
9140
9356
|
if (oldBuf.numberOfChannels > 0) {
|
|
9141
9357
|
const ch0 = oldBuf.getChannelData(0);
|
|
9142
|
-
for (let
|
|
9143
|
-
const abs = Math.abs(ch0[
|
|
9358
|
+
for (let i2 = 0; i2 < ch0.length; i2++) {
|
|
9359
|
+
const abs = Math.abs(ch0[i2]);
|
|
9144
9360
|
if (abs > totalPeak) totalPeak = abs;
|
|
9145
|
-
if (
|
|
9361
|
+
if (i2 >= loopStartFrame && i2 < loopEndFrame) {
|
|
9146
9362
|
if (abs > loopPeak) loopPeak = abs;
|
|
9147
9363
|
}
|
|
9148
9364
|
}
|
|
@@ -9161,16 +9377,16 @@ var adjustZone = async (ctx, fontName, zone) => {
|
|
|
9161
9377
|
for (let ch = 0; ch < oldBuf.numberOfChannels; ch++) {
|
|
9162
9378
|
const oldData = oldBuf.getChannelData(ch);
|
|
9163
9379
|
const newData = newBuf.getChannelData(ch);
|
|
9164
|
-
for (let
|
|
9165
|
-
const ratio = attackLength > 1 ?
|
|
9380
|
+
for (let i2 = 0; i2 < attackLength; i2++) {
|
|
9381
|
+
const ratio = attackLength > 1 ? i2 / (attackLength - 1) : 0;
|
|
9166
9382
|
const m = 1 + (gainMultiplier - 1) * ratio;
|
|
9167
|
-
newData[
|
|
9383
|
+
newData[i2] = oldData[i2] * m;
|
|
9168
9384
|
}
|
|
9169
9385
|
let offset = attackLength;
|
|
9170
9386
|
const loopData = oldData.subarray(loopStartFrame, loopEndFrame);
|
|
9171
9387
|
const normalizedLoopData = new Float32Array(loopLengthFrame);
|
|
9172
|
-
for (let
|
|
9173
|
-
normalizedLoopData[
|
|
9388
|
+
for (let i2 = 0; i2 < loopLengthFrame; i2++) {
|
|
9389
|
+
normalizedLoopData[i2] = loopData[i2] * gainMultiplier;
|
|
9174
9390
|
}
|
|
9175
9391
|
for (let r = 0; r < repeatCount; r++) {
|
|
9176
9392
|
newData.set(normalizedLoopData, offset);
|
|
@@ -9180,8 +9396,8 @@ var adjustZone = async (ctx, fontName, zone) => {
|
|
|
9180
9396
|
const releaseData = oldData.subarray(loopEndFrame);
|
|
9181
9397
|
if (gainMultiplier !== 1) {
|
|
9182
9398
|
const normalizedRelease = new Float32Array(releaseLength);
|
|
9183
|
-
for (let
|
|
9184
|
-
normalizedRelease[
|
|
9399
|
+
for (let i2 = 0; i2 < releaseLength; i2++) {
|
|
9400
|
+
normalizedRelease[i2] = releaseData[i2] * gainMultiplier;
|
|
9185
9401
|
}
|
|
9186
9402
|
newData.set(normalizedRelease, offset);
|
|
9187
9403
|
} else {
|
|
@@ -9377,10 +9593,10 @@ var buildDisplayLines = (chords, eventsCount) => {
|
|
|
9377
9593
|
if (segments.length === 0) continue;
|
|
9378
9594
|
const hasMultiSeg = segments.length > 1;
|
|
9379
9595
|
const parts = [];
|
|
9380
|
-
for (let
|
|
9381
|
-
if (hasMultiSeg &&
|
|
9596
|
+
for (let i2 = 0; i2 < segments.length; i2++) {
|
|
9597
|
+
if (hasMultiSeg && i2 > 0)
|
|
9382
9598
|
parts.push({ text: "|", isChord: false, eventIdx: -1 });
|
|
9383
|
-
const bar = segments[
|
|
9599
|
+
const bar = segments[i2].trim();
|
|
9384
9600
|
if (!bar) continue;
|
|
9385
9601
|
const splitAt = [];
|
|
9386
9602
|
for (let j = 0; j < bar.length; j++) {
|
|
@@ -10543,6 +10759,11 @@ var buildUI = (target, options) => {
|
|
|
10543
10759
|
<input type="range" class="dtm-range dtm-grow" data-dtm="master-volume" value="50" min="0" max="100">
|
|
10544
10760
|
<span class="dtm-label" data-dtm="master-volume-label">50%</span>
|
|
10545
10761
|
</div>
|
|
10762
|
+
<div class="dtm-row">
|
|
10763
|
+
<span class="dtm-label">\u30EA\u30D0\u30FC\u30D6</span>
|
|
10764
|
+
<input type="range" class="dtm-range dtm-grow" data-dtm="reverb-amount" value="0" min="0" max="100" aria-label="\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\uFF08\u5168\u30C8\u30E9\u30C3\u30AF\u3078\u4E00\u5F8B\u306B\u639B\u304B\u308B\u6B8B\u97FF\uFF09" title="\u30EA\u30D0\u30FC\u30D6">
|
|
10765
|
+
<span class="dtm-label" data-dtm="reverb-amount-label">0%</span>
|
|
10766
|
+
</div>
|
|
10546
10767
|
<div class="dtm-track-body" data-dtm="track-body"></div>
|
|
10547
10768
|
</div>
|
|
10548
10769
|
</details>
|
|
@@ -10766,6 +10987,8 @@ var buildUI = (target, options) => {
|
|
|
10766
10987
|
hScrollThumb: sel("hscroll-thumb"),
|
|
10767
10988
|
masterVolume: sel("master-volume"),
|
|
10768
10989
|
masterVolumeLabel: sel("master-volume-label"),
|
|
10990
|
+
reverbAmount: sel("reverb-amount"),
|
|
10991
|
+
reverbAmountLabel: sel("reverb-amount-label"),
|
|
10769
10992
|
trackTabs: sel("track-tabs"),
|
|
10770
10993
|
trackBody: sel("track-body"),
|
|
10771
10994
|
drumSelect: sel("drum-select"),
|
|
@@ -10934,16 +11157,16 @@ var generateRandomPattern = (core, options) => {
|
|
|
10934
11157
|
const scale = SCALES[Math.floor(Math.random() * SCALES.length)];
|
|
10935
11158
|
const rootOffset = Math.floor(Math.random() * 12);
|
|
10936
11159
|
const availablePitches = [];
|
|
10937
|
-
for (let
|
|
10938
|
-
const noteInOctave = (
|
|
10939
|
-
if (scale.includes(noteInOctave)) availablePitches.push(basePitch +
|
|
11160
|
+
for (let i2 = 0; i2 < 12; i2++) {
|
|
11161
|
+
const noteInOctave = (i2 - rootOffset + 12) % 12;
|
|
11162
|
+
if (scale.includes(noteInOctave)) availablePitches.push(basePitch + i2);
|
|
10940
11163
|
}
|
|
10941
11164
|
core.beginBatch();
|
|
10942
11165
|
for (let bar = 0; bar < numBars; bar++) {
|
|
10943
11166
|
const barStart = startStep + bar * stepsPerBar;
|
|
10944
11167
|
const numNotes = Math.floor(Math.random() * 4) + 2;
|
|
10945
11168
|
const occupied = /* @__PURE__ */ new Set();
|
|
10946
|
-
for (let
|
|
11169
|
+
for (let i2 = 0; i2 < numNotes; i2++) {
|
|
10947
11170
|
const stepInRange = Math.floor(Math.random() * (stepsPerBar / noteLength)) * noteLength;
|
|
10948
11171
|
const step = barStart + stepInRange;
|
|
10949
11172
|
if (occupied.has(step)) continue;
|
|
@@ -11044,10 +11267,10 @@ var STEPS_PER_BEAT3 = 48;
|
|
|
11044
11267
|
var analyzeMidiTracks = (midi) => {
|
|
11045
11268
|
const { tracks } = midi;
|
|
11046
11269
|
const result = [];
|
|
11047
|
-
for (let
|
|
11270
|
+
for (let i2 = 0; i2 < tracks.length; i2++) {
|
|
11048
11271
|
const notes = [];
|
|
11049
11272
|
let currentTime = 0;
|
|
11050
|
-
for (const event of tracks[
|
|
11273
|
+
for (const event of tracks[i2]) {
|
|
11051
11274
|
currentTime += event.delta;
|
|
11052
11275
|
if (event.noteOn && event.noteOn.velocity > 0) {
|
|
11053
11276
|
notes.push({
|
|
@@ -11070,8 +11293,8 @@ var analyzeMidiTracks = (midi) => {
|
|
|
11070
11293
|
const editableNotes = validNotes.filter((n) => n.channel !== 9);
|
|
11071
11294
|
if (validNotes.length > 0 && editableNotes.length === 0) continue;
|
|
11072
11295
|
result.push({
|
|
11073
|
-
index:
|
|
11074
|
-
name: `Ch${
|
|
11296
|
+
index: i2,
|
|
11297
|
+
name: `Ch${i2 + 1}`,
|
|
11075
11298
|
noteCount: editableNotes.length,
|
|
11076
11299
|
selected: editableNotes.length > 0
|
|
11077
11300
|
});
|
|
@@ -11118,8 +11341,8 @@ var extractMidiPlacements = (midi, selectedTrackIndices) => {
|
|
|
11118
11341
|
const pitch = noteOff.noteNumber;
|
|
11119
11342
|
const channel = event.channel ?? 0;
|
|
11120
11343
|
if (channelNotes[channel]) {
|
|
11121
|
-
for (let
|
|
11122
|
-
const note = channelNotes[channel][
|
|
11344
|
+
for (let i2 = channelNotes[channel].length - 1; i2 >= 0; i2--) {
|
|
11345
|
+
const note = channelNotes[channel][i2];
|
|
11123
11346
|
if (note.pitch === pitch && note.end === null) {
|
|
11124
11347
|
note.end = currentTime;
|
|
11125
11348
|
break;
|
|
@@ -11149,10 +11372,10 @@ var extractMidiPlacements = (midi, selectedTrackIndices) => {
|
|
|
11149
11372
|
const avgPitch = validNotes.reduce((sum, n) => sum + n.pitch, 0) / validNotes.length;
|
|
11150
11373
|
let maxSimultaneous = 0;
|
|
11151
11374
|
const sortedNotes = [...validNotes].sort((a, b) => a.start - b.start);
|
|
11152
|
-
for (let
|
|
11375
|
+
for (let i2 = 0; i2 < sortedNotes.length; i2++) {
|
|
11153
11376
|
let simultaneous = 1;
|
|
11154
|
-
for (let j =
|
|
11155
|
-
if (sortedNotes[j].start < sortedNotes[
|
|
11377
|
+
for (let j = i2 + 1; j < sortedNotes.length; j++) {
|
|
11378
|
+
if (sortedNotes[j].start < sortedNotes[i2].end) {
|
|
11156
11379
|
simultaneous++;
|
|
11157
11380
|
}
|
|
11158
11381
|
}
|
|
@@ -11163,14 +11386,14 @@ var extractMidiPlacements = (midi, selectedTrackIndices) => {
|
|
|
11163
11386
|
const blocks = [];
|
|
11164
11387
|
let blockStart = sortedNotes[0].start;
|
|
11165
11388
|
let blockEnd = sortedNotes[0].end;
|
|
11166
|
-
for (let
|
|
11167
|
-
const gap = sortedNotes[
|
|
11389
|
+
for (let i2 = 1; i2 < sortedNotes.length; i2++) {
|
|
11390
|
+
const gap = sortedNotes[i2].start - sortedNotes[i2 - 1].end;
|
|
11168
11391
|
if (gap >= ticksPerBar) {
|
|
11169
11392
|
blocks.push({ start: blockStart, end: blockEnd });
|
|
11170
|
-
blockStart = sortedNotes[
|
|
11171
|
-
blockEnd = sortedNotes[
|
|
11393
|
+
blockStart = sortedNotes[i2].start;
|
|
11394
|
+
blockEnd = sortedNotes[i2].end;
|
|
11172
11395
|
} else {
|
|
11173
|
-
blockEnd = sortedNotes[
|
|
11396
|
+
blockEnd = sortedNotes[i2].end;
|
|
11174
11397
|
}
|
|
11175
11398
|
}
|
|
11176
11399
|
blocks.push({ start: blockStart, end: blockEnd });
|
|
@@ -11284,9 +11507,9 @@ var extractMidiPlacementsByTrack = (midi, selectedIndices, trackIds) => {
|
|
|
11284
11507
|
const noteOff = event.noteOff || event.noteOn;
|
|
11285
11508
|
if (noteOff) {
|
|
11286
11509
|
const pitch = noteOff.noteNumber;
|
|
11287
|
-
for (let
|
|
11288
|
-
if (active[
|
|
11289
|
-
active[
|
|
11510
|
+
for (let i2 = active.length - 1; i2 >= 0; i2--) {
|
|
11511
|
+
if (active[i2].pitch === pitch && active[i2].end === null) {
|
|
11512
|
+
active[i2].end = currentTime;
|
|
11290
11513
|
break;
|
|
11291
11514
|
}
|
|
11292
11515
|
}
|
|
@@ -11451,17 +11674,17 @@ var extractDrumPatternFromNotes = (rawNotes, drumFont = "FluidR3_GM_sf2_file:0")
|
|
|
11451
11674
|
let prev = bList[0];
|
|
11452
11675
|
const parts = key.split("_");
|
|
11453
11676
|
const noteObj = { step: +parts[0], pitch: +parts[1], velocity: +parts[2] };
|
|
11454
|
-
for (let
|
|
11455
|
-
if (
|
|
11677
|
+
for (let i2 = 1; i2 <= bList.length; i2++) {
|
|
11678
|
+
if (i2 === bList.length || bList[i2] !== prev + 1) {
|
|
11456
11679
|
const rKey = `${start}-${prev}`;
|
|
11457
11680
|
if (!rangeMap[rKey]) rangeMap[rKey] = [];
|
|
11458
11681
|
rangeMap[rKey].push(noteObj);
|
|
11459
|
-
if (
|
|
11460
|
-
start = bList[
|
|
11461
|
-
prev = bList[
|
|
11682
|
+
if (i2 < bList.length) {
|
|
11683
|
+
start = bList[i2];
|
|
11684
|
+
prev = bList[i2];
|
|
11462
11685
|
}
|
|
11463
11686
|
} else {
|
|
11464
|
-
prev = bList[
|
|
11687
|
+
prev = bList[i2];
|
|
11465
11688
|
}
|
|
11466
11689
|
}
|
|
11467
11690
|
}
|
|
@@ -11838,8 +12061,8 @@ var drawHeader = () => {
|
|
|
11838
12061
|
(g_draw_offset_x + g_header_canvas.width) / (stepsPerBar * stepWidth)
|
|
11839
12062
|
);
|
|
11840
12063
|
for (let bar = startBar; bar <= endBar + 1; bar++) {
|
|
11841
|
-
const
|
|
11842
|
-
const screenX =
|
|
12064
|
+
const x2 = bar * stepsPerBar * stepWidth;
|
|
12065
|
+
const screenX = x2;
|
|
11843
12066
|
g_header_ctx.beginPath();
|
|
11844
12067
|
g_header_ctx.moveTo(screenX, 0);
|
|
11845
12068
|
g_header_ctx.lineTo(screenX, HEADER_HEIGHT);
|
|
@@ -11886,11 +12109,11 @@ var drawGrid = (noteLengthSteps = 1) => {
|
|
|
11886
12109
|
const startX = Math.floor(g_draw_offset_x / (stepWidth * gridStep)) * stepWidth * gridStep;
|
|
11887
12110
|
const endX = g_draw_offset_x + g_grid_canvas.width;
|
|
11888
12111
|
const lineStep = stepWidth * gridStep;
|
|
11889
|
-
for (let
|
|
11890
|
-
const step =
|
|
12112
|
+
for (let x2 = startX; x2 <= endX; x2 += lineStep) {
|
|
12113
|
+
const step = x2 / stepWidth;
|
|
11891
12114
|
const isBarLine = step % stepsPerBar === 0;
|
|
11892
12115
|
const isNoteLine = step % gridStep === 0;
|
|
11893
|
-
const screenX =
|
|
12116
|
+
const screenX = x2 - g_draw_offset_x;
|
|
11894
12117
|
g_grid_ctx.beginPath();
|
|
11895
12118
|
g_grid_ctx.strokeStyle = isBarLine ? "#3d405b" : isNoteLine ? "#242840" : "#1a1d30";
|
|
11896
12119
|
g_grid_ctx.lineWidth = isBarLine ? 2 : 1;
|
|
@@ -11952,26 +12175,26 @@ var drawSelectedNotes = (notes, selectedIds, baseColor = [59, 130, 246, 1]) => {
|
|
|
11952
12175
|
var getXY = (e) => {
|
|
11953
12176
|
const { clientX, clientY } = e;
|
|
11954
12177
|
const rect = g_grid_canvas.getBoundingClientRect();
|
|
11955
|
-
const
|
|
12178
|
+
const x2 = Math.floor(clientX - rect.left);
|
|
11956
12179
|
const y = Math.floor(clientY - rect.top);
|
|
11957
|
-
return [
|
|
12180
|
+
return [x2, y, e.buttons];
|
|
11958
12181
|
};
|
|
11959
12182
|
var getGridPosition = (e) => {
|
|
11960
|
-
const [
|
|
12183
|
+
const [x2, y] = getXY(e);
|
|
11961
12184
|
const { keyCount, pitchRangeStart, keyHeight, stepWidth } = g_config;
|
|
11962
|
-
const step = Math.floor((
|
|
12185
|
+
const step = Math.floor((x2 + g_draw_offset_x) / stepWidth);
|
|
11963
12186
|
const absoluteY = y + g_draw_offset_y;
|
|
11964
12187
|
const yIndex = Math.floor(absoluteY / keyHeight);
|
|
11965
12188
|
const pitch = keyCount - 1 - yIndex + pitchRangeStart;
|
|
11966
|
-
return { step, pitch, x, y };
|
|
12189
|
+
return { step, pitch, x: x2, y };
|
|
11967
12190
|
};
|
|
11968
12191
|
var onClick = (callback) => {
|
|
11969
12192
|
g_grid_canvas.addEventListener(
|
|
11970
12193
|
"click",
|
|
11971
12194
|
(e) => {
|
|
11972
|
-
const [
|
|
12195
|
+
const [x2, y] = getXY(e);
|
|
11973
12196
|
const { keyCount, pitchRangeStart, keyHeight, stepWidth } = g_config;
|
|
11974
|
-
const step = Math.floor((
|
|
12197
|
+
const step = Math.floor((x2 + g_draw_offset_x) / stepWidth);
|
|
11975
12198
|
const absoluteY = y + g_draw_offset_y;
|
|
11976
12199
|
const yIndex = Math.floor(absoluteY / keyHeight);
|
|
11977
12200
|
const pitch = keyCount - 1 - yIndex + pitchRangeStart;
|
|
@@ -11983,8 +12206,8 @@ var onClick = (callback) => {
|
|
|
11983
12206
|
);
|
|
11984
12207
|
g_grid_canvas.addEventListener("contextmenu", (e) => e.preventDefault());
|
|
11985
12208
|
};
|
|
11986
|
-
var setDrawOffset = (
|
|
11987
|
-
g_draw_offset_x =
|
|
12209
|
+
var setDrawOffset = (x2, y) => {
|
|
12210
|
+
g_draw_offset_x = x2;
|
|
11988
12211
|
g_draw_offset_y = y;
|
|
11989
12212
|
drawKeyboard();
|
|
11990
12213
|
drawHeader();
|
|
@@ -12299,12 +12522,12 @@ var MMLCore = class _MMLCore {
|
|
|
12299
12522
|
currentCursor += steps;
|
|
12300
12523
|
}
|
|
12301
12524
|
};
|
|
12302
|
-
for (let
|
|
12303
|
-
const startStep = sortedSteps[
|
|
12525
|
+
for (let i2 = 0; i2 < sortedSteps.length; i2++) {
|
|
12526
|
+
const startStep = sortedSteps[i2];
|
|
12304
12527
|
const notes = notesByStep.get(startStep);
|
|
12305
12528
|
if (!notes) continue;
|
|
12306
12529
|
fillRests(startStep);
|
|
12307
|
-
const nextStart = sortedSteps[
|
|
12530
|
+
const nextStart = sortedSteps[i2 + 1] ?? endStep;
|
|
12308
12531
|
const physicsLimit = nextStart - currentCursor;
|
|
12309
12532
|
if (physicsLimit < MIN_STEP) {
|
|
12310
12533
|
continue;
|
|
@@ -12369,10 +12592,10 @@ var decomposeToMonophonic = (notes) => {
|
|
|
12369
12592
|
for (const note of sorted) {
|
|
12370
12593
|
let assigned = -1;
|
|
12371
12594
|
let minEnd = Infinity;
|
|
12372
|
-
for (let
|
|
12373
|
-
if (trackEnds[
|
|
12374
|
-
minEnd = trackEnds[
|
|
12375
|
-
assigned =
|
|
12595
|
+
for (let i2 = 0; i2 < tracks.length; i2++) {
|
|
12596
|
+
if (trackEnds[i2] <= note.startStep && trackEnds[i2] < minEnd) {
|
|
12597
|
+
minEnd = trackEnds[i2];
|
|
12598
|
+
assigned = i2;
|
|
12376
12599
|
}
|
|
12377
12600
|
}
|
|
12378
12601
|
if (assigned === -1) {
|
|
@@ -12428,6 +12651,14 @@ var CHORD_INFO_HTML2 = `
|
|
|
12428
12651
|
</ul>
|
|
12429
12652
|
</div>
|
|
12430
12653
|
`;
|
|
12654
|
+
var VIBRATO_INFO_HTML = `
|
|
12655
|
+
<div class="dtm-modal-body-content">
|
|
12656
|
+
<h4>\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u3068\u306F</h4>
|
|
12657
|
+
<p>ON\u306B\u3059\u308B\u3068\u3001\u4E00\u5B9A\u306E\u9577\u3055\uFF08\u7D040.35\u79D2\uFF09\u4EE5\u4E0A\u306E\u97F3\u7B26\uFF08\u30ED\u30F3\u30B0\u30C8\u30FC\u30F3\uFF09\u306B\u3060\u3051\u3001\u81EA\u52D5\u3067\u30D4\u30C3\u30C1\u304C\u5C0F\u523B\u307F\u306B\u63FA\u308C\u308B\u6B4C\u5531\u8868\u73FE\uFF08\u30D3\u30D6\u30E9\u30FC\u30C8\uFF09\u304C\u639B\u304B\u308A\u307E\u3059\u3002</p>
|
|
12658
|
+
<p style="margin-top:4px;"><small>\u77ED\u3044\u97F3\u7B26\u306B\u306F\u639B\u304B\u308A\u307E\u305B\u3093\u30021\u5468\u671F\u3082\u63FA\u308C\u304D\u3089\u306A\u3044\u3046\u3061\u306B\u6B21\u306E\u97F3\u3078\u79FB\u3063\u3066\u3057\u307E\u3044\u3001\u30D3\u30D6\u30E9\u30FC\u30C8\u3068\u3044\u3046\u3088\u308A\u5358\u306A\u308B\u97F3\u7A0B\u306E\u30D6\u30EC\u3068\u3057\u3066\u4E0D\u81EA\u7136\u306B\u805E\u3053\u3048\u308B\u305F\u3081\u3067\u3059\u3002</small></p>
|
|
12659
|
+
<p style="margin-top:4px;"><small>\u901F\u3055\u30FB\u6DF1\u3055\u306F\u8ABF\u6574\u3067\u304D\u307E\u305B\u3093\uFF08\u6B4C\u3068\u3057\u3066\u7834\u7DBB\u3057\u306B\u304F\u3044\u63A7\u3048\u3081\u306A\u91CF\u306B\u56FA\u5B9A\u3057\u3066\u3044\u307E\u3059\uFF09\u3002\u66F2\u3084\u7B87\u6240\u3054\u3068\u306B\u639B\u3051\u305F\u3044/\u639B\u3051\u305F\u304F\u306A\u3044\u304C\u3042\u308B\u5834\u5408\u306F\u3001\u30C8\u30E9\u30C3\u30AF\u3092\u5206\u3051\u3066\u6B4C\u8A5E\u3092\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\u3002</small></p>
|
|
12660
|
+
</div>
|
|
12661
|
+
`;
|
|
12431
12662
|
var MIDI_INFO_HTML = `
|
|
12432
12663
|
<div class="dtm-modal-body-content">
|
|
12433
12664
|
<h4>1. MIDI\u30D5\u30A1\u30A4\u30EB\u3068\u306F</h4>
|
|
@@ -12631,7 +12862,7 @@ var LYRIC_MODEL_CATEGORIES = [
|
|
|
12631
12862
|
},
|
|
12632
12863
|
{
|
|
12633
12864
|
label: "\u304A\u3093J",
|
|
12634
|
-
models: ["roze", "shiyo", "rino"]
|
|
12865
|
+
models: ["roze", "shiyo", "rino", "rino121", "uc"]
|
|
12635
12866
|
},
|
|
12636
12867
|
{
|
|
12637
12868
|
label: "\u4E00\u822C",
|
|
@@ -12699,6 +12930,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
12699
12930
|
});
|
|
12700
12931
|
refs.masterVolume.value = String(options.masterVolume ?? 50);
|
|
12701
12932
|
refs.masterVolumeLabel.textContent = `${options.masterVolume ?? 50}%`;
|
|
12933
|
+
refs.reverbAmount.value = String(options.reverbAmount ?? 0);
|
|
12934
|
+
refs.reverbAmountLabel.textContent = `${options.reverbAmount ?? 0}%`;
|
|
12702
12935
|
refs.drumVolume.value = String(options.drumVolume ?? 80);
|
|
12703
12936
|
refs.drumVolumeLabel.textContent = `${options.drumVolume ?? 80}%`;
|
|
12704
12937
|
const renderConfig = {
|
|
@@ -12714,6 +12947,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
12714
12947
|
let bpm = options.defaultBpm ?? DEFAULT_BPM;
|
|
12715
12948
|
let masterVolume = options.masterVolume ?? 50;
|
|
12716
12949
|
options.singingVoices?.setVolume(masterVolume / 100);
|
|
12950
|
+
let reverbAmount = options.reverbAmount ?? 0;
|
|
12951
|
+
options.onReverbChange?.(reverbAmount);
|
|
12717
12952
|
let drumVolume = options.drumVolume ?? 80;
|
|
12718
12953
|
let currentDrumPattern = refs.drumSelect.value;
|
|
12719
12954
|
let currentDrumFont = options.drumFont ?? "FluidR3_GM_sf2_file:0";
|
|
@@ -12791,7 +13026,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
12791
13026
|
vocalVolume: t.vocalVolume,
|
|
12792
13027
|
vocalGate: t.vocalGate,
|
|
12793
13028
|
vocalPan: t.vocalPan,
|
|
12794
|
-
vocalOctave: t.vocalOctave
|
|
13029
|
+
vocalOctave: t.vocalOctave,
|
|
13030
|
+
vocalVibrato: t.vocalVibrato,
|
|
13031
|
+
vocalReverb: t.vocalReverb
|
|
12795
13032
|
};
|
|
12796
13033
|
if (lyricsDebounceTimer) clearTimeout(lyricsDebounceTimer);
|
|
12797
13034
|
lyricsDebounceTimer = setTimeout(() => {
|
|
@@ -12851,25 +13088,29 @@ var mountDAW = (target, options = {}) => {
|
|
|
12851
13088
|
vocalGate: 100,
|
|
12852
13089
|
vocalPan: 64,
|
|
12853
13090
|
vocalOctave: 0,
|
|
13091
|
+
vocalVibrato: false,
|
|
13092
|
+
vocalReverb: 0,
|
|
12854
13093
|
trackInstrument: ""
|
|
12855
13094
|
};
|
|
12856
13095
|
});
|
|
12857
13096
|
};
|
|
12858
13097
|
const buildLyricsMap = () => {
|
|
12859
13098
|
const map = /* @__PURE__ */ new Map();
|
|
12860
|
-
trackStates.forEach((t,
|
|
13099
|
+
trackStates.forEach((t, i2) => {
|
|
12861
13100
|
const model = t.lyricModel.trim();
|
|
12862
13101
|
const text = t.lyrics.trim();
|
|
12863
13102
|
if (!model || !text) return;
|
|
12864
13103
|
const syllables = normalizeLyrics(text);
|
|
12865
13104
|
if (syllables.length === 0) return;
|
|
12866
|
-
map.set(
|
|
12867
|
-
trackId:
|
|
13105
|
+
map.set(i2, {
|
|
13106
|
+
trackId: i2,
|
|
12868
13107
|
model: model.toLowerCase(),
|
|
12869
13108
|
volume: t.vocalVolume,
|
|
12870
13109
|
gate: t.vocalGate,
|
|
12871
13110
|
pan: t.vocalPan,
|
|
12872
13111
|
octave: t.vocalOctave,
|
|
13112
|
+
vibrato: t.vocalVibrato,
|
|
13113
|
+
reverb: t.vocalReverb,
|
|
12873
13114
|
syllables
|
|
12874
13115
|
});
|
|
12875
13116
|
});
|
|
@@ -12909,15 +13150,15 @@ var mountDAW = (target, options = {}) => {
|
|
|
12909
13150
|
const ctx = getGridContext();
|
|
12910
13151
|
const canvas = getGridCanvas();
|
|
12911
13152
|
if (!ctx) return;
|
|
12912
|
-
const
|
|
12913
|
-
if (
|
|
13153
|
+
const x2 = playStartStep * renderConfig.stepWidth - currentOffsetX;
|
|
13154
|
+
if (x2 < -10 || x2 > canvas.width + 10) return;
|
|
12914
13155
|
ctx.save();
|
|
12915
13156
|
ctx.strokeStyle = "#ffec27";
|
|
12916
13157
|
ctx.lineWidth = 2;
|
|
12917
13158
|
ctx.setLineDash([4, 4]);
|
|
12918
13159
|
ctx.beginPath();
|
|
12919
|
-
ctx.moveTo(
|
|
12920
|
-
ctx.lineTo(
|
|
13160
|
+
ctx.moveTo(x2, 0);
|
|
13161
|
+
ctx.lineTo(x2, canvas.height);
|
|
12921
13162
|
ctx.stroke();
|
|
12922
13163
|
ctx.restore();
|
|
12923
13164
|
};
|
|
@@ -12925,14 +13166,14 @@ var mountDAW = (target, options = {}) => {
|
|
|
12925
13166
|
const ctx = getGridContext();
|
|
12926
13167
|
const canvas = getGridCanvas();
|
|
12927
13168
|
if (!ctx) return;
|
|
12928
|
-
const
|
|
12929
|
-
if (
|
|
13169
|
+
const x2 = currentPlayStep * renderConfig.stepWidth - currentOffsetX;
|
|
13170
|
+
if (x2 < 0 || x2 > canvas.width) return;
|
|
12930
13171
|
ctx.save();
|
|
12931
13172
|
ctx.strokeStyle = "#ff004d";
|
|
12932
13173
|
ctx.lineWidth = 2;
|
|
12933
13174
|
ctx.beginPath();
|
|
12934
|
-
ctx.moveTo(
|
|
12935
|
-
ctx.lineTo(
|
|
13175
|
+
ctx.moveTo(x2, 0);
|
|
13176
|
+
ctx.lineTo(x2, canvas.height);
|
|
12936
13177
|
ctx.stroke();
|
|
12937
13178
|
ctx.restore();
|
|
12938
13179
|
};
|
|
@@ -13060,8 +13301,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
13060
13301
|
if (maxOffsetX <= 0) return;
|
|
13061
13302
|
const rect = refs.hScroll.getBoundingClientRect();
|
|
13062
13303
|
const thumbW = Number.parseFloat(refs.hScrollThumb.style.width) || 40;
|
|
13063
|
-
const
|
|
13064
|
-
const ratio =
|
|
13304
|
+
const x2 = clamp3(clientX - rect.left - thumbW / 2, 0, rect.width - thumbW);
|
|
13305
|
+
const ratio = x2 / (rect.width - thumbW);
|
|
13065
13306
|
currentOffsetX = clamp3(ratio * maxOffsetX, 0, maxOffsetX);
|
|
13066
13307
|
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
13067
13308
|
redrawAll();
|
|
@@ -13088,13 +13329,60 @@ var mountDAW = (target, options = {}) => {
|
|
|
13088
13329
|
let selectionStart = null;
|
|
13089
13330
|
let selectedOriginal = [];
|
|
13090
13331
|
let lastMultiPreviewPitch = null;
|
|
13332
|
+
const AUTO_SCROLL_MARGIN = 30;
|
|
13333
|
+
const AUTO_SCROLL_MAX_SPEED = 22;
|
|
13334
|
+
let autoScrollRAF = null;
|
|
13335
|
+
let lastMoveEvent = null;
|
|
13336
|
+
const computeEdgeSpeed = (pos, size) => {
|
|
13337
|
+
if (pos < AUTO_SCROLL_MARGIN) {
|
|
13338
|
+
const t = (AUTO_SCROLL_MARGIN - pos) / AUTO_SCROLL_MARGIN;
|
|
13339
|
+
return -Math.ceil(t * AUTO_SCROLL_MAX_SPEED);
|
|
13340
|
+
}
|
|
13341
|
+
if (pos > size - AUTO_SCROLL_MARGIN) {
|
|
13342
|
+
const t = (pos - (size - AUTO_SCROLL_MARGIN)) / AUTO_SCROLL_MARGIN;
|
|
13343
|
+
return Math.ceil(t * AUTO_SCROLL_MAX_SPEED);
|
|
13344
|
+
}
|
|
13345
|
+
return 0;
|
|
13346
|
+
};
|
|
13347
|
+
const stopAutoScroll = () => {
|
|
13348
|
+
if (autoScrollRAF !== null) {
|
|
13349
|
+
cancelAnimationFrame(autoScrollRAF);
|
|
13350
|
+
autoScrollRAF = null;
|
|
13351
|
+
}
|
|
13352
|
+
lastMoveEvent = null;
|
|
13353
|
+
};
|
|
13354
|
+
const autoScrollTick = () => {
|
|
13355
|
+
autoScrollRAF = null;
|
|
13356
|
+
if (!isSelecting || !lastMoveEvent) return;
|
|
13357
|
+
const canvas = getGridCanvas();
|
|
13358
|
+
const { x: x2, y } = getGridPosition(lastMoveEvent);
|
|
13359
|
+
const dx = computeEdgeSpeed(x2, canvas.width);
|
|
13360
|
+
const dy = computeEdgeSpeed(y, canvas.height);
|
|
13361
|
+
if (dx !== 0 || dy !== 0) {
|
|
13362
|
+
const maxOffsetX = getMaxOffsetX();
|
|
13363
|
+
const maxOffsetY = getMaxOffsetY();
|
|
13364
|
+
currentOffsetX = clamp3(currentOffsetX + dx, 0, maxOffsetX);
|
|
13365
|
+
currentOffsetY = clamp3(currentOffsetY + dy, 0, maxOffsetY);
|
|
13366
|
+
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
13367
|
+
onPointerMove(lastMoveEvent);
|
|
13368
|
+
}
|
|
13369
|
+
if (isSelecting) {
|
|
13370
|
+
autoScrollRAF = requestAnimationFrame(autoScrollTick);
|
|
13371
|
+
}
|
|
13372
|
+
};
|
|
13373
|
+
const ensureAutoScroll = (event) => {
|
|
13374
|
+
lastMoveEvent = event;
|
|
13375
|
+
if (autoScrollRAF === null) {
|
|
13376
|
+
autoScrollRAF = requestAnimationFrame(autoScrollTick);
|
|
13377
|
+
}
|
|
13378
|
+
};
|
|
13091
13379
|
const playPreview = (pitch) => {
|
|
13092
13380
|
if (isLoading) return;
|
|
13093
13381
|
options.onResumeAudio?.();
|
|
13094
13382
|
const active = getActive();
|
|
13095
13383
|
dispatchNote(active.config.id, pitch, active.volume, 100, 0, 0.5);
|
|
13096
13384
|
};
|
|
13097
|
-
const findActiveNoteAt = (
|
|
13385
|
+
const findActiveNoteAt = (x2, y, margin = 0) => {
|
|
13098
13386
|
const active = getActive();
|
|
13099
13387
|
const { stepWidth, keyHeight, keyCount, pitchRangeStart } = renderConfig;
|
|
13100
13388
|
const offset = getDrawOffset();
|
|
@@ -13105,7 +13393,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
13105
13393
|
const w = note.durationSteps * stepWidth;
|
|
13106
13394
|
const renderX = logicalX - offset.x;
|
|
13107
13395
|
const renderY = logicalY - offset.y;
|
|
13108
|
-
if (
|
|
13396
|
+
if (x2 >= renderX - margin && x2 <= renderX + w + margin && y >= renderY - margin && y <= renderY + keyHeight + margin)
|
|
13109
13397
|
return note;
|
|
13110
13398
|
}
|
|
13111
13399
|
return null;
|
|
@@ -13124,17 +13412,17 @@ var mountDAW = (target, options = {}) => {
|
|
|
13124
13412
|
const onGridPointerDown = (event) => {
|
|
13125
13413
|
event.preventDefault();
|
|
13126
13414
|
options.onResumeAudio?.();
|
|
13127
|
-
const { x, y, step, pitch } = getGridPosition(event);
|
|
13415
|
+
const { x: x2, y, step, pitch } = getGridPosition(event);
|
|
13128
13416
|
const active = getActive();
|
|
13129
13417
|
if (activeToolMode === "eraser") {
|
|
13130
13418
|
if (isActiveLocked()) return;
|
|
13131
|
-
const note = findActiveNoteAt(
|
|
13419
|
+
const note = findActiveNoteAt(x2, y);
|
|
13132
13420
|
if (note) active.core.deleteNoteById(note.id);
|
|
13133
13421
|
return;
|
|
13134
13422
|
}
|
|
13135
13423
|
if (activeToolMode === "select") {
|
|
13136
13424
|
if (selectedNotes.length > 0) {
|
|
13137
|
-
const clicked2 = findActiveNoteAt(
|
|
13425
|
+
const clicked2 = findActiveNoteAt(x2, y);
|
|
13138
13426
|
if (clicked2 && selectedNotes.some((n) => n.id === clicked2.id)) {
|
|
13139
13427
|
selectedOriginal = selectedNotes.map((n) => ({
|
|
13140
13428
|
id: n.id,
|
|
@@ -13143,7 +13431,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
13143
13431
|
}));
|
|
13144
13432
|
isSelecting = true;
|
|
13145
13433
|
dragMode = "move";
|
|
13146
|
-
selectionStart = { x, y, step, pitch };
|
|
13434
|
+
selectionStart = { x: x2, y, step, pitch };
|
|
13147
13435
|
hasDragged = false;
|
|
13148
13436
|
lastMultiPreviewPitch = null;
|
|
13149
13437
|
return;
|
|
@@ -13151,7 +13439,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
13151
13439
|
selectedNotes = [];
|
|
13152
13440
|
selectionRect = null;
|
|
13153
13441
|
}
|
|
13154
|
-
const clicked = findActiveNoteAt(
|
|
13442
|
+
const clicked = findActiveNoteAt(x2, y);
|
|
13155
13443
|
if (clicked) {
|
|
13156
13444
|
selectedNotes = [clicked];
|
|
13157
13445
|
selectedOriginal = [
|
|
@@ -13169,19 +13457,19 @@ var mountDAW = (target, options = {}) => {
|
|
|
13169
13457
|
isSelecting = true;
|
|
13170
13458
|
dragMode = "rect";
|
|
13171
13459
|
}
|
|
13172
|
-
selectionStart = { x, y, step, pitch };
|
|
13460
|
+
selectionStart = { x: x2, y, step, pitch };
|
|
13173
13461
|
hasDragged = false;
|
|
13174
13462
|
return;
|
|
13175
13463
|
}
|
|
13176
13464
|
hasDragged = false;
|
|
13177
|
-
const existing = findActiveNoteAt(
|
|
13465
|
+
const existing = findActiveNoteAt(x2, y, TOUCH_HIT_MARGIN);
|
|
13178
13466
|
if (existing) {
|
|
13179
13467
|
playPreview(existing.pitch);
|
|
13180
13468
|
const { stepWidth } = renderConfig;
|
|
13181
13469
|
const offset = getDrawOffset();
|
|
13182
13470
|
const renderX = existing.startStep * stepWidth - offset.x;
|
|
13183
13471
|
const w = existing.durationSteps * stepWidth;
|
|
13184
|
-
if (
|
|
13472
|
+
if (x2 >= renderX + w - resizeHandleWidth && x2 <= renderX + w) {
|
|
13185
13473
|
dragState = {
|
|
13186
13474
|
noteId: existing.id,
|
|
13187
13475
|
mode: "resize",
|
|
@@ -13260,12 +13548,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
13260
13548
|
return;
|
|
13261
13549
|
}
|
|
13262
13550
|
if (activeToolMode === "select" && isSelecting && selectionStart) {
|
|
13263
|
-
|
|
13551
|
+
ensureAutoScroll(event);
|
|
13552
|
+
const { x: x2, y, step, pitch } = getGridPosition(event);
|
|
13264
13553
|
if (dragMode === "rect") {
|
|
13265
13554
|
const rect = {
|
|
13266
|
-
x: Math.min(
|
|
13555
|
+
x: Math.min(x2, selectionStart.x),
|
|
13267
13556
|
y: Math.min(y, selectionStart.y),
|
|
13268
|
-
width: Math.abs(
|
|
13557
|
+
width: Math.abs(x2 - selectionStart.x),
|
|
13269
13558
|
height: Math.abs(y - selectionStart.y)
|
|
13270
13559
|
};
|
|
13271
13560
|
selectionRect = rect;
|
|
@@ -13278,7 +13567,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
13278
13567
|
const nx = logicalX - offset.x;
|
|
13279
13568
|
const ny = logicalY - offset.y;
|
|
13280
13569
|
const nw = note.durationSteps * stepWidth;
|
|
13281
|
-
return rect.x
|
|
13570
|
+
return nx >= rect.x && nx + nw <= rect.x + rect.width && ny >= rect.y && ny + keyHeight <= rect.y + rect.height;
|
|
13282
13571
|
});
|
|
13283
13572
|
redrawAll();
|
|
13284
13573
|
} else {
|
|
@@ -13335,8 +13624,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
13335
13624
|
selectionStart = null;
|
|
13336
13625
|
hasDragged = false;
|
|
13337
13626
|
lastMultiPreviewPitch = null;
|
|
13338
|
-
|
|
13627
|
+
if (dragMode !== "rect" || selectedNotes.length === 0) {
|
|
13628
|
+
selectionRect = null;
|
|
13629
|
+
}
|
|
13339
13630
|
selectedOriginal = [];
|
|
13631
|
+
stopAutoScroll();
|
|
13340
13632
|
redrawAll();
|
|
13341
13633
|
}
|
|
13342
13634
|
};
|
|
@@ -13484,8 +13776,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
13484
13776
|
headerCanvas.addEventListener("click", (event) => {
|
|
13485
13777
|
if (playbackState === "playing") return;
|
|
13486
13778
|
const rect = headerCanvas.getBoundingClientRect();
|
|
13487
|
-
const
|
|
13488
|
-
const step = Math.floor((
|
|
13779
|
+
const x2 = event.clientX - rect.left;
|
|
13780
|
+
const step = Math.floor((x2 + currentOffsetX) / renderConfig.stepWidth);
|
|
13489
13781
|
playStartStep = Math.max(
|
|
13490
13782
|
0,
|
|
13491
13783
|
Math.floor(step / snapGridSteps) * snapGridSteps
|
|
@@ -13606,11 +13898,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
13606
13898
|
const semis = (lt.octave ?? 0) * 12;
|
|
13607
13899
|
const count = Math.min(sorted.length, lt.syllables.length);
|
|
13608
13900
|
const notes = [];
|
|
13609
|
-
for (let
|
|
13610
|
-
const n = sorted[
|
|
13901
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
13902
|
+
const n = sorted[i2];
|
|
13611
13903
|
if (n.startStep < fromStep) continue;
|
|
13612
13904
|
notes.push({
|
|
13613
|
-
syllable: lt.syllables[
|
|
13905
|
+
syllable: lt.syllables[i2],
|
|
13614
13906
|
pitch: n.pitch + semis,
|
|
13615
13907
|
startSec: (n.startStep - fromStep) * secondsPerStep,
|
|
13616
13908
|
durationSec: n.durationSteps * secondsPerStep * gate
|
|
@@ -13621,6 +13913,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
13621
13913
|
model: lt.model,
|
|
13622
13914
|
volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME),
|
|
13623
13915
|
pan: panToStereo(lt.pan ?? DEFAULT_PAN),
|
|
13916
|
+
vibrato: lt.vibrato,
|
|
13917
|
+
reverbSend: (lt.reverb ?? 0) / 100,
|
|
13624
13918
|
notes
|
|
13625
13919
|
};
|
|
13626
13920
|
}) : [];
|
|
@@ -13709,13 +14003,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
13709
14003
|
const updateTrackPanel = () => {
|
|
13710
14004
|
refs.trackTabs.innerHTML = "";
|
|
13711
14005
|
trackPillEls.clear();
|
|
13712
|
-
for (const [
|
|
14006
|
+
for (const [i2, t] of trackStates.entries()) {
|
|
13713
14007
|
const [r, g, b] = t.config.color;
|
|
13714
14008
|
const btn = document.createElement("button");
|
|
13715
14009
|
btn.className = `dtm-pill ${t.config.id === activeTrackId ? "dtm-pill--active" : ""}`;
|
|
13716
14010
|
btn.style.setProperty("--dtm-pill-color", `rgb(${r},${g},${b})`);
|
|
13717
14011
|
btn.title = t.config.name;
|
|
13718
|
-
btn.textContent = String(
|
|
14012
|
+
btn.textContent = String(i2 + 1);
|
|
13719
14013
|
btn.addEventListener("click", () => switchTrack(t.config.id));
|
|
13720
14014
|
refs.trackTabs.appendChild(btn);
|
|
13721
14015
|
trackPillEls.set(t.config.id, btn);
|
|
@@ -13813,6 +14107,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
13813
14107
|
<option value="-1">-1 oct</option>
|
|
13814
14108
|
<option value="-2">-2 oct</option>
|
|
13815
14109
|
</select>
|
|
14110
|
+
<label class="dtm-label" style="display:inline-flex;align-items:center;gap:2px;white-space:nowrap" title="\u30ED\u30F3\u30B0\u30C8\u30FC\u30F3\u306B\u81EA\u52D5\u3067\u30D3\u30D6\u30E9\u30FC\u30C8\u3092\u639B\u3051\u307E\u3059">
|
|
14111
|
+
<input type="checkbox" data-dtm="lyric-vibrato" aria-label="\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8">\u30D3\u30D6\u30E9\u30FC\u30C8
|
|
14112
|
+
</label>
|
|
14113
|
+
<button class="dtm-infobtn" data-dtm="lyric-vibrato-info" title="\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
|
|
13816
14114
|
<span class="dtm-label dtm-grow" data-dtm="lyric-count" style="text-align:right"></span>
|
|
13817
14115
|
</div>
|
|
13818
14116
|
<div class="dtm-row dtm-hidden" data-dtm="lyric-terms" style="font-size:10px;gap:4px;color:var(--dtm-warn)">
|
|
@@ -13843,6 +14141,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
13843
14141
|
<input type="range" class="dtm-range dtm-grow" data-dtm="lyric-pan" min="0" max="127" aria-label="\u6B4C\u5531\u306E\u30B9\u30C6\u30EC\u30AA\u5B9A\u4F4D\uFF08\u5DE6\u53F3\uFF09">
|
|
13844
14142
|
<span class="dtm-label" data-dtm="lyric-pan-label"></span>
|
|
13845
14143
|
</div>
|
|
14144
|
+
<div class="dtm-row">
|
|
14145
|
+
<span class="dtm-label">\u30EA\u30D0\u30FC\u30D6\u9001\u308A</span>
|
|
14146
|
+
<input type="range" class="dtm-range dtm-grow" data-dtm="lyric-reverb" min="0" max="100" aria-label="\u3053\u306E\u30C8\u30E9\u30C3\u30AF\u304B\u3089\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\u3078\u9001\u308B\u91CF\uFF08\u30DE\u30B9\u30BF\u306E\u30EA\u30D0\u30FC\u30D6\u3064\u307E\u307F\u304C0%\u3060\u3068\u7121\u97F3\uFF09" title="\u30DE\u30B9\u30BF\u30EA\u30D0\u30FC\u30D6\u3078\u306E\u30BB\u30F3\u30C9\u91CF">
|
|
14147
|
+
<span class="dtm-label" data-dtm="lyric-reverb-label"></span>
|
|
14148
|
+
</div>
|
|
13846
14149
|
<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>
|
|
13847
14150
|
</div>`;
|
|
13848
14151
|
refs.trackBody.appendChild(lyricDiv);
|
|
@@ -13876,6 +14179,21 @@ var mountDAW = (target, options = {}) => {
|
|
|
13876
14179
|
const lyricPanLabel = lyricDiv.querySelector(
|
|
13877
14180
|
'[data-dtm="lyric-pan-label"]'
|
|
13878
14181
|
);
|
|
14182
|
+
const lyricReverb = lyricDiv.querySelector(
|
|
14183
|
+
'[data-dtm="lyric-reverb"]'
|
|
14184
|
+
);
|
|
14185
|
+
const lyricReverbLabel = lyricDiv.querySelector(
|
|
14186
|
+
'[data-dtm="lyric-reverb-label"]'
|
|
14187
|
+
);
|
|
14188
|
+
const lyricVibrato = lyricDiv.querySelector(
|
|
14189
|
+
'[data-dtm="lyric-vibrato"]'
|
|
14190
|
+
);
|
|
14191
|
+
const lyricVibratoInfo = lyricDiv.querySelector(
|
|
14192
|
+
'[data-dtm="lyric-vibrato-info"]'
|
|
14193
|
+
);
|
|
14194
|
+
lyricVibratoInfo.addEventListener("click", () => {
|
|
14195
|
+
showModal("\u81EA\u52D5\u30D3\u30D6\u30E9\u30FC\u30C8\u89E3\u8AAC", VIBRATO_INFO_HTML);
|
|
14196
|
+
});
|
|
13879
14197
|
const lyricTerms = lyricDiv.querySelector(
|
|
13880
14198
|
'[data-dtm="lyric-terms"]'
|
|
13881
14199
|
);
|
|
@@ -13937,6 +14255,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
13937
14255
|
lyricVolLabel.textContent = String(active.vocalVolume);
|
|
13938
14256
|
lyricPan.value = String(active.vocalPan);
|
|
13939
14257
|
lyricPanLabel.textContent = fmtPan(active.vocalPan);
|
|
14258
|
+
lyricReverb.value = String(active.vocalReverb);
|
|
14259
|
+
lyricReverbLabel.textContent = `${active.vocalReverb}%`;
|
|
14260
|
+
lyricVibrato.checked = active.vocalVibrato;
|
|
13940
14261
|
const updateLyricCount = () => {
|
|
13941
14262
|
const n = normalizeLyrics(lyricInput.value).length;
|
|
13942
14263
|
lyricCount.textContent = active.lyricModel && n > 0 ? `${n}\u97F3\u7BC0` : "";
|
|
@@ -14055,6 +14376,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
14055
14376
|
active.vocalOctave = Number.parseInt(lyricOctaveSel.value, 10);
|
|
14056
14377
|
fireLyricsChange(active);
|
|
14057
14378
|
});
|
|
14379
|
+
lyricVibrato.addEventListener("change", () => {
|
|
14380
|
+
active.vocalVibrato = lyricVibrato.checked;
|
|
14381
|
+
fireLyricsChange(active);
|
|
14382
|
+
});
|
|
14058
14383
|
lyricInput.addEventListener("input", () => {
|
|
14059
14384
|
active.lyrics = lyricInput.value;
|
|
14060
14385
|
updateLyricCount();
|
|
@@ -14078,6 +14403,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
14078
14403
|
lyricPanLabel.textContent = fmtPan(64);
|
|
14079
14404
|
fireLyricsChange(active);
|
|
14080
14405
|
});
|
|
14406
|
+
lyricReverb.addEventListener("input", () => {
|
|
14407
|
+
active.vocalReverb = Number.parseInt(lyricReverb.value, 10);
|
|
14408
|
+
lyricReverbLabel.textContent = `${active.vocalReverb}%`;
|
|
14409
|
+
fireLyricsChange(active);
|
|
14410
|
+
});
|
|
14081
14411
|
}
|
|
14082
14412
|
if (active.config.id === "chord" && showChord) {
|
|
14083
14413
|
const div = document.createElement("div");
|
|
@@ -14161,8 +14491,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14161
14491
|
const limitSteps = barLimitBars > 0 ? barLimitBars * renderConfig.stepsPerBar : Infinity;
|
|
14162
14492
|
const clipNotes = (notes) => limitSteps === Infinity ? notes : notes.filter((n) => n.startStep < limitSteps);
|
|
14163
14493
|
const trackInstrumentsForMeta = {};
|
|
14164
|
-
trackStates.forEach((t,
|
|
14165
|
-
if (t.trackInstrument) trackInstrumentsForMeta[
|
|
14494
|
+
trackStates.forEach((t, i2) => {
|
|
14495
|
+
if (t.trackInstrument) trackInstrumentsForMeta[i2] = t.trackInstrument;
|
|
14166
14496
|
});
|
|
14167
14497
|
const trackInstMeta = Object.keys(trackInstrumentsForMeta).length > 0 ? trackInstrumentsForMeta : void 0;
|
|
14168
14498
|
const metaLineFull = formatMmlMeta(
|
|
@@ -14197,10 +14527,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
14197
14527
|
const monoTracks = decomposeToMonophonic(allNotes);
|
|
14198
14528
|
const refCore = trackStates[0].core;
|
|
14199
14529
|
const decomposedFull = monoTracks.map(
|
|
14200
|
-
(notes,
|
|
14530
|
+
(notes, i2) => `@${i2} ${refCore.getMMLFromNotes(notes, bpm, 100).trim()}`
|
|
14201
14531
|
);
|
|
14202
14532
|
const decomposedMini = monoTracks.map(
|
|
14203
|
-
(notes,
|
|
14533
|
+
(notes, i2) => `@${i2}${refCore.getMMLFromNotes(notes, bpm, 100).trim().replace(/\s+/g, "")}`
|
|
14204
14534
|
);
|
|
14205
14535
|
const full2 = [metaLineFull, ...decomposedFull, MML_END_MARKER].filter((s) => s.length > 0).join(";\n");
|
|
14206
14536
|
const minified2 = [metaLineMini, ...decomposedMini, MML_END_MARKER].filter((s) => s.length > 0).join(";");
|
|
@@ -14214,34 +14544,38 @@ var mountDAW = (target, options = {}) => {
|
|
|
14214
14544
|
}
|
|
14215
14545
|
const trackLines = [];
|
|
14216
14546
|
const trackLinesMini = [];
|
|
14217
|
-
trackStates.forEach((t,
|
|
14547
|
+
trackStates.forEach((t, i2) => {
|
|
14218
14548
|
const notes = clipNotes(t.core.getNotes());
|
|
14219
14549
|
if (notes.length > 0) {
|
|
14220
14550
|
const mml = t.core.getMMLFromNotes(notes, bpm, t.volume).trim();
|
|
14221
|
-
trackLines.push(`@${
|
|
14222
|
-
trackLinesMini.push(`@${
|
|
14551
|
+
trackLines.push(`@${i2} ${mml}`);
|
|
14552
|
+
trackLinesMini.push(`@${i2}${mml.replace(/\s+/g, "")}`);
|
|
14223
14553
|
}
|
|
14224
14554
|
});
|
|
14225
|
-
const lyricLines = trackStates.map((t,
|
|
14226
|
-
i,
|
|
14555
|
+
const lyricLines = trackStates.map((t, i2) => ({
|
|
14556
|
+
i: i2,
|
|
14227
14557
|
notes: clipNotes(t.core.getNotes()),
|
|
14228
14558
|
text: t.lyrics.replace(/[\r\n]+/g, " ").trim(),
|
|
14229
14559
|
model: t.lyricModel.trim(),
|
|
14230
14560
|
vol: t.vocalVolume,
|
|
14231
14561
|
gate: t.vocalGate,
|
|
14232
14562
|
pan: t.vocalPan,
|
|
14233
|
-
oct: t.vocalOctave
|
|
14563
|
+
oct: t.vocalOctave,
|
|
14564
|
+
vib: t.vocalVibrato,
|
|
14565
|
+
rev: t.vocalReverb
|
|
14234
14566
|
})).filter(
|
|
14235
|
-
(
|
|
14236
|
-
).map((
|
|
14567
|
+
(x2) => x2.model.length > 0 && x2.text.length > 0 && x2.notes.length > 0
|
|
14568
|
+
).map((x2) => {
|
|
14237
14569
|
const params = [
|
|
14238
|
-
|
|
14239
|
-
|
|
14240
|
-
|
|
14241
|
-
|
|
14570
|
+
x2.vol === DEFAULT_VOCAL_VOLUME ? "" : `v${x2.vol}`,
|
|
14571
|
+
x2.gate === 100 ? "" : `q${x2.gate}`,
|
|
14572
|
+
x2.pan === 64 ? "" : `p${x2.pan}`,
|
|
14573
|
+
x2.oct === 0 ? "" : `o${x2.oct}`,
|
|
14574
|
+
x2.vib ? "b1" : "",
|
|
14575
|
+
x2.rev === 0 ? "" : `r${x2.rev}`
|
|
14242
14576
|
].filter((s) => s.length > 0).join(" ");
|
|
14243
|
-
const head = params ? `${
|
|
14244
|
-
return `@@${
|
|
14577
|
+
const head = params ? `${x2.model} ${params}` : x2.model;
|
|
14578
|
+
return `@@${x2.i} ${head} ${x2.text}`;
|
|
14245
14579
|
});
|
|
14246
14580
|
const customVocalDecls = [];
|
|
14247
14581
|
for (const [key, def] of customVocalsMap) {
|
|
@@ -14379,17 +14713,17 @@ var mountDAW = (target, options = {}) => {
|
|
|
14379
14713
|
refs.drumVolumeLabel.textContent = `${drumVolume}%`;
|
|
14380
14714
|
}
|
|
14381
14715
|
}
|
|
14382
|
-
trackStates.forEach((t,
|
|
14383
|
-
if (applyActiveOnly &&
|
|
14384
|
-
const name = normalizeInstrumentName(meta.trackInstruments?.[
|
|
14716
|
+
trackStates.forEach((t, i2) => {
|
|
14717
|
+
if (applyActiveOnly && i2 !== activeTrackIndex) return;
|
|
14718
|
+
const name = normalizeInstrumentName(meta.trackInstruments?.[i2] ?? "");
|
|
14385
14719
|
if (t.trackInstrument !== name) {
|
|
14386
14720
|
t.trackInstrument = name;
|
|
14387
|
-
options.onTrackInstrumentChange?.(
|
|
14721
|
+
options.onTrackInstrumentChange?.(i2, name);
|
|
14388
14722
|
}
|
|
14389
14723
|
});
|
|
14390
|
-
trackStates.forEach((t,
|
|
14391
|
-
if (applyActiveOnly &&
|
|
14392
|
-
const v = trackVelocity.get(
|
|
14724
|
+
trackStates.forEach((t, i2) => {
|
|
14725
|
+
if (applyActiveOnly && i2 !== activeTrackIndex) return;
|
|
14726
|
+
const v = trackVelocity.get(i2);
|
|
14393
14727
|
if (v !== void 0 && v !== t.volume) {
|
|
14394
14728
|
t.volume = v;
|
|
14395
14729
|
t.core.setVolume(v);
|
|
@@ -14403,6 +14737,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14403
14737
|
active.vocalGate = 100;
|
|
14404
14738
|
active.vocalPan = 64;
|
|
14405
14739
|
active.vocalOctave = 0;
|
|
14740
|
+
active.vocalVibrato = false;
|
|
14741
|
+
active.vocalReverb = 0;
|
|
14406
14742
|
} else {
|
|
14407
14743
|
for (const t of trackStates) {
|
|
14408
14744
|
t.lyrics = "";
|
|
@@ -14411,6 +14747,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14411
14747
|
t.vocalGate = 100;
|
|
14412
14748
|
t.vocalPan = 64;
|
|
14413
14749
|
t.vocalOctave = 0;
|
|
14750
|
+
t.vocalVibrato = false;
|
|
14751
|
+
t.vocalReverb = 0;
|
|
14414
14752
|
}
|
|
14415
14753
|
}
|
|
14416
14754
|
lyrics?.forEach((lt) => {
|
|
@@ -14423,6 +14761,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14423
14761
|
t.vocalGate = lt.gate;
|
|
14424
14762
|
t.vocalPan = lt.pan;
|
|
14425
14763
|
t.vocalOctave = lt.octave ?? 0;
|
|
14764
|
+
t.vocalVibrato = lt.vibrato ?? false;
|
|
14765
|
+
t.vocalReverb = lt.reverb ?? 0;
|
|
14426
14766
|
});
|
|
14427
14767
|
for (const p of placements) {
|
|
14428
14768
|
if (applyActiveOnly && p.trackIndex !== activeTrackIndex) continue;
|
|
@@ -14501,6 +14841,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14501
14841
|
active.vocalGate = 100;
|
|
14502
14842
|
active.vocalPan = 64;
|
|
14503
14843
|
active.vocalOctave = 0;
|
|
14844
|
+
active.vocalVibrato = false;
|
|
14845
|
+
active.vocalReverb = 0;
|
|
14504
14846
|
} else {
|
|
14505
14847
|
clearAll();
|
|
14506
14848
|
for (const t of trackStates) t.core.setLoadMode(true);
|
|
@@ -14511,6 +14853,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14511
14853
|
t.vocalGate = 100;
|
|
14512
14854
|
t.vocalPan = 64;
|
|
14513
14855
|
t.vocalOctave = 0;
|
|
14856
|
+
t.vocalVibrato = false;
|
|
14857
|
+
t.vocalReverb = 0;
|
|
14514
14858
|
}
|
|
14515
14859
|
}
|
|
14516
14860
|
const { placements, bpm: parsedBpm } = isAdvanced ? extractMidiPlacementsByTrack(
|
|
@@ -14720,6 +15064,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
14720
15064
|
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
14721
15065
|
options.singingVoices?.setVolume(masterVolume / 100);
|
|
14722
15066
|
});
|
|
15067
|
+
refs.reverbAmount.addEventListener("input", () => {
|
|
15068
|
+
reverbAmount = Number.parseInt(refs.reverbAmount.value, 10) || 0;
|
|
15069
|
+
refs.reverbAmountLabel.textContent = `${reverbAmount}%`;
|
|
15070
|
+
options.onReverbChange?.(reverbAmount);
|
|
15071
|
+
});
|
|
14723
15072
|
refs.drumSelect.addEventListener("change", () => {
|
|
14724
15073
|
currentDrumPattern = refs.drumSelect.value;
|
|
14725
15074
|
options.onDrumChange?.(currentDrumPattern);
|
|
@@ -15000,7 +15349,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15000
15349
|
console.error(e);
|
|
15001
15350
|
}
|
|
15002
15351
|
refs.midiTrackSelection.innerHTML = `<span class="dtm-label">\u30C8\u30E9\u30C3\u30AF</span>`;
|
|
15003
|
-
detectedTracks.forEach((t,
|
|
15352
|
+
detectedTracks.forEach((t, i2) => {
|
|
15004
15353
|
const btn = document.createElement("button");
|
|
15005
15354
|
btn.className = `dtm-btn ${t.selected ? "dtm-btn--primary" : "dtm-btn--ghost"}`;
|
|
15006
15355
|
btn.dataset.selected = String(t.selected);
|
|
@@ -15012,7 +15361,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15012
15361
|
btn.classList.toggle("dtm-btn--ghost", !on);
|
|
15013
15362
|
});
|
|
15014
15363
|
refs.midiTrackSelection.appendChild(btn);
|
|
15015
|
-
if (
|
|
15364
|
+
if (i2 === 0) refs.midiTrackSelection.dataset.ready = "1";
|
|
15016
15365
|
});
|
|
15017
15366
|
refs.midiTrackSelection.classList.remove("dtm-hidden");
|
|
15018
15367
|
refs.overlay.hidden = true;
|
|
@@ -15022,9 +15371,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
15022
15371
|
if (!pendingMidi) return;
|
|
15023
15372
|
const selected = [];
|
|
15024
15373
|
const btns = refs.midiTrackSelection.querySelectorAll("button");
|
|
15025
|
-
btns.forEach((b,
|
|
15374
|
+
btns.forEach((b, i2) => {
|
|
15026
15375
|
if (b.dataset.selected === "true")
|
|
15027
|
-
selected.push(detectedTracks[
|
|
15376
|
+
selected.push(detectedTracks[i2].index);
|
|
15028
15377
|
});
|
|
15029
15378
|
if (selected.length === 0) return;
|
|
15030
15379
|
if (!isAdvanced && options.onRequestAdvancedMode && selected.length > trackStates.length) {
|
|
@@ -15058,7 +15407,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15058
15407
|
const refCore = trackStates[0].core;
|
|
15059
15408
|
const tempo = Math.round(parsedBpm) || bpm;
|
|
15060
15409
|
const lines = [];
|
|
15061
|
-
let
|
|
15410
|
+
let i2 = 0;
|
|
15062
15411
|
for (const notes of byTrack.values()) {
|
|
15063
15412
|
const asNotes = notes.map((p) => ({
|
|
15064
15413
|
id: 0,
|
|
@@ -15068,8 +15417,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
15068
15417
|
velocity: p.velocity
|
|
15069
15418
|
}));
|
|
15070
15419
|
const mml = refCore.getMMLFromNotes(asNotes, tempo, 100).trim();
|
|
15071
|
-
lines.push(`@${
|
|
15072
|
-
|
|
15420
|
+
lines.push(`@${i2} ${mml}`);
|
|
15421
|
+
i2++;
|
|
15073
15422
|
}
|
|
15074
15423
|
return [...lines, MML_END_MARKER].join(";\n");
|
|
15075
15424
|
};
|
|
@@ -15684,6 +16033,12 @@ var mountDAW = (target, options = {}) => {
|
|
|
15684
16033
|
refs.drumVolume.value = String(drumVolume);
|
|
15685
16034
|
refs.drumVolumeLabel.textContent = `${drumVolume}%`;
|
|
15686
16035
|
},
|
|
16036
|
+
setReverbAmount: (amount) => {
|
|
16037
|
+
reverbAmount = clamp3(amount, 0, 100);
|
|
16038
|
+
refs.reverbAmount.value = String(reverbAmount);
|
|
16039
|
+
refs.reverbAmountLabel.textContent = `${reverbAmount}%`;
|
|
16040
|
+
options.onReverbChange?.(reverbAmount);
|
|
16041
|
+
},
|
|
15687
16042
|
applyPatch: (trackId, added, removed) => {
|
|
15688
16043
|
const track = trackStates.find((t) => t.config.id === trackId);
|
|
15689
16044
|
if (!track) return;
|
|
@@ -15723,6 +16078,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
15723
16078
|
t.vocalGate = data.vocalGate;
|
|
15724
16079
|
t.vocalPan = data.vocalPan;
|
|
15725
16080
|
t.vocalOctave = data.vocalOctave;
|
|
16081
|
+
t.vocalVibrato = data.vocalVibrato ?? false;
|
|
16082
|
+
t.vocalReverb = data.vocalReverb ?? 0;
|
|
15726
16083
|
},
|
|
15727
16084
|
applyTrackInstrument: (trackIndex, instrumentName) => {
|
|
15728
16085
|
const t = trackStates[trackIndex];
|
|
@@ -15733,17 +16090,17 @@ var mountDAW = (target, options = {}) => {
|
|
|
15733
16090
|
},
|
|
15734
16091
|
noteToCanvas: (step, pitch) => {
|
|
15735
16092
|
const canvas = getGridCanvas();
|
|
15736
|
-
const
|
|
16093
|
+
const x2 = step * renderConfig.stepWidth - currentOffsetX;
|
|
15737
16094
|
const y = (renderConfig.keyCount - 1 - pitch) * renderConfig.keyHeight - currentOffsetY;
|
|
15738
|
-
const onScreen =
|
|
16095
|
+
const onScreen = x2 >= 0 && x2 <= canvas.width && y >= 0 && y <= canvas.height;
|
|
15739
16096
|
let side = null;
|
|
15740
16097
|
if (!onScreen) {
|
|
15741
|
-
if (
|
|
15742
|
-
else if (
|
|
16098
|
+
if (x2 < 0) side = "left";
|
|
16099
|
+
else if (x2 > canvas.width) side = "right";
|
|
15743
16100
|
else if (y < 0) side = "top";
|
|
15744
16101
|
else side = "bottom";
|
|
15745
16102
|
}
|
|
15746
|
-
return { x, y, onScreen, side };
|
|
16103
|
+
return { x: x2, y, onScreen, side };
|
|
15747
16104
|
},
|
|
15748
16105
|
destroy: () => {
|
|
15749
16106
|
sequencer.stop();
|
|
@@ -15823,11 +16180,11 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
15823
16180
|
const semis = (lt.octave ?? 0) * 12;
|
|
15824
16181
|
const count = Math.min(sorted.length, lt.syllables.length);
|
|
15825
16182
|
const notes = [];
|
|
15826
|
-
for (let
|
|
15827
|
-
const n = sorted[
|
|
16183
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
16184
|
+
const n = sorted[i2];
|
|
15828
16185
|
if (n.startStep < fromStep) continue;
|
|
15829
16186
|
notes.push({
|
|
15830
|
-
syllable: lt.syllables[
|
|
16187
|
+
syllable: lt.syllables[i2],
|
|
15831
16188
|
pitch: n.pitch + semis,
|
|
15832
16189
|
startSec: (n.startStep - fromStep) * secondsPerStep,
|
|
15833
16190
|
durationSec: n.durationSteps * secondsPerStep * gate
|
|
@@ -15838,6 +16195,8 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
15838
16195
|
model: lt.model,
|
|
15839
16196
|
volume: vocalVolumeToGain(lt.volume ?? DEFAULT_VOCAL_VOLUME),
|
|
15840
16197
|
pan: panToStereo(lt.pan ?? DEFAULT_PAN),
|
|
16198
|
+
vibrato: lt.vibrato,
|
|
16199
|
+
reverbSend: (lt.reverb ?? 0) / 100,
|
|
15841
16200
|
notes
|
|
15842
16201
|
};
|
|
15843
16202
|
});
|
|
@@ -16052,7 +16411,7 @@ var createPianoRoll = (options, handlers) => {
|
|
|
16052
16411
|
let dragState = null;
|
|
16053
16412
|
let hasDragged = false;
|
|
16054
16413
|
let lastPreviewPitch = null;
|
|
16055
|
-
const findNoteAtPosition = (
|
|
16414
|
+
const findNoteAtPosition = (x2, y) => {
|
|
16056
16415
|
const { stepWidth, keyHeight, keyCount, pitchRangeStart } = getRenderConfig();
|
|
16057
16416
|
const offset = getDrawOffset();
|
|
16058
16417
|
for (const note of core.getNotes()) {
|
|
@@ -16063,7 +16422,7 @@ var createPianoRoll = (options, handlers) => {
|
|
|
16063
16422
|
const h = keyHeight;
|
|
16064
16423
|
const renderX = logicalX - offset.x;
|
|
16065
16424
|
const renderY = logicalY - offset.y;
|
|
16066
|
-
if (
|
|
16425
|
+
if (x2 >= renderX && x2 <= renderX + w && y >= renderY && y <= renderY + h) {
|
|
16067
16426
|
return note;
|
|
16068
16427
|
}
|
|
16069
16428
|
}
|
|
@@ -16071,10 +16430,10 @@ var createPianoRoll = (options, handlers) => {
|
|
|
16071
16430
|
};
|
|
16072
16431
|
const handlePointerMove = (e) => {
|
|
16073
16432
|
if (core.getToolMode() === "select" && isSelecting && selectionStart) {
|
|
16074
|
-
const { x, y } = getGridPosition(e);
|
|
16075
|
-
const minX = Math.min(
|
|
16433
|
+
const { x: x2, y } = getGridPosition(e);
|
|
16434
|
+
const minX = Math.min(x2, selectionStart.x);
|
|
16076
16435
|
const minY = Math.min(y, selectionStart.y);
|
|
16077
|
-
const width2 = Math.abs(
|
|
16436
|
+
const width2 = Math.abs(x2 - selectionStart.x);
|
|
16078
16437
|
const height2 = Math.abs(y - selectionStart.y);
|
|
16079
16438
|
selectionRect = { x: minX, y: minY, width: width2, height: height2 };
|
|
16080
16439
|
selectedNotes = getNotesInRect(selectionRect);
|
|
@@ -16132,10 +16491,10 @@ var createPianoRoll = (options, handlers) => {
|
|
|
16132
16491
|
}
|
|
16133
16492
|
};
|
|
16134
16493
|
gridCanvas.addEventListener("pointerdown", (e) => {
|
|
16135
|
-
const { x, y, step, pitch } = getGridPosition(e);
|
|
16494
|
+
const { x: x2, y, step, pitch } = getGridPosition(e);
|
|
16136
16495
|
const currentMode = core.getToolMode();
|
|
16137
16496
|
if (currentMode === "select") {
|
|
16138
|
-
const clickedNote = findNoteAtPosition(
|
|
16497
|
+
const clickedNote = findNoteAtPosition(x2, y);
|
|
16139
16498
|
if (selectionRect && clickedNote) {
|
|
16140
16499
|
const notesInRect = getNotesInRect(selectionRect);
|
|
16141
16500
|
if (notesInRect.some((n) => n.id === clickedNote.id)) {
|
|
@@ -16156,10 +16515,10 @@ var createPianoRoll = (options, handlers) => {
|
|
|
16156
16515
|
selectedNotes = [];
|
|
16157
16516
|
selectionRect = null;
|
|
16158
16517
|
isSelecting = true;
|
|
16159
|
-
selectionStart = { x, y, step, pitch };
|
|
16518
|
+
selectionStart = { x: x2, y, step, pitch };
|
|
16160
16519
|
return;
|
|
16161
16520
|
}
|
|
16162
|
-
const note = findNoteAtPosition(
|
|
16521
|
+
const note = findNoteAtPosition(x2, y);
|
|
16163
16522
|
if (!note) return;
|
|
16164
16523
|
const { stepWidth, keyHeight, keyCount, pitchRangeStart } = getRenderConfig();
|
|
16165
16524
|
const offset = getDrawOffset();
|
|
@@ -16169,7 +16528,7 @@ var createPianoRoll = (options, handlers) => {
|
|
|
16169
16528
|
const renderX = logicalX - offset.x;
|
|
16170
16529
|
const renderY = logicalY - offset.y;
|
|
16171
16530
|
const w = note.durationSteps * stepWidth;
|
|
16172
|
-
if (
|
|
16531
|
+
if (x2 >= renderX + w - resizeHandleWidth && x2 <= renderX + w && y >= renderY && y <= renderY + keyHeight) {
|
|
16173
16532
|
dragState = {
|
|
16174
16533
|
noteId: note.id,
|
|
16175
16534
|
mode: "resize",
|
|
@@ -16445,6 +16804,25 @@ var isSupported = midiJsonParser.isSupported;
|
|
|
16445
16804
|
var parseArrayBuffer = midiJsonParser.parseArrayBuffer;
|
|
16446
16805
|
URL.revokeObjectURL(url);
|
|
16447
16806
|
|
|
16807
|
+
// src/reverb.ts
|
|
16808
|
+
var IMPULSE_DURATION_SEC = 2.2;
|
|
16809
|
+
var IMPULSE_DECAY = 2.5;
|
|
16810
|
+
var createReverbImpulse = (ctx) => {
|
|
16811
|
+
const rate = ctx.sampleRate;
|
|
16812
|
+
const length = Math.max(1, Math.floor(rate * IMPULSE_DURATION_SEC));
|
|
16813
|
+
const impulse = ctx.createBuffer(2, length, rate);
|
|
16814
|
+
for (let ch = 0; ch < impulse.numberOfChannels; ch++) {
|
|
16815
|
+
const data = impulse.getChannelData(ch);
|
|
16816
|
+
for (let i2 = 0; i2 < length; i2++) {
|
|
16817
|
+
const envelope = (1 - i2 / length) ** IMPULSE_DECAY;
|
|
16818
|
+
data[i2] = (Math.random() * 2 - 1) * envelope;
|
|
16819
|
+
}
|
|
16820
|
+
}
|
|
16821
|
+
return impulse;
|
|
16822
|
+
};
|
|
16823
|
+
var REVERB_MAX_WET = 0.6;
|
|
16824
|
+
var reverbAmountToGain = (amount) => Math.max(0, Math.min(100, amount)) / 100 * REVERB_MAX_WET;
|
|
16825
|
+
|
|
16448
16826
|
// src/sf/SoundFont_drum.ts
|
|
16449
16827
|
var touch = (map, key, ctor) => {
|
|
16450
16828
|
if (!map.has(key)) map.set(key, new ctor());
|
|
@@ -16601,6 +16979,21 @@ var createDtmStudio = async (options = {}) => {
|
|
|
16601
16979
|
const drumGain = audioCtx.createGain();
|
|
16602
16980
|
drumGain.gain.value = options.drumVolume ?? 1;
|
|
16603
16981
|
drumGain.connect(masterGain);
|
|
16982
|
+
const reverbConvolver = audioCtx.createConvolver();
|
|
16983
|
+
reverbConvolver.buffer = createReverbImpulse(audioCtx);
|
|
16984
|
+
reverbConvolver.normalize = true;
|
|
16985
|
+
const reverbWetGain = audioCtx.createGain();
|
|
16986
|
+
reverbWetGain.gain.value = reverbAmountToGain(options.reverbAmount ?? 0);
|
|
16987
|
+
masterGain.connect(reverbConvolver);
|
|
16988
|
+
reverbConvolver.connect(reverbWetGain);
|
|
16989
|
+
reverbWetGain.connect(audioCtx.destination);
|
|
16990
|
+
const setReverbAmount = (amount) => {
|
|
16991
|
+
reverbWetGain.gain.setTargetAtTime(
|
|
16992
|
+
reverbAmountToGain(amount),
|
|
16993
|
+
audioCtx.currentTime,
|
|
16994
|
+
0.02
|
|
16995
|
+
);
|
|
16996
|
+
};
|
|
16604
16997
|
const resumeAudio = () => {
|
|
16605
16998
|
if (audioCtx.state === "closed") return Promise.resolve();
|
|
16606
16999
|
return audioCtx.resume();
|
|
@@ -16631,7 +17024,10 @@ var createDtmStudio = async (options = {}) => {
|
|
|
16631
17024
|
const singingVoices = createSingingVoices(audioCtx, masterGain, {
|
|
16632
17025
|
voiceWorkerUrl,
|
|
16633
17026
|
voicebanks,
|
|
16634
|
-
worldlineScriptUrl: options.worldlineScriptUrl
|
|
17027
|
+
worldlineScriptUrl: options.worldlineScriptUrl,
|
|
17028
|
+
// ボーカルトラック個別のリバーブセンド(`r`トークン)先。マスタリバーブの
|
|
17029
|
+
// Convolver 入力へ直接センドする(masterGain 経由のドライ段は素通り)。
|
|
17030
|
+
reverbBus: reverbConvolver
|
|
16635
17031
|
});
|
|
16636
17032
|
const listReady = new Promise((resolve) => {
|
|
16637
17033
|
sfList.init();
|
|
@@ -16906,6 +17302,8 @@ var createDtmStudio = async (options = {}) => {
|
|
|
16906
17302
|
void handleTrackInstrumentChange(idx, name);
|
|
16907
17303
|
externalOnTrackInstrumentChange?.(idx, name);
|
|
16908
17304
|
},
|
|
17305
|
+
reverbAmount: options.reverbAmount,
|
|
17306
|
+
onReverbChange: setReverbAmount,
|
|
16909
17307
|
...dawOverrides,
|
|
16910
17308
|
// dawOverrides で上書きされないよう、スプレッドの後に配置して合成する
|
|
16911
17309
|
onDrumChange: (name) => {
|
|
@@ -16963,8 +17361,8 @@ var createDtmStudio = async (options = {}) => {
|
|
|
16963
17361
|
presetSelect?.destroy();
|
|
16964
17362
|
if (editorPresetSelects.get(target) === presetSelect)
|
|
16965
17363
|
editorPresetSelects.delete(target);
|
|
16966
|
-
const
|
|
16967
|
-
if (
|
|
17364
|
+
const i2 = mountedEditors.indexOf(daw);
|
|
17365
|
+
if (i2 >= 0) mountedEditors.splice(i2, 1);
|
|
16968
17366
|
};
|
|
16969
17367
|
return {
|
|
16970
17368
|
...daw,
|
|
@@ -17067,8 +17465,8 @@ var createDtmStudio = async (options = {}) => {
|
|
|
17067
17465
|
destroy: () => {
|
|
17068
17466
|
doUnmount();
|
|
17069
17467
|
wrapper.remove();
|
|
17070
|
-
const
|
|
17071
|
-
if (
|
|
17468
|
+
const i2 = mountedModeSwitches.indexOf(instance);
|
|
17469
|
+
if (i2 >= 0) mountedModeSwitches.splice(i2, 1);
|
|
17072
17470
|
}
|
|
17073
17471
|
};
|
|
17074
17472
|
mountedModeSwitches.push(instance);
|
|
@@ -17155,8 +17553,8 @@ var createDtmStudio = async (options = {}) => {
|
|
|
17155
17553
|
mountedPlayers.push(player);
|
|
17156
17554
|
const destroy = () => {
|
|
17157
17555
|
player.destroy();
|
|
17158
|
-
const
|
|
17159
|
-
if (
|
|
17556
|
+
const i2 = mountedPlayers.indexOf(player);
|
|
17557
|
+
if (i2 >= 0) mountedPlayers.splice(i2, 1);
|
|
17160
17558
|
};
|
|
17161
17559
|
return { ...player, destroy };
|
|
17162
17560
|
};
|
|
@@ -17417,8 +17815,8 @@ var createDtmStudio = async (options = {}) => {
|
|
|
17417
17815
|
mountedChordPlayers.push(player);
|
|
17418
17816
|
const destroy = () => {
|
|
17419
17817
|
player.destroy();
|
|
17420
|
-
const
|
|
17421
|
-
if (
|
|
17818
|
+
const i2 = mountedChordPlayers.indexOf(player);
|
|
17819
|
+
if (i2 >= 0) mountedChordPlayers.splice(i2, 1);
|
|
17422
17820
|
};
|
|
17423
17821
|
return {
|
|
17424
17822
|
...player,
|
|
@@ -17473,6 +17871,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
17473
17871
|
mountModeSwitch,
|
|
17474
17872
|
setMasterVolume,
|
|
17475
17873
|
setVolume: setMasterVolume,
|
|
17874
|
+
setReverbAmount,
|
|
17476
17875
|
dispose
|
|
17477
17876
|
};
|
|
17478
17877
|
};
|
|
@@ -17504,6 +17903,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
17504
17903
|
PREWARM_NOTES,
|
|
17505
17904
|
TRACKS_ADVANCED,
|
|
17506
17905
|
TRACKS_SIMPLE,
|
|
17906
|
+
VIBRATO_MIN_SEC,
|
|
17507
17907
|
VOICE_IMAGES,
|
|
17508
17908
|
VOICE_IMAGE_KEY,
|
|
17509
17909
|
analyzeMidiTracks,
|