@waveform-playlist/playout 9.1.0 → 9.1.2

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 CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Gain, ToneAudioNode, SynthOptions, Volume, BaseContext, Context } from 'tone';
2
2
  import { Fade, Track, MidiNoteData } from '@waveform-playlist/core';
3
+ import { ZoneMap, Generator, GeneratorType } from 'soundfont2';
3
4
  import { PlayoutAdapter } from '@waveform-playlist/engine';
4
5
 
5
6
  type TrackEffectsFunction = (graphEnd: Gain, masterGainNode: ToneAudioNode, isOffline: boolean) => void | (() => void);
@@ -201,6 +202,75 @@ interface SoundFontSample {
201
202
  /** Volume envelope release time in seconds */
202
203
  releaseVolEnv: number;
203
204
  }
205
+ /**
206
+ * Convert SF2 timecents to seconds.
207
+ * SF2 formula: seconds = 2^(timecents / 1200)
208
+ * Default -12000 timecents ≈ 0.001s (effectively instant).
209
+ */
210
+ declare function timecentsToSeconds(tc: number): number;
211
+ /**
212
+ * Get a numeric generator value from a zone map.
213
+ */
214
+ declare function getGeneratorValue(generators: ZoneMap<Generator>, type: GeneratorType): number | undefined;
215
+ /**
216
+ * Convert Int16Array sample data to Float32Array.
217
+ * SF2 samples are 16-bit signed integers; Web Audio needs Float32 [-1, 1].
218
+ */
219
+ declare function int16ToFloat32(samples: Int16Array): Float32Array;
220
+ /**
221
+ * Input parameters for playback rate calculation.
222
+ */
223
+ interface PlaybackRateParams {
224
+ /** Target MIDI note number (0-127) */
225
+ midiNote: number;
226
+ /** OverridingRootKey generator value, or undefined if not set */
227
+ overrideRootKey: number | undefined;
228
+ /** sample.header.originalPitch (255 means unpitched) */
229
+ originalPitch: number;
230
+ /** CoarseTune generator value in semitones (default 0) */
231
+ coarseTune: number;
232
+ /** FineTune generator value in cents (default 0) */
233
+ fineTune: number;
234
+ /** sample.header.pitchCorrection in cents (default 0) */
235
+ pitchCorrection: number;
236
+ }
237
+ /**
238
+ * Calculate playback rate for a MIDI note using the SF2 generator chain.
239
+ *
240
+ * SF2 root key resolution priority:
241
+ * 1. OverridingRootKey generator (per-zone, most specific)
242
+ * 2. sample.header.originalPitch (sample header)
243
+ * 3. MIDI note 60 (middle C fallback)
244
+ *
245
+ * Tuning adjustments:
246
+ * - CoarseTune generator (semitones, additive)
247
+ * - FineTune generator (cents, additive)
248
+ * - sample.header.pitchCorrection (cents, additive)
249
+ */
250
+ declare function calculatePlaybackRate(params: PlaybackRateParams): number;
251
+ /**
252
+ * Input parameters for loop and envelope extraction.
253
+ */
254
+ interface LoopAndEnvelopeParams {
255
+ /** SF2 generators zone map */
256
+ generators: ZoneMap<Generator>;
257
+ /** Sample header with loop points and sample rate */
258
+ header: {
259
+ startLoop: number;
260
+ endLoop: number;
261
+ sampleRate: number;
262
+ };
263
+ }
264
+ /**
265
+ * Extract loop points and volume envelope data from per-zone generators.
266
+ *
267
+ * Loop points are stored as absolute indices into the SF2 sample pool.
268
+ * We convert to AudioBuffer-relative seconds by subtracting header.start
269
+ * and dividing by sampleRate.
270
+ *
271
+ * Volume envelope times are in SF2 timecents; sustain is centibels attenuation.
272
+ */
273
+ declare function extractLoopAndEnvelope(params: LoopAndEnvelopeParams): Omit<SoundFontSample, 'buffer' | 'playbackRate'>;
204
274
  /**
205
275
  * Caches parsed SoundFont2 data and AudioBuffers for efficient playback.
206
276
  *
@@ -239,33 +309,9 @@ declare class SoundFontCache {
239
309
  * @returns SoundFontSample or null if no sample found for this note
240
310
  */
