@idealyst/audio 1.2.77 → 1.2.79

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/audio",
3
- "version": "1.2.77",
3
+ "version": "1.2.79",
4
4
  "description": "Unified cross-platform audio for React and React Native - recording, playback, and PCM streaming",
5
5
  "documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/audio#readme",
6
6
  "readme": "README.md",
@@ -104,6 +104,14 @@ export function usePlayer(options: UsePlayerOptions = {}): UsePlayerResult {
104
104
  }
105
105
  }, []);
106
106
 
107
+ // Clear buffer (immediately stops all buffered/scheduled audio)
108
+ const clearBuffer = useCallback(() => {
109
+ const player = playerRef.current;
110
+ if (player) {
111
+ player.clearBuffer();
112
+ }
113
+ }, []);
114
+
107
115
  // Play
108
116
  const play = useCallback(async () => {
109
117
  const player = getPlayer();
@@ -233,6 +241,7 @@ export function usePlayer(options: UsePlayerOptions = {}): UsePlayerResult {
233
241
  loadPCMStream,
234
242
  feedPCMData,
235
243
  flush,
244
+ clearBuffer,
236
245
 
237
246
  // Playback control
238
247
  play,
@@ -49,6 +49,7 @@ export class NativePlayer implements IPlayer {
49
49
  private isStreaming: boolean = false;
50
50
  private streamScheduleInterval: ReturnType<typeof setInterval> | null = null;
51
51
  private nextScheduleTime: number = 0;
52
+ private scheduledSources: RNAudioBufferSourceNode[] = [];
52
53
 
53
54
  // Position tracking
54
55
  private positionIntervalId: ReturnType<typeof setInterval> | null = null;
@@ -193,6 +194,24 @@ export class NativePlayer implements IPlayer {
193
194
  }
194
195
  }
195
196
 
197
+ clearBuffer(): void {
198
+ // Clear pending PCM chunks
199
+ this.pcmBuffer = [];
200
+
201
+ // Stop all scheduled sources that haven't finished playing
202
+ this.stopScheduledSources();
203
+
204
+ // Reset schedule time to now so new audio plays immediately
205
+ const ctx = this.audioContext.getContext() as RNAudioContext;
206
+ if (ctx) {
207
+ this.nextScheduleTime = ctx.currentTime;
208
+ }
209
+
210
+ // Update buffer status
211
+ this._status.buffered = 0;
212
+ this.notifyBufferChange(0);
213
+ }
214
+
196
215
  async play(): Promise<void> {
197
216
  const ctx = this.audioContext.getContext() as RNAudioContext;
198
217
  if (!ctx) {
@@ -367,6 +386,18 @@ export class NativePlayer implements IPlayer {
367
386
  source.buffer = audioBuffer;
368
387
  source.connect(this.gainNode);
369
388
 
389
+ // Track this source so we can stop it on clearBuffer()
390
+ this.scheduledSources.push(source);
391
+
392
+ // Clean up reference when source ends (use timeout as fallback since onended may not be supported)
393
+ const duration = audioBuffer.duration * 1000;
394
+ setTimeout(() => {
395
+ const idx = this.scheduledSources.indexOf(source);
396
+ if (idx !== -1) {
397
+ this.scheduledSources.splice(idx, 1);
398
+ }
399
+ }, duration + 100);
400
+
370
401
  const scheduleTime = Math.max(this.nextScheduleTime, ctx.currentTime);
371
402
  source.start(scheduleTime);
372
403
 
@@ -412,9 +443,22 @@ export class NativePlayer implements IPlayer {
412
443
  this.stopPositionTracking();
413
444
  this.stopStreamScheduler();
414
445
  this.stopSourceNode();
446
+ this.stopScheduledSources();
415
447
  this.isStreaming = false;
416
448
  }
417
449
 
450
+ private stopScheduledSources(): void {
451
+ for (const source of this.scheduledSources) {
452
+ try {
453
+ source.stop();
454
+ source.disconnect();
455
+ } catch {
456
+ // Ignore errors
457
+ }
458
+ }
459
+ this.scheduledSources = [];
460
+ }
461
+
418
462
  private updateState(state: PlayerState): void {
419
463
  this._status.state = state;
420
464
  this._status.isPlaying = state === 'playing';
@@ -43,6 +43,7 @@ export class WebPlayer implements IPlayer {
43
43
  private isStreaming: boolean = false;
44
44
  private streamScheduleInterval: ReturnType<typeof setInterval> | null = null;
45
45
  private nextScheduleTime: number = 0;
46
+ private scheduledSources: AudioBufferSourceNode[] = [];
46
47
 
47
48
  // Position tracking
48
49
  private positionIntervalId: ReturnType<typeof setInterval> | null = null;
@@ -187,6 +188,24 @@ export class WebPlayer implements IPlayer {
187
188
  }
188
189
  }
189
190
 
191
+ clearBuffer(): void {
192
+ // Clear pending PCM chunks
193
+ this.pcmBuffer = [];
194
+
195
+ // Stop all scheduled sources that haven't finished playing
196
+ this.stopScheduledSources();
197
+
198
+ // Reset schedule time to now so new audio plays immediately
199
+ const ctx = this.audioContext.getContext() as AudioContext;
200
+ if (ctx) {
201
+ this.nextScheduleTime = ctx.currentTime;
202
+ }
203
+
204
+ // Update buffer status
205
+ this._status.buffered = 0;
206
+ this.notifyBufferChange(0);
207
+ }
208
+
190
209
  async play(): Promise<void> {
191
210
  const ctx = this.audioContext.getContext() as AudioContext;
192
211
  if (!ctx) {
@@ -368,6 +387,15 @@ export class WebPlayer implements IPlayer {
368
387
  source.buffer = audioBuffer;
369
388
  source.connect(this.gainNode);
370
389
 
390
+ // Track this source so we can stop it on clearBuffer()
391
+ this.scheduledSources.push(source);
392
+ source.onended = () => {
393
+ const idx = this.scheduledSources.indexOf(source);
394
+ if (idx !== -1) {
395
+ this.scheduledSources.splice(idx, 1);
396
+ }
397
+ };
398
+
371
399
  const scheduleTime = Math.max(this.nextScheduleTime, ctx.currentTime);
372
400
  source.start(scheduleTime);
373
401
 
@@ -413,9 +441,22 @@ export class WebPlayer implements IPlayer {
413
441
  this.stopPositionTracking();
414
442
  this.stopStreamScheduler();
415
443
  this.stopSourceNode();
444
+ this.stopScheduledSources();
416
445
  this.isStreaming = false;
417
446
  }
418
447
 
448
+ private stopScheduledSources(): void {
449
+ for (const source of this.scheduledSources) {
450
+ try {
451
+ source.stop();
452
+ source.disconnect();
453
+ } catch {
454
+ // Ignore errors
455
+ }
456
+ }
457
+ this.scheduledSources = [];
458
+ }
459
+
419
460
  private updateState(state: PlayerState): void {
420
461
  this._status.state = state;
421
462
  this._status.isPlaying = state === 'playing';
package/src/types.ts CHANGED
@@ -234,6 +234,7 @@ export interface IPlayer {
234
234
  loadPCMStream(config: AudioConfig): Promise<void>;
235
235
  feedPCMData(data: ArrayBuffer | Int16Array): void;
236
236
  flush(): Promise<void>;
237
+ clearBuffer(): void;
237
238
 
238
239
  // Playback control
239
240
  play(): Promise<void>;
@@ -438,6 +439,7 @@ export interface UsePlayerResult {
438
439
  loadPCMStream: (config: AudioConfig) => Promise<void>;
439
440
  feedPCMData: (data: ArrayBuffer | Int16Array) => void;
440
441
  flush: () => Promise<void>;
442
+ clearBuffer: () => void;
441
443
 
442
444
  // Playback control
443
445
  play: () => Promise<void>;