/** * Memory limit checking utilities for audio file handling */ export interface MemoryCheckResult { allowed: boolean; warning?: string; estimatedMemoryMB: number; availableMemoryMB?: number; } /** * Estimate memory required for an audio buffer * @param duration Duration in seconds * @param sampleRate Sample rate (default: 48000 Hz) * @param channels Number of channels (default: 2 for stereo) * @returns Estimated memory in MB */ export function estimateAudioMemory( duration: number, sampleRate: number = 48000, channels: number = 2 ): number { // Each sample is a 32-bit float (4 bytes) const bytesPerSample = 4; const totalSamples = duration * sampleRate * channels; const bytes = totalSamples * bytesPerSample; // Convert to MB return bytes / (1024 * 1024); } /** * Get available device memory if supported * @returns Available memory in MB, or undefined if not supported */ export function getAvailableMemory(): number | undefined { if (typeof navigator === 'undefined') return undefined; // @ts-ignore - deviceMemory is not in TypeScript types yet const deviceMemory = navigator.deviceMemory; if (typeof deviceMemory === 'number') { // deviceMemory is in GB, convert to MB return deviceMemory * 1024; } return undefined; } /** * Check if a file size is within safe memory limits * @param fileSizeBytes File size in bytes * @returns Memory check result */ export function checkFileMemoryLimit(fileSizeBytes: number): MemoryCheckResult { // Estimate memory usage (audio files decompress to ~10x their size) const estimatedMemoryMB = (fileSizeBytes / (1024 * 1024)) * 10; const availableMemoryMB = getAvailableMemory(); // Conservative limits const WARN_THRESHOLD_MB = 100; // Warn if file will use > 100MB const MAX_RECOMMENDED_MB = 500; // Don't recommend files > 500MB if (estimatedMemoryMB > MAX_RECOMMENDED_MB) { return { allowed: false, warning: `This file may require ${Math.round(estimatedMemoryMB)}MB of memory. ` + `Files larger than ${MAX_RECOMMENDED_MB}MB are not recommended as they may cause performance issues or crashes.`, estimatedMemoryMB, availableMemoryMB, }; } if (estimatedMemoryMB > WARN_THRESHOLD_MB) { const warning = availableMemoryMB ? `This file will require approximately ${Math.round(estimatedMemoryMB)}MB of memory. ` + `Your device has ${Math.round(availableMemoryMB)}MB available.` : `This file will require approximately ${Math.round(estimatedMemoryMB)}MB of memory. ` + `Large files may cause performance issues on devices with limited memory.`; return { allowed: true, warning, estimatedMemoryMB, availableMemoryMB, }; } return { allowed: true, estimatedMemoryMB, availableMemoryMB, }; } /** * Check if an audio buffer is within safe memory limits * @param duration Duration in seconds * @param sampleRate Sample rate * @param channels Number of channels * @returns Memory check result */ export function checkAudioBufferMemoryLimit( duration: number, sampleRate: number = 48000, channels: number = 2 ): MemoryCheckResult { const estimatedMemoryMB = estimateAudioMemory(duration, sampleRate, channels); const availableMemoryMB = getAvailableMemory(); const WARN_THRESHOLD_MB = 100; const MAX_RECOMMENDED_MB = 500; if (estimatedMemoryMB > MAX_RECOMMENDED_MB) { return { allowed: false, warning: `This audio (${Math.round(duration / 60)} minutes) will require ${Math.round(estimatedMemoryMB)}MB of memory. ` + `Audio longer than ${Math.round((MAX_RECOMMENDED_MB / sampleRate / channels / 4) / 60)} minutes may cause performance issues.`, estimatedMemoryMB, availableMemoryMB, }; } if (estimatedMemoryMB > WARN_THRESHOLD_MB) { const warning = availableMemoryMB ? `This audio will require approximately ${Math.round(estimatedMemoryMB)}MB of memory. ` + `Your device has ${Math.round(availableMemoryMB)}MB available.` : `This audio will require approximately ${Math.round(estimatedMemoryMB)}MB of memory.`; return { allowed: true, warning, estimatedMemoryMB, availableMemoryMB, }; } return { allowed: true, estimatedMemoryMB, availableMemoryMB, }; } /** * Format memory size in human-readable format * @param bytes Size in bytes * @returns Formatted string (e.g., "1.5 MB", "250 KB") */ export function formatMemorySize(bytes: number): string { if (bytes < 1024) { return `${bytes} B`; } else if (bytes < 1024 * 1024) { return `${(bytes / 1024).toFixed(1)} KB`; } else if (bytes < 1024 * 1024 * 1024) { return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } else { return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; } }