smplr 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +17 -3
- package/dist/index.js +60 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +60 -51
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -94,6 +94,19 @@ type SampleStart = {
|
|
|
94
94
|
stopId?: string | number;
|
|
95
95
|
time?: number;
|
|
96
96
|
} & SampleOptions;
|
|
97
|
+
type SampleRegion = {
|
|
98
|
+
sampleName: string;
|
|
99
|
+
sampleCenter: number;
|
|
100
|
+
rangeMidi?: [number, number];
|
|
101
|
+
rangeVol?: [number, number];
|
|
102
|
+
offsetVol?: number;
|
|
103
|
+
offsetDetune?: number;
|
|
104
|
+
sample?: Partial<SampleOptions>;
|
|
105
|
+
};
|
|
106
|
+
type SampleLayer = {
|
|
107
|
+
regions: SampleRegion[];
|
|
108
|
+
sample: Partial<SampleOptions>;
|
|
109
|
+
};
|
|
97
110
|
|
|
98
111
|
declare function getDrumMachineNames(): string[];
|
|
99
112
|
type DrumMachineConfig = ChannelOptions & SampleOptions & {
|
|
@@ -305,13 +318,14 @@ declare class Soundfont {
|
|
|
305
318
|
readonly context: AudioContext;
|
|
306
319
|
readonly config: Readonly<SoundfontConfig>;
|
|
307
320
|
private readonly player;
|
|
308
|
-
readonly load: Promise<
|
|
321
|
+
readonly load: Promise<this>;
|
|
322
|
+
readonly layer: SampleLayer;
|
|
309
323
|
constructor(context: AudioContext, options: SoundfontOptions);
|
|
310
324
|
get output(): OutputChannel;
|
|
311
|
-
loaded(): Promise<unknown>;
|
|
312
325
|
get hasLoops(): boolean;
|
|
326
|
+
loaded(): Promise<this>;
|
|
313
327
|
disconnect(): void;
|
|
314
|
-
start(sample: SampleStart): (time?: number | undefined) => void;
|
|
328
|
+
start(sample: SampleStart | string | number): (time?: number | undefined) => void;
|
|
315
329
|
stop(sample?: SampleStop | string | number): void;
|
|
316
330
|
}
|
|
317
331
|
|
package/dist/index.js
CHANGED
|
@@ -140,16 +140,6 @@ function toMidi(note) {
|
|
|
140
140
|
function midiVelToGain(vel) {
|
|
141
141
|
return vel * vel / 16129;
|
|
142
142
|
}
|
|
143
|
-
function findNearestMidi(midi, isAvailable) {
|
|
144
|
-
let i = 0;
|
|
145
|
-
while (isAvailable[midi + i] === void 0 && i < 128) {
|
|
146
|
-
if (i > 0)
|
|
147
|
-
i = -i;
|
|
148
|
-
else
|
|
149
|
-
i = -i + 1;
|
|
150
|
-
}
|
|
151
|
-
return i === 127 ? [midi, 0] : [midi + i, -i * 100];
|
|
152
|
-
}
|
|
153
143
|
|
|
154
144
|
// src/player/signals.ts
|
|
155
145
|
function createControl(initialValue) {
|
|
@@ -1165,7 +1155,7 @@ var Mellotron = class {
|
|
|
1165
1155
|
start(sample) {
|
|
1166
1156
|
const found = findFirstSampleInLayer(
|
|
1167
1157
|
this.layer,
|
|
1168
|
-
typeof sample === "object" ?
|
|
1158
|
+
typeof sample === "object" ? sample : { note: sample }
|
|
1169
1159
|
);
|
|
1170
1160
|
if (!found)
|
|
1171
1161
|
return () => void 0;
|
|
@@ -1366,8 +1356,8 @@ function gleitzKitUrl(name, kit) {
|
|
|
1366
1356
|
const format = (_a = findFirstSupportedFormat(["ogg", "mp3"])) != null ? _a : "mp3";
|
|
1367
1357
|
return `https://gleitz.github.io/midi-js-soundfonts/${kit}/${name}-${format}.js`;
|
|
1368
1358
|
}
|
|
1369
|
-
function soundfontInstrumentLoader(url,
|
|
1370
|
-
return (context,
|
|
1359
|
+
function soundfontInstrumentLoader(url, buffers, layer) {
|
|
1360
|
+
return (context, storage) => __async(this, null, function* () {
|
|
1371
1361
|
const sourceFile = yield (yield storage.fetch(url)).text();
|
|
1372
1362
|
const json = midiJsToJson(sourceFile);
|
|
1373
1363
|
const noteNames = Object.keys(json);
|
|
@@ -1380,9 +1370,14 @@ function soundfontInstrumentLoader(url, storage) {
|
|
|
1380
1370
|
removeBase64Prefix(json[noteName])
|
|
1381
1371
|
);
|
|
1382
1372
|
const buffer = yield context.decodeAudioData(audioData);
|
|
1383
|
-
buffers[
|
|
1373
|
+
buffers[noteName] = buffer;
|
|
1374
|
+
layer.regions.push({
|
|
1375
|
+
sampleName: noteName,
|
|
1376
|
+
sampleCenter: midi
|
|
1377
|
+
});
|
|
1384
1378
|
}))
|
|
1385
1379
|
);
|
|
1380
|
+
spreadRegions(layer.regions);
|
|
1386
1381
|
});
|
|
1387
1382
|
}
|
|
1388
1383
|
function midiJsToJson(source) {
|
|
@@ -1545,26 +1540,25 @@ function getGoldstSoundfontLoopsUrl(instrument, kit) {
|
|
|
1545
1540
|
}
|
|
1546
1541
|
function fetchSoundfontLoopData(url) {
|
|
1547
1542
|
return __async(this, null, function* () {
|
|
1543
|
+
if (!url)
|
|
1544
|
+
return void 0;
|
|
1548
1545
|
try {
|
|
1549
1546
|
const req = yield fetch(url);
|
|
1550
1547
|
if (req.status !== 200)
|
|
1551
|
-
return
|
|
1548
|
+
return;
|
|
1552
1549
|
const raw = yield req.json();
|
|
1553
|
-
const loopData = {
|
|
1550
|
+
const loopData = {};
|
|
1554
1551
|
const sampleRate = 41e3;
|
|
1555
1552
|
Object.keys(raw).forEach((key) => {
|
|
1556
1553
|
const midi = toMidi(key);
|
|
1557
1554
|
if (midi) {
|
|
1558
1555
|
const offsets = raw[key];
|
|
1559
|
-
loopData
|
|
1560
|
-
offsets[0] / sampleRate,
|
|
1561
|
-
offsets[1] / sampleRate
|
|
1562
|
-
];
|
|
1556
|
+
loopData[midi] = [offsets[0] / sampleRate, offsets[1] / sampleRate];
|
|
1563
1557
|
}
|
|
1564
1558
|
});
|
|
1565
1559
|
return loopData;
|
|
1566
1560
|
} catch (err) {
|
|
1567
|
-
return
|
|
1561
|
+
return void 0;
|
|
1568
1562
|
}
|
|
1569
1563
|
});
|
|
1570
1564
|
}
|
|
@@ -1576,64 +1570,79 @@ function getSoundfontKits() {
|
|
|
1576
1570
|
function getSoundfontNames() {
|
|
1577
1571
|
return SOUNDFONT_INSTRUMENTS;
|
|
1578
1572
|
}
|
|
1579
|
-
var
|
|
1573
|
+
var _hasLoops;
|
|
1580
1574
|
var Soundfont = class {
|
|
1581
1575
|
constructor(context, options) {
|
|
1582
1576
|
this.context = context;
|
|
1583
|
-
__privateAdd(this,
|
|
1577
|
+
__privateAdd(this, _hasLoops, void 0);
|
|
1584
1578
|
this.config = getSoundfontConfig(options);
|
|
1585
1579
|
this.player = new DefaultPlayer(context, options);
|
|
1586
|
-
|
|
1580
|
+
this.layer = createEmptySampleLayer();
|
|
1581
|
+
__privateSet(this, _hasLoops, false);
|
|
1582
|
+
const loader = soundfontLoader(
|
|
1587
1583
|
this.config.instrumentUrl,
|
|
1588
|
-
this.config.
|
|
1584
|
+
this.config.loopDataUrl,
|
|
1585
|
+
this.player.buffers,
|
|
1586
|
+
this.layer
|
|
1589
1587
|
);
|
|
1590
|
-
this.load = loader(context, this.
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
this.load = Promise.all([
|
|
1595
|
-
this.load,
|
|
1596
|
-
fetchSoundfontLoopData(this.config.loopDataUrl).then((loopData) => {
|
|
1597
|
-
__privateSet(this, _loops, loopData);
|
|
1598
|
-
})
|
|
1599
|
-
]).then(() => this);
|
|
1600
|
-
}
|
|
1588
|
+
this.load = loader(context, this.config.storage).then((hasLoops) => {
|
|
1589
|
+
__privateSet(this, _hasLoops, hasLoops);
|
|
1590
|
+
return this;
|
|
1591
|
+
});
|
|
1601
1592
|
const gain = new GainNode(context, { gain: this.config.extraGain });
|
|
1602
1593
|
this.player.output.addInsert(gain);
|
|
1603
1594
|
}
|
|
1604
1595
|
get output() {
|
|
1605
1596
|
return this.player.output;
|
|
1606
1597
|
}
|
|
1598
|
+
get hasLoops() {
|
|
1599
|
+
return __privateGet(this, _hasLoops);
|
|
1600
|
+
}
|
|
1607
1601
|
loaded() {
|
|
1608
1602
|
return __async(this, null, function* () {
|
|
1609
1603
|
console.warn("deprecated: use load instead");
|
|
1610
1604
|
return this.load;
|
|
1611
1605
|
});
|
|
1612
1606
|
}
|
|
1613
|
-
get hasLoops() {
|
|
1614
|
-
return __privateGet(this, _loops).status === "loaded";
|
|
1615
|
-
}
|
|
1616
1607
|
disconnect() {
|
|
1617
|
-
console.log("disconnecting");
|
|
1618
1608
|
this.player.disconnect();
|
|
1619
1609
|
}
|
|
1620
1610
|
start(sample) {
|
|
1621
|
-
const
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
loopStart: loop == null ? void 0 : loop[0],
|
|
1629
|
-
loopEnd: loop == null ? void 0 : loop[1]
|
|
1630
|
-
}));
|
|
1611
|
+
const found = findFirstSampleInLayer(
|
|
1612
|
+
this.layer,
|
|
1613
|
+
typeof sample === "object" ? sample : { note: sample }
|
|
1614
|
+
);
|
|
1615
|
+
if (!found)
|
|
1616
|
+
return () => void 0;
|
|
1617
|
+
return this.player.start(found);
|
|
1631
1618
|
}
|
|
1632
1619
|
stop(sample) {
|
|
1633
1620
|
return this.player.stop(sample);
|
|
1634
1621
|
}
|
|
1635
1622
|
};
|
|
1636
|
-
|
|
1623
|
+
_hasLoops = new WeakMap();
|
|
1624
|
+
function soundfontLoader(url, loopsUrl, buffers, layer) {
|
|
1625
|
+
const loadInstrument = soundfontInstrumentLoader(url, buffers, layer);
|
|
1626
|
+
return (context, storage) => __async(this, null, function* () {
|
|
1627
|
+
const [_, loops] = yield Promise.all([
|
|
1628
|
+
loadInstrument(context, storage),
|
|
1629
|
+
fetchSoundfontLoopData(loopsUrl)
|
|
1630
|
+
]);
|
|
1631
|
+
if (loops) {
|
|
1632
|
+
layer.regions.forEach((region) => {
|
|
1633
|
+
var _a;
|
|
1634
|
+
const loop = loops[region.sampleCenter];
|
|
1635
|
+
if (loop) {
|
|
1636
|
+
(_a = region.sample) != null ? _a : region.sample = {};
|
|
1637
|
+
region.sample.loop = true;
|
|
1638
|
+
region.sample.loopStart = loop[0];
|
|
1639
|
+
region.sample.loopEnd = loop[1];
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
return !!loops;
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1637
1646
|
function getSoundfontConfig(options) {
|
|
1638
1647
|
var _a, _b, _c, _d;
|
|
1639
1648
|
if (!options.instrument && !options.instrumentUrl) {
|