241
311
  getAudioBuffer(midiNote: number, bankNumber?: number, presetNumber?: number): SoundFontSample | null;
242
- /**
243
- * Extract loop points and volume envelope data from per-zone generators.
244
- *
245
- * Loop points are stored as absolute indices into the SF2 sample pool.
246
- * We convert to AudioBuffer-relative seconds by subtracting header.start
247
- * and dividing by sampleRate.
248
- *
249
- * Volume envelope times are in SF2 timecents; sustain is centibels attenuation.
250
- */
251
- private extractLoopAndEnvelope;
252
- /**
253
- * Calculate playback rate for a MIDI note using the SF2 generator chain.
254
- *
255
- * SF2 root key resolution priority:
256
- * 1. OverridingRootKey generator (per-zone, most specific)
257
- * 2. sample.header.originalPitch (sample header)
258
- * 3. MIDI note 60 (middle C fallback)
259
- *
260
- * Tuning adjustments:
261
- * - CoarseTune generator (semitones, additive)
262
- * - FineTune generator (cents, additive)
263
- * - sample.header.pitchCorrection (cents, additive)
264
- */
265
- private calculatePlaybackRate;
266
312
  /**
267
313
  * Convert Int16Array sample data to an AudioBuffer.
268
- * SF2 samples are 16-bit signed integers; Web Audio needs Float32 [-1, 1].
314
+ * Uses the extracted int16ToFloat32 for the conversion, then copies into an AudioBuffer.
269
315
  */
270
316
  private int16ToAudioBuffer;
271
317
  /**
@@ -536,4 +582,4 @@ interface ToneAdapterOptions {
536
582
  }
537
583
  declare function createToneAdapter(options?: ToneAdapterOptions): PlayoutAdapter;
538
584
 
539
- export { type EffectsFunction, type FadeConfig, type FadeType, type MidiClipInfo, MidiToneTrack, type MidiToneTrackOptions, type PlayableTrack, SoundFontCache, type SoundFontSample, SoundFontToneTrack, type SoundFontToneTrackOptions, type ToneAdapterOptions, TonePlayout, type TonePlayoutOptions, ToneTrack, type ToneTrackOptions, type TrackEffectsFunction, applyFadeIn, applyFadeOut, closeGlobalAudioContext, createToneAdapter, getGlobalAudioContext, getGlobalAudioContextState, getGlobalContext, getGlobalToneContext, getMediaStreamSource, getUnderlyingAudioParam, hasMediaStreamSource, releaseMediaStreamSource, resumeGlobalAudioContext };
585
+ export { type EffectsFunction, type FadeConfig, type FadeType, type LoopAndEnvelopeParams, type MidiClipInfo, MidiToneTrack, type MidiToneTrackOptions, type PlayableTrack, type PlaybackRateParams, SoundFontCache, type SoundFontSample, SoundFontToneTrack, type SoundFontToneTrackOptions, type ToneAdapterOptions, TonePlayout, type TonePlayoutOptions, ToneTrack, type ToneTrackOptions, type TrackEffectsFunction, applyFadeIn, applyFadeOut, calculatePlaybackRate, closeGlobalAudioContext, createToneAdapter, extractLoopAndEnvelope, getGeneratorValue, getGlobalAudioContext, getGlobalAudioContextState, getGlobalContext, getGlobalToneContext, getMediaStreamSource, getUnderlyingAudioParam, hasMediaStreamSource, int16ToFloat32, releaseMediaStreamSource, resumeGlobalAudioContext, timecentsToSeconds };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Gain, ToneAudioNode, SynthOptions, Volume, BaseContext, Context } from 'tone';
2
2
  import { Fade, Track, MidiNoteData } from '@waveform-playlist/core';
3
+ import { ZoneMap, Generator, GeneratorType } from 'soundfont2';
3
4
  import { PlayoutAdapter } from '@waveform-playlist/engine';
4
5
 
5
6
  type TrackEffectsFunction = (graphEnd: Gain, masterGainNode: ToneAudioNode, isOffline: boolean) => void | (() => void);
@@ -201,6 +202,75 @@ interface SoundFontSample {
201
202
  /** Volume envelope release time in seconds */
