add dexie as indexedDB buffer store
parent
833de3656e
commit
3f213c021a
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "audio-app",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"deploy": "rm -rf ../audio-host/public/audio-app/* && vite build && cp -r dist/* ../audio-host/public/audio-app/"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "~5.8.3",
|
||||||
|
"vite": "^6.3.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@rnbo/js": "^1.3.4",
|
||||||
|
"dexie": "^4.0.11"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
// composables/useBufferStore.ts
|
||||||
|
import { db } from '../lib/db'
|
||||||
|
|
||||||
|
interface AudioNodeItem {
|
||||||
|
type: string;
|
||||||
|
node: AudioNode | null;
|
||||||
|
started?: boolean;
|
||||||
|
previousNodeName?:string;
|
||||||
|
nextNodeName?:string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interner Buffer-Cache (nicht reaktiv)
|
||||||
|
const buffers = new Map<string, AudioBuffer>()
|
||||||
|
|
||||||
|
export const useBufferStore = () => ({
|
||||||
|
get: (id: string) => buffers.get(id),
|
||||||
|
set: (id: string, val: AudioBuffer) => buffers.set(id, val),
|
||||||
|
all: () => buffers
|
||||||
|
})
|
||||||
|
|
||||||
|
// Holt einen AudioBuffer aus dem Cache
|
||||||
|
export function getAudioBuffer(key: string): AudioBuffer | undefined {
|
||||||
|
return buffers.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Holt oder erstellt einen BufferSourceNode mit geladenem AudioBuffer
|
||||||
|
export async function createBufferSource(
|
||||||
|
name: string,
|
||||||
|
audioContext: AudioContext
|
||||||
|
): Promise<AudioNodeItem> {
|
||||||
|
const store = useBufferStore()
|
||||||
|
const buffer = store.get(name)
|
||||||
|
|
||||||
|
if (!buffer) throw new Error(`Kein Buffer für "${name}" im Cache gefunden.`)
|
||||||
|
|
||||||
|
const source = audioContext.createBufferSource()
|
||||||
|
source.buffer = buffer
|
||||||
|
source.loop = true
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'bufferSource',
|
||||||
|
node: source,
|
||||||
|
started: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speichert einen ArrayBuffer in IndexedDB unter gegebenem Namen
|
||||||
|
export async function saveBufferInIndexedDb(
|
||||||
|
fileName: string,
|
||||||
|
buffer: ArrayBuffer
|
||||||
|
): Promise<void> {
|
||||||
|
|
||||||
|
if (!db?.audiosources) {
|
||||||
|
console.warn('[BufferStore] Dexie DB nicht verfügbar')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const existing = await db.audiosources.get({ name: fileName })
|
||||||
|
if (existing) {
|
||||||
|
await db.audiosources.update(existing.id!, { buffer })
|
||||||
|
console.info(`[BufferStore] Buffer für "${fileName}" aktualisiert`)
|
||||||
|
} else {
|
||||||
|
console.log("BUFFER", {buffer})
|
||||||
|
await db.audiosources.add({ name: fileName, buffer })
|
||||||
|
console.info(`[BufferStore] Buffer für "${fileName}" gespeichert`)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[BufferStore] Fehler beim Speichern in IndexedDB:', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lädt AudioBuffer aus IndexedDB oder via fetch
|
||||||
|
export async function fetchOrLoadBuffer(
|
||||||
|
name: string,
|
||||||
|
url: string,
|
||||||
|
audioContext: AudioContext
|
||||||
|
): Promise<AudioBuffer> {
|
||||||
|
const store = useBufferStore()
|
||||||
|
|
||||||
|
// Zuerst aus Cache
|
||||||
|
const cached = store.get(name)
|
||||||
|
if (cached) return cached
|
||||||
|
|
||||||
|
let arrayBuffer: ArrayBuffer
|
||||||
|
|
||||||
|
// Versuch aus IndexedDB
|
||||||
|
const existing = await db?.audiosources?.get({ name })
|
||||||
|
if (existing?.buffer) {
|
||||||
|
arrayBuffer = existing.buffer
|
||||||
|
console.info(`[BufferStore] Buffer für "${name}" aus IndexedDB geladen`)
|
||||||
|
} else {
|
||||||
|
// Fallback: fetch
|
||||||
|
const response = await fetch(url)
|
||||||
|
if (!response.ok) throw new Error(`Fetch fehlgeschlagen für ${url}`)
|
||||||
|
arrayBuffer = await response.arrayBuffer()
|
||||||
|
await saveBufferInIndexedDb(name, arrayBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dekodieren & speichern
|
||||||
|
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer.slice(0))
|
||||||
|
store.set(name, audioBuffer)
|
||||||
|
return audioBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Dexie, type Table } from 'dexie'
|
||||||
|
|
||||||
|
export interface AudioSource {
|
||||||
|
id?: number;
|
||||||
|
name: string;
|
||||||
|
buffer: ArrayBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Database extends Dexie {
|
||||||
|
// 'audiosources' is added by dexie when declaring the stores()
|
||||||
|
// We just tell the typing system this is the case
|
||||||
|
audiosources!: Table<AudioSource>
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
super('audiosources')
|
||||||
|
this.version(1).stores({
|
||||||
|
audiosources: '++id, &name' // Primary key and indexed props
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const db = new Database()
|
Loading…
Reference in New Issue