@waveform-playlist/playout 9.1.1 → 9.2.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.d.mts +72 -26
- package/dist/index.d.ts +72 -26
- package/dist/index.js +74 -78
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +68 -77
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1306,6 +1306,52 @@ var MAX_RELEASE_SECONDS = 5;
|
|
|
1306
1306
|
function getGeneratorValue(generators, type) {
|
|
1307
1307
|
return generators[type]?.value;
|
|
1308
1308
|
}
|
|
1309
|
+
function int16ToFloat32(samples) {
|
|
1310
|
+
const floats = new Float32Array(samples.length);
|
|
1311
|
+
for (let i = 0; i < samples.length; i++) {
|
|
1312
|
+
floats[i] = samples[i] / 32768;
|
|
1313
|
+
}
|
|
1314
|
+
return floats;
|
|
1315
|
+
}
|
|
1316
|
+
function calculatePlaybackRate(params) {
|
|
1317
|
+
const { midiNote, overrideRootKey, originalPitch, coarseTune, fineTune, pitchCorrection } = params;
|
|
1318
|
+
const rootKey = overrideRootKey !== void 0 ? overrideRootKey : originalPitch !== 255 ? originalPitch : 60;
|
|
1319
|
+
const totalSemitones = midiNote - rootKey + coarseTune + (fineTune + pitchCorrection) / 100;
|
|
1320
|
+
return Math.pow(2, totalSemitones / 12);
|
|
1321
|
+
}
|
|
1322
|
+
function extractLoopAndEnvelope(params) {
|
|
1323
|
+
const { generators, header } = params;
|
|
1324
|
+
const loopMode = getGeneratorValue(generators, GeneratorType.SampleModes) ?? 0;
|
|
1325
|
+
const rawLoopStart = header.startLoop + (getGeneratorValue(generators, GeneratorType.StartLoopAddrsOffset) ?? 0) + (getGeneratorValue(generators, GeneratorType.StartLoopAddrsCoarseOffset) ?? 0) * 32768;
|
|
1326
|
+
const rawLoopEnd = header.endLoop + (getGeneratorValue(generators, GeneratorType.EndLoopAddrsOffset) ?? 0) + (getGeneratorValue(generators, GeneratorType.EndLoopAddrsCoarseOffset) ?? 0) * 32768;
|
|
1327
|
+
const loopStart = rawLoopStart / header.sampleRate;
|
|
1328
|
+
const loopEnd = rawLoopEnd / header.sampleRate;
|
|
1329
|
+
const attackVolEnv = timecentsToSeconds(
|
|
1330
|
+
getGeneratorValue(generators, GeneratorType.AttackVolEnv) ?? -12e3
|
|
1331
|
+
);
|
|
1332
|
+
const holdVolEnv = timecentsToSeconds(
|
|
1333
|
+
getGeneratorValue(generators, GeneratorType.HoldVolEnv) ?? -12e3
|
|
1334
|
+
);
|
|
1335
|
+
const decayVolEnv = timecentsToSeconds(
|
|
1336
|
+
getGeneratorValue(generators, GeneratorType.DecayVolEnv) ?? -12e3
|
|
1337
|
+
);
|
|
1338
|
+
const releaseVolEnv = Math.min(
|
|
1339
|
+
timecentsToSeconds(getGeneratorValue(generators, GeneratorType.ReleaseVolEnv) ?? -12e3),
|
|
1340
|
+
MAX_RELEASE_SECONDS
|
|
1341
|
+
);
|
|
1342
|
+
const sustainCb = getGeneratorValue(generators, GeneratorType.SustainVolEnv) ?? 0;
|
|
1343
|
+
const sustainVolEnv = Math.pow(10, -sustainCb / 200);
|
|
1344
|
+
return {
|
|
1345
|
+
loopMode,
|
|
1346
|
+
loopStart,
|
|
1347
|
+
loopEnd,
|
|
1348
|
+
attackVolEnv,
|
|
1349
|
+
holdVolEnv,
|
|
1350
|
+
decayVolEnv,
|
|
1351
|
+
sustainVolEnv,
|
|
1352
|
+
releaseVolEnv
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1309
1355
|
var SoundFontCache = class {
|
|
1310
1356
|
/**
|
|
1311
1357
|
* @param context Optional AudioContext for createBuffer(). If omitted, uses
|
|
@@ -1368,88 +1414,28 @@ var SoundFontCache = class {
|
|
|
1368
1414
|
buffer = this.int16ToAudioBuffer(sample.data, sample.header.sampleRate);
|
|
1369
1415
|
this.audioBufferCache.set(sampleIndex, buffer);
|
|
1370
1416
|
}
|
|
1371
|
-
const playbackRate =
|
|
1372
|
-
|
|
1417
|
+
const playbackRate = calculatePlaybackRate({
|
|
1418
|
+
midiNote,
|
|
1419
|
+
overrideRootKey: getGeneratorValue(keyData.generators, GeneratorType.OverridingRootKey),
|
|
1420
|
+
originalPitch: sample.header.originalPitch,
|
|
1421
|
+
coarseTune: getGeneratorValue(keyData.generators, GeneratorType.CoarseTune) ?? 0,
|
|
1422
|
+
fineTune: getGeneratorValue(keyData.generators, GeneratorType.FineTune) ?? 0,
|
|
1423
|
+
pitchCorrection: sample.header.pitchCorrection ?? 0
|
|
1424
|
+
});
|
|
1425
|
+
const loopAndEnvelope = extractLoopAndEnvelope({
|
|
1426
|
+
generators: keyData.generators,
|
|
1427
|
+
header: keyData.sample.header
|
|
1428
|
+
});
|
|
1373
1429
|
return { buffer, playbackRate, ...loopAndEnvelope };
|
|
1374
1430
|
}
|
|
1375
|
-
/**
|
|
1376
|
-
* Extract loop points and volume envelope data from per-zone generators.
|
|
1377
|
-
*
|
|
1378
|
-
* Loop points are stored as absolute indices into the SF2 sample pool.
|
|
1379
|
-
* We convert to AudioBuffer-relative seconds by subtracting header.start
|
|
1380
|
-
* and dividing by sampleRate.
|
|
1381
|
-
*
|
|
1382
|
-
* Volume envelope times are in SF2 timecents; sustain is centibels attenuation.
|
|
1383
|
-
*/
|
|
1384
|
-
extractLoopAndEnvelope(keyData) {
|
|
1385
|
-
const { generators } = keyData;
|
|
1386
|
-
const header = keyData.sample.header;
|
|
1387
|
-
const loopMode = getGeneratorValue(generators, GeneratorType.SampleModes) ?? 0;
|
|
1388
|
-
const rawLoopStart = header.startLoop + (getGeneratorValue(generators, GeneratorType.StartLoopAddrsOffset) ?? 0) + (getGeneratorValue(generators, GeneratorType.StartLoopAddrsCoarseOffset) ?? 0) * 32768;
|
|
1389
|
-
const rawLoopEnd = header.endLoop + (getGeneratorValue(generators, GeneratorType.EndLoopAddrsOffset) ?? 0) + (getGeneratorValue(generators, GeneratorType.EndLoopAddrsCoarseOffset) ?? 0) * 32768;
|
|
1390
|
-
const loopStart = rawLoopStart / header.sampleRate;
|
|
1391
|
-
const loopEnd = rawLoopEnd / header.sampleRate;
|
|
1392
|
-
const attackVolEnv = timecentsToSeconds(
|
|
1393
|
-
getGeneratorValue(generators, GeneratorType.AttackVolEnv) ?? -12e3
|
|
1394
|
-
);
|
|
1395
|
-
const holdVolEnv = timecentsToSeconds(
|
|
1396
|
-
getGeneratorValue(generators, GeneratorType.HoldVolEnv) ?? -12e3
|
|
1397
|
-
);
|
|
1398
|
-
const decayVolEnv = timecentsToSeconds(
|
|
1399
|
-
getGeneratorValue(generators, GeneratorType.DecayVolEnv) ?? -12e3
|
|
1400
|
-
);
|
|
1401
|
-
const releaseVolEnv = Math.min(
|
|
1402
|
-
timecentsToSeconds(getGeneratorValue(generators, GeneratorType.ReleaseVolEnv) ?? -12e3),
|
|
1403
|
-
MAX_RELEASE_SECONDS
|
|
1404
|
-
);
|
|
1405
|
-
const sustainCb = getGeneratorValue(generators, GeneratorType.SustainVolEnv) ?? 0;
|
|
1406
|
-
const sustainVolEnv = Math.pow(10, -sustainCb / 200);
|
|
1407
|
-
return {
|
|
1408
|
-
loopMode,
|
|
1409
|
-
loopStart,
|
|
1410
|
-
loopEnd,
|
|
1411
|
-
attackVolEnv,
|
|
1412
|
-
holdVolEnv,
|
|
1413
|
-
decayVolEnv,
|
|
1414
|
-
sustainVolEnv,
|
|
1415
|
-
releaseVolEnv
|
|
1416
|
-
};
|
|
1417
|
-
}
|
|
1418
|
-
/**
|
|
1419
|
-
* Calculate playback rate for a MIDI note using the SF2 generator chain.
|
|
1420
|
-
*
|
|
1421
|
-
* SF2 root key resolution priority:
|
|
1422
|
-
* 1. OverridingRootKey generator (per-zone, most specific)
|
|
1423
|
-
* 2. sample.header.originalPitch (sample header)
|
|
1424
|
-
* 3. MIDI note 60 (middle C fallback)
|
|
1425
|
-
*
|
|
1426
|
-
* Tuning adjustments:
|
|
1427
|
-
* - CoarseTune generator (semitones, additive)
|
|
1428
|
-
* - FineTune generator (cents, additive)
|
|
1429
|
-
* - sample.header.pitchCorrection (cents, additive)
|
|
1430
|
-
*/
|
|
1431
|
-
calculatePlaybackRate(midiNote, keyData) {
|
|
1432
|
-
const sample = keyData.sample;
|
|
1433
|
-
const generators = keyData.generators;
|
|
1434
|
-
const overrideRootKey = getGeneratorValue(generators, GeneratorType.OverridingRootKey);
|
|
1435
|
-
const originalPitch = sample.header.originalPitch;
|
|
1436
|
-
const rootKey = overrideRootKey !== void 0 ? overrideRootKey : originalPitch !== 255 ? originalPitch : 60;
|
|
1437
|
-
const coarseTune = getGeneratorValue(generators, GeneratorType.CoarseTune) ?? 0;
|
|
1438
|
-
const fineTune = getGeneratorValue(generators, GeneratorType.FineTune) ?? 0;
|
|
1439
|
-
const pitchCorrection = sample.header.pitchCorrection ?? 0;
|
|
1440
|
-
const totalSemitones = midiNote - rootKey + coarseTune + (fineTune + pitchCorrection) / 100;
|
|
1441
|
-
return Math.pow(2, totalSemitones / 12);
|
|
1442
|
-
}
|
|
1443
1431
|
/**
|
|
1444
1432
|
* Convert Int16Array sample data to an AudioBuffer.
|
|
1445
|
-
*
|
|
1433
|
+
* Uses the extracted int16ToFloat32 for the conversion, then copies into an AudioBuffer.
|
|
1446
1434
|
*/
|
|
1447
1435
|
int16ToAudioBuffer(data, sampleRate) {
|
|
1448
|
-
const
|
|
1449
|
-
const
|
|
1450
|
-
|
|
1451
|
-
channel[i] = data[i] / 32768;
|
|
1452
|
-
}
|
|
1436
|
+
const floats = int16ToFloat32(data);
|
|
1437
|
+
const buffer = this.context.createBuffer(1, floats.length, sampleRate);
|
|
1438
|
+
buffer.getChannelData(0).set(floats);
|
|
1453
1439
|
return buffer;
|
|
1454
1440
|
}
|
|
1455
1441
|
/**
|
|
@@ -1729,8 +1715,11 @@ export {
|
|
|
1729
1715
|
ToneTrack,
|
|
1730
1716
|
applyFadeIn,
|
|
1731
1717
|
applyFadeOut,
|
|
1718
|
+
calculatePlaybackRate,
|
|
1732
1719
|
closeGlobalAudioContext,
|
|
1733
1720
|
createToneAdapter,
|
|
1721
|
+
extractLoopAndEnvelope,
|
|
1722
|
+
getGeneratorValue,
|
|
1734
1723
|
getGlobalAudioContext,
|
|
1735
1724
|
getGlobalAudioContextState,
|
|
1736
1725
|
getGlobalContext,
|
|
@@ -1738,7 +1727,9 @@ export {
|
|
|
1738
1727
|
getMediaStreamSource,
|
|
1739
1728
|
getUnderlyingAudioParam,
|
|
1740
1729
|
hasMediaStreamSource,
|
|
1730
|
+
int16ToFloat32,
|
|
1741
1731
|
releaseMediaStreamSource,
|
|
1742
|
-
resumeGlobalAudioContext
|
|
1732
|
+
resumeGlobalAudioContext,
|
|
1733
|
+
timecentsToSeconds
|
|
1743
1734
|
};
|
|
1744
1735
|
//# sourceMappingURL=index.mjs.map
|