202
203
  releaseVolEnv: number;
203
204
  }
205
+ /**
206
+ * Convert SF2 timecents to seconds.
207
+ * SF2 formula: seconds = 2^(timecents / 1200)
208
+ * Default -12000 timecents ≈ 0.001s (effectively instant).
209
+ */
210
+ declare function timecentsToSeconds(tc: number): number;
211
+ /**
212
+ * Get a numeric generator value from a zone map.
213
+ */
214
+ declare function getGeneratorValue(generators: ZoneMap<Generator>, type: GeneratorType): number | undefined;
215
+ /**
216
+ * Convert Int16Array sample data to Float32Array.
217
+ * SF2 samples are 16-bit signed integers; Web Audio needs Float32 [-1, 1].
218
+ */
219
+ declare function int16ToFloat32(samples: Int16Array): Float32Array;
220
+ /**
221
+ * Input parameters for playback rate calculation.
222
+ */
223
+ interface PlaybackRateParams {
224
+ /** Target MIDI note number (0-127) */
225
+ midiNote: number;
226
+ /** OverridingRootKey generator value, or undefined if not set */
227
+ overrideRootKey: number | undefined;
228
+ /** sample.header.originalPitch (255 means unpitched) */
229
+ originalPitch: number;
230
+ /** CoarseTune generator value in semitones (default 0) */
231
+ coarseTune: number;
232
+ /** FineTune generator value in cents (default 0) */
233
+ fineTune: number;
234
+ /** sample.header.pitchCorrection in cents (default 0) */
235
+ pitchCorrection: number;
236
+ }
237
+ /**
238
+ * Calculate playback rate for a MIDI note using the SF2 generator chain.
239
+ *
240
+ * SF2 root key resolution priority:
241
+ * 1. OverridingRootKey generator (per-zone, most specific)
242
+ * 2. sample.header.originalPitch (sample header)
243
+ * 3. MIDI note 60 (middle C fallback)
244
+ *
245
+ * Tuning adjustments:
246
+ * - CoarseTune generator (semitones, additive)
247
+ * - FineTune generator (cents, additive)
248
+ * - sample.header.pitchCorrection (cents, additive)
249
+ */
250
+ declare function calculatePlaybackRate(params: PlaybackRateParams): number;
251
+ /**
252
+ * Input parameters for loop and envelope extraction.
253
+ */
254
+ interface LoopAndEnvelopeParams {
255
+ /** SF2 generators zone map */
256
+ generators: ZoneMap<Generator>;
257
+ /** Sample header with loop points and sample rate */
258
+ header: {
259
+ startLoop: number;
260
+ endLoop: number;
261
+ sampleRate: number;
262
+ };
263
+ }
264
+ /**
265
+ * Extract loop points and volume envelope data from per-zone generators.
266
+ *
267
+ * Loop points are stored as absolute indices into the SF2 sample pool.
268
+ * We convert to AudioBuffer-relative seconds by subtracting header.start
269
+ * and dividing by sampleRate.
270
+ *
271
+ * Volume envelope times are in SF2 timecents; sustain is centibels attenuation.
272
+ */
273
+ declare function extractLoopAndEnvelope(params: LoopAndEnvelopeParams): Omit<SoundFontSample, 'buffer' | 'playbackRate'>;
204
274
  /**
205
275
  * Caches parsed SoundFont2 data and AudioBuffers for efficient playback.
206
276
  *
@@ -239,33 +309,9 @@ declare class SoundFontCache {
239
309
  * @returns SoundFontSample or null if no sample found for this note
240
310
  */
241
311
  getAudioBuffer(midiNote: number, bankNumber?: number, presetNumber?: number): SoundFontSample | null;
