qualia_core_db/audio/
audio_sidecar_link.rs1use crate::audio::audio_spectral_sheet::preview_bins_from_tensor;
6use crate::audio::audio_spectral_sheet::{
7 copy_sidecar_frame_to_preview_bins, SPECTRAL_PREVIEW_BINS,
8};
9use crate::audio::cqt_bake::bake_cqt_sidecar_from_preview;
10use crate::audio::stft_bake::{bake_stft_sidecar_from_preview, StftBakeError};
11use crate::tensor::bake_pipeline::{audio_sidecar_relpath, PRED_HAS_SPECTRAL_SHEET};
12use crate::tensor::Tensor10D;
13use crate::NQuin;
14
15#[inline]
17pub fn sidecar_content_hash(preview: &[f32; SPECTRAL_PREVIEW_BINS]) -> u64 {
18 let mut h: u64 = 0xcbf2_9ce4_8422_2325;
19 for &b in preview {
20 let bits = b.to_bits() as u64;
21 h ^= bits;
22 h = h.wrapping_mul(0x0100_0000_01b3);
23 }
24 h
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum SidecarBakeKind {
29 Stft,
30 Cqt,
31}
32
33pub fn bake_audio_sidecar_into(
35 preview: &[f32; SPECTRAL_PREVIEW_BINS],
36 frame_count: u32,
37 kind: SidecarBakeKind,
38 out: &mut [u8],
39) -> Result<(u64, usize), StftBakeError> {
40 let hash = sidecar_content_hash(preview);
41 let n = match kind {
42 SidecarBakeKind::Stft => bake_stft_sidecar_from_preview(preview, frame_count, 48_000, out)?,
43 SidecarBakeKind::Cqt => bake_cqt_sidecar_from_preview(preview, frame_count, 48_000, out)?,
44 };
45 Ok((hash, n))
46}
47
48#[inline]
50pub fn compile_spectral_sheet_quin(subject_hash: u64, sheet_index: u32) -> NQuin {
51 let mut q = NQuin::default();
52 q.subject = subject_hash;
53 q.predicate = PRED_HAS_SPECTRAL_SHEET;
54 q.object = (sheet_index as u64) & 0x0FFF_FFFF_FFFF_FFFF;
55 q
56}
57
58#[inline]
60pub fn format_sidecar_relpath(content_hash: u64, out: &mut [u8]) -> usize {
61 audio_sidecar_relpath(content_hash, out)
62}
63
64pub fn link_tensor_audio_sidecar(
66 t: &Tensor10D,
67 subject_hash: u64,
68 frame_count: u32,
69 kind: SidecarBakeKind,
70 out_bytes: &mut [u8],
71) -> Result<(NQuin, u64, usize), StftBakeError> {
72 let preview = preview_bins_from_tensor(t);
73 let (hash, n) = bake_audio_sidecar_into(&preview, frame_count, kind, out_bytes)?;
74 let index = (hash & 0xffff_ffff) as u32;
75 let quin = compile_spectral_sheet_quin(subject_hash, index);
76 Ok((quin, hash, n))
77}
78
79#[inline]
81pub fn enrich_preview_from_sidecar(
82 sidecar_bytes: &[u8],
83 frame_index: u32,
84 preview: &mut [f32; SPECTRAL_PREVIEW_BINS],
85) -> bool {
86 copy_sidecar_frame_to_preview_bins(sidecar_bytes, frame_index, preview)
87}
88
89#[cfg(not(target_arch = "wasm32"))]
90pub fn write_sidecar_file(
91 storage_root: &std::path::Path,
92 content_hash: u64,
93 bytes: &[u8],
94) -> std::io::Result<std::path::PathBuf> {
95 use std::io::Write;
96 let mut rel = [0u8; 64];
97 let n = format_sidecar_relpath(content_hash, &mut rel);
98 let rel_str = std::str::from_utf8(&rel[..n])
99 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
100 let path = storage_root.join(rel_str);
101 if let Some(parent) = path.parent() {
102 std::fs::create_dir_all(parent)?;
103 }
104 let mut f = std::fs::File::create(&path)?;
105 f.write_all(bytes)?;
106 Ok(path)
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn hash_stable_for_same_preview() {
115 let p = [0.5_f32; SPECTRAL_PREVIEW_BINS];
116 assert_eq!(sidecar_content_hash(&p), sidecar_content_hash(&p));
117 }
118
119 #[test]
120 fn link_emits_sheet_quin() {
121 let t = Tensor10D::new(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.55);
122 let mut buf = [0u8; 20 + 64 * 16 * 4];
123 let (q, _, n) =
124 link_tensor_audio_sidecar(&t, 0xabc, 16, SidecarBakeKind::Cqt, &mut buf).unwrap();
125 assert!(n > 20);
126 assert_eq!(q.subject, 0xabc);
127 assert_eq!(q.predicate, PRED_HAS_SPECTRAL_SHEET);
128 }
129
130 #[test]
131 fn enrich_preview_from_baked_sidecar() {
132 let preview = [0.7_f32; SPECTRAL_PREVIEW_BINS];
133 let mut buf = [0u8; 20 + 64 * 4 * 4];
134 bake_stft_sidecar_from_preview(&preview, 4, 48_000, &mut buf).unwrap();
135 let mut out = [0.0_f32; SPECTRAL_PREVIEW_BINS];
136 assert!(enrich_preview_from_sidecar(&buf, 2, &mut out));
137 assert!(out[0] > 0.0);
138 }
139}