@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.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 = this.calculatePlaybackRate(midiNote, keyData);
1372
- const loopAndEnvelope = this.extractLoopAndEnvelope(keyData);
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
- * SF2 samples are 16-bit signed integers; Web Audio needs Float32 [-1, 1].
1433
+ * Uses the extracted int16ToFloat32 for the conversion, then copies into an AudioBuffer.
1446
1434
  */
1447
1435
  int16ToAudioBuffer(data, sampleRate) {
1448
- const buffer = this.context.createBuffer(1, data.length, sampleRate);
1449
- const channel = buffer.getChannelData(0);
1450
- for (let i = 0; i < data.length; i++) {
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