242
- /**
243
- * Extract loop points and volume envelope data from per-zone generators.
244
- *
245
- * Loop points are stored as absolute indices into the SF2 sample pool.
246
- * We convert to AudioBuffer-relative seconds by subtracting header.start
247
- * and dividing by sampleRate.
248
- *
249
- * Volume envelope times are in SF2 timecents; sustain is centibels attenuation.
250
- */
251
- private extractLoopAndEnvelope;
252
- /**
253
- * Calculate playback rate for a MIDI note using the SF2 generator chain.
254
- *
255
- * SF2 root key resolution priority:
256
- * 1. OverridingRootKey generator (per-zone, most specific)
257
- * 2. sample.header.originalPitch (sample header)
258
- * 3. MIDI note 60 (middle C fallback)
259
- *
260
- * Tuning adjustments:
261
- * - CoarseTune generator (semitones, additive)
262
- * - FineTune generator (cents, additive)
263
- * - sample.header.pitchCorrection (cents, additive)
264
- */
265
- private calculatePlaybackRate;
266
312
  /**
267
313
  * Convert Int16Array sample data to an AudioBuffer.
268
- * SF2 samples are 16-bit signed integers; Web Audio needs Float32 [-1, 1].
314
+ * Uses the extracted int16ToFloat32 for the conversion, then copies into an AudioBuffer.
269
315
  */
270
316
  private int16ToAudioBuffer;
