@idealyst/audio 1.2.76 → 1.2.78
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 +1 -1
- package/src/hooks/usePlayer.ts +9 -0
- package/src/playback/Player.native.ts +44 -0
- package/src/playback/Player.web.ts +41 -0
- package/src/types.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/audio",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.78",
|
|
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",
|
package/src/hooks/usePlayer.ts
CHANGED
|
@@ -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>;
|