"""File-based cache for loaded/resampled WAV data. Stores .npy files in `cache/waveforms/` keyed by source path + target_sr + mono. Invalidated when source file mtime changes. """ from __future__ import annotations import hashlib import os import numpy as np CACHE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cache", "waveforms") def _cache_key(path: str, target_sr: int, mono: bool) -> str: real = os.path.realpath(path) mtime = os.path.getmtime(real) raw = f"{real}|{target_sr}|{mono}|{mtime:.6f}" return hashlib.sha256(raw.encode()).hexdigest()[:16] def cached_load_wav(path: str, target_sr: int = 48000, mono: bool = True, _loader=None) -> tuple[np.ndarray, int]: """Load WAV with disk cache. Falls back to _loader(data, sr) if cache miss.""" key = _cache_key(path, target_sr, mono) npy_path = os.path.join(CACHE_DIR, f"{key}.npy") sr_path = os.path.join(CACHE_DIR, f"{key}.sr") if os.path.exists(npy_path) and os.path.exists(sr_path): try: data = np.load(npy_path, allow_pickle=False) sr = int(np.load(sr_path, allow_pickle=False).item()) return data, sr except Exception: pass # corrupted cache, rebuild if _loader is None: from sync_audio import load_wav as _loader data, sr = _loader(path, target_sr=target_sr, mono=mono) os.makedirs(CACHE_DIR, exist_ok=True) np.save(npy_path, data, allow_pickle=False) np.save(sr_path, np.array(sr), allow_pickle=False) return data, sr