271
317
  /**
@@ -536,4 +582,4 @@ interface ToneAdapterOptions {
536
582
  }
537
583
  declare function createToneAdapter(options?: ToneAdapterOptions): PlayoutAdapter;
538
584
 
539
- export { type EffectsFunction, type FadeConfig, type FadeType, type MidiClipInfo, MidiToneTrack, type MidiToneTrackOptions, type PlayableTrack, SoundFontCache, type SoundFontSample, SoundFontToneTrack, type SoundFontToneTrackOptions, type ToneAdapterOptions, TonePlayout, type TonePlayoutOptions, ToneTrack, type ToneTrackOptions, type TrackEffectsFunction, applyFadeIn, applyFadeOut, closeGlobalAudioContext, createToneAdapter, getGlobalAudioContext, getGlobalAudioContextState, getGlobalContext, getGlobalToneContext, getMediaStreamSource, getUnderlyingAudioParam, hasMediaStreamSource, releaseMediaStreamSource, resumeGlobalAudioContext };
585
+ export { type EffectsFunction, type FadeConfig, type FadeType, type LoopAndEnvelopeParams, type MidiClipInfo, MidiToneTrack, type MidiToneTrackOptions, type PlayableTrack, type PlaybackRateParams, SoundFontCache, type SoundFontSample, SoundFontToneTrack, type SoundFontToneTrackOptions, type ToneAdapterOptions, TonePlayout, type TonePlayoutOptions, ToneTrack, type ToneTrackOptions, type TrackEffectsFunction, applyFadeIn, applyFadeOut, calculatePlaybackRate, closeGlobalAudioContext, createToneAdapter, extractLoopAndEnvelope, getGeneratorValue, getGlobalAudioContext, getGlobalAudioContextState, getGlobalContext, getGlobalToneContext, getMediaStreamSource, getUnderlyingAudioParam, hasMediaStreamSource, int16ToFloat32, releaseMediaStreamSource, resumeGlobalAudioContext, timecentsToSeconds };
package/dist/index.js CHANGED
@@ -27,8 +27,11 @@ __export(index_exports, {
27
27
  ToneTrack: () => ToneTrack,
28
28
  applyFadeIn: () => applyFadeIn,
29
29
  applyFadeOut: () => applyFadeOut,
30
+ calculatePlaybackRate: () => calculatePlaybackRate,
30
31
  closeGlobalAudioContext: () => closeGlobalAudioContext,
31
32
  createToneAdapter: () => createToneAdapter,
33
+ extractLoopAndEnvelope: () => extractLoopAndEnvelope,
34
+ getGeneratorValue: () => getGeneratorValue,
32
35
  getGlobalAudioContext: () => getGlobalAudioContext,
33
36
  getGlobalAudioContextState: () => getGlobalAudioContextState,
34
37
  getGlobalContext: () => getGlobalContext,
@@ -36,8 +39,10 @@ __export(index_exports, {
36
39
  getMediaStreamSource: () => getMediaStreamSource,
37
40
  getUnderlyingAudioParam: () => getUnderlyingAudioParam,
38
41
  hasMediaStreamSource: () => hasMediaStreamSource,
42
+ int16ToFloat32: () => int16ToFloat32,
39
43
  releaseMediaStreamSource: () => releaseMediaStreamSource,
40
- resumeGlobalAudioContext: () => resumeGlobalAudioContext
44
+ resumeGlobalAudioContext: () => resumeGlobalAudioContext,
45
+ timecentsToSeconds: () => timecentsToSeconds
41
46
  });
42
47
  module.exports = __toCommonJS(index_exports);
43
48
 
@@ -1323,6 +1328,52 @@ var MAX_RELEASE_SECONDS = 5;
1323
1328
  function getGeneratorValue(generators, type) {
1324
1329
  return generators[type]?.value;
1325
1330
  }
1331
+ function int16ToFloat32(samples) {
1332
+ const floats = new Float32Array(samples.length);
1333
+ for (let i = 0; i < samples.length; i++) {
1334
+ floats[i] = samples[i] / 32768;
1335
+ }
1336
+ return floats;
1337
+ }
1338
+ function calculatePlaybackRate(params) {
1339
+ const { midiNote, overrideRootKey, originalPitch, coarseTune, fineTune, pitchCorrection } = params;
1340
+ const rootKey = overrideRootKey !== void 0 ? overrideRootKey : originalPitch !== 255 ? originalPitch : 60;
1341
+ const totalSemitones = midiNote - rootKey + coarseTune + (fineTune + pitchCorrection) / 100;
1342
+ return Math.pow(2, totalSemitones / 12);
1343
+ }
1344
+ function extractLoopAndEnvelope(params) {
1345
+ const { generators, header } = params;
1346
+ const loopMode = getGeneratorValue(generators, import_soundfont2.GeneratorType.SampleModes) ?? 0;
1347
+ const rawLoopStart = header.startLoop + (getGeneratorValue(generators, import_soundfont2.GeneratorType.StartLoopAddrsOffset) ?? 0) + (getGeneratorValue(generators, import_soundfont2.GeneratorType.StartLoopAddrsCoarseOffset) ?? 0) * 32768;
1348
+ const rawLoopEnd = header.endLoop + (getGeneratorValue(generators, import_soundfont2.GeneratorType.EndLoopAddrsOffset) ?? 0) + (getGeneratorValue(generators, import_soundfont2.GeneratorType.EndLoopAddrsCoarseOffset) ?? 0) * 32768;
1349
+ const loopStart = rawLoopStart / header.sampleRate;
1350
+ const loopEnd = rawLoopEnd / header.sampleRate;
1351
+ const attackVolEnv = timecentsToSeconds(
1352
+ getGeneratorValue(generators, import_soundfont2.GeneratorType.AttackVolEnv) ?? -12e3
1353
+ );
1354
+ const holdVolEnv = timecentsToSeconds(
1355
+ getGeneratorValue(generators, import_soundfont2.GeneratorType.HoldVolEnv) ?? -12e3
1356
+ );
1357
+ const decayVolEnv = timecentsToSeconds(
1358
+ getGeneratorValue(generators, import_soundfont2.GeneratorType.DecayVolEnv) ?? -12e3
1359
+ );
1360
+ const releaseVolEnv = Math.min(
1361
+ timecentsToSeconds(getGeneratorValue(generators, import_soundfont2.GeneratorType.ReleaseVolEnv) ?? -12e3),
1362
+ MAX_RELEASE_SECONDS
1363
+ );
1364
+ const sustainCb = getGeneratorValue(generators, import_soundfont2.GeneratorType.SustainVolEnv) ?? 0;
1365
+ const sustainVolEnv = Math.pow(10, -sustainCb / 200);
1366
+ return {
1367
+ loopMode,
1368
+ loopStart,
1369
+ loopEnd,
1370
+ attackVolEnv,
1371
+ holdVolEnv,
1372
+ decayVolEnv,
1373
+ sustainVolEnv,
1374
+ releaseVolEnv
1375
+ };
1376
+ }
1326
1377
  var SoundFontCache = class {
1327
1378
  /**
1328
1379
  * @param context Optional AudioContext for createBuffer(). If omitted, uses
@@ -1385,88 +1436,28 @@ var SoundFontCache = class {
1385
1436
  buffer = this.int16ToAudioBuffer(sample.data, sample.header.sampleRate);
1386
1437
  this.audioBufferCache.set(sampleIndex, buffer);
1387
1438
  }
1388
- const playbackRate = this.calculatePlaybackRate(midiNote, keyData);
1389
- const loopAndEnvelope = this.extractLoopAndEnvelope(keyData);
1439
+ const playbackRate = calculatePlaybackRate({
1440
+ midiNote,
1441
+ overrideRootKey: getGeneratorValue(keyData.generators, import_soundfont2.GeneratorType.OverridingRootKey),
1442
+ originalPitch: sample.header.originalPitch,
1443
+ coarseTune: getGeneratorValue(keyData.generators, import_soundfont2.GeneratorType.CoarseTune) ?? 0,
1444
+ fineTune: getGeneratorValue(keyData.generators, import_soundfont2.GeneratorType.FineTune) ?? 0,
1445
+ pitchCorrection: sample.header.pitchCorrection ?? 0
1446
+ });
1447
+ const loopAndEnvelope = extractLoopAndEnvelope({
1448
+ generators: keyData.generators,
1449
+ header: keyData.sample.header
1450
+ });
1390
1451
  return { buffer, playbackRate, ...loopAndEnvelope };
1391
1452
  }
1392
- /**
1393
- * Extract loop points and volume envelope data from per-zone generators.
1394
- *
1395
- * Loop points are stored as absolute indices into the SF2 sample pool.
1396
- * We convert to AudioBuffer-relative seconds by subtracting header.start
1397
- * and dividing by sampleRate.
1398
- *
1399
- * Volume envelope times are in SF2 timecents; sustain is centibels attenuation.
1400
- */
1401
- extractLoopAndEnvelope(keyData) {
1402
- const { generators } = keyData;
1403
- const header = keyData.sample.header;
1404
- const loopMode = getGeneratorValue(generators, import_soundfont2.GeneratorType.SampleModes) ?? 0;
1405
- const rawLoopStart = header.startLoop + (getGeneratorValue(generators, import_soundfont2.GeneratorType.StartLoopAddrsOffset) ?? 0) + (getGeneratorValue(generators, import_soundfont2.GeneratorType.StartLoopAddrsCoarseOffset) ?? 0) * 32768;
1406
- const rawLoopEnd = header.endLoop + (getGeneratorValue(generators, import_soundfont2.GeneratorType.EndLoopAddrsOffset) ?? 0) + (getGeneratorValue(generators, import_soundfont2.GeneratorType.EndLoopAddrsCoarseOffset) ?? 0) * 32768;
1407
- const loopStart = rawLoopStart / header.sampleRate;
1408
- const loopEnd = rawLoopEnd / header.sampleRate;
1409
- const attackVolEnv = timecentsToSeconds(
1410
- getGeneratorValue(generators, import_soundfont2.GeneratorType.AttackVolEnv) ?? -12e3
1411
- );
1412
- const holdVolEnv = timecentsToSeconds(
1413
- getGeneratorValue(generators, import_soundfont2.GeneratorType.HoldVolEnv) ?? -12e3
1414
- );
1415
- const decayVolEnv = timecentsToSeconds(
1416
- getGeneratorValue(generators, import_soundfont2.GeneratorType.DecayVolEnv) ?? -12e3
1417
- );
1418
- const releaseVolEnv = Math.min(
1419
- timecentsToSeconds(getGeneratorValue(generators, import_soundfont2.GeneratorType.ReleaseVolEnv) ?? -12e3),
1420
- MAX_RELEASE_SECONDS
1421
- );
1422
- const sustainCb = getGeneratorValue(generators, import_soundfont2.GeneratorType.SustainVolEnv) ?? 0;
1423
- const sustainVolEnv = Math.pow(10, -sustainCb / 200);
1424
- return {
1425
- loopMode,
1426
- loopStart,
1427
- loopEnd,
1428
- attackVolEnv,
1429
- holdVolEnv,
1430
- decayVolEnv,
1431
- sustainVolEnv,
1432
- releaseVolEnv
1433
- };
1434
- }
1435
- /**
1436
- * Calculate playback rate for a MIDI note using the SF2 generator chain.
1437
- *
1438
- * SF2 root key resolution priority:
1439
- * 1. OverridingRootKey generator (per-zone, most specific)
1440
- * 2. sample.header.originalPitch (sample header)
1441
- * 3. MIDI note 60 (middle C fallback)
1442
- *
1443
- * Tuning adjustments:
1444
- * - CoarseTune generator (semitones, additive)
1445
- * - FineTune generator (cents, additive)
1446
- * - sample.header.pitchCorrection (cents, additive)
1447
- */
1448
- calculatePlaybackRate(midiNote, keyData) {
1449
- const sample = keyData.sample;
1450
- const generators = keyData.generators;
1451
- const overrideRootKey = getGeneratorValue(generators, import_soundfont2.GeneratorType.OverridingRootKey);
1452
- const originalPitch = sample.header.originalPitch;
1453
- const rootKey = overrideRootKey !== void 0 ? overrideRootKey : originalPitch !== 255 ? originalPitch : 60;
1454
- const coarseTune = getGeneratorValue(generators, import_soundfont2.GeneratorType.CoarseTune) ?? 0;
1455
- const fineTune = getGeneratorValue(generators, import_soundfont2.GeneratorType.FineTune) ?? 0;
1456
- const pitchCorrection = sample.header.pitchCorrection ?? 0;
1457
- const totalSemitones = midiNote - rootKey + coarseTune + (fineTune + pitchCorrection) / 100;
1458
- return Math.pow(2, totalSemitones / 12);
1459
- }
1460
1453
  /**
1461
1454
  * Convert Int16Array sample data to an AudioBuffer.
1462
- * SF2 samples are 16-bit signed integers; Web Audio needs Float32 [-1, 1].
1455
+ * Uses the extracted int16ToFloat32 for the conversion, then copies into an AudioBuffer.
1463
1456
  */
