/** * IndexedDB cache for preset stem PCM data. * Stores raw int16 bytes + metadata so preset loads are instant after first visit. * * Bump CACHE_VERSION whenever preset audio files change on the server — * old entries will be ignored and re-fetched. */ const DB_NAME = 'jam-tracks-presets' const DB_VERSION = 1 const STORE_NAME = 'stems' export const CACHE_VERSION = 1 function openDB() { return new Promise((resolve, reject) => { const req = indexedDB.open(DB_NAME, DB_VERSION) req.onupgradeneeded = (e) => { const db = e.target.result if (!db.objectStoreNames.contains(STORE_NAME)) { db.createObjectStore(STORE_NAME) } } req.onsuccess = (e) => resolve(e.target.result) req.onerror = (e) => reject(e.target.error) }) } function dbGet(db, key) { return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, 'readonly') const req = tx.objectStore(STORE_NAME).get(key) req.onsuccess = (e) => resolve(e.target.result) req.onerror = (e) => reject(e.target.error) }) } function dbPut(db, key, value) { return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, 'readwrite') const req = tx.objectStore(STORE_NAME).put(value, key) req.onsuccess = () => resolve() req.onerror = (e) => reject(e.target.error) }) } function stemKey(presetName, stemName) { return `v${CACHE_VERSION}:${presetName}:${stemName}` } /** Returns true if all stemNames for this preset are cached. */ export async function isCached(presetName, stemNames) { try { const db = await openDB() const checks = await Promise.all(stemNames.map(s => dbGet(db, stemKey(presetName, s)))) return checks.every(v => v != null) } catch { return false } } /** * Returns cached data for all stems or null if any stem is missing. * Shape: { stemName: { bytes: ArrayBuffer, sampleRate, numChannels, numFrames } } */ export async function getCachedPreset(presetName, stemNames) { try { const db = await openDB() const result = {} for (const stemName of stemNames) { const entry = await dbGet(db, stemKey(presetName, stemName)) if (!entry) return null result[stemName] = entry } return result } catch { return null } } /** Store a single stem's PCM data in IndexedDB. */ export async function cacheStem(presetName, stemName, data) { try { const db = await openDB() await dbPut(db, stemKey(presetName, stemName), data) } catch (err) { console.warn('[presetCache] Failed to cache stem:', err) } }