smplr 0.24.0 → 0.26.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/dist/index.mjs CHANGED
@@ -318,7 +318,7 @@ function pickPlaybackParams(obj) {
318
318
  return result;
319
319
  }
320
320
  function resolveParams(defaults, group, region, midi, velocity, overrides) {
321
- var _a, _b, _c, _d, _e;
321
+ var _a, _b, _c, _d, _e, _f;
322
322
  const merged = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, PARAM_DEFAULTS), defaults), pickPlaybackParams(group)), pickPlaybackParams(region));
323
323
  const pitch = (_b = (_a = region.pitch) != null ? _a : region.key) != null ? _b : midi;
324
324
  const semitones = midi - pitch;
@@ -337,7 +337,7 @@ function resolveParams(defaults, group, region, midi, velocity, overrides) {
337
337
  loopEnd: merged.loopEnd,
338
338
  ampVelCurve: region.ampVelCurve,
339
339
  loopAuto: region.loopAuto,
340
- reverse: overrides == null ? void 0 : overrides.reverse
340
+ reverse: (_f = overrides == null ? void 0 : overrides.reverse) != null ? _f : merged.reverse
341
341
  };
342
342
  }
343
343
 
@@ -515,14 +515,6 @@ var SampleLoaderImpl = class {
515
515
  __privateSet(this, _context, context);
516
516
  __privateSet(this, _storage, (_a = options == null ? void 0 : options.storage) != null ? _a : HttpStorage);
517
517
  }