1464
1457
  int16ToAudioBuffer(data, sampleRate) {
1465
- const buffer = this.context.createBuffer(1, data.length, sampleRate);
1466
- const channel = buffer.getChannelData(0);
1467
- for (let i = 0; i < data.length; i++) {
1468
- channel[i] = data[i] / 32768;
1469
- }
1458
+ const floats = int16ToFloat32(data);
1459
+ const buffer = this.context.createBuffer(1, floats.length, sampleRate);
1460
+ buffer.getChannelData(0).set(floats);
1470
1461
  return buffer;
1471
1462
  }
1472
1463
  /**
@@ -1742,8 +1733,11 @@ function createToneAdapter(options) {
1742
1733
  ToneTrack,
1743
1734
  applyFadeIn,
1744
1735
  applyFadeOut,
1736
+ calculatePlaybackRate,
1745
1737
  closeGlobalAudioContext,
1746
1738
  createToneAdapter,
1739
+ extractLoopAndEnvelope,
1740
+ getGeneratorValue,
1747
1741
  getGlobalAudioContext,
1748
1742
  getGlobalAudioContextState,
1749
1743
  getGlobalContext,
@@ -1751,7 +1745,9 @@ function createToneAdapter(options) {
1751
1745
  getMediaStreamSource,
1752
1746
  getUnderlyingAudioParam,
1753
1747
  hasMediaStreamSource,
1748
+ int16ToFloat32,
1754
1749
  releaseMediaStreamSource,
1755
- resumeGlobalAudioContext
1750
+ resumeGlobalAudioContext,
1751
+ timecentsToSeconds
1756
1752
  });
1757
1753
  //# sourceMappingURL=index.js.map