From ea993bcf96a0414e7d24ff6c0448fea8ddca395d Mon Sep 17 00:00:00 2001 From: Honney OC Date: Sun, 13 Sep 2026 23:58:04 +0000 Subject: [PATCH] fix: waveform cache --- Makefile | 6 +++--- waveform_cache.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 waveform_cache.py diff --git a/Makefile b/Makefile index 12c142e..0cb33a9 100644 --- a/Makefile +++ b/Makefile @@ -81,10 +81,10 @@ extract: @if [ -x $(PY) ]; then $(PY) extract.py; else python3 extract.py; fi sync: - @if [ -x $(PY) ]; then $(PY) apply_sync.py --config $(CONFIG) --lag $(LAG); else python3 apply_sync.py --config $(CONFIG) --lag $(LAG); fi + @if [ -x $(PY) ]; then PYTHONPATH=. $(PY) apply_sync.py --config $(CONFIG) --lag $(LAG); else PYTHONPATH=. python3 apply_sync.py --config $(CONFIG) --lag $(LAG); fi verify: - @if [ -x $(PY) ]; then LC_NUMERIC=C QT_STYLE_OVERRIDE=Fusion $(PY) verify_sync.py --config $(CONFIG) --lag $(LAG); else LC_NUMERIC=C QT_STYLE_OVERRIDE=Fusion python3 verify_sync.py --config $(CONFIG) --lag $(LAG); fi + @if [ -x $(PY) ]; then LC_NUMERIC=C PYTHONPATH=. QT_STYLE_OVERRIDE=Fusion $(PY) verify_sync.py --config $(CONFIG) --lag $(LAG); else LC_NUMERIC=C PYTHONPATH=. QT_STYLE_OVERRIDE=Fusion python3 verify_sync.py --config $(CONFIG) --lag $(LAG); fi ast: @AUDIO_A=$$(python3 -c "import tomllib; c=tomllib.load(open('config.toml','rb')); print(c['extract'].get('file_a',''))" 2>/dev/null); \ @@ -100,7 +100,7 @@ ast: else \ echo "=> extracted wavs exist ($$OUT/$${STEM_A}_audio.wav, $$OUT/$${STEM_B}_audio.wav), skipping extract"; \ fi - @if [ -x $(PY) ]; then LC_NUMERIC=C QT_STYLE_OVERRIDE=Fusion $(PY) sync_audio_test.py --config $(CONFIG); else LC_NUMERIC=C QT_STYLE_OVERRIDE=Fusion python3 sync_audio_test.py --config $(CONFIG); fi + @if [ -x $(PY) ]; then LC_NUMERIC=C PYTHONPATH=. QT_STYLE_OVERRIDE=Fusion $(PY) sync_audio_test.py --config $(CONFIG); else LC_NUMERIC=C PYTHONPATH=. QT_STYLE_OVERRIDE=Fusion python3 sync_audio_test.py --config $(CONFIG); fi clean: rm -rf $(VENV) __pycache__ .pytest_cache diff --git a/waveform_cache.py b/waveform_cache.py new file mode 100644 index 0000000..bcd56c7 --- /dev/null +++ b/waveform_cache.py @@ -0,0 +1,43 @@ +"""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