UNPKG

audio.libx.js

Version:

Comprehensive audio library with progressive streaming, recording capabilities, real-time processing, and intelligent caching for web applications

1,177 lines (1,030 loc) β€’ 123 kB
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>audio.libx.js - Comprehensive Demo</title> <style> :root { --primary-color: #007cba; --primary-dark: #005a8b; --secondary-color: #28a745; --danger-color: #dc3545; --warning-color: #ffc107; --info-color: #17a2b8; --light-bg: #f8f9fa; --dark-bg: #343a40; --border-color: #dee2e6; --text-color: #212529; --text-muted: #6c757d; --shadow: 0 2px 4px rgba(0, 0, 0, 0.1); --radius: 8px; --transition: all 0.3s ease; } * { box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; line-height: 1.6; color: var(--text-color); margin: 0; padding: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; } .header { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); padding: 1rem 0; box-shadow: var(--shadow); position: sticky; top: 0; z-index: 100; } .header-content { max-width: 1200px; margin: 0 auto; padding: 0 1rem; display: flex; justify-content: space-between; align-items: center; } .logo { font-size: 1.5rem; font-weight: bold; color: var(--primary-color); } .version { background: var(--primary-color); color: white; padding: 0.25rem 0.5rem; border-radius: 12px; font-size: 0.75rem; font-weight: 500; } .main-container { max-width: 1200px; margin: 2rem auto; padding: 0 1rem; } .tabs { display: flex; background: rgba(255, 255, 255, 0.9); border-radius: var(--radius) var(--radius) 0 0; overflow: hidden; box-shadow: var(--shadow); flex-wrap: wrap; } .tab { flex: 1; min-width: 120px; padding: 1rem; background: transparent; border: none; cursor: pointer; font-weight: 500; color: var(--text-muted); transition: var(--transition); border-bottom: 3px solid transparent; } .tab:hover { background: rgba(0, 124, 186, 0.1); color: var(--primary-color); } .tab.active { color: var(--primary-color); background: rgba(0, 124, 186, 0.1); border-bottom-color: var(--primary-color); } .tab-content { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); border-radius: 0 0 var(--radius) var(--radius); box-shadow: var(--shadow); min-height: 600px; } .tab-pane { display: none; padding: 2rem; animation: fadeIn 0.3s ease; } .tab-pane.active { display: block; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .section { margin-bottom: 2rem; padding: 1.5rem; background: white; border-radius: var(--radius); box-shadow: var(--shadow); } .section-title { margin: 0 0 1rem 0; color: var(--primary-color); font-size: 1.25rem; font-weight: 600; display: flex; align-items: center; gap: 0.5rem; } .section-description { color: var(--text-muted); margin-bottom: 1.5rem; font-size: 0.9rem; } .controls { display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; margin: 1rem 0; } .btn { padding: 0.5rem 1rem; border: none; border-radius: 6px; cursor: pointer; font-weight: 500; font-size: 0.875rem; transition: var(--transition); display: inline-flex; align-items: center; gap: 0.5rem; text-decoration: none; } .btn-primary { background: var(--primary-color); color: white; } .btn-primary:hover:not(:disabled) { background: var(--primary-dark); transform: translateY(-1px); } .btn-secondary { background: var(--text-muted); color: white; } .btn-success { background: var(--secondary-color); color: white; } .btn-danger { background: var(--danger-color); color: white; } .btn-warning { background: var(--warning-color); color: var(--text-color); } .btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none !important; } .btn.recording { animation: pulse 1.5s infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } } .form-control { padding: 0.5rem 0.75rem; border: 1px solid var(--border-color); border-radius: 6px; font-size: 0.875rem; transition: var(--transition); flex: 1; min-width: 200px; } .form-control:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.2); } .progress-container { margin: 1rem 0; } .progress { width: 100%; height: 8px; background: var(--light-bg); border-radius: 4px; overflow: hidden; } .progress-bar { height: 100%; background: linear-gradient(90deg, var(--primary-color), var(--info-color)); width: 0%; transition: width 0.3s ease; border-radius: 4px; } .level-meter { width: 200px; height: 20px; background: var(--light-bg); border-radius: 10px; overflow: hidden; position: relative; } .level-bar { height: 100%; background: linear-gradient(to right, #28a745, #ffc107, #dc3545); width: 0%; transition: width 0.1s ease; } .status { padding: 0.75rem 1rem; border-radius: 6px; margin: 1rem 0; border-left: 4px solid var(--info-color); background: rgba(23, 162, 184, 0.1); font-size: 0.875rem; } .status.error { background: rgba(220, 53, 69, 0.1); border-left-color: var(--danger-color); color: var(--danger-color); } .status.success { background: rgba(40, 167, 69, 0.1); border-left-color: var(--secondary-color); color: var(--secondary-color); } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem; margin: 1rem 0; } .stat-card { background: var(--light-bg); padding: 1rem; border-radius: 6px; text-align: center; border: 1px solid var(--border-color); } .stat-value { font-size: 1.5rem; font-weight: bold; color: var(--primary-color); display: block; } .stat-label { font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; } .log { background: #1e1e1e; color: #f8f8f2; border-radius: 6px; padding: 1rem; max-height: 300px; overflow-y: auto; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 0.8rem; line-height: 1.4; } .log-entry { margin-bottom: 0.25rem; } .log-timestamp { color: #6c757d; } .log-message { color: #f8f8f2; } .log-data { color: #50fa7b; font-style: italic; } .config-section { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin: 1rem 0; } .config-item { display: flex; flex-direction: column; gap: 0.5rem; } .config-item label { font-weight: 500; font-size: 0.875rem; color: var(--text-color); } .checkbox-item { display: flex; align-items: center; gap: 0.5rem; } .checkbox-item input[type='checkbox'] { margin: 0; } .visualizer { width: 100%; height: 120px; background: #000; border-radius: 6px; margin: 1rem 0; } .audio-element { width: 100%; margin: 1rem 0; } .recordings-list { max-height: 400px; overflow-y: auto; } .recording-item { background: var(--light-bg); padding: 1rem; margin: 0.5rem 0; border-radius: 6px; border: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center; gap: 1rem; } .recording-info { flex: 1; } .recording-title { font-weight: 600; margin-bottom: 0.25rem; } .recording-meta { font-size: 0.8rem; color: var(--text-muted); } .recording-actions { display: flex; gap: 0.5rem; } .duration-display { font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 1.25rem; font-weight: bold; color: var(--primary-color); padding: 0.5rem 1rem; background: var(--light-bg); border-radius: 6px; border: 1px solid var(--border-color); } .processing-controls { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem; margin: 1rem 0; } .processing-section { background: var(--light-bg); padding: 1rem; border-radius: 6px; border: 1px solid var(--border-color); } .processing-title { font-weight: 600; margin-bottom: 0.5rem; color: var(--primary-color); } .range-input { width: 100%; margin: 0.5rem 0; } .range-value { font-weight: 500; color: var(--primary-color); } .feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; margin: 1rem 0; } .feature-card { background: white; padding: 1.5rem; border-radius: var(--radius); box-shadow: var(--shadow); border: 1px solid var(--border-color); } .feature-icon { font-size: 2rem; margin-bottom: 0.5rem; } .feature-title { font-weight: 600; margin-bottom: 0.5rem; color: var(--primary-color); } .feature-description { font-size: 0.875rem; color: var(--text-muted); line-height: 1.5; } .quick-examples { display: flex; gap: 0.5rem; flex-wrap: wrap; margin: 0.5rem 0; } .quick-example { padding: 0.25rem 0.5rem; background: var(--light-bg); border: 1px solid var(--border-color); border-radius: 4px; font-size: 0.75rem; cursor: pointer; transition: var(--transition); } .quick-example:hover { background: var(--primary-color); color: white; } @media (max-width: 768px) { .tabs { flex-direction: column; } .tab { flex: none; text-align: left; } .controls { flex-direction: column; align-items: stretch; } .controls .btn { justify-content: center; } .config-section { grid-template-columns: 1fr; } .stats-grid { grid-template-columns: repeat(2, 1fr); } .recording-item { flex-direction: column; align-items: stretch; gap: 0.5rem; } .recording-actions { justify-content: center; } } .loading { display: inline-block; width: 12px; height: 12px; border: 2px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: white; animation: spin 0.8s ease-in-out infinite; } @keyframes spin { to { transform: rotate(360deg); } } .tooltip { position: relative; cursor: help; } .tooltip:hover::after { content: attr(data-tooltip); position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background: var(--dark-bg); color: white; padding: 0.5rem; border-radius: 4px; font-size: 0.75rem; white-space: nowrap; z-index: 1000; } .empty-state { text-align: center; padding: 2rem; color: var(--text-muted); font-style: italic; } .empty-state-icon { font-size: 3rem; opacity: 0.5; margin-bottom: 1rem; } </style> </head> <body> <header class="header"> <div class="header-content"> <div class="logo">🎡 audio.libx.js</div> <div class="version">v0.2.0</div> </div> </header> <main class="main-container"> <div class="tabs"> <button class="tab active" data-tab="overview">πŸ“– Overview</button> <button class="tab" data-tab="streaming">🎡 Streaming</button> <button class="tab" data-tab="recording">🎀 Recording</button> <button class="tab" data-tab="processing">βš™οΈ Processing</button> <button class="tab" data-tab="caching">πŸ’Ύ Caching</button> <button class="tab" data-tab="realtime">πŸ“Š Real-time</button> <button class="tab" data-tab="permissions">πŸ”’ Permissions</button> </div> <div class="tab-content"> <!-- Overview Tab --> <div id="overview" class="tab-pane active"> <div class="section"> <h2 class="section-title">🎡 Welcome to audio.libx.js</h2> <p class="section-description"> A comprehensive progressive audio streaming library with real-time processing, recording, and caching capabilities. Explore all features using the tabs above. </p> <div class="feature-grid"> <div class="feature-card"> <div class="feature-icon">🎡</div> <div class="feature-title">Progressive Streaming</div> <div class="feature-description"> Stream audio with MediaSource Extensions, intelligent buffering, and cross-platform compatibility including iOS ManagedMediaSource support. </div> </div> <div class="feature-card"> <div class="feature-icon">🎀</div> <div class="feature-title">Audio Recording</div> <div class="feature-description"> High-quality audio recording with real-time processing, permission management, and configurable quality settings. </div> </div> <div class="feature-card"> <div class="feature-icon">βš™οΈ</div> <div class="feature-title">Audio Processing</div> <div class="feature-description"> Advanced audio processing including silence trimming, format conversion, and audio buffer manipulation. </div> </div> <div class="feature-card"> <div class="feature-icon">πŸ’Ύ</div> <div class="feature-title">Intelligent Caching</div> <div class="feature-description"> Persistent IndexedDB storage with memory caching, statistics tracking, and automatic cleanup management. </div> </div> <div class="feature-card"> <div class="feature-icon">πŸ“Š</div> <div class="feature-title">Real-time Processing</div> <div class="feature-description"> Live audio analysis with effects, silence detection, level monitoring, and visual feedback. </div> </div> <div class="feature-card"> <div class="feature-icon">πŸ”’</div> <div class="feature-title">Permission Management</div> <div class="feature-description"> Cross-browser permission handling with user-friendly error messages and device enumeration. </div> </div> </div> </div> <div class="section"> <h3 class="section-title">πŸš€ Quick Start</h3> <p class="section-description">Get started with audio.libx.js in just a few lines of code:</p> <div class="log"> <pre><code>// Import the library import { createAudioStreamer, createAudioRecorder } from 'audio.libx.js'; // Create audio streamer const audioElement = document.getElementById('audio'); const streamer = createAudioStreamer(audioElement, { enableCaching: true, enableTrimming: true, bufferThreshold: 5 }); // Stream audio const result = await streamer.streamFromUrl('path/to/audio.mp3'); await result.onLoaded; // Create audio recorder const recorder = createAudioRecorder({ enableRealtimeProcessing: true, audioBitsPerSecond: 128000 }); // Start recording const recording = await recorder.startRecording(); await recording.onStarted;</code></pre> </div> </div> <div class="section"> <h3 class="section-title">🌐 Browser Compatibility</h3> <div class="stats-grid"> <div class="stat-card"> <span class="stat-value">βœ…</span> <span class="stat-label">Chrome</span> </div> <div class="stat-card"> <span class="stat-value">βœ…</span> <span class="stat-label">Firefox</span> </div> <div class="stat-card"> <span class="stat-value">βœ…</span> <span class="stat-label">Safari 17.1+</span> </div> <div class="stat-card"> <span class="stat-value">βœ…</span> <span class="stat-label">Edge</span> </div> </div> </div> </div> <!-- Streaming Tab --> <div id="streaming" class="tab-pane"> <div class="section"> <h2 class="section-title">🎡 Progressive Audio Streaming</h2> <p class="section-description">Stream audio with progressive loading, intelligent buffering, and persistent caching.</p> <audio id="audioPlayer" class="audio-element" controls>Your browser does not support the audio element.</audio> <div class="controls"> <input type="url" id="audioUrl" class="form-control" placeholder="Enter audio URL (MP3, WAV, etc.)" value="./files/Jazz.wav" /> <button id="streamBtn" class="btn btn-primary">🎡 Stream Audio</button> <button id="playFromCacheBtn" class="btn btn-secondary" disabled>πŸ’Ύ Play from Cache</button> <button id="clearCacheBtn" class="btn btn-warning">πŸ—‘οΈ Clear Cache</button> </div> <div class="quick-examples"> <span>Quick Examples:</span> <button class="quick-example" onclick="document.getElementById('audioUrl').value='./files/Jazz.wav'">WAV File</button> <button class="quick-example" onclick="document.getElementById('audioUrl').value='./files/Sorry.mp3'">MP3 File</button> </div> <div class="progress-container"> <div class="progress"> <div class="progress-bar" id="bufferProgress"></div> </div> <small class="text-muted">Buffer Progress</small> </div> <div id="streamingStatus" class="status">Ready to stream audio...</div> </div> <div class="section"> <h3 class="section-title">πŸ“Š Streaming Statistics</h3> <div class="stats-grid"> <div class="stat-card"> <span class="stat-value" id="cacheEntries">0</span> <span class="stat-label">Cache Entries</span> </div> <div class="stat-card"> <span class="stat-value" id="cacheSize">0 MB</span> <span class="stat-label">Cache Size</span> </div> <div class="stat-card"> <span class="stat-value" id="hitRatio">0%</span> <span class="stat-label">Cache Hit Ratio</span> </div> <div class="stat-card"> <span class="stat-value" id="streamState">idle</span> <span class="stat-label">Stream State</span> </div> </div> </div> <div class="section"> <h3 class="section-title">βš™οΈ Configuration</h3> <div class="config-section"> <div class="config-item checkbox-item"> <input type="checkbox" id="enableCaching" checked /> <label for="enableCaching">Enable Caching</label> </div> <div class="config-item checkbox-item"> <input type="checkbox" id="enableTrimming" checked /> <label for="enableTrimming">Enable Silence Trimming</label> </div> <div class="config-item"> <label for="bufferThreshold">Buffer Threshold: <span id="bufferThresholdValue" class="range-value">5</span>s</label> <input type="range" id="bufferThreshold" class="range-input" min="1" max="10" value="5" /> </div> </div> </div> <div class="section"> <h3 class="section-title">πŸ“‹ Event Log</h3> <div id="streamingEventLog" class="log"></div> <button id="clearStreamingLogBtn" class="btn btn-secondary">Clear Log</button> </div> </div> <!-- Recording Tab --> <div id="recording" class="tab-pane"> <div class="section"> <h2 class="section-title">🎀 Audio Recording</h2> <p class="section-description">Record high-quality audio with real-time processing, level monitoring, and visual feedback.</p> <div class="controls"> <button id="startRecordingBtn" class="btn btn-primary">🎀 Start Recording</button> <button id="stopRecordingBtn" class="btn btn-danger" disabled>⏹️ Stop</button> <button id="pauseRecordingBtn" class="btn btn-warning" disabled>⏸️ Pause</button> <button id="resumeRecordingBtn" class="btn btn-success" disabled>▢️ Resume</button> <button id="testPermissionBtn" class="btn btn-secondary">πŸ”’ Test Permission</button> </div> <div class="controls"> <div class="duration-display" id="recordingDuration">00:00</div> <div class="level-meter"> <div class="level-bar" id="recordingLevelBar"></div> </div> </div> <canvas id="recordingVisualizer" class="visualizer" width="800" height="120"></canvas> <div id="recordingStatus" class="status">Ready to record...</div> </div> <div class="section"> <h3 class="section-title">βš™οΈ Recording Configuration</h3> <div class="config-section"> <div class="config-item"> <label for="recordingMimeType">MIME Type:</label> <select id="recordingMimeType" class="form-control"> <option value="">Auto-detect</option> <option value="audio/webm;codecs=opus">WebM Opus</option> <option value="audio/webm">WebM</option> <option value="audio/mp4">MP4</option> <option value="audio/wav">WAV</option> </select> </div> <div class="config-item"> <label for="recordingBitrate">Bitrate (kbps):</label> <input type="number" id="recordingBitrate" class="form-control" value="128" min="64" max="320" step="32" /> </div> <div class="config-item"> <label for="recordingMaxDuration">Max Duration (sec):</label> <input type="number" id="recordingMaxDuration" class="form-control" value="300" min="10" max="3600" step="10" /> </div> </div> <div class="config-section"> <div class="config-item checkbox-item"> <input type="checkbox" id="recordingEchoCancellation" checked /> <label for="recordingEchoCancellation">Echo Cancellation</label> </div> <div class="config-item checkbox-item"> <input type="checkbox" id="recordingNoiseSuppression" checked /> <label for="recordingNoiseSuppression">Noise Suppression</label> </div> <div class="config-item checkbox-item"> <input type="checkbox" id="recordingAutoGainControl" checked /> <label for="recordingAutoGainControl">Auto Gain Control</label> </div> <div class="config-item checkbox-item"> <input type="checkbox" id="recordingRealtimeProcessing" checked /> <label for="recordingRealtimeProcessing">Real-time Processing</label> </div> </div> </div> <div class="section"> <h3 class="section-title">🎡 Recordings</h3> <div id="recordingsList" class="recordings-list"> <div class="empty-state"> <div class="empty-state-icon">🎀</div> <p>No recordings yet. Start recording to see them here.</p> </div> </div> <button id="clearRecordingsBtn" class="btn btn-warning">Clear All Recordings</button> </div> <div class="section"> <h3 class="section-title">πŸ“‹ Recording Event Log</h3> <div id="recordingEventLog" class="log"></div> <button id="clearRecordingLogBtn" class="btn btn-secondary">Clear Log</button> </div> </div> <!-- Processing Tab --> <div id="processing" class="tab-pane"> <div class="section"> <h2 class="section-title">βš™οΈ Audio Processing</h2> <p class="section-description">Process audio with silence trimming, format conversion, and advanced audio manipulation.</p> <div class="processing-controls"> <div class="processing-section"> <div class="processing-title">πŸ”‡ Silence Trimming</div> <div class="config-item"> <label for="silenceThreshold">Silence Threshold: <span id="silenceThresholdValue" class="range-value">-50</span> dB</label> <input type="range" id="silenceThreshold" class="range-input" min="-80" max="-10" value="-50" /> </div> <div class="config-item"> <label for="minSilenceDuration">Min Silence: <span id="minSilenceDurationValue" class="range-value">100</span> ms</label> <input type="range" id="minSilenceDuration" class="range-input" min="50" max="500" value="100" /> </div> <button id="trimSilenceBtn" class="btn btn-primary">Trim Silence</button> </div> <div class="processing-section"> <div class="processing-title">πŸ”„ Format Conversion</div> <div class="config-item"> <label for="outputFormat">Output Format:</label> <select id="outputFormat" class="form-control"> <option value="wav">WAV</option> <option value="mp3">MP3</option> <option value="webm">WebM</option> </select> </div> <button id="convertFormatBtn" class="btn btn-primary">Convert Format</button> </div> <div class="processing-section"> <div class="processing-title">🏷️ Metadata Processing</div> <div class="checkbox-item"> <input type="checkbox" id="removeId3Tags" checked /> <label for="removeId3Tags">Remove ID3 Tags</label> </div> <button id="processMetadataBtn" class="btn btn-primary">Process Metadata</button> </div> <div class="processing-section"> <div class="processing-title">⏱️ Duration Analysis</div> <button id="estimateDurationBtn" class="btn btn-primary">Estimate Duration</button> <div id="durationResult" class="status" style="display: none"></div> </div> </div> <div class="controls"> <input type="file" id="audioFileInput" class="form-control" accept="audio/*" /> <button id="loadAudioFileBtn" class="btn btn-secondary">Load Audio File</button> <button id="downloadProcessedBtn" class="btn btn-success" disabled>πŸ’Ύ Download Processed</button> </div> <div id="processingStatus" class="status">Select an audio file to start processing...</div> </div> <div class="section"> <h3 class="section-title">πŸ“Š Processing Results</h3> <div class="stats-grid"> <div class="stat-card"> <span class="stat-value" id="originalSize">0 MB</span> <span class="stat-label">Original Size</span> </div> <div class="stat-card"> <span class="stat-value" id="processedSize">0 MB</span> <span class="stat-label">Processed Size</span> </div> <div class="stat-card"> <span class="stat-value" id="compressionRatio">0%</span> <span class="stat-label">Size Reduction</span> </div> <div class="stat-card"> <span class="stat-value" id="processingTime">0ms</span> <span class="stat-label">Processing Time</span> </div> </div> </div> <div class="section"> <h3 class="section-title">πŸ“‹ Processing Event Log</h3> <div id="processingEventLog" class="log"></div> <button id="clearProcessingLogBtn" class="btn btn-secondary">Clear Log</button> </div> </div> <!-- Caching Tab --> <div id="caching" class="tab-pane"> <div class="section"> <h2 class="section-title">πŸ’Ύ Audio Caching System</h2> <p class="section-description">Manage persistent audio cache with IndexedDB storage and memory optimization.</p> <div class="controls"> <button id="refreshCacheStatsBtn" class="btn btn-primary">πŸ”„ Refresh Stats</button> <button id="clearAllCacheBtn" class="btn btn-warning">πŸ—‘οΈ Clear All Cache</button> <button id="exportCacheBtn" class="btn btn-secondary">πŸ“€ Export Cache Data</button> <button id="optimizeCacheBtn" class="btn btn-success">⚑ Optimize Cache</button> </div> <div id="cachingStatus" class="status">Cache system ready...</div> </div> <div class="section"> <h3 class="section-title">πŸ“Š Cache Statistics</h3> <div class="stats-grid"> <div class="stat-card"> <span class="stat-value" id="totalCacheEntries">0</span> <span class="stat-label">Total Entries</span> </div> <div class="stat-card"> <span class="stat-value" id="totalCacheSize">0 MB</span> <span class="stat-label">Total Size</span> </div> <div class="stat-card"> <span class="stat-value" id="cacheHitRatio">0%</span> <span class="stat-label">Hit Ratio</span> </div> <div class="stat-card"> <span class="stat-value" id="memoryUsage">0 MB</span> <span class="stat-label">Memory Usage</span> </div> <div class="stat-card"> <span class="stat-value" id="storageQuota">0 MB</span> <span class="stat-label">Storage Quota</span> </div> <div class="stat-card"> <span class="stat-value" id="lastCleanup">Never</span> <span class="stat-label">Last Cleanup</span> </div> </div> </div> <div class="section"> <h3 class="section-title">πŸ—‚οΈ Cache Entries</h3> <div id="cacheEntriesList" class="recordings-list"> <div class="empty-state"> <div class="empty-state-icon">πŸ’Ύ</div> <p>No cache entries found. Stream some audio to populate the cache.</p> </div> </div> </div> <div class="section"> <h3 class="section-title">βš™οΈ Cache Configuration</h3> <div class="config-section"> <div class="config-item"> <label for="maxCacheSize">Max Cache Size: <span id="maxCacheSizeValue" class="range-value">100</span> MB</label> <input type="range" id="maxCacheSize" class="range-input" min="10" max="500" value="100" /> </div> <div class="config-item"> <label for="maxCacheAge">Max Age: <span id="maxCacheAgeValue" class="range-value">7</span> days</label> <input type="range" id="maxCacheAge" class="range-input" min="1" max="30" value="7" /> </div> <div class="config-item checkbox-item"> <input type="checkbox" id="enableMemoryCache" checked /> <label for="enableMemoryCache">Enable Memory Cache</label> </div> <div class="config-item checkbox-item"> <input type="checkbox" id="enableAutoCleanup" checked /> <label for="enableAutoCleanup">Enable Auto Cleanup</label> </div> </div> </div> <div class="section"> <h3 class="section-title">πŸ“‹ Cache Event Log</h3> <div id="cachingEventLog" class="log"></div> <button id="clearCachingLogBtn" class="btn btn-secondary">Clear Log</button> </div> </div> <!-- Real-time Tab --> <div id="realtime" class="tab-pane"> <div class="section"> <h2 class="section-title">πŸ“Š Real-time Audio Processing</h2> <p class="section-description">Live audio analysis with effects, visualization, and real-time processing capabilities.</p> <div class="controls"> <button id="startRealtimeBtn" class="btn btn-primary">🎀 Start Real-time Processing</button> <button id="stopRealtimeBtn" class="btn btn-danger" disabled>⏹️ Stop Processing</button> <button id="toggleEffectsBtn" class="btn btn-secondary" disabled>✨ Toggle Effects</button> <button id="toggleMuteBtn" class="btn btn-warning" disabled>πŸ”‡ Mute Output</button> </div> <canvas id="realtimeVisualizer" class="visualizer" width="800" height="120"></canvas> <div class="controls"> <div class="level-meter"> <div class="level-bar" id="realtimeLevelBar"></div> </div> <div id="silenceIndicator" class="status">πŸ”‡ Silence</div> </div> <div id="realtimeStatus" class="status">Real-time processing ready...</div> </div> <div class="section"> <h3 class="section-title">πŸŽ›οΈ Audio Effects</h3> <div class="processing-controls"> <div class="processing-section"> <div class="processing-title">πŸ”Š Gain Control</div> <div class="config-item"> <label for="gainLevel">Gain: <span id="gainLevelValue" class="range-value">1.0</span>x</label> <input type="range" id="gainLevel" class="range-input" min="0" max="3" step="0.1" value="1" /> </div> </div> <div class="processing-section"> <div class="processing-title">🎚️ Filter</div> <div class="config-item"> <label for="filterType">Type:</label> <select id="filterType" class="form-control"> <option value="none">None</option> <option value="lowpass">Low Pass</option> <option value="highpass">High Pass</option> <option value="bandpass">Band Pass</option> </select> </div> <div class="config-item"> <label for="filterFrequency">Frequency: <span id="filterFrequencyValue" class="range-value">1000</span> Hz</label> <input type="range" id="filterFrequency" class="range-input" min="100" max="5000" value="1000" /> </div> </div> <div class="processing-section"> <div class="processing-title">🌊 Reverb</div> <div class="config-item"> <label for="reverbLevel">Level: <span id="reverbLevelValue" class="range-value">0</span>%</label> <input type="range" id="reverbLevel" class="range-input" min="0" max="100" value="0" /> </div> </div> <div class="processing-section"> <div class="processing-title">πŸ”„ Echo</div> <div class="config-item"> <label for="echoDelay">Delay: <span id="echoDelayValue" class="range-value">0</span> ms</label> <input type="range" id="echoDelay" class="range-input" min="0" max="500" value="0" /> </div> <div class="config-item"> <label for="echoFeedback">Feedback: <span id="echoFeedbackValue" class="range-value">0</span>%</label> <input type="range" id="echoFeedback" class="range-input" min="0" max="90" value="0" /> </div> </div> </div> </div> <div class="section"> <