518
- /**
519
- * Load all samples referenced in `json`. Returns a Map of sample name →
520
- * AudioBuffer. Progress is reported via `onProgress` callback or via
521
- * options object.
522
- *
523
- * - `buffers` in options: pre-loaded buffers — skips fetch for these names.
524
- * - All samples load in parallel. Failed samples are silently omitted.
525
- */
526
518
  load(json, onProgressOrOptions) {
527
519
  return __async(this, null, function* () {
528
520
  var _a, _b;
@@ -642,14 +634,6 @@ var SchedulerImpl = class {
642
634
  __privateSet(this, _intervalMs, (_b = options == null ? void 0 : options.intervalMs) != null ? _b : INTERVAL_MS_DEFAULT);
643
635
  __privateSet(this, _queue, new SortedQueue((a, b) => a.time - b.time));
644
636
  }
645
- /**
646
- * Schedule a callback for a NoteEvent.
647
- *
648
- * - If the event's time falls within the lookahead window (or has no time), the
649
- * callback is called synchronously and a no-op StopFn is returned.
650
- * - Otherwise the event is queued, the interval is started if needed, and a StopFn
651
- * is returned that removes the event from the queue before it is dispatched.
652
- */
653
637
  schedule(event, callback) {
654
638
  var _a;
655
639
  const now = __privateGet(this, _context2).currentTime;
@@ -665,10 +649,6 @@ var SchedulerImpl = class {
665
649
  __privateGet(this, _queue).removeAll((q) => q === item);
666
650
  };
667
651
  }
668
- /**
669
- * Clear all queued (not-yet-dispatched) events and stop the interval.
670
- * Does not affect voices that are already playing.
671
- */
672
652
  stop() {
673
653
  __privateGet(this, _queue).clear();
674
654
  if (__privateGet(this, _intervalId) !== void 0) {
@@ -941,10 +921,12 @@ var SmplrImpl = class {
941
921
  __privateSet(this, _voices2, new VoiceManager());
942
922
  this.loader = (_c = options == null ? void 0 : options.loader) != null ? _c : SampleLoader(context, { storage: options == null ? void 0 : options.storage });
943
923
  if (json) {
944
- this.ready = this.loader.load(json, (loaded, total) => {
945
- var _a2;
946
- __privateSet(this, _loadProgress, { loaded, total });
947
- (_a2 = __privateGet(this, _onLoadProgress)) == null ? void 0 : _a2.call(this, { loaded, total });
924
+ this.ready = this.loader.load(json, {
925
+ onProgress: (loaded, total) => {
926
+ var _a2;
927
+ __privateSet(this, _loadProgress, { loaded, total });
928
+ (_a2 = __privateGet(this, _onLoadProgress)) == null ? void 0 : _a2.call(this, { loaded, total });
929
+ }
948
930
  }).then((buffers) => {
949
931
  __privateSet(this, _buffers, buffers);
950
932
  });
@@ -1400,6 +1382,465 @@ function drumMachineToPreset(instrument) {
1400
1382
  };
1401
1383
  }
1402
1384
 
1385
+ // src/drum-abuse/index.ts
1386
+ var DEFAULT_BASE_URL = "https://smpldsnds.github.io";
1387
+ var MIDI_BASE = 36;
1388
+ var DRUM_ABUSE_PACKS = [
1389
+ "vol1",
1390
+ "vol2",
1391
+ "vol3",
1392
+ "vol4",
1393
+ "vol5"
1394
+ ];
1395
+ var MACHINES_BY_PACK = {
1396
+ vol1: [
1397
+ "4-inthefloor-percussioncombo",
1398
+ "ace-tone-rhythm-ace-fr-1",
1399
+ "ace-tone-rhythm-ace-fr-7l",
1400
+ "ace-tone-rhythm-ace-fr6",
1401
+ "ace-tone-rhythm-king",
1402
+ "ace-tone-rhythm-master",
1403
+ "antonelli-2377",
1404
+ "arp-axxe",
1405
+ "conn-min-o-matic",
1406
+ "eko-compu-rhythm",
1407
+ "eko-ritmo-12",
1408
+ "eko-ritmo-20",
1409
+ "elgam-carousel",
1410
+ "emu-modular",
1411
+ "farfisa-pro",
1412
+ "farfisa-rhythm-10",
1413
+ "farfisa-rhythm-maker-16",
1414
+ "gibson-maestro-g-2",
1415
+ "gibson-maestro-rhythm-jester",
1416
+ "gibson-maestro-rhythm-king-mrk-1",
1417
+ "gulbransen-organ",
1418
+ "hammond-rhythm",
1419
+ "hammond-rhythm-ii",
1420
+ "hohner-automatic-rhythm-player",
1421
+ "jen-sx-1000",
1422
+ "kay-r-8",
1423
+ "keio-checkmate",
1424
+ "kent-k-200",
1425
+ "kent-rhythm-master",
1426
+ "korg-kr-33",
1427
+ "korg-krz",
1428
+ "korg-minipops-series",
1429
+ "korg-s-3",
1430
+ "korg-univox-micro-rhythmer-12",
1431
+ "korg-univox-sr-120",
1432
+ "korg-univox-sr-95",
1433
+ "luxor-passat",
1434
+ "moog-modular-55",
1435
+ "roland-arr",
1436
+ "roland-edp-1",
1437
+ "roland-sh-3a",
1438
+ "roland-system-100",
1439
+ "roland-tr-1-prototype",
1440
+ "roland-tr-33",
1441
+ "roland-tr-41-prototype",
1442
+ "roland-tr-66",
1443
+ "roland-tr-77",
1444
+ "seeburg-rhythm-prince",
1445
+ "seeburg-select-a-rhythm",
1446
+ "solton-disco-64",
1447
+ "sonor-mini-mammut-module",
1448
+ "video-tech-rythmic-10",
1449
+ "vox-percussion-king",
1450
+ "whippany-melo-sonic-350",
1451
+ "wurlitzer-swinging-rhythm",
1452
+ "yamaha-cs-15d",
1453
+ "yamaha-cs-5",
1454
+ "yamaha-cs-6",
1455
+ "yamaha-ps-1",
1456
+ "yamaha-ps-2",
1457
+ "yamaha-ps-3"
1458
+ ],
1459
+ vol2: [
1460
+ "bontempi-hf222",
1461
+ "boss-dr-55",
1462
+ "casio-mt-18",
1463
+ "casio-pt-30",
1464
+ "casio-vl-1",
1465
+ "chaser-computer-drum-pr-80",
1466
+ "crb-rhythmboy-480",
1467
+ "eko-musicbox-12",
1468
+ "electro-harmonix-drm-15",
1469
+ "electro-harmonix-drm-16",
1470
+ "electro-harmonix-spacedrum",
1471
+ "elka-drumstar-80",
1472
+ "elka-x-1000",
1473
+ "emu-e-drum",
1474
+ "gem-drum-15",
1475
+ "hammond-autovari-64",
1476
+ "hohner-rhythm-80k",
1477
+ "korg-kpr-77",
1478
+ "korg-kr-55",
1479
+ "korg-kr-mini",
1480
+ "korg-monopoly",
1481
+ "korg-ms-10",
1482
+ "korg-trident",
1483
+ "linn-lm-1",
1484
+ "monacor-rhythmical-choice",
1485
+ "mti-auto-orchestra-ao-1",
1486
+ "multi-moog",
1487
+ "mxr-185",
1488
+ "new-england-digital-synclavier",
1489
+ "oberheim-dmx",
1490
+ "pearl-drum-x",
1491
+ "pollard-syndrum-178",
1492
+ "roland-cr-1000",
1493
+ "roland-cr-68",
1494
+ "roland-cr-78",
1495
+ "roland-cr-80",
1496
+ "roland-cr-8000",
1497
+ "roland-dr-55",
1498
+ "roland-jupiter-8",
1499
+ "roland-pb-300-rhythm-plus",
1500
+ "roland-rhy-33",
1501
+ "roland-rhy-55",
1502
+ "roland-sh-09",
1503
+ "roland-tr-55",
1504
+ "roland-tr-606",
1505
+ "roland-tr-808",
1506
+ "simmons-drum",
1507
+ "simmons-sds-1",
1508
+ "simmons-sds-5",
1509
+ "solton-programmer-24",
1510
+ "star-instruments-synare-3",
1511
+ "star-instruments-synare-ps-1",
1512
+ "visco-space-drum",
1513
+ "watford-electronics-rhythm-generator",
1514
+ "yamaha-cs-40m",
1515
+ "yamaha-mr-10",
1516
+ "yamaha-ps-55"
1517
+ ],
1518
+ vol3: [
1519
+ "amdek-pck-100",
1520
+ "austin-arb-6",
1521
+ "bme-rattlesnake-parametric-percussion-system",
1522
+ "boss-dr-110",
1523
+ "casio-mt-100",
1524
+ "coron-drumsynce-ds-7",
1525
+ "coron-rds",
1526
+ "denon-crb-90",
1527
+ "drumfire-df-2000",
1528
+ "drumfire-df-500",
1529
+ "electro-harmonix-drm-32",
1530
+ "emu-drumulator",
1531
+ "kay-drm-1",
1532
+ "korg-ddm-110",
1533
+ "korg-ddm-220",
1534
+ "korg-poly-800",
1535
+ "linn-linndrum-lm-1-vinyl",
1536
+ "linn-lm-2",
1537
+ "mattel-electronics-synsonics-drm",
1538
+ "mattel-electronics-synsonics-pro",
1539
+ "panasonic-rd-9844",
1540
+ "pearl-drx-1",
1541
+ "roland-ddr-30",
1542
+ "roland-mc-202",
1543
+ "roland-rhy-77",
1544
+ "roland-tr-909",
1545
+ "rsf-dd-30",
1546
+ "sakata-dpm-48",
1547
+ "sequential-circuits-drumtraks",
1548
+ "simmons-clap-trap",
1549
+ "simmons-sds-200",
1550
+ "simmons-sds-400",
1551
+ "soundmaster-sm-8",
1552
+ "soundmaster-sr-88",
1553
+ "tama-ts-206",
1554
+ "tama-ts-305",
1555
+ "wersi-wm-24",
1556
+ "yamaha-dx7"
1557
+ ],
1558
+ vol4: [
1559
+ "atlantex-mpc-1",
1560
+ "boss-hc-2",
1561
+ "boss-pc-2",
1562
+ "casio-ct-310",
1563
+ "casio-mt-500",
1564
+ "casio-mt-800",
1565
+ "casio-pt-68",
1566
+ "casio-pt-82",
1567
+ "casio-sk-1",
1568
+ "dr-b-hm-digital-drums",
1569
+ "emu-sp-12",
1570
+ "ensoniq-mirage",
1571
+ "hing-hon-ek-001",
1572
+ "kawai-acr-20",
1573
+ "kawai-sx-240",
1574
+ "klone-dual-percussion-synthesiser",
1575
+ "korg-ddd-1",
1576
+ "korg-pss-50",
1577
+ "kurzweil-electrodrum-prototype",
1578
+ "linn-9000",
1579
+ "linn-linndrum-lm-2-vinyl",
1580
+ "nasta-hitstix-2",
1581
+ "oberheim-dx",
1582
+ "pearl-sc-40",
1583
+ "rhodes-polaris",
1584
+ "roland-juno-106",
1585
+ "roland-super-quartet-mks-7",
1586
+ "roland-tr-707",
1587
+ "roland-tr-727",
1588
+ "siel-mdp-40",
1589
+ "simmons-sds-1000",
1590
+ "simmons-sds-7",
1591
+ "simmons-sds-8",
1592
+ "simmons-sds-9",
1593
+ "sony-drp-1",
1594
+ "soundmaster-stix-st-305",
1595
+ "suzuki-rpm-40",
1596
+ "tama-ts-500",
1597
+ "technics-ax-5",
1598
+ "technics-pcm-dp-50",
1599
+ "wersi-prisma-dx-5",
1600
+ "yamaha-dd-5",
1601
+ "yamaha-rx-11",
1602
+ "yamaha-rx-15",
1603
+ "yamaha-rx-21",
1604
+ "yamaha-rx-5"
1605
+ ],
1606
+ vol5: [
1607
+ "boss-dr-pad-drp-i",
1608
+ "casio-ct-403",
1609
+ "casio-cz-230s",
1610
+ "casio-ht-700",
1611
+ "casio-rz-1",
1612
+ "cheetah-spec-drum",
1613
+ "forat-f-9000",
1614
+ "korg-ddd-5",
1615
+ "korg-dss-1",
1616
+ "m-p-c-electronics-dsm-1",
1617
+ "pearl-sy-1",
1618
+ "roland-tr-505",
1619
+ "sequential-circuits-studio-440",
1620
+ "simmons-sds-2000",
1621
+ "simmons-sdx",
1622
+ "yamaha-pss-130",
1623
+ "yamaha-ptx8",
1624
+ "yamaha-rx-21l"
1625
+ ]
1626
+ };
1627
+ var machineToPack = (() => {
1628
+ const m = /* @__PURE__ */ new Map();
1629
+ for (const pack of DRUM_ABUSE_PACKS) {
1630
+ for (const id of MACHINES_BY_PACK[pack]) m.set(id, pack);
1631
+ }
1632
+ return m;
1633
+ })();
1634
+ function getDrumAbuseMachineNames() {
1635
+ return [...machineToPack.keys()];
1636
+ }
1637
+ function getDrumAbuseMachinesForPack(pack) {
1638
+ return MACHINES_BY_PACK[pack];
1639
+ }
1640
+ function getDrumAbusePackNames() {
1641
+ return DRUM_ABUSE_PACKS;
1642
+ }
1643
+ function getDrumAbuseMachinePack(id) {
1644
+ return machineToPack.get(id);
1645
+ }
1646
+ var encSeg = (s) => s.split("/").map(encodeURIComponent).join("/");
1647
+ function packBase(baseUrl, pack) {
1648
+ return `${baseUrl}/drum-abuse-${pack}`;
1649
+ }
1650
+ function sampleBaseUrl(baseUrl, pack, urlPath) {
1651
+ return `${packBase(baseUrl, pack)}/samples/${encSeg(urlPath)}/`;
1652
+ }
1653
+ function drumAbuseSampleUrl(pack, urlPath, fileNoExt, format = "wav", baseUrl = DEFAULT_BASE_URL) {
1654
+ return `${sampleBaseUrl(baseUrl, pack, urlPath)}${encodeURIComponent(fileNoExt)}.${format}`;
1655
+ }
1656
+ function stripExt(filename) {
1657
+ return filename.replace(/\.[^.]+$/, "");
1658
+ }
1659
+ var jsonCache = /* @__PURE__ */ new Map();
1660
+ function fetchJSON(url, storage) {
1661
+ let p = jsonCache.get(url);
1662
+ if (!p) {
1663
+ p = storage.fetch(url).then((r) => {
1664
+ if (r.status >= 400) throw new Error(`DrumAbuse: ${r.status} ${url}`);
1665
+ return r.json();
1666
+ });
1667
+ jsonCache.set(url, p);
1668
+ }
1669
+ return p;
1670
+ }
1671
+ function buildMachinePreset(machine, setPath, baseUrl, pack) {
1672
+ if (machine.sample_sets.length === 0) {
1673
+ throw new Error(`DrumAbuse: machine "${machine.id}" has no sample sets`);
1674
+ }
1675
+ const set = setPath ? machine.sample_sets.find((s) => s.path === setPath) : machine.sample_sets[0];
1676
+ if (!set) {
1677
+ throw new Error(`DrumAbuse: set "${setPath}" not found on "${machine.id}"`);
1678
+ }
1679
+ if (set.samples.length === 0) {
1680
+ throw new Error(
1681
+ `DrumAbuse: set "${set.path}" of "${machine.id}" has no samples`
1682
+ );
1683
+ }
1684
+ const sampleNames = [];
1685
+ const groupNames = [];
1686
+ const sampleNamesForGroup = {};
1687
+ const aliases = {};
1688
+ const regions = set.samples.map((file, i) => {
1689
+ const key = stripExt(file);
1690
+ const midi = MIDI_BASE + i;
1691
+ sampleNames.push(key);
1692
+ aliases[key] = midi;
1693
+ const group = set.sample_instruments[i] || "";
1694
+ if (group) {
1695
+ if (!sampleNamesForGroup[group]) {
1696
+ sampleNamesForGroup[group] = [];
1697
+ groupNames.push(group);
1698
+ aliases[group] = midi;
1699
+ }
1700
+ sampleNamesForGroup[group].push(key);
1701
+ }
1702
+ return {
1703
+ sample: key,
1704
+ keyRange: [midi, midi],
1705
+ pitch: midi
1706
+ };
1707
+ });
1708
+ return {
1709
+ preset: {
1710
+ samples: {
1711
+ baseUrl: sampleBaseUrl(baseUrl, pack, set.url_path),
1712
+ formats: ["wav"]
1713
+ },
1714
+ groups: [{ regions }],
1715
+ aliases
1716
+ },
1717
+ sampleNames,
1718
+ groupNames,
1719
+ sampleNamesForGroup,
1720
+ setPath: set.path
1721
+ };
1722
+ }
1723
+ function buildPackPreset(list, baseUrl, pack) {
1724
+ var _a;
1725
+ if (list.length === 0) {
1726
+ throw new Error(`DrumAbuse: empty pack-instrument list for pack "${pack}"`);
1727
+ }
1728
+ const fileCount = {};
1729
+ for (const s of list) {
1730
+ const f = stripExt(s.file);
1731
+ fileCount[f] = ((_a = fileCount[f]) != null ? _a : 0) + 1;
1732
+ }
1733
+ const sampleNames = [];
1734
+ const groupNames = [];
1735
+ const sampleNamesForGroup = {};
1736
+ const map = {};
1737
+ const aliases = {};
1738
+ const regions = list.map((s, i) => {
1739
+ const fileKey = stripExt(s.file);
1740
+ const uniqueKey = `${s.machine_id}/${fileKey}`;
1741
+ const midi = MIDI_BASE + i;
1742
+ sampleNames.push(uniqueKey);
1743
+ aliases[uniqueKey] = midi;
1744
+ if (fileCount[fileKey] === 1) aliases[fileKey] = midi;
1745
+ map[uniqueKey] = `${packBase(baseUrl, pack)}/samples/${encSeg(s.url_path)}/${encodeURIComponent(s.file)}`;
1746
+ if (!sampleNamesForGroup[s.machine_id]) {
1747
+ sampleNamesForGroup[s.machine_id] = [];
1748
+ groupNames.push(s.machine_id);
1749
+ aliases[s.machine_id] = midi;
1750
+ }
1751
+ sampleNamesForGroup[s.machine_id].push(uniqueKey);
1752
+ return {
1753
+ sample: uniqueKey,
1754
+ keyRange: [midi, midi],
1755
+ pitch: midi
1756
+ };
1757
+ });
1758
+ return {
1759
+ preset: {
1760
+ samples: { baseUrl: "", formats: ["wav"], map },
1761
+ groups: [{ regions }],
1762
+ aliases
1763
+ },
1764
+ sampleNames,
1765
+ groupNames,
1766
+ sampleNamesForGroup,
1767
+ setPath: null
1768
+ };
1769
+ }
1770
+ var DrumAbuse = Instrument(
1771
+ (_ctx, options = {}, smplr) => {
1772
+ var _a, _b;
1773
+ const source = options.source;
1774
+ if (!source) {
1775
+ throw new Error("DrumAbuse: options.source is required");
1776
+ }
1777
+ const baseUrl = (_a = options.baseUrl) != null ? _a : DEFAULT_BASE_URL;
1778
+ const storage = (_b = options.storage) != null ? _b : HttpStorage;
1779
+ let sampleNames = [];
1780
+ let groupNames = [];
1781
+ let sampleNamesForGroup = {};
1782
+ let machineId = null;
1783
+ let setPath = null;
1784
+ let packId;
1785
+ let mode;
1786
+ let presetPromise;
1787
+ if (source.kind === "machine") {
1788
+ mode = "machine";
1789
+ const pack = getDrumAbuseMachinePack(source.machine);
1790
+ if (!pack) {
1791
+ throw new Error(`DrumAbuse: unknown machine "${source.machine}"`);
1792
+ }
1793
+ packId = pack;
1794
+ machineId = source.machine;
1795
+ const url = `${packBase(baseUrl, pack)}/machines/${encodeURIComponent(source.machine)}.json`;
1796
+ presetPromise = fetchJSON(url, storage).then((machine) => {
1797
+ const built = buildMachinePreset(machine, source.set, baseUrl, pack);
1798
+ sampleNames = built.sampleNames;
1799
+ groupNames = built.groupNames;
1800
+ sampleNamesForGroup = built.sampleNamesForGroup;
1801
+ setPath = built.setPath;
1802
+ return built.preset;
1803
+ });
1804
+ } else {
1805
+ mode = "pack";
1806
+ if (!DRUM_ABUSE_PACKS.includes(source.pack)) {
1807
+ throw new Error(`DrumAbuse: unknown pack "${source.pack}"`);
1808
+ }
1809
+ packId = source.pack;
1810
+ const url = `${packBase(baseUrl, source.pack)}/instruments/${encodeURIComponent(source.instrument)}.json`;
1811
+ presetPromise = fetchJSON(url, storage).then((list) => {
1812
+ const built = buildPackPreset(list, baseUrl, source.pack);
1813
+ sampleNames = built.sampleNames;
1814
+ groupNames = built.groupNames;
1815
+ sampleNamesForGroup = built.sampleNamesForGroup;
1816
+ return built.preset;
1817
+ });
1818
+ }
1819
+ const baseStart = smplr.start.bind(smplr);
1820
+ const extras = {
1821
+ get mode() {
1822
+ return mode;
1823
+ },
1824
+ getSampleNames: () => sampleNames.slice(),
1825
+ getGroupNames: () => groupNames.slice(),
1826
+ getSampleNamesForGroup: (g) => {
1827
+ var _a2;
1828
+ return ((_a2 = sampleNamesForGroup[g]) != null ? _a2 : []).slice();
1829
+ },
1830
+ getMachineId: () => machineId,
1831
+ getSetPath: () => setPath,
1832
+ getPackId: () => packId,
1833
+ start: (event) => {
1834
+ var _a2;
1835
+ const ev = typeof event === "object" ? event : { note: event };
1836
+ return baseStart(__spreadProps(__spreadValues({}, ev), { stopId: (_a2 = ev.stopId) != null ? _a2 : ev.note }));
1837
+ }
1838
+ };
1839
+ const ready = presetPromise.then((preset) => smplr.loadInstrument(preset));
1840
+ return { extras, ready };
1841
+ }
1842
+ );
1843
+
1403
1844
  // src/offline/wav-encoder.ts
1404
1845
  function audioBufferToWav(buffer) {
1405
1846
  return encodeWav(buffer, 32);
@@ -1693,6 +2134,11 @@ var TransportClock = class {
1693
2134
  *
1694
2135
  * Any checkpoints at or after `audioTime` are removed and replaced with this
1695
2136
  * new anchor, preserving the BPM that was in effect at that time.
2137
+ *
2138
+ * @internal Leaks checkpoint-list semantics; only called by `Sequencer._flush`
2139
+ * for loop-boundary re-anchoring. Not safe to share between Sequencers — see
2140
+ * thoughts/research/2026-05-17_20-18-43_shared-transport.md §3. Must not be
2141
+ * exported from any barrel.
1696
2142
  */
1697
2143
  seekAt(tick, audioTime) {
1698
2144
  let bpm = this._bpm;
@@ -1746,9 +2192,13 @@ var TransportClock = class {
1746
2192
  return cp.tick + (audioTime - cp.audioTime) / this._secondsPerTick(cp.bpm);
1747
2193
  }
1748
2194
  /**
1749
- * Duration in seconds for a given number of ticks at the current BPM.
2195
+ * Convert a tick count to seconds at the current (snapshot) BPM.
2196
+ *
2197
+ * Uses `this._bpm` directly, not the checkpoint history — so after a
2198
+ * mid-play BPM change this returns a value based on the *latest* BPM only.
2199
+ * For checkpoint-aware durations, use `tickToAudioTime(end) - tickToAudioTime(start)`.
1750
2200
  */
1751
- tickDuration(ticks) {
2201
+ ticksToSeconds(ticks) {
1752
2202
  return ticks * this._secondsPerTick(this._bpm);
1753
2203
  }
1754
2204
  // ---------------------------------------------------------------------------
@@ -2035,6 +2485,7 @@ var SequencerImpl = class {
2035
2485
  this._clock.stop();
2036
2486
  this._stopLoop();
2037
2487
  this._endScheduled = false;
2488
+ for (const stopFn of this._activeVoices.values()) stopFn();
2038
2489
  this._activeVoices.clear();
2039
2490
  this._emitStateChange("stopped");
2040
2491
  return this;
@@ -2301,7 +2752,7 @@ var SequencerImpl = class {
2301
2752
  if (Math.random() * 100 >= note.chance) continue;
2302
2753
  }
2303
2754
  const audioTime = this._clock.tickToAudioTime(noteTick);
2304
- const durationSec = note.duration !== void 0 ? this._clock.tickDuration(
2755
+ const durationSec = note.duration !== void 0 ? this._clock.ticksToSeconds(
2305
2756
  parseTicks(note.duration, this._ppq, this._timeSignature)
2306
2757
  ) : void 0;
2307
2758
  const timingOffset = trackHumanize.timing ? (Math.random() * 2 - 1) * trackHumanize.timing / 1e3 : 0;
@@ -2672,8 +3123,10 @@ function createTremolo(context, depth) {
2672
3123
  splitter.disconnect(ampR, 1);
2673
3124
  ampL.disconnect(merger, 0, 0);
2674
3125
  ampR.disconnect(merger, 0, 1);
2675
- lfoL.disconnect(ampL);
2676
- lfoR.disconnect(ampR);
3126
+ lfoL.disconnect(lfoLAmp);
3127
+ lfoLAmp.disconnect(ampL.gain);
3128
+ lfoR.disconnect(lfoRAmp);
3129
+ lfoRAmp.disconnect(ampR.gain);
2677
3130
  merger.disconnect(output);
2678
3131
  };
2679
3132
  return { input, output };
@@ -2744,15 +3197,11 @@ var ElectricPiano = Instrument(
2744
3197
 
2745
3198
  // src/versilian.ts
2746
3199
  var VCSL_BASE_URL = "https://smpldsnds.github.io/sgossner-vcsl";
2747
- var instruments = [];
3200
+ var instrumentsPromise;
2748
3201
  function getVersilianInstruments() {
2749
- return __async(this, null, function* () {
2750
- if (instruments.length) return instruments;
2751
- instruments = yield fetch(VCSL_BASE_URL + "/sfz_files.json").then(
2752
- (res) => res.json()
2753
- );
2754
- return instruments;
2755
- });
3202
+ return instrumentsPromise != null ? instrumentsPromise : instrumentsPromise = fetch(
3203
+ VCSL_BASE_URL + "/sfz_files.json"
3204
+ ).then((res) => res.json());
2756
3205
  }
2757
3206
  var Versilian = Instrument(
2758
3207
  (ctx, options = {}, smplr) => loadVersilianInstrument(smplr, options)
@@ -2762,11 +3211,11 @@ function loadVersilianInstrument(smplr, options) {
2762
3211
  const instrument = (_a = options.instrument) != null ? _a : "Strings/Violin/Violin - Arco";
2763
3212
  const sfzUrl = `${VCSL_BASE_URL}/${instrument}.sfz`;
2764
3213
  const base = instrument.slice(0, instrument.lastIndexOf("/") + 1);
2765
- const sampleBaseUrl = `${VCSL_BASE_URL}/${base}`;
3214
+ const sampleBaseUrl2 = `${VCSL_BASE_URL}/${base}`;
2766
3215
  return fetch(sfzUrl).then((r) => r.text()).then(
2767
3216
  (sfzText) => smplr.loadInstrument(
2768
3217
  sfzToPreset(sfzText, {
2769
- baseUrl: sampleBaseUrl,
3218
+ baseUrl: sampleBaseUrl2,
2770
3219
  pathFromSampleName: (name) => name.replace(/\.wav$/i, ""),
2771
3220
  formats: ["ogg", "m4a"]
2772
3221
  })
@@ -2884,7 +3333,7 @@ function mellotronToPreset(sampleNames, config) {
2884
3333
  for (const sampleName of sampleNames) {
2885
3334
  if (config.variation && !sampleName.includes(config.variation)) continue;
2886
3335
  const midi = toMidi((_a = sampleName.split(" ")[0]) != null ? _a : "");
2887
- if (!midi) continue;
3336
+ if (midi === void 0) continue;
2888
3337
  entries.push([midi, sampleName]);
2889
3338
  }
2890
3339
  const spread = spreadKeyRanges(entries);
@@ -2969,7 +3418,7 @@ var ReverbImpl = class {
2969
3418
  }
2970
3419
  getParam(name) {
2971
3420
  var _a;
2972
- return (_a = __privateGet(this, _effect)) == null ? void 0 : _a.parameters.get("preDelay");
3421
+ return (_a = __privateGet(this, _effect)) == null ? void 0 : _a.parameters.get(name);
2973
3422
  }
2974
3423
  get isReady() {
2975
3424
  return __privateGet(this, _effect) !== void 0;
@@ -3296,10 +3745,9 @@ function fetchSoundfontLoopData(url, sampleRate = 44100) {
3296
3745
  const loopData = {};
3297
3746
  Object.keys(raw).forEach((key) => {
3298
3747
  const midi = toMidi(key);
3299
- if (midi) {
3300
- const offsets = raw[key];
3301
- loopData[midi] = [offsets[0] / sampleRate, offsets[1] / sampleRate];
3302
- }
3748
+ if (midi === void 0) return;
3749
+ const offsets = raw[key];
3750
+ loopData[midi] = [offsets[0] / sampleRate, offsets[1] / sampleRate];
3303
3751
  });
3304
3752
  return loopData;
3305
3753
  } catch (err) {
@@ -3344,7 +3792,7 @@ function decodeSoundfontFile(context, config) {
3344
3792
  yield Promise.all(
3345
3793
  noteNames.map((noteName) => __async(null, null, function* () {
3346
3794
  const midi = toMidi(noteName);
3347
- if (!midi) return;
3795
+ if (midi === void 0) return;
3348
3796
  try {
3349
3797
  const audioData = base64ToArrayBuffer(
3350
3798
  removeBase64Prefix(json[noteName])
@@ -3914,6 +4362,8 @@ var LAYERS = [
3914
4362
  ];
3915
4363
  export {
3916
4364
  CacheStorage,
4365
+ DRUM_ABUSE_PACKS,
4366
+ DrumAbuse,
3917
4367
  DrumMachine,
3918
4368
  ElectricPiano,
3919
4369
  HttpStorage,
@@ -3935,7 +4385,12 @@ export {
3935
4385
  Versilian,
3936
4386
  audioBufferToWav,
3937
4387
  audioBufferToWav16,
4388
+ drumAbuseSampleUrl,
3938
4389
  drumMachineToPreset,
4390
+ getDrumAbuseMachineNames,
4391
+ getDrumAbuseMachinePack,
4392
+ getDrumAbuseMachinesForPack,
4393
+ getDrumAbusePackNames,
3939
4394
  getDrumMachineNames,
3940
4395
  getElectricPianoNames,
3941
4396
  getMalletNames,