@pinkpixel/haku 0.1.0 → 1.0.0
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 +18 -11
- package/dist/cli.mjs +38 -4
- package/dist/{mcp-DDuNECfs.mjs → mcp-DsSn95Rn.mjs} +3 -2
- package/dist/samples/drums/cabasa_v0_r1.ogg +0 -0
- package/dist/samples/drums/cabasa_v0_r2.ogg +0 -0
- package/dist/samples/drums/cabasa_v1_r1.ogg +0 -0
- package/dist/samples/drums/cabasa_v1_r2.ogg +0 -0
- package/dist/samples/drums/shaker_v0_r1.ogg +0 -0
- package/dist/samples/drums/shaker_v0_r2.ogg +0 -0
- package/dist/samples/drums/shaker_v1_r1.ogg +0 -0
- package/dist/samples/drums/shaker_v1_r2.ogg +0 -0
- package/dist/samples/guitar/v0_31.ogg +0 -0
- package/dist/samples/guitar/v0_32.ogg +0 -0
- package/dist/samples/guitar/v0_33.ogg +0 -0
- package/dist/samples/guitar/v0_34.ogg +0 -0
- package/dist/samples/guitar/v0_35.ogg +0 -0
- package/dist/samples/guitar/v0_36.ogg +0 -0
- package/dist/samples/guitar/v0_37.ogg +0 -0
- package/dist/samples/guitar/v0_38.ogg +0 -0
- package/dist/samples/guitar/v0_39.ogg +0 -0
- package/dist/samples/guitar/v0_40.ogg +0 -0
- package/dist/samples/guitar/v0_41.ogg +0 -0
- package/dist/samples/guitar/v0_43.ogg +0 -0
- package/dist/samples/guitar/v0_45.ogg +0 -0
- package/dist/samples/guitar/v0_47.ogg +0 -0
- package/dist/samples/guitar/v0_48.ogg +0 -0
- package/dist/samples/guitar/v0_50.ogg +0 -0
- package/dist/samples/guitar/v0_52.ogg +0 -0
- package/dist/samples/guitar/v0_53.ogg +0 -0
- package/dist/samples/guitar/v0_54.ogg +0 -0
- package/dist/samples/guitar/v0_55.ogg +0 -0
- package/dist/samples/guitar/v0_56.ogg +0 -0
- package/dist/samples/guitar/v0_57.ogg +0 -0
- package/dist/samples/guitar/v0_58.ogg +0 -0
- package/dist/samples/guitar/v0_59.ogg +0 -0
- package/dist/samples/guitar/v0_60.ogg +0 -0
- package/dist/samples/guitar/v0_61.ogg +0 -0
- package/dist/samples/guitar/v0_62.ogg +0 -0
- package/dist/samples/guitar/v0_63.ogg +0 -0
- package/dist/samples/guitar/v0_64.ogg +0 -0
- package/dist/samples/guitar/v0_65.ogg +0 -0
- package/dist/samples/guitar/v0_66.ogg +0 -0
- package/dist/samples/guitar/v0_67.ogg +0 -0
- package/dist/samples/guitar/v0_69.ogg +0 -0
- package/dist/samples/guitar/v0_70.ogg +0 -0
- package/dist/samples/guitar/v0_71.ogg +0 -0
- package/dist/samples/guitar/v0_72.ogg +0 -0
- package/dist/samples/guitar/v0_73.ogg +0 -0
- package/dist/samples/guitar/v0_74.ogg +0 -0
- package/dist/samples/guitar/v0_75.ogg +0 -0
- package/dist/samples/guitar/v0_76.ogg +0 -0
- package/dist/samples/guitar/v0_77.ogg +0 -0
- package/dist/samples/guitar/v0_78.ogg +0 -0
- package/dist/samples/guitar/v0_79.ogg +0 -0
- package/dist/samples/guitar/v0_80.ogg +0 -0
- package/dist/samples/guitar/v0_81.ogg +0 -0
- package/dist/samples/guitar/v0_82.ogg +0 -0
- package/dist/samples/guitar/v0_83.ogg +0 -0
- package/dist/samples/guitar/v0_84.ogg +0 -0
- package/dist/samples/kalimba/v0_44.ogg +0 -0
- package/dist/samples/kalimba/v0_46.ogg +0 -0
- package/dist/samples/kalimba/v0_49.ogg +0 -0
- package/dist/samples/kalimba/v0_51.ogg +0 -0
- package/dist/samples/kalimba/v0_53.ogg +0 -0
- package/dist/samples/kalimba/v0_56.ogg +0 -0
- package/dist/samples/kalimba/v0_59.ogg +0 -0
- package/dist/samples/kalimba/v0_61.ogg +0 -0
- package/dist/samples/kalimba/v0_64.ogg +0 -0
- package/dist/samples/kalimba/v0_65.ogg +0 -0
- package/dist/samples/kalimba/v0_67.ogg +0 -0
- package/dist/samples/kalimba/v0_71.ogg +0 -0
- package/dist/samples/kalimba/v0_73.ogg +0 -0
- package/dist/samples/kalimba/v0_74.ogg +0 -0
- package/dist/samples/kalimba/v0_75.ogg +0 -0
- package/dist/samples/kalimba/v0_77.ogg +0 -0
- package/dist/samples/kalimba/v0_78.ogg +0 -0
- package/dist/samples/kalimba/v0_79.ogg +0 -0
- package/dist/samples/kalimba/v0_82.ogg +0 -0
- package/dist/samples/kalimba/v0_83.ogg +0 -0
- package/dist/samples/manifest.json +181 -0
- package/dist/samples/vibes/v0_53.ogg +0 -0
- package/dist/samples/vibes/v0_57.ogg +0 -0
- package/dist/samples/vibes/v0_60.ogg +0 -0
- package/dist/samples/vibes/v0_64.ogg +0 -0
- package/dist/samples/vibes/v0_67.ogg +0 -0
- package/dist/samples/vibes/v0_71.ogg +0 -0
- package/dist/samples/vibes/v0_74.ogg +0 -0
- package/dist/samples/vibes/v0_77.ogg +0 -0
- package/dist/samples/vibes/v0_81.ogg +0 -0
- package/dist/samples/vibes/v0_84.ogg +0 -0
- package/dist/samples/vibes/v0_88.ogg +0 -0
- package/dist/samples/vibes/v1_53.ogg +0 -0
- package/dist/samples/vibes/v1_57.ogg +0 -0
- package/dist/samples/vibes/v1_60.ogg +0 -0
- package/dist/samples/vibes/v1_64.ogg +0 -0
- package/dist/samples/vibes/v1_67.ogg +0 -0
- package/dist/samples/vibes/v1_71.ogg +0 -0
- package/dist/samples/vibes/v1_74.ogg +0 -0
- package/dist/samples/vibes/v1_77.ogg +0 -0
- package/dist/samples/vibes/v1_81.ogg +0 -0
- package/dist/samples/vibes/v1_84.ogg +0 -0
- package/dist/samples/vibes/v1_88.ogg +0 -0
- package/dist/{src-D5fW7AC1.mjs → src-Bz-0ulVm.mjs} +179 -23
- package/dist/{src-JrRbTOJC.mjs → src-DNP73wX3.mjs} +60 -3
- package/dist/{version-DoZ2etXG.mjs → tools-CO-tdB7d.mjs} +321 -82
- package/dist/web/assets/esm-J-U37_Wq.js +210 -0
- package/dist/web/assets/figtree-latin-ext-wght-normal-DCwSJGxG.woff2 +0 -0
- package/dist/web/assets/figtree-latin-wght-normal-D_ZTVpCC.woff2 +0 -0
- package/dist/web/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
- package/dist/web/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
- package/dist/web/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
- package/dist/web/assets/index-BkzOde44.js +65 -0
- package/dist/web/assets/index-D0m3L8JK.css +1 -0
- package/dist/web/assets/rolldown-runtime-BWaJ3C7D.js +1 -0
- package/dist/web/assets/src-m48ItyJA.js +1 -0
- package/dist/web/icon-192.png +0 -0
- package/dist/web/icon-512.png +0 -0
- package/dist/web/index.html +19 -0
- package/dist/web/manifest.webmanifest +24 -0
- package/dist/web/sw.js +37 -0
- package/package.json +2 -2
|
@@ -775,18 +775,36 @@ const INSTRUMENTS = {
|
|
|
775
775
|
kind: "tonal",
|
|
776
776
|
gm: 9
|
|
777
777
|
},
|
|
778
|
+
vibes: {
|
|
779
|
+
label: "Vibraphone",
|
|
780
|
+
range: [53, 86],
|
|
781
|
+
kind: "tonal",
|
|
782
|
+
gm: 11
|
|
783
|
+
},
|
|
778
784
|
marimba: {
|
|
779
785
|
label: "Marimba",
|
|
780
786
|
range: [55, 84],
|
|
781
787
|
kind: "tonal",
|
|
782
788
|
gm: 12
|
|
783
789
|
},
|
|
790
|
+
kalimba: {
|
|
791
|
+
label: "Kalimba",
|
|
792
|
+
range: [60, 83],
|
|
793
|
+
kind: "tonal",
|
|
794
|
+
gm: 108
|
|
795
|
+
},
|
|
784
796
|
harp: {
|
|
785
797
|
label: "Harp",
|
|
786
798
|
range: [48, 84],
|
|
787
799
|
kind: "tonal",
|
|
788
800
|
gm: 46
|
|
789
801
|
},
|
|
802
|
+
guitar: {
|
|
803
|
+
label: "Nylon guitar",
|
|
804
|
+
range: [45, 79],
|
|
805
|
+
kind: "tonal",
|
|
806
|
+
gm: 24
|
|
807
|
+
},
|
|
790
808
|
pad: {
|
|
791
809
|
label: "Warm pad",
|
|
792
810
|
range: [55, 79],
|
|
@@ -799,6 +817,18 @@ const INSTRUMENTS = {
|
|
|
799
817
|
kind: "tonal",
|
|
800
818
|
gm: 24
|
|
801
819
|
},
|
|
820
|
+
celesta: {
|
|
821
|
+
label: "Celesta",
|
|
822
|
+
range: [60, 96],
|
|
823
|
+
kind: "tonal",
|
|
824
|
+
gm: 8
|
|
825
|
+
},
|
|
826
|
+
musicbox: {
|
|
827
|
+
label: "Music box",
|
|
828
|
+
range: [64, 96],
|
|
829
|
+
kind: "tonal",
|
|
830
|
+
gm: 10
|
|
831
|
+
},
|
|
802
832
|
bass: {
|
|
803
833
|
label: "Round bass",
|
|
804
834
|
range: [31, 55],
|
|
@@ -890,6 +920,14 @@ const DRUM_PIECES = {
|
|
|
890
920
|
tom: {
|
|
891
921
|
label: "Tom",
|
|
892
922
|
gm: 45
|
|
923
|
+
},
|
|
924
|
+
shaker: {
|
|
925
|
+
label: "Shaker",
|
|
926
|
+
gm: 82
|
|
927
|
+
},
|
|
928
|
+
cabasa: {
|
|
929
|
+
label: "Cabasa",
|
|
930
|
+
gm: 69
|
|
893
931
|
}
|
|
894
932
|
};
|
|
895
933
|
const DRUM_PIECE_IDS = Object.keys(DRUM_PIECES);
|
|
@@ -918,7 +956,9 @@ const COMP_STYLES = [
|
|
|
918
956
|
"sustain",
|
|
919
957
|
"lazy",
|
|
920
958
|
"stabs",
|
|
921
|
-
"arp"
|
|
959
|
+
"arp",
|
|
960
|
+
"rolled",
|
|
961
|
+
"shells"
|
|
922
962
|
];
|
|
923
963
|
const BASS_STYLES = [
|
|
924
964
|
"roots",
|
|
@@ -930,14 +970,18 @@ const CHORD_INSTRUMENTS = [
|
|
|
930
970
|
"wurli",
|
|
931
971
|
"fm-ep",
|
|
932
972
|
"pad",
|
|
933
|
-
"harp"
|
|
973
|
+
"harp",
|
|
974
|
+
"vibes",
|
|
975
|
+
"guitar"
|
|
934
976
|
];
|
|
935
977
|
const GROOVE_IDS = [
|
|
936
978
|
"dusty",
|
|
937
979
|
"brushes",
|
|
938
980
|
"halftime",
|
|
939
981
|
"bossa",
|
|
940
|
-
"sparse"
|
|
982
|
+
"sparse",
|
|
983
|
+
"jazz-ride",
|
|
984
|
+
"lazy-kick"
|
|
941
985
|
];
|
|
942
986
|
const WINDOW_SCENES = [
|
|
943
987
|
"rain",
|
|
@@ -1412,11 +1456,20 @@ function beatStrength(beat) {
|
|
|
1412
1456
|
if (Math.abs(inBar - Math.round(inBar)) > 1e-6) return "weak";
|
|
1413
1457
|
return Math.round(inBar) % 2 === 0 ? "strong" : "medium";
|
|
1414
1458
|
}
|
|
1459
|
+
/**
|
|
1460
|
+
* The key's scale bent to fit the chord. When a chord brings in a note from outside the key (G# in
|
|
1461
|
+
* E7 over A minor), scale tones a half step from it drop out, so G and G# never both show up.
|
|
1462
|
+
* Chord tones always stay.
|
|
1463
|
+
*/
|
|
1464
|
+
function fittedScale(scale, chord) {
|
|
1465
|
+
const outside = chord.filter((pc) => !scale.includes(pc));
|
|
1466
|
+
return scale.filter((pc) => chord.includes(pc) || !outside.some((o) => mod12(pc - o) === 1 || mod12(o - pc) === 1));
|
|
1467
|
+
}
|
|
1415
1468
|
/** The pitch classes a note is allowed to land on in this context. */
|
|
1416
1469
|
function allowedPcs(ctx) {
|
|
1417
1470
|
const tonic = tonicPc(ctx.key);
|
|
1418
|
-
const scale = scalePcs(ctx.key);
|
|
1419
1471
|
const chord = chordPcs(mod12(tonic + ctx.chord.root), ctx.chord.quality);
|
|
1472
|
+
const scale = ctx.mode === "smart" ? fittedScale(scalePcs(ctx.key), chord) : scalePcs(ctx.key);
|
|
1420
1473
|
switch (ctx.mode) {
|
|
1421
1474
|
case "chromatic": return [
|
|
1422
1475
|
0,
|
|
@@ -1742,8 +1795,17 @@ function closeVoicing(order, bottom) {
|
|
|
1742
1795
|
}
|
|
1743
1796
|
return notes;
|
|
1744
1797
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1798
|
+
/**
|
|
1799
|
+
* The intervals a comping voicing uses. Rootless leaves the root to the bass, the way jazz pianists
|
|
1800
|
+
* comp, as long as at least three notes are left (plain triads keep their root).
|
|
1801
|
+
*/
|
|
1802
|
+
function voicingIntervals(quality, rootless = false) {
|
|
1803
|
+
const all = QUALITIES[quality].voicing;
|
|
1804
|
+
const without = all.filter((i) => mod12(i) !== 0);
|
|
1805
|
+
return rootless && without.length >= 3 ? without : all;
|
|
1806
|
+
}
|
|
1807
|
+
function candidateVoicings(rootPc, quality, range = VOICING_RANGE, rootless = false) {
|
|
1808
|
+
const pcs = [...new Set(voicingIntervals(quality, rootless).map((i) => mod12(rootPc + i)))];
|
|
1747
1809
|
const out = [];
|
|
1748
1810
|
for (let r = 0; r < pcs.length; r++) {
|
|
1749
1811
|
const order = [...pcs.slice(r), ...pcs.slice(0, r)];
|
|
@@ -1772,8 +1834,8 @@ function roughness(v) {
|
|
|
1772
1834
|
if (v[v.length - 1] > 74) penalty += 1.5;
|
|
1773
1835
|
return penalty;
|
|
1774
1836
|
}
|
|
1775
|
-
function voiceChord(rootPc, quality, prev, range = VOICING_RANGE) {
|
|
1776
|
-
const candidates = candidateVoicings(rootPc, quality, range);
|
|
1837
|
+
function voiceChord(rootPc, quality, prev, range = VOICING_RANGE, rootless = false) {
|
|
1838
|
+
const candidates = candidateVoicings(rootPc, quality, range, rootless);
|
|
1777
1839
|
let best = candidates[0] ?? [];
|
|
1778
1840
|
let bestCost = Infinity;
|
|
1779
1841
|
for (const v of candidates) {
|
|
@@ -1810,6 +1872,11 @@ const GROOVES = {
|
|
|
1810
1872
|
kick: "o.......o.......",
|
|
1811
1873
|
brush: "g...X..g....X.g.",
|
|
1812
1874
|
brushhat: "x...x.o.x...x.o."
|
|
1875
|
+
},
|
|
1876
|
+
turnaround: {
|
|
1877
|
+
kick: "o.......o.....o.",
|
|
1878
|
+
brush: "g...X..g....XgXg",
|
|
1879
|
+
brushhat: "x...x.o.x...x..."
|
|
1813
1880
|
}
|
|
1814
1881
|
},
|
|
1815
1882
|
halftime: {
|
|
@@ -1833,7 +1900,14 @@ const GROOVES = {
|
|
|
1833
1900
|
bar: {
|
|
1834
1901
|
kick: "x..ox..ox..ox..o",
|
|
1835
1902
|
rim: "x..x..x...x..x..",
|
|
1836
|
-
hat: "o.g.o.g.o.g.o.g."
|
|
1903
|
+
hat: "o.g.o.g.o.g.o.g.",
|
|
1904
|
+
cabasa: ".g.o.g.o.g.o.g.o"
|
|
1905
|
+
},
|
|
1906
|
+
turnaround: {
|
|
1907
|
+
kick: "x..ox..ox..ox..o",
|
|
1908
|
+
rim: "..x..x...x..x...",
|
|
1909
|
+
hat: "o.g.o.g.o.g.o.g.",
|
|
1910
|
+
cabasa: ".g.o.g.o.g.o.gxo"
|
|
1837
1911
|
}
|
|
1838
1912
|
},
|
|
1839
1913
|
sparse: {
|
|
@@ -1843,6 +1917,46 @@ const GROOVES = {
|
|
|
1843
1917
|
kick: "x.......o.......",
|
|
1844
1918
|
rim: "........o.......",
|
|
1845
1919
|
ride: "o...o...o...o..."
|
|
1920
|
+
},
|
|
1921
|
+
turnaround: {
|
|
1922
|
+
kick: "x.......o.....o.",
|
|
1923
|
+
rim: "........o...o...",
|
|
1924
|
+
ride: "o...o...o...o.o."
|
|
1925
|
+
}
|
|
1926
|
+
},
|
|
1927
|
+
"jazz-ride": {
|
|
1928
|
+
label: "Jazzy ride",
|
|
1929
|
+
kit: "sticks",
|
|
1930
|
+
bar: {
|
|
1931
|
+
kick: "g...g...g...g...",
|
|
1932
|
+
ride: "x...X..ox...X..o",
|
|
1933
|
+
hat: "....o.......o...",
|
|
1934
|
+
snare: "..........g....."
|
|
1935
|
+
},
|
|
1936
|
+
turnaround: {
|
|
1937
|
+
kick: "g...g...g...g...",
|
|
1938
|
+
ride: "x...X..ox...X...",
|
|
1939
|
+
hat: "....o.......o...",
|
|
1940
|
+
snare: "......g...g..xg."
|
|
1941
|
+
}
|
|
1942
|
+
},
|
|
1943
|
+
"lazy-kick": {
|
|
1944
|
+
label: "Lazy kick",
|
|
1945
|
+
kit: "sticks",
|
|
1946
|
+
bar: {
|
|
1947
|
+
kick: "X.....x...X..x..",
|
|
1948
|
+
snare: "....X.......X...",
|
|
1949
|
+
shaker: "xgogxgogxgogxgog"
|
|
1950
|
+
},
|
|
1951
|
+
turnaround: {
|
|
1952
|
+
kick: "X.....x...X.....",
|
|
1953
|
+
snare: "....X.......X.gg",
|
|
1954
|
+
shaker: "xgogxgogxgogx...",
|
|
1955
|
+
openhat: "..............o."
|
|
1956
|
+
},
|
|
1957
|
+
lag: {
|
|
1958
|
+
kick: .06,
|
|
1959
|
+
snare: .03
|
|
1846
1960
|
}
|
|
1847
1961
|
}
|
|
1848
1962
|
};
|
|
@@ -1866,6 +1980,12 @@ function grooveBar(id, turnaround) {
|
|
|
1866
1980
|
}
|
|
1867
1981
|
return hits.sort((a, b) => a.beat - b.beat);
|
|
1868
1982
|
}
|
|
1983
|
+
/** Pushes the band's drum hits behind the grid by the groove's lag. Run after swing. */
|
|
1984
|
+
function applyGrooveLag(events, id) {
|
|
1985
|
+
const lag = GROOVES[id].lag;
|
|
1986
|
+
if (!lag) return;
|
|
1987
|
+
for (const e of events) if (e.track === "backing:drums" && e.drum) e.time += lag[e.drum] ?? 0;
|
|
1988
|
+
}
|
|
1869
1989
|
// ../../packages/core/src/engine/backing.ts
|
|
1870
1990
|
/** Lays the progression across the scene, looping it if it's shorter than the scene. */
|
|
1871
1991
|
function placeChords(progression, key, totalBeats) {
|
|
@@ -1885,6 +2005,16 @@ function placeChords(progression, key, totalBeats) {
|
|
|
1885
2005
|
}
|
|
1886
2006
|
return out;
|
|
1887
2007
|
}
|
|
2008
|
+
/** Splits a chord into whole bars and hits each one the same way, clipped to the chord's length. */
|
|
2009
|
+
function perBar(beats, pattern) {
|
|
2010
|
+
const hits = [];
|
|
2011
|
+
for (let bar = 0; bar < beats; bar += 4) for (const h of pattern) if (bar + h.at < beats) hits.push({
|
|
2012
|
+
...h,
|
|
2013
|
+
at: bar + h.at,
|
|
2014
|
+
len: Math.min(h.len, beats - bar - h.at)
|
|
2015
|
+
});
|
|
2016
|
+
return hits;
|
|
2017
|
+
}
|
|
1888
2018
|
/** Hit times (beat offset, length) for one chord, by comping style. */
|
|
1889
2019
|
function compRhythm(style, beats) {
|
|
1890
2020
|
switch (style) {
|
|
@@ -1898,19 +2028,44 @@ function compRhythm(style, beats) {
|
|
|
1898
2028
|
len: beats - .3,
|
|
1899
2029
|
strum: .035
|
|
1900
2030
|
}];
|
|
1901
|
-
case "stabs":
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
[1.5, .4],
|
|
1906
|
-
[3, .4]
|
|
1907
|
-
]) if (bar + at < beats) hits.push({
|
|
1908
|
-
at: bar + at,
|
|
1909
|
-
len: Math.min(len, beats - bar - at),
|
|
2031
|
+
case "stabs": return perBar(beats, [
|
|
2032
|
+
{
|
|
2033
|
+
at: 0,
|
|
2034
|
+
len: .9,
|
|
1910
2035
|
strum: .01
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
|
|
2036
|
+
},
|
|
2037
|
+
{
|
|
2038
|
+
at: 1.5,
|
|
2039
|
+
len: .4,
|
|
2040
|
+
strum: .01
|
|
2041
|
+
},
|
|
2042
|
+
{
|
|
2043
|
+
at: 3,
|
|
2044
|
+
len: .4,
|
|
2045
|
+
strum: .01
|
|
2046
|
+
}
|
|
2047
|
+
]);
|
|
2048
|
+
case "rolled": return perBar(beats, [{
|
|
2049
|
+
at: 0,
|
|
2050
|
+
len: 2.4,
|
|
2051
|
+
strum: .08
|
|
2052
|
+
}, {
|
|
2053
|
+
at: 2.5,
|
|
2054
|
+
len: 1.5,
|
|
2055
|
+
strum: .07,
|
|
2056
|
+
vel: .4
|
|
2057
|
+
}]);
|
|
2058
|
+
case "shells": return perBar(beats, [{
|
|
2059
|
+
at: 0,
|
|
2060
|
+
len: 1.2,
|
|
2061
|
+
strum: .012,
|
|
2062
|
+
vel: .52
|
|
2063
|
+
}, {
|
|
2064
|
+
at: 1.5,
|
|
2065
|
+
len: 2.2,
|
|
2066
|
+
strum: .012,
|
|
2067
|
+
vel: .44
|
|
2068
|
+
}]);
|
|
1914
2069
|
case "arp": return [];
|
|
1915
2070
|
}
|
|
1916
2071
|
}
|
|
@@ -1923,7 +2078,7 @@ function compChords(placed, backing, carry) {
|
|
|
1923
2078
|
brightness: .6
|
|
1924
2079
|
};
|
|
1925
2080
|
for (const p of placed) {
|
|
1926
|
-
const voicing = voiceChord(p.rootPc, p.chord.quality, carry.voicing);
|
|
2081
|
+
const voicing = voiceChord(p.rootPc, p.chord.quality, carry.voicing, VOICING_RANGE, backing.style === "shells");
|
|
1927
2082
|
carry.voicing = voicing;
|
|
1928
2083
|
if (backing.style === "arp") {
|
|
1929
2084
|
const order = [...voicing, ...voicing.slice(1, -1).reverse()];
|
|
@@ -1943,7 +2098,7 @@ function compChords(placed, backing, carry) {
|
|
|
1943
2098
|
time: p.start + at,
|
|
1944
2099
|
duration: Math.max(.1, hit.len - i * hit.strum),
|
|
1945
2100
|
midi,
|
|
1946
|
-
velocity: .5 - i * .03
|
|
2101
|
+
velocity: (hit.vel ?? .5) - i * .03
|
|
1947
2102
|
});
|
|
1948
2103
|
});
|
|
1949
2104
|
}
|
|
@@ -2131,6 +2286,7 @@ function sceneEvents(project, scene, { offset = 0, pass = 0, carry = newCarry()
|
|
|
2131
2286
|
const kept = events.filter((e) => e.time < totalBeats);
|
|
2132
2287
|
for (const e of kept) e.duration = Math.min(e.duration, totalBeats - e.time);
|
|
2133
2288
|
applySwing(kept, project.swing);
|
|
2289
|
+
if (backing && scene.backing.drums.enabled) applyGrooveLag(kept, scene.backing.drums.groove);
|
|
2134
2290
|
applyHumanize(kept, project.humanize, hashSeed(project.seed, scene.id), pass);
|
|
2135
2291
|
for (const e of kept) e.time += offset;
|
|
2136
2292
|
const flats = prefersFlats(project.key);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as DRUM_PIECE_IDS, T as INSTRUMENTS, i as buildSceneScore, p as createRng, r as buildArrangementScore } from "./src-
|
|
1
|
+
import { S as DRUM_PIECE_IDS, T as INSTRUMENTS, i as buildSceneScore, p as createRng, r as buildArrangementScore } from "./src-Bz-0ulVm.mjs";
|
|
2
2
|
import * as Tone from "tone";
|
|
3
3
|
// ../../packages/core/src/engine/types.ts
|
|
4
4
|
function beatsToSeconds(beats, bpm) {
|
|
@@ -44,8 +44,13 @@ const INSTRUMENT_TRIM_DB = {
|
|
|
44
44
|
glock: -9,
|
|
45
45
|
marimba: -2,
|
|
46
46
|
harp: -3,
|
|
47
|
+
vibes: -9,
|
|
48
|
+
kalimba: -3,
|
|
49
|
+
guitar: -9,
|
|
47
50
|
pad: -11,
|
|
48
51
|
pluck: -6,
|
|
52
|
+
celesta: -4.5,
|
|
53
|
+
musicbox: -3,
|
|
49
54
|
bass: -19,
|
|
50
55
|
drums: 0
|
|
51
56
|
};
|
|
@@ -66,7 +71,9 @@ const DRUM_TRIM_DB = {
|
|
|
66
71
|
brushhat: 2,
|
|
67
72
|
openhat: -12,
|
|
68
73
|
ride: -10,
|
|
69
|
-
tom: -4
|
|
74
|
+
tom: -4,
|
|
75
|
+
shaker: -3,
|
|
76
|
+
cabasa: -10
|
|
70
77
|
};
|
|
71
78
|
/** Reverb return level at reverb = 1. */
|
|
72
79
|
const REVERB_RETURN = .55;
|
|
@@ -172,6 +179,46 @@ function synthVoice(instrument) {
|
|
|
172
179
|
});
|
|
173
180
|
filter.Q.value = .5;
|
|
174
181
|
break;
|
|
182
|
+
case "celesta":
|
|
183
|
+
synth = new Tone.PolySynth(Tone.FMSynth, {
|
|
184
|
+
harmonicity: 1,
|
|
185
|
+
modulationIndex: 16,
|
|
186
|
+
oscillator: { type: "sine" },
|
|
187
|
+
modulation: { type: "sine" },
|
|
188
|
+
envelope: {
|
|
189
|
+
attack: .002,
|
|
190
|
+
decay: 2.2,
|
|
191
|
+
sustain: 0,
|
|
192
|
+
release: 1.2
|
|
193
|
+
},
|
|
194
|
+
modulationEnvelope: {
|
|
195
|
+
attack: .001,
|
|
196
|
+
decay: .35,
|
|
197
|
+
sustain: .01,
|
|
198
|
+
release: .5
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
break;
|
|
202
|
+
case "musicbox":
|
|
203
|
+
synth = new Tone.PolySynth(Tone.FMSynth, {
|
|
204
|
+
harmonicity: 6.27,
|
|
205
|
+
modulationIndex: 24,
|
|
206
|
+
oscillator: { type: "sine" },
|
|
207
|
+
modulation: { type: "sine" },
|
|
208
|
+
envelope: {
|
|
209
|
+
attack: .001,
|
|
210
|
+
decay: 1.5,
|
|
211
|
+
sustain: 0,
|
|
212
|
+
release: .7
|
|
213
|
+
},
|
|
214
|
+
modulationEnvelope: {
|
|
215
|
+
attack: .001,
|
|
216
|
+
decay: .5,
|
|
217
|
+
sustain: 0,
|
|
218
|
+
release: .4
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
break;
|
|
175
222
|
case "pluck":
|
|
176
223
|
synth = new Tone.PolySynth(Tone.Synth, {
|
|
177
224
|
oscillator: { type: "triangle" },
|
|
@@ -550,7 +597,17 @@ function loopScore(score, loops) {
|
|
|
550
597
|
function scoreFor(project, opts = {}) {
|
|
551
598
|
return opts.scope === "song" ? buildArrangementScore(project) : loopScore(buildSceneScore(project, opts.sceneId), opts.loops ?? 1);
|
|
552
599
|
}
|
|
553
|
-
|
|
600
|
+
/**
|
|
601
|
+
* Tone.Offline swaps Tone's global context while it runs, so two overlapping renders would build
|
|
602
|
+
* nodes on each other's context. Renders wait their turn instead.
|
|
603
|
+
*/
|
|
604
|
+
let queue = Promise.resolve();
|
|
605
|
+
function renderProject(project, bank, opts = {}) {
|
|
606
|
+
const run = queue.then(() => renderNow(project, bank, opts));
|
|
607
|
+
queue = run.catch(() => {});
|
|
608
|
+
return run;
|
|
609
|
+
}
|
|
610
|
+
async function renderNow(project, bank, opts) {
|
|
554
611
|
const score = scoreFor(project, opts);
|
|
555
612
|
const sampleRate = opts.sampleRate ?? 44100;
|
|
556
613
|
const seconds = LEAD_IN + beatsToSeconds(score.beats, score.bpm) + (opts.tail ?? 3);
|