Files
audio-ui/lib/utils/audio-cleanup.ts

150 lines
3.2 KiB
TypeScript
Raw Normal View History

/**
* Audio cleanup utilities to prevent memory leaks
*/
/**
* Safely disconnect and cleanup an AudioNode
*/
export function cleanupAudioNode(node: AudioNode | null | undefined): void {
if (!node) return;
try {
node.disconnect();
} catch (error) {
// Node may already be disconnected, ignore error
console.debug('AudioNode cleanup error (expected):', error);
}
}
/**
* Cleanup multiple audio nodes
*/
export function cleanupAudioNodes(nodes: Array<AudioNode | null | undefined>): void {
nodes.forEach(cleanupAudioNode);
}
/**
* Safely stop and cleanup an AudioBufferSourceNode
*/
export function cleanupAudioSource(source: AudioBufferSourceNode | null | undefined): void {
if (!source) return;
try {
source.stop();
} catch (error) {
// Source may already be stopped, ignore error
console.debug('AudioSource stop error (expected):', error);
}
cleanupAudioNode(source);
}
/**
* Cleanup canvas and release resources
*/
export function cleanupCanvas(canvas: HTMLCanvasElement | null | undefined): void {
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (ctx) {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Reset transform
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
// Release context (helps with memory)
canvas.width = 0;
canvas.height = 0;
}
/**
* Cancel animation frame safely
*/
export function cleanupAnimationFrame(frameId: number | null | undefined): void {
if (frameId !== null && frameId !== undefined) {
cancelAnimationFrame(frameId);
}
}
/**
* Cleanup media stream tracks
*/
export function cleanupMediaStream(stream: MediaStream | null | undefined): void {
if (!stream) return;
stream.getTracks().forEach(track => {
track.stop();
});
}
/**
* Create a cleanup registry for managing multiple cleanup tasks
*/
export class CleanupRegistry {
private cleanupTasks: Array<() => void> = [];
/**
* Register a cleanup task
*/
register(cleanup: () => void): void {
this.cleanupTasks.push(cleanup);
}
/**
* Register an audio node for cleanup
*/
registerAudioNode(node: AudioNode): void {
this.register(() => cleanupAudioNode(node));
}
/**
* Register an audio source for cleanup
*/
registerAudioSource(source: AudioBufferSourceNode): void {
this.register(() => cleanupAudioSource(source));
}
/**
* Register a canvas for cleanup
*/
registerCanvas(canvas: HTMLCanvasElement): void {
this.register(() => cleanupCanvas(canvas));
}
/**
* Register an animation frame for cleanup
*/
registerAnimationFrame(frameId: number): void {
this.register(() => cleanupAnimationFrame(frameId));
}
/**
* Register a media stream for cleanup
*/
registerMediaStream(stream: MediaStream): void {
this.register(() => cleanupMediaStream(stream));
}
/**
* Execute all cleanup tasks and clear the registry
*/
cleanup(): void {
this.cleanupTasks.forEach(task => {
try {
task();
} catch (error) {
console.error('Cleanup task failed:', error);
}
});
this.cleanupTasks = [];
}
/**
* Get the number of registered cleanup tasks
*/
get size(): number {
return this.cleanupTasks.length;
}
}