@marmooo/soundfont-split 0.0.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/LICENSE +21 -0
- package/README.md +71 -0
- package/esm/_dnt.polyfills.d.ts +11 -0
- package/esm/_dnt.polyfills.js +15 -0
- package/esm/_dnt.shims.d.ts +5 -0
- package/esm/_dnt.shims.js +61 -0
- package/esm/cli.d.ts +2 -0
- package/esm/cli.js +59 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/_data.d.ts +8 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/_data.js +17 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/_run_length.d.ts +8 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/_run_length.js +34 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/mod.d.ts +17 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/mod.js +18 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/parse_args.d.ts +342 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/parse_args.js +372 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/prompt_secret.d.ts +27 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/prompt_secret.js +109 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/unicode_width.d.ts +39 -0
- package/esm/deps/jsr.io/@std/cli/1.0.32/unicode_width.js +74 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/_os.d.ts +1 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/_os.js +14 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/os.d.ts +2 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/os.js +5 -0
- package/esm/package.json +3 -0
- package/esm/src/mod.d.ts +3 -0
- package/esm/src/mod.js +2 -0
- package/esm/src/split.d.ts +16 -0
- package/esm/src/split.js +265 -0
- package/package.json +36 -0
- package/script/_dnt.polyfills.d.ts +11 -0
- package/script/_dnt.polyfills.js +16 -0
- package/script/_dnt.shims.d.ts +5 -0
- package/script/_dnt.shims.js +65 -0
- package/script/package.json +3 -0
- package/script/src/mod.d.ts +3 -0
- package/script/src/mod.js +7 -0
- package/script/src/split.d.ts +16 -0
- package/script/src/split.js +302 -0
package/esm/src/split.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import * as dntShim from "../_dnt.shims.js";
|
|
2
|
+
import { Bag, GeneratorList, Instrument, ModulatorList, parse, PresetHeader, RangeValue, SampleHeader, SoundFont, write, } from "@marmooo/soundfont";
|
|
3
|
+
import { createDefaultEncoder } from "@marmooo/sf3-codec/encoder";
|
|
4
|
+
import { createDefaultDecoder } from "@marmooo/sf3-codec/decoder";
|
|
5
|
+
function cloneGenerator(g) {
|
|
6
|
+
if (g.value instanceof RangeValue) {
|
|
7
|
+
return new GeneratorList(g.code, new RangeValue(g.value.lo, g.value.hi));
|
|
8
|
+
}
|
|
9
|
+
return new GeneratorList(g.code, g.value);
|
|
10
|
+
}
|
|
11
|
+
function cloneModulator(m) {
|
|
12
|
+
return new ModulatorList(m.sourceOper, m.destinationOper, m.amount, m.amountSourceOper, m.transOper);
|
|
13
|
+
}
|
|
14
|
+
function isTerminalMod(m) {
|
|
15
|
+
return (m.destinationOper === 0 &&
|
|
16
|
+
m.amount === 0 &&
|
|
17
|
+
m.transOper === 0 &&
|
|
18
|
+
m.sourceOper.toValue() === 0 &&
|
|
19
|
+
m.amountSourceOper.toValue() === 0);
|
|
20
|
+
}
|
|
21
|
+
function defaultConcurrency() {
|
|
22
|
+
const nav = dntShim.dntGlobalThis.navigator;
|
|
23
|
+
return nav?.hardwareConcurrency ?? 4;
|
|
24
|
+
}
|
|
25
|
+
// Extract a single preset (by index into soundFont.presetHeaders) into a
|
|
26
|
+
// minimal SoundFont that contains only the instruments and samples that
|
|
27
|
+
// preset depends on. Indices are remapped so the result is a valid
|
|
28
|
+
// standalone SF2/SF3.
|
|
29
|
+
export function extractPreset(soundFont, presetHeaderIndex) {
|
|
30
|
+
const headers = soundFont.presetHeaders;
|
|
31
|
+
if (presetHeaderIndex < 0 ||
|
|
32
|
+
presetHeaderIndex >= headers.length ||
|
|
33
|
+
headers[presetHeaderIndex].isEnd) {
|
|
34
|
+
throw new Error(`invalid presetHeaderIndex: ${presetHeaderIndex}`);
|
|
35
|
+
}
|
|
36
|
+
const ph = headers[presetHeaderIndex];
|
|
37
|
+
const nextPh = headers[presetHeaderIndex + 1];
|
|
38
|
+
// Zones for this preset: bags [ph.presetBagIndex, nextBagIndex)
|
|
39
|
+
// The terminal bag that bounds the last zone is not a zone itself.
|
|
40
|
+
const bagFrom = ph.presetBagIndex;
|
|
41
|
+
const bagTo = nextPh
|
|
42
|
+
? nextPh.presetBagIndex
|
|
43
|
+
: soundFont.presetZone.length - 1;
|
|
44
|
+
const instrumentIdSet = new Set();
|
|
45
|
+
const presetZoneSlice = [];
|
|
46
|
+
const presetGenSlice = [];
|
|
47
|
+
const presetModSlice = [];
|
|
48
|
+
let genCursor = 0;
|
|
49
|
+
let modCursor = 0;
|
|
50
|
+
for (let zi = bagFrom; zi < bagTo; zi++) {
|
|
51
|
+
const bag = soundFont.presetZone[zi];
|
|
52
|
+
const nextBag = soundFont.presetZone[zi + 1];
|
|
53
|
+
if (!nextBag)
|
|
54
|
+
break;
|
|
55
|
+
const gens = soundFont.presetGenerators
|
|
56
|
+
.slice(bag.generatorIndex, nextBag.generatorIndex)
|
|
57
|
+
.map(cloneGenerator);
|
|
58
|
+
const mods = soundFont.presetModulators
|
|
59
|
+
.slice(bag.modulatorIndex, nextBag.modulatorIndex)
|
|
60
|
+
.map(cloneModulator);
|
|
61
|
+
for (const g of gens) {
|
|
62
|
+
if (g.type === "instrument" && typeof g.value === "number") {
|
|
63
|
+
instrumentIdSet.add(g.value);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
presetZoneSlice.push(new Bag(genCursor, modCursor));
|
|
67
|
+
presetGenSlice.push(...gens);
|
|
68
|
+
presetModSlice.push(...mods);
|
|
69
|
+
genCursor += gens.length;
|
|
70
|
+
modCursor += mods.length;
|
|
71
|
+
}
|
|
72
|
+
// Terminal bag for preset zones
|
|
73
|
+
presetZoneSlice.push(new Bag(genCursor, modCursor));
|
|
74
|
+
if (presetModSlice.length === 0 ||
|
|
75
|
+
!isTerminalMod(presetModSlice[presetModSlice.length - 1])) {
|
|
76
|
+
presetModSlice.push(ModulatorList.end());
|
|
77
|
+
}
|
|
78
|
+
if (presetGenSlice.length === 0 ||
|
|
79
|
+
!presetGenSlice[presetGenSlice.length - 1].isEnd) {
|
|
80
|
+
presetGenSlice.push(GeneratorList.end());
|
|
81
|
+
}
|
|
82
|
+
const instrumentIds = [...instrumentIdSet].sort((a, b) => a - b);
|
|
83
|
+
const instMap = new Map();
|
|
84
|
+
instrumentIds.forEach((id, i) => instMap.set(id, i));
|
|
85
|
+
for (const g of presetGenSlice) {
|
|
86
|
+
if (g.type === "instrument" && typeof g.value === "number") {
|
|
87
|
+
const mapped = instMap.get(g.value);
|
|
88
|
+
if (mapped === undefined) {
|
|
89
|
+
throw new Error(`instrument ${g.value} not collected`);
|
|
90
|
+
}
|
|
91
|
+
g.value = mapped;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const sampleIdSet = new Set();
|
|
95
|
+
const instruments = [];
|
|
96
|
+
const instrumentZone = [];
|
|
97
|
+
const instrumentGenerators = [];
|
|
98
|
+
const instrumentModulators = [];
|
|
99
|
+
let iGenCursor = 0;
|
|
100
|
+
let iModCursor = 0;
|
|
101
|
+
for (const oldInstId of instrumentIds) {
|
|
102
|
+
const inst = soundFont.instruments[oldInstId];
|
|
103
|
+
const nextInst = soundFont.instruments[oldInstId + 1];
|
|
104
|
+
const iBagFrom = inst.instrumentBagIndex;
|
|
105
|
+
const iBagTo = nextInst
|
|
106
|
+
? nextInst.instrumentBagIndex
|
|
107
|
+
: soundFont.instrumentZone.length - 1;
|
|
108
|
+
const newInst = new Instrument();
|
|
109
|
+
newInst.instrumentName = inst.instrumentName;
|
|
110
|
+
newInst.instrumentBagIndex = instrumentZone.length;
|
|
111
|
+
instruments.push(newInst);
|
|
112
|
+
for (let zi = iBagFrom; zi < iBagTo; zi++) {
|
|
113
|
+
const bag = soundFont.instrumentZone[zi];
|
|
114
|
+
const nextBag = soundFont.instrumentZone[zi + 1];
|
|
115
|
+
if (!nextBag)
|
|
116
|
+
break;
|
|
117
|
+
const gens = soundFont.instrumentGenerators
|
|
118
|
+
.slice(bag.generatorIndex, nextBag.generatorIndex)
|
|
119
|
+
.map(cloneGenerator);
|
|
120
|
+
const mods = soundFont.instrumentModulators
|
|
121
|
+
.slice(bag.modulatorIndex, nextBag.modulatorIndex)
|
|
122
|
+
.map(cloneModulator);
|
|
123
|
+
for (const g of gens) {
|
|
124
|
+
if (g.type === "sampleID" && typeof g.value === "number") {
|
|
125
|
+
sampleIdSet.add(g.value);
|
|
126
|
+
const sh = soundFont.sampleHeaders[g.value];
|
|
127
|
+
if (sh && sh.sampleLink !== 0 && !sh.isEnd) {
|
|
128
|
+
sampleIdSet.add(sh.sampleLink);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
instrumentZone.push(new Bag(iGenCursor, iModCursor));
|
|
133
|
+
instrumentGenerators.push(...gens);
|
|
134
|
+
instrumentModulators.push(...mods);
|
|
135
|
+
iGenCursor += gens.length;
|
|
136
|
+
iModCursor += mods.length;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
instrumentZone.push(new Bag(iGenCursor, iModCursor));
|
|
140
|
+
if (instrumentModulators.length === 0 ||
|
|
141
|
+
!isTerminalMod(instrumentModulators[instrumentModulators.length - 1])) {
|
|
142
|
+
instrumentModulators.push(ModulatorList.end());
|
|
143
|
+
}
|
|
144
|
+
if (instrumentGenerators.length === 0 ||
|
|
145
|
+
!instrumentGenerators[instrumentGenerators.length - 1].isEnd) {
|
|
146
|
+
instrumentGenerators.push(GeneratorList.end());
|
|
147
|
+
}
|
|
148
|
+
const sampleIds = [...sampleIdSet].sort((a, b) => a - b);
|
|
149
|
+
const sampleMap = new Map();
|
|
150
|
+
sampleIds.forEach((id, i) => sampleMap.set(id, i));
|
|
151
|
+
for (const g of instrumentGenerators) {
|
|
152
|
+
if (g.type === "sampleID" && typeof g.value === "number") {
|
|
153
|
+
const mapped = sampleMap.get(g.value);
|
|
154
|
+
if (mapped === undefined) {
|
|
155
|
+
throw new Error(`sample ${g.value} not collected`);
|
|
156
|
+
}
|
|
157
|
+
g.value = mapped;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const sampleHeaders = [];
|
|
161
|
+
const samples = [];
|
|
162
|
+
for (const oldId of sampleIds) {
|
|
163
|
+
const sh = soundFont.sampleHeaders[oldId];
|
|
164
|
+
const sample = soundFont.samples[oldId];
|
|
165
|
+
const newLink = sh.sampleLink !== 0 && sampleMap.has(sh.sampleLink)
|
|
166
|
+
? sampleMap.get(sh.sampleLink)
|
|
167
|
+
: 0;
|
|
168
|
+
sampleHeaders.push(new SampleHeader(sh.sampleName, sh.start, sh.end, sh.loopStart, sh.loopEnd, sh.sampleRate, sh.originalPitch, sh.pitchCorrection, newLink, sh.sampleType));
|
|
169
|
+
samples.push(sample);
|
|
170
|
+
}
|
|
171
|
+
const newPh = new PresetHeader(ph.presetName, ph.preset, ph.bank, 0, ph.library, ph.genre, ph.morphology);
|
|
172
|
+
return new SoundFont({
|
|
173
|
+
presetHeaders: [newPh],
|
|
174
|
+
presetZone: presetZoneSlice,
|
|
175
|
+
presetModulators: presetModSlice,
|
|
176
|
+
presetGenerators: presetGenSlice,
|
|
177
|
+
instruments,
|
|
178
|
+
instrumentZone,
|
|
179
|
+
instrumentModulators,
|
|
180
|
+
instrumentGenerators,
|
|
181
|
+
sampleHeaders,
|
|
182
|
+
samples,
|
|
183
|
+
samplingData: soundFont.samplingData,
|
|
184
|
+
info: soundFont.info,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
// Split every preset in the soundfont into its own file under outDir:
|
|
188
|
+
// outDir/{bank:03d}/{preset:03d}.sf3
|
|
189
|
+
//
|
|
190
|
+
// Presets are processed in parallel. Sample encodes share a single encoder
|
|
191
|
+
// pool sized by `options.concurrency` (so the budget is global, not
|
|
192
|
+
// per-preset).
|
|
193
|
+
export async function splitSoundFont(input, outDir, options = {}) {
|
|
194
|
+
const bytes = typeof input === "string" ? dntShim.Deno.readFileSync(input) : input;
|
|
195
|
+
const soundFont = parse(bytes);
|
|
196
|
+
const toSf3 = options.toSf3 !== false;
|
|
197
|
+
const concurrency = Math.max(1, options.concurrency ?? defaultConcurrency());
|
|
198
|
+
await dntShim.Deno.mkdir(outDir, { recursive: true });
|
|
199
|
+
// Shared encoder pool for the whole job. Callers that supply their own
|
|
200
|
+
// encode via sf2ToSf3 are not used here - we own the pool so we can dispose
|
|
201
|
+
// it and let the process exit (workers otherwise keep the event loop alive).
|
|
202
|
+
let encode;
|
|
203
|
+
let decode;
|
|
204
|
+
if (toSf3) {
|
|
205
|
+
encode = createDefaultEncoder({
|
|
206
|
+
quality: options.quality,
|
|
207
|
+
poolSize: concurrency,
|
|
208
|
+
});
|
|
209
|
+
if (options.recompress) {
|
|
210
|
+
decode = createDefaultDecoder({ poolSize: concurrency });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const headers = soundFont.presetHeaders;
|
|
215
|
+
const jobs = [];
|
|
216
|
+
for (let i = 0; i < headers.length; i++) {
|
|
217
|
+
if (!headers[i].isEnd)
|
|
218
|
+
jobs.push({ index: i, ph: headers[i] });
|
|
219
|
+
}
|
|
220
|
+
// Cap how many presets we assemble at once (encode pool is the real
|
|
221
|
+
// limiter; this just bounds peak memory from parallel write()s).
|
|
222
|
+
const presetParallel = concurrency;
|
|
223
|
+
const results = new Array(jobs.length);
|
|
224
|
+
let nextJob = 0;
|
|
225
|
+
const runWorker = async () => {
|
|
226
|
+
while (true) {
|
|
227
|
+
const jobIndex = nextJob++;
|
|
228
|
+
if (jobIndex >= jobs.length)
|
|
229
|
+
return;
|
|
230
|
+
const { index, ph } = jobs[jobIndex];
|
|
231
|
+
const extracted = extractPreset(soundFont, index);
|
|
232
|
+
const bankDir = `${outDir}/${String(ph.bank).padStart(3, "0")}`;
|
|
233
|
+
await dntShim.Deno.mkdir(bankDir, { recursive: true });
|
|
234
|
+
const ext = toSf3 ? "sf3" : "sf2";
|
|
235
|
+
const fileName = `${String(ph.preset).padStart(3, "0")}.${ext}`;
|
|
236
|
+
const path = `${bankDir}/${fileName}`;
|
|
237
|
+
const outBytes = toSf3
|
|
238
|
+
? await write(extracted, {
|
|
239
|
+
// write() may schedule many encodes; the pool caps actual work.
|
|
240
|
+
encode: encode,
|
|
241
|
+
decode,
|
|
242
|
+
concurrency,
|
|
243
|
+
})
|
|
244
|
+
: await write(extracted);
|
|
245
|
+
dntShim.Deno.writeFileSync(path, outBytes);
|
|
246
|
+
const presetName = ph.presetName.replace(/\0+$/, "").trim();
|
|
247
|
+
results[jobIndex] = {
|
|
248
|
+
bank: ph.bank,
|
|
249
|
+
preset: ph.preset,
|
|
250
|
+
presetName,
|
|
251
|
+
path,
|
|
252
|
+
byteLength: outBytes.byteLength,
|
|
253
|
+
};
|
|
254
|
+
console.log(`wrote ${path} (${outBytes.byteLength} bytes) - ${presetName}`);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
const workers = Array.from({ length: Math.min(presetParallel, jobs.length) }, () => runWorker());
|
|
258
|
+
await Promise.all(workers);
|
|
259
|
+
return results;
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
encode?.dispose?.();
|
|
263
|
+
decode?.dispose?.();
|
|
264
|
+
}
|
|
265
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@marmooo/soundfont-split",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Split a SoundFont (SF2 / SF3) into per-preset SF3 files.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/marmooo/soundfont-split.git"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/marmooo/soundfont-split/issues"
|
|
12
|
+
},
|
|
13
|
+
"main": "./script/src/mod.js",
|
|
14
|
+
"module": "./esm/src/mod.js",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./esm/src/mod.js",
|
|
18
|
+
"require": "./script/src/mod.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "node test_runner.cjs"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"soundfont-split": "./esm/cli.js"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@marmooo/sf3-codec": "^0.0.2",
|
|
29
|
+
"@marmooo/soundfont": "^0.3.2",
|
|
30
|
+
"@deno/shim-deno": "~0.18.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.9.0"
|
|
34
|
+
},
|
|
35
|
+
"_generatedBy": "dnt@0.43.2"
|
|
36
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// https://github.com/tc39/proposal-accessible-object-hasownproperty/blob/main/polyfill.js
|
|
4
|
+
if (!Object.hasOwn) {
|
|
5
|
+
Object.defineProperty(Object, "hasOwn", {
|
|
6
|
+
value: function (object, property) {
|
|
7
|
+
if (object == null) {
|
|
8
|
+
throw new TypeError("Cannot convert undefined or null to object");
|
|
9
|
+
}
|
|
10
|
+
return Object.prototype.hasOwnProperty.call(Object(object), property);
|
|
11
|
+
},
|
|
12
|
+
configurable: true,
|
|
13
|
+
enumerable: false,
|
|
14
|
+
writable: true,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dntGlobalThis = exports.Deno = void 0;
|
|
4
|
+
const shim_deno_1 = require("@deno/shim-deno");
|
|
5
|
+
var shim_deno_2 = require("@deno/shim-deno");
|
|
6
|
+
Object.defineProperty(exports, "Deno", { enumerable: true, get: function () { return shim_deno_2.Deno; } });
|
|
7
|
+
const dntGlobals = {
|
|
8
|
+
Deno: shim_deno_1.Deno,
|
|
9
|
+
};
|
|
10
|
+
exports.dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
|
|
11
|
+
function createMergeProxy(baseObj, extObj) {
|
|
12
|
+
return new Proxy(baseObj, {
|
|
13
|
+
get(_target, prop, _receiver) {
|
|
14
|
+
if (prop in extObj) {
|
|
15
|
+
return extObj[prop];
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
return baseObj[prop];
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
set(_target, prop, value) {
|
|
22
|
+
if (prop in extObj) {
|
|
23
|
+
delete extObj[prop];
|
|
24
|
+
}
|
|
25
|
+
baseObj[prop] = value;
|
|
26
|
+
return true;
|
|
27
|
+
},
|
|
28
|
+
deleteProperty(_target, prop) {
|
|
29
|
+
let success = false;
|
|
30
|
+
if (prop in extObj) {
|
|
31
|
+
delete extObj[prop];
|
|
32
|
+
success = true;
|
|
33
|
+
}
|
|
34
|
+
if (prop in baseObj) {
|
|
35
|
+
delete baseObj[prop];
|
|
36
|
+
success = true;
|
|
37
|
+
}
|
|
38
|
+
return success;
|
|
39
|
+
},
|
|
40
|
+
ownKeys(_target) {
|
|
41
|
+
const baseKeys = Reflect.ownKeys(baseObj);
|
|
42
|
+
const extKeys = Reflect.ownKeys(extObj);
|
|
43
|
+
const extKeysSet = new Set(extKeys);
|
|
44
|
+
return [...baseKeys.filter((k) => !extKeysSet.has(k)), ...extKeys];
|
|
45
|
+
},
|
|
46
|
+
defineProperty(_target, prop, desc) {
|
|
47
|
+
if (prop in extObj) {
|
|
48
|
+
delete extObj[prop];
|
|
49
|
+
}
|
|
50
|
+
Reflect.defineProperty(baseObj, prop, desc);
|
|
51
|
+
return true;
|
|
52
|
+
},
|
|
53
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
54
|
+
if (prop in extObj) {
|
|
55
|
+
return Reflect.getOwnPropertyDescriptor(extObj, prop);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
return Reflect.getOwnPropertyDescriptor(baseObj, prop);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
has(_target, prop) {
|
|
62
|
+
return prop in extObj || prop in baseObj;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.splitSoundFont = exports.extractPreset = void 0;
|
|
4
|
+
require("../_dnt.polyfills.js");
|
|
5
|
+
var split_js_1 = require("./split.js");
|
|
6
|
+
Object.defineProperty(exports, "extractPreset", { enumerable: true, get: function () { return split_js_1.extractPreset; } });
|
|
7
|
+
Object.defineProperty(exports, "splitSoundFont", { enumerable: true, get: function () { return split_js_1.splitSoundFont; } });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SoundFont } from "@marmooo/soundfont";
|
|
2
|
+
export interface SplitOptions {
|
|
3
|
+
quality?: number;
|
|
4
|
+
concurrency?: number;
|
|
5
|
+
toSf3?: boolean;
|
|
6
|
+
recompress?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface SplitResult {
|
|
9
|
+
bank: number;
|
|
10
|
+
preset: number;
|
|
11
|
+
presetName: string;
|
|
12
|
+
path: string;
|
|
13
|
+
byteLength: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function extractPreset(soundFont: SoundFont, presetHeaderIndex: number): SoundFont;
|
|
16
|
+
export declare function splitSoundFont(input: Uint8Array | string, outDir: string, options?: SplitOptions): Promise<SplitResult[]>;
|