1use crate::pool::Pool;
17
18pub static AVAE_TIME: [std::sync::atomic::AtomicU64; 6] = [
23 std::sync::atomic::AtomicU64::new(0),
24 std::sync::atomic::AtomicU64::new(0),
25 std::sync::atomic::AtomicU64::new(0),
26 std::sync::atomic::AtomicU64::new(0),
27 std::sync::atomic::AtomicU64::new(0),
28 std::sync::atomic::AtomicU64::new(0),
29];
30
31fn atime_on() -> bool {
32 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
33 *ON.get_or_init(|| std::env::var("CMF_AVAE_TIME").is_ok())
34}
35
36fn atime(slot: usize, t: std::time::Instant) {
37 if atime_on() {
38 AVAE_TIME[slot].fetch_add(
39 t.elapsed().as_micros() as u64,
40 std::sync::atomic::Ordering::Relaxed,
41 );
42 }
43}
44
45pub fn avae_time_report() -> Option<String> {
47 if !atime_on() {
48 return None;
49 }
50 const NAMES: [&str; 6] = [
51 "dec_in", "conv_pre", "upsamples", "resblocks", "act_post", "conv_post",
52 ];
53 let mut v: Vec<(u64, &str)> = AVAE_TIME
54 .iter()
55 .map(|a| a.load(std::sync::atomic::Ordering::Relaxed))
56 .zip(NAMES)
57 .collect();
58 let total: u64 = v.iter().map(|(u, _)| u).sum();
59 if total == 0 {
60 return None;
61 }
62 v.sort_by(|a, b| b.0.cmp(&a.0));
63 let mut out = format!("audio vae phases (total {:.1} s):\n", total as f64 / 1e6);
64 for (us, name) in v {
65 out.push_str(&format!(
66 " {name:<12} {:>6.1} s {:>5.1}%\n",
67 us as f64 / 1e6,
68 100.0 * us as f64 / total as f64
69 ));
70 }
71 Some(out)
72}
73use cortiq_core::CmfModel;
74use std::sync::Arc;
75
76const FILTER_LEN: usize = 12;
79
80struct Conv1d {
81 w: Vec<f32>, b: Option<Vec<f32>>,
83 out_ch: usize,
84 in_ch: usize,
85 k: usize,
86 pad: usize,
87 dilation: usize,
88}
89
90impl Conv1d {
91 fn load(model: &Arc<CmfModel>, name: &str, pad: usize, dilation: usize) -> Result<Self, String> {
92 let e = model
93 .tensor(&format!("{name}.weight"))
94 .ok_or_else(|| format!("missing {name}.weight"))?;
95 let w = crate::dit::cmf_f32(model, &format!("{name}.weight"))?;
96 let b = crate::dit::cmf_f32(model, &format!("{name}.bias")).ok();
97 Ok(Self {
98 out_ch: e.shape[0],
99 in_ch: e.shape[1],
100 k: e.shape[2],
101 w,
102 b,
103 pad,
104 dilation,
105 })
106 }
107
108 fn apply(&self, x: &[f32], n: usize, pool: Option<&Pool>) -> Vec<f32> {
111 let out_n = (n + 2 * self.pad).saturating_sub(self.dilation * (self.k - 1));
112 let mut out = vec![0f32; self.out_ch * out_n];
113 let ptr = SendPtr(out.as_mut_ptr());
114 if std::env::var("CMF_AVAE_CONV_GPU").as_deref() != Ok("0")
148 && crate::gpu::enabled_here()
149 {
150 let kk = self.in_ch * self.k;
151 {
158 let mut yt = vec![0f32; out_n * self.out_ch];
159 if crate::gpu::conv1d_gemm(
160 x,
161 &self.w,
162 self.in_ch,
163 self.out_ch,
164 n,
165 self.k,
166 self.pad,
167 self.dilation,
168 out_n,
169 &mut yt,
170 ) {
171 for o in 0..self.out_ch {
172 let bias = self.b.as_ref().map_or(0.0, |b| b[o]);
173 let dst = unsafe { ptr.row(o * out_n, out_n) };
175 for (t, d) in dst.iter_mut().enumerate() {
176 *d = yt[t * self.out_ch + o] + bias;
177 }
178 }
179 return out;
180 }
181 }
182 const MAX_COL_FLOATS: usize = 96 << 20; let span = (MAX_COL_FLOATS / kk.max(1)).max(1).min(out_n);
192 if span < out_n {
193 let mut done = 0usize;
194 while done < out_n {
195 let w = span.min(out_n - done);
196 let need = w + self.dilation * (self.k - 1);
202 let mut sub = vec![0f32; self.in_ch * need];
203 for i in 0..self.in_ch {
204 let src = &x[i * n..(i + 1) * n];
205 let dst = &mut sub[i * need..(i + 1) * need];
206 for (u, d) in dst.iter_mut().enumerate() {
207 let p = (done + u) as isize - self.pad as isize;
208 if p >= 0 && (p as usize) < n {
209 *d = src[p as usize];
210 }
211 }
212 }
213 let piece = Self {
214 w: self.w.clone(),
215 b: self.b.clone(),
216 out_ch: self.out_ch,
217 in_ch: self.in_ch,
218 k: self.k,
219 pad: 0,
220 dilation: self.dilation,
221 }
222 .apply(&sub, need, pool);
223 for o in 0..self.out_ch {
224 let dst = unsafe { ptr.row(o * out_n + done, w) };
226 dst.copy_from_slice(&piece[o * w..(o + 1) * w]);
227 }
228 done += w;
229 }
230 return out;
231 }
232 if let Some(col_len) = kk.checked_mul(out_n) {
233 let mut col = vec![0f32; col_len];
234 let pc = SendPtr(col.as_mut_ptr());
235 let fill = |lo: usize, hi: usize| {
236 for r in lo..hi {
237 let (i, j) = (r / self.k, r % self.k);
238 let src = &x[i * n..(i + 1) * n];
239 let dst = unsafe { pc.row(r * out_n, out_n) };
241 for (t, d) in dst.iter_mut().enumerate() {
242 let p = (t + j * self.dilation) as isize - self.pad as isize;
243 *d = if p >= 0 && (p as usize) < n {
244 src[p as usize]
245 } else {
246 0.0
247 };
248 }
249 }
250 };
251 match pool {
252 Some(p) => p.run_rows(kk, &fill),
253 None => fill(0, kk),
254 }
255 let mut colt = vec![0f32; col_len];
259 for r in 0..kk {
260 for t in 0..out_n {
261 colt[t * kk + r] = col[r * out_n + t];
262 }
263 }
264 let mut yt = vec![0f32; out_n * self.out_ch];
265 if crate::gpu::gemm_nt_f32(&colt, &self.w, &mut yt, out_n, kk, self.out_ch) {
266 for o in 0..self.out_ch {
267 let bias = self.b.as_ref().map_or(0.0, |b| b[o]);
268 let dst = unsafe { ptr.row(o * out_n, out_n) };
270 for (t, d) in dst.iter_mut().enumerate() {
271 *d = yt[t * self.out_ch + o] + bias;
272 }
273 }
274 return out;
275 }
276 }
277 }
278 let tiles = (128 / self.out_ch.max(1)).max(1);
279 let tile = out_n.div_ceil(tiles.max(1));
280 let rows = self.out_ch * tiles;
281 let work = |lo: usize, hi: usize| {
282 for r in lo..hi {
283 let o = r / tiles;
284 let t0 = (r - o * tiles) * tile;
285 if t0 >= out_n {
286 continue;
287 }
288 let len = tile.min(out_n - t0);
289 let dst = unsafe { ptr.row(o * out_n + t0, len) };
291 let bias = self.b.as_ref().map_or(0.0, |b| b[o]);
292 dst.fill(bias);
293 for i in 0..self.in_ch {
294 let ker = &self.w[(o * self.in_ch + i) * self.k..(o * self.in_ch + i + 1) * self.k];
295 let src = &x[i * n..(i + 1) * n];
296 for (tt, d) in dst.iter_mut().enumerate() {
297 let t = t0 + tt;
298 let mut acc = 0f32;
299 for (j, &kv) in ker.iter().enumerate() {
300 let p = (t + j * self.dilation) as isize - self.pad as isize;
301 if p >= 0 && (p as usize) < n {
302 acc += kv * src[p as usize];
303 }
304 }
305 *d += acc;
306 }
307 }
308 }
309 };
310 match pool {
311 Some(p) => p.run_rows(rows, &work),
312 None => work(0, rows),
313 }
314 out
315 }
316}
317
318struct ConvT1d {
319 w: Vec<f32>, b: Vec<f32>,
321 in_ch: usize,
322 out_ch: usize,
323 k: usize,
324 stride: usize,
325 pad: usize,
326}
327
328impl ConvT1d {
329 fn load(model: &Arc<CmfModel>, name: &str, stride: usize) -> Result<Self, String> {
330 let e = model
331 .tensor(&format!("{name}.weight"))
332 .ok_or_else(|| format!("missing {name}.weight"))?;
333 let (in_ch, out_ch, k) = (e.shape[0], e.shape[1], e.shape[2]);
334 Ok(Self {
335 w: crate::dit::cmf_f32(model, &format!("{name}.weight"))?,
336 b: crate::dit::cmf_f32(model, &format!("{name}.bias"))?,
337 in_ch,
338 out_ch,
339 k,
340 stride,
341 pad: (k - stride) / 2,
342 })
343 }
344
345 fn apply(&self, x: &[f32], n: usize, pool: Option<&Pool>) -> Vec<f32> {
346 let full = (n - 1) * self.stride + self.k;
347 let out_n = full - 2 * self.pad;
348 let mut out = vec![0f32; self.out_ch * out_n];
349 let ptr = SendPtr(out.as_mut_ptr());
350 let work = |lo: usize, hi: usize| {
351 for o in lo..hi {
352 let dst = unsafe { ptr.row(o * out_n, out_n) };
354 dst.fill(self.b[o]);
355 for i in 0..self.in_ch {
356 let ker = &self.w[(i * self.out_ch + o) * self.k..(i * self.out_ch + o + 1) * self.k];
357 let src = &x[i * n..(i + 1) * n];
358 for (t, &sv) in src.iter().enumerate() {
359 if sv == 0.0 {
360 continue;
361 }
362 let base = t * self.stride;
363 for (j, &kv) in ker.iter().enumerate() {
364 let p = base + j;
365 if p >= self.pad && p - self.pad < out_n {
366 dst[p - self.pad] += sv * kv;
367 }
368 }
369 }
370 }
371 }
372 };
373 match pool {
374 Some(p) => p.run_rows(self.out_ch, &work),
375 None => work(0, self.out_ch),
376 }
377 out
378 }
379}
380
381struct Snake {
390 alpha: Vec<f32>,
391}
392
393impl Snake {
394 fn load(model: &Arc<CmfModel>, name: &str) -> Result<Self, String> {
395 Ok(Self {
396 alpha: crate::dit::cmf_f32(model, &format!("{name}.alpha"))?,
397 })
398 }
399
400 fn apply(&self, x: &mut [f32], n: usize) {
401 for (c, row) in x.chunks_exact_mut(n).enumerate() {
402 let a = self.alpha[c];
403 let inv = 1.0 / (a + 1e-9);
404 for v in row.iter_mut() {
405 let s = (a * *v).sin();
406 *v += s * s * inv;
407 }
408 }
409 }
410}
411
412struct DavUnit {
414 a1: Snake,
415 c1: Conv1d,
416 a2: Snake,
417 c2: Conv1d,
418}
419
420impl DavUnit {
421 fn load(model: &Arc<CmfModel>, p: &str, dilation: usize) -> Result<Self, String> {
422 Ok(Self {
423 a1: Snake::load(model, &format!("{p}.block.0"))?,
424 c1: Conv1d::load(model, &format!("{p}.block.1"), 3 * dilation, dilation)?,
425 a2: Snake::load(model, &format!("{p}.block.2"))?,
426 c2: Conv1d::load(model, &format!("{p}.block.3"), 0, 1)?,
427 })
428 }
429
430 fn apply(&self, x: &[f32], n: usize, pool: Option<&Pool>) -> Vec<f32> {
431 let mut h = x.to_vec();
432 self.a1.apply(&mut h, n);
433 let mut h = self.c1.apply(&h, n, pool);
434 self.a2.apply(&mut h, n);
435 let r = self.c2.apply(&h, n, pool);
436 debug_assert_eq!(r.len(), x.len());
439 x.iter().zip(&r).map(|(a, b)| a + b).collect()
440 }
441}
442
443struct DavStage {
446 act: Snake,
447 up: ConvT1d,
448 units: Vec<DavUnit>,
449}
450
451impl DavStage {
452 fn load(model: &Arc<CmfModel>, p: &str, stride: usize) -> Result<Self, String> {
453 Ok(Self {
454 act: Snake::load(model, &format!("{p}.block.0"))?,
455 up: ConvT1d::load(model, &format!("{p}.block.1"), stride)?,
456 units: [1usize, 3, 9]
457 .iter()
458 .enumerate()
459 .map(|(i, &d)| DavUnit::load(model, &format!("{p}.block.{}", i + 2), d))
460 .collect::<Result<_, _>>()?,
461 })
462 }
463
464 fn apply(&self, x: &[f32], n: usize, pool: Option<&Pool>) -> (Vec<f32>, usize) {
465 let mut h = x.to_vec();
466 self.act.apply(&mut h, n);
467 let mut h = self.up.apply(&h, n, pool);
468 let n = n * self.up.stride;
469 for u in &self.units {
470 h = u.apply(&h, n, pool);
471 }
472 (h, n)
473 }
474}
475
476pub struct Music3Dav {
483 dec_in: Conv1d,
484 conv_pre: Conv1d,
485 stages: Vec<DavStage>,
486 act_post: Snake,
487 conv_post: Conv1d,
488}
489
490impl Music3Dav {
491 pub const STRIDES: [usize; 4] = [8, 8, 4, 2];
492 pub const HOP: usize = 512;
494 pub const SAMPLE_RATE: usize = 44100;
495
496 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
497 Ok(Self {
498 dec_in: Conv1d::load(model, "mvae.dec_in_proj", 0, 1)?,
499 conv_pre: Conv1d::load(model, "mvae.decoder.model.0", 3, 1)?,
500 stages: Self::STRIDES
501 .iter()
502 .enumerate()
503 .map(|(i, &s)| {
504 DavStage::load(model, &format!("mvae.decoder.model.{}", i + 1), s)
505 })
506 .collect::<Result<_, _>>()?,
507 act_post: Snake::load(model, "mvae.decoder.model.5")?,
508 conv_post: Conv1d::load(model, "mvae.decoder.model.6", 3, 1)?,
509 })
510 }
511
512 pub fn decode(&self, latent: &[f32], frames: usize, pool: Option<&Pool>) -> Vec<f32> {
515 let mut chans: Vec<Vec<f32>> = Vec::with_capacity(2);
516 for half in 0..2 {
517 let src = &latent[half * 64 * frames..(half + 1) * 64 * frames];
518 let mut h = self.dec_in.apply(src, frames, pool);
519 h = self.conv_pre.apply(&h, frames, pool);
520 let mut n = frames;
521 for st in &self.stages {
522 let (nh, nn) = st.apply(&h, n, pool);
523 h = nh;
524 n = nn;
525 }
526 self.act_post.apply(&mut h, n);
527 let w = self.conv_post.apply(&h, n, pool);
528 chans.push(w.iter().map(|v| v.tanh()).collect());
531 }
532 let n = chans[0].len();
533 let mut out = vec![0f32; n * 2];
534 for (i, o) in out.chunks_exact_mut(2).enumerate() {
535 o[0] = chans[0][i];
536 o[1] = chans[1][i];
537 }
538 out
539 }
540}
541
542struct SnakeBeta {
544 alpha: Vec<f32>,
545 beta: Vec<f32>,
546}
547
548impl SnakeBeta {
549 fn load(model: &Arc<CmfModel>, name: &str) -> Result<Self, String> {
550 Ok(Self {
551 alpha: crate::dit::cmf_f32(model, &format!("{name}.alpha"))?
552 .iter()
553 .map(|v| v.exp())
554 .collect(),
555 beta: crate::dit::cmf_f32(model, &format!("{name}.beta"))?
556 .iter()
557 .map(|v| v.exp())
558 .collect(),
559 })
560 }
561
562 fn apply(&self, x: &mut [f32], n: usize) {
563 for (c, row) in x.chunks_exact_mut(n).enumerate() {
564 let (a, b) = (self.alpha[c], 1.0 / (self.beta[c] + 1e-9));
565 for v in row.iter_mut() {
566 let s = (a * *v).sin();
567 *v += s * s * b;
568 }
569 }
570 }
571}
572
573fn bessel_i0(x: f64) -> f64 {
574 let mut sum = 1.0;
577 let mut term = 1.0;
578 for k in 1..40 {
579 term *= (x / (2.0 * k as f64)).powi(2);
580 sum += term;
581 if term < 1e-18 * sum {
582 break;
583 }
584 }
585 sum
586}
587
588fn sinc(x: f64) -> f64 {
589 if x == 0.0 {
590 1.0
591 } else {
592 (std::f64::consts::PI * x).sin() / (std::f64::consts::PI * x)
593 }
594}
595
596fn kaiser_sinc(cutoff: f64, half_width: f64, k: usize) -> Vec<f32> {
598 let half = k / 2;
599 let delta_f = 4.0 * half_width;
600 let a = 2.285 * (half as f64 - 1.0) * std::f64::consts::PI * delta_f + 7.95;
601 let beta = if a > 50.0 {
602 0.1102 * (a - 8.7)
603 } else if a >= 21.0 {
604 0.5842 * (a - 21.0).powf(0.4) + 0.078_86 * (a - 21.0)
605 } else {
606 0.0
607 };
608 let denom = bessel_i0(beta);
609 let n = k as f64 - 1.0;
610 let mut f: Vec<f64> = (0..k)
611 .map(|i| {
612 let r = (2.0 * i as f64 / n) - 1.0;
613 let win = bessel_i0(beta * (1.0 - r * r).max(0.0).sqrt()) / denom;
614 let t = -(half as f64) + i as f64 + 0.5;
616 2.0 * cutoff * win * sinc(2.0 * cutoff * t)
617 })
618 .collect();
619 let s: f64 = f.iter().sum();
620 for v in f.iter_mut() {
621 *v /= s;
622 }
623 f.into_iter().map(|v| v as f32).collect()
624}
625
626#[allow(clippy::too_many_arguments)]
651fn fir_pad(
652 x: &[f32],
653 ch: usize,
654 n: usize,
655 f: &[f32],
656 pad_l: usize,
657 pad_r: usize,
658 stride: usize,
659 pool: Option<&Pool>,
660) -> (Vec<f32>, usize) {
661 let padded = n + pad_l + pad_r;
662 let out_n = (padded - f.len()) / stride + 1;
663 let mut out = vec![0f32; ch * out_n];
664 struct P(*mut f32);
665 unsafe impl Send for P {}
668 unsafe impl Sync for P {}
669 impl P {
670 #[allow(clippy::mut_from_ref)]
674 pub(crate) unsafe fn row(&self, off: usize, len: usize) -> &mut [f32] {
675 unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
676 }
677 }
678 let po = P(out.as_mut_ptr());
679 let work = |lo: usize, hi: usize| {
680 let mut buf = vec![0f32; padded];
683 for c in lo..hi {
684 let src = &x[c * n..(c + 1) * n];
685 for (i, b) in buf.iter_mut().enumerate() {
686 let p = i as isize - pad_l as isize;
687 *b = src[p.clamp(0, n as isize - 1) as usize];
688 }
689 let dst = unsafe { po.row(c * out_n, out_n) };
690 for (t, d) in dst.iter_mut().enumerate() {
691 let mut acc = 0f32;
692 for (j, &kv) in f.iter().enumerate() {
693 acc += kv * buf[t * stride + j];
694 }
695 *d = acc;
696 }
697 }
698 };
699 match pool {
700 Some(p) => p.run_rows(ch, &work),
701 None => work(0, ch),
702 }
703 (out, out_n)
704}
705
706struct Activation1d {
708 act: SnakeBeta,
709 up: Vec<f32>,
710 down: Vec<f32>,
711}
712
713impl Activation1d {
714 fn load(model: &Arc<CmfModel>, name: &str) -> Result<Self, String> {
719 let designed = || kaiser_sinc(0.25, 0.3, FILTER_LEN);
720 Ok(Self {
721 act: SnakeBeta::load(model, &format!("{name}.act"))?,
722 up: crate::dit::cmf_f32(model, &format!("{name}.upsample.filter"))
723 .unwrap_or_else(|_| designed()),
724 down: crate::dit::cmf_f32(model, &format!("{name}.downsample.lowpass.filter"))
725 .unwrap_or_else(|_| designed()),
726 })
727 }
728
729 fn apply(&self, x: &[f32], ch: usize, n: usize, pool: Option<&Pool>) -> (Vec<f32>, usize) {
730 let pad = FILTER_LEN / 2 - 1;
733 let pad_l = pad * 2 + (FILTER_LEN - 2) / 2;
734 let pad_r = pad * 2 + (FILTER_LEN - 2 + 1) / 2;
735 let pn = n + 2 * pad;
736 let full = (pn - 1) * 2 + FILTER_LEN;
737 let mut up = vec![0f32; ch * full];
738 for c in 0..ch {
739 let src = &x[c * n..(c + 1) * n];
740 let dst = &mut up[c * full..(c + 1) * full];
741 for i in 0..pn {
742 let p = i as isize - pad as isize;
743 let v = src[p.clamp(0, n as isize - 1) as usize] * 2.0;
744 if v == 0.0 {
745 continue;
746 }
747 for (j, &kv) in self.up.iter().enumerate() {
748 dst[i * 2 + j] += v * kv;
749 }
750 }
751 }
752 let keep = full - pad_l - pad_r;
753 let mut mid = vec![0f32; ch * keep];
754 for c in 0..ch {
755 mid[c * keep..(c + 1) * keep]
756 .copy_from_slice(&up[c * full + pad_l..c * full + pad_l + keep]);
757 }
758 self.act.apply(&mut mid, keep);
759 fir_pad(&mid, ch, keep, &self.down, FILTER_LEN / 2 - 1, FILTER_LEN / 2, 2, pool)
761 }
762}
763
764struct AmpBlock {
765 convs1: Vec<Conv1d>,
766 convs2: Vec<Conv1d>,
767 acts: Vec<Activation1d>,
768}
769
770pub struct AudioVae {
771 dec_in: Conv1d,
772 conv_pre: Conv1d,
773 ups: Vec<ConvT1d>,
774 resblocks: Vec<AmpBlock>,
775 act_post: Activation1d,
776 conv_post: Conv1d,
777 latents_mean: Vec<f32>,
778 latents_std: Vec<f32>,
779 pool: Option<Arc<Pool>>,
780 n_kernels: usize,
781 pub sample_rate: usize,
782}
783
784fn get_padding(k: usize, d: usize) -> usize {
785 (k * d - d) / 2
786}
787
788impl AudioVae {
789 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
790 let cfg: serde_json::Value = serde_json::from_slice(
791 model.tensor_bytes("avae.config_json").map_err(|e| e.to_string())?,
792 )
793 .map_err(|e| format!("avae.config_json: {e}"))?;
794 let rates: Vec<usize> = cfg["upsample_rates"]
795 .as_array()
796 .ok_or("upsample_rates")?
797 .iter()
798 .map(|v| v.as_u64().unwrap_or(1) as usize)
799 .collect();
800 let rk: Vec<usize> = cfg["resblock_kernel_sizes"]
801 .as_array()
802 .ok_or("resblock_kernel_sizes")?
803 .iter()
804 .map(|v| v.as_u64().unwrap_or(3) as usize)
805 .collect();
806 let rd: Vec<Vec<usize>> = cfg["resblock_dilation_sizes"]
807 .as_array()
808 .ok_or("resblock_dilation_sizes")?
809 .iter()
810 .map(|a| {
811 a.as_array()
812 .unwrap()
813 .iter()
814 .map(|v| v.as_u64().unwrap_or(1) as usize)
815 .collect()
816 })
817 .collect();
818
819 let mut ups = Vec::new();
820 for (i, &u) in rates.iter().enumerate() {
821 ups.push(ConvT1d::load(model, &format!("avae.decoder.ups.{i}.0"), u)?);
822 }
823 let mut resblocks = Vec::new();
824 for i in 0..rates.len() {
825 for (j, (&k, d)) in rk.iter().zip(&rd).enumerate() {
826 let p = format!("avae.decoder.resblocks.{}", i * rk.len() + j);
827 let convs1 = (0..d.len())
828 .map(|q| Conv1d::load(model, &format!("{p}.convs1.{q}"), get_padding(k, d[q]), d[q]))
829 .collect::<Result<Vec<_>, _>>()?;
830 let convs2 = (0..d.len())
831 .map(|q| Conv1d::load(model, &format!("{p}.convs2.{q}"), get_padding(k, 1), 1))
832 .collect::<Result<Vec<_>, _>>()?;
833 let acts = (0..convs1.len() + convs2.len())
834 .map(|q| Activation1d::load(model, &format!("{p}.activations.{q}")))
835 .collect::<Result<Vec<_>, _>>()?;
836 resblocks.push(AmpBlock { convs1, convs2, acts });
837 }
838 }
839 Ok(Self {
840 dec_in: Conv1d::load(model, "avae.dec_in_proj", 0, 1)?,
841 conv_pre: Conv1d::load(model, "avae.decoder.conv_pre", 3, 1)?,
842 ups,
843 resblocks,
844 act_post: Activation1d::load(model, "avae.decoder.activation_post")?,
845 conv_post: Conv1d::load(model, "avae.decoder.conv_post", 3, 1)?,
846 latents_mean: crate::dit::cmf_f32(model, "avae.latents_mean")?,
847 latents_std: crate::dit::cmf_f32(model, "avae.latents_std")?,
848 pool: Pool::from_env(),
849 n_kernels: rk.len(),
850 sample_rate: cfg["sample_rate"].as_u64().unwrap_or(32000) as usize,
851 })
852 }
853
854 pub fn decode(&self, z: &[f32], c: usize, t: usize) -> (Vec<f32>, usize) {
881 let pool = self.pool.as_deref();
882 let mut chans: Vec<Vec<f32>> = Vec::with_capacity(2);
883 for ch in 0..2 {
884 let mut lat = vec![0f32; c * t];
885 for ci in 0..c {
886 let (m, s) = (self.latents_mean[ci], self.latents_std[ci]);
887 for ti in 0..t {
888 lat[ci * t + ti] = z[(ci * 2 + ch) * t + ti] * s + m;
889 }
890 }
891 let prof = std::env::var_os("CMF_AVAE_PROF").is_some();
894 let rms = |x: &[f32]| (x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>()
895 / x.len() as f64)
896 .sqrt();
897 let tt = std::time::Instant::now();
898 let mut x = self.dec_in.apply(&lat, t, pool);
899 atime(0, tt);
900 let mut n = t;
901 if prof {
902 eprintln!("ch{ch} dec_in rms {:.6e} n {n}", rms(&x));
903 }
904 let tt = std::time::Instant::now();
905 x = self.conv_pre.apply(&x, n, pool);
906 atime(1, tt);
907 if prof {
908 eprintln!("ch{ch} conv_pre rms {:.6e} n {n}", rms(&x));
909 }
910 for i in 0..self.ups.len() {
911 let up = &self.ups[i];
912 let tt = std::time::Instant::now();
913 x = up.apply(&x, n, pool);
914 atime(2, tt);
915 n = (n - 1) * up.stride + up.k - 2 * up.pad;
916 let ch_n = up.out_ch;
917 let mut acc = vec![0f32; ch_n * n];
918 for j in 0..self.n_kernels {
919 let tt = std::time::Instant::now();
920 let r = self.resblocks[i * self.n_kernels + j].apply(&x, ch_n, n, pool);
921 atime(3, tt);
922 for (a, b) in acc.iter_mut().zip(&r) {
923 *a += b;
924 }
925 }
926 let inv = 1.0 / self.n_kernels as f32;
927 for v in acc.iter_mut() {
928 *v *= inv;
929 }
930 x = acc;
931 if prof {
932 eprintln!("ch{ch} up{i} rms {:.6e} ch {ch_n} n {n}", rms(&x));
933 }
934 }
935 let last_ch = self.ups[self.ups.len() - 1].out_ch;
936 let tt = std::time::Instant::now();
937 let (mut y, yn) = self.act_post.apply(&x, last_ch, n, pool);
938 atime(4, tt);
939 let tt = std::time::Instant::now();
940 y = self.conv_post.apply(&y, yn, pool);
941 atime(5, tt);
942 for v in y.iter_mut() {
943 *v = v.clamp(-1.0, 1.0);
944 }
945 chans.push(y);
946 n = yn;
947 let _ = n;
948 }
949 let len = chans[0].len().min(chans[1].len());
950 let mut out = vec![0f32; 2 * len];
951 for (ch, c) in chans.iter().enumerate() {
952 out[ch * len..(ch + 1) * len].copy_from_slice(&c[..len]);
953 }
954 (out, len)
955 }
956}
957
958impl AmpBlock {
959 fn apply(&self, x: &[f32], ch: usize, n: usize, pool: Option<&Pool>) -> Vec<f32> {
960 let mut cur = x.to_vec();
961 for i in 0..self.convs1.len() {
962 let (a1, a2) = (&self.acts[i * 2], &self.acts[i * 2 + 1]);
963 let (xt, tn) = a1.apply(&cur, ch, n, pool);
964 let xt = self.convs1[i].apply(&xt, tn, pool);
965 let (xt, tn2) = a2.apply(&xt, ch, tn, pool);
966 let xt = self.convs2[i].apply(&xt, tn2, pool);
967 for (a, b) in cur.iter_mut().zip(&xt) {
968 *a += b;
969 }
970 }
971 cur
972 }
973}
974
975#[doc(hidden)]
977pub fn kaiser_sinc_for_test() -> Vec<f32> {
978 kaiser_sinc(0.25, 0.3, FILTER_LEN)
979}
980
981pub(crate) struct SendPtr(pub *mut f32);
982unsafe impl Send for SendPtr {}
983unsafe impl Sync for SendPtr {}
984impl SendPtr {
985 #[allow(clippy::mut_from_ref)]
987 pub(crate) unsafe fn row(&self, off: usize, len: usize) -> &mut [f32] {
988 unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
989 }
990}
991
992#[cfg(test)]
993mod tests {
994 use super::*;
995
996 #[test]
1001 fn music3_dav_decodes_to_the_reference_geometry() {
1002 let Ok(p) = std::env::var("CMF_MUSIC3_VAE") else {
1003 eprintln!("CMF_MUSIC3_VAE unset — skipping Music-3 vocoder test");
1004 return;
1005 };
1006 let model = Arc::new(CmfModel::open(&p).expect("open packed vocoder"));
1007 let dav = Music3Dav::from_cmf(&model).expect("load DAV");
1008 let frames = 12usize;
1009 let latent: Vec<f32> = (0..128 * frames)
1012 .map(|i| {
1013 let (c, t) = (i / frames, i % frames);
1014 0.4 * ((c as f32 * 0.13 + t as f32 * 0.7).sin())
1015 })
1016 .collect();
1017 let pcm = dav.decode(&latent, frames, None);
1018 assert_eq!(
1019 pcm.len(),
1020 frames * Music3Dav::HOP * 2,
1021 "512 samples a frame, two sides interleaved"
1022 );
1023 assert!(pcm.iter().all(|v| v.is_finite()), "non-finite sample");
1024 assert!(
1025 pcm.iter().all(|v| v.abs() <= 1.0),
1026 "tanh range violated: {}",
1027 pcm.iter().fold(0f32, |m, v| m.max(v.abs()))
1028 );
1029 let rms = (pcm.iter().map(|v| v * v).sum::<f32>() / pcm.len() as f32).sqrt();
1031 assert!(rms > 1e-4, "decoded to near-silence, rms {rms}");
1032 let (l, r): (Vec<f32>, Vec<f32>) =
1033 pcm.chunks_exact(2).map(|c| (c[0], c[1])).unzip();
1034 let d = l.iter().zip(&r).map(|(a, b)| (a - b).abs()).fold(0f32, f32::max);
1035 assert!(d > 0.0, "both sides identical — the 128 latent was not split");
1036 eprintln!("music3 dav: {} samples/side, rms {rms:.4}, L-R max {d:.4}", l.len());
1037 }
1038
1039 #[test]
1040 fn the_resampling_filter_is_the_references() {
1041 let f = kaiser_sinc(0.25, 0.3, FILTER_LEN);
1042 assert_eq!(f.len(), FILTER_LEN);
1043 assert!((f.iter().sum::<f32>() - 1.0).abs() < 1e-6);
1046 for i in 0..FILTER_LEN / 2 {
1048 assert!((f[i] - f[FILTER_LEN - 1 - i]).abs() < 1e-6, "asymmetric at {i}");
1049 }
1050 let peak = f.iter().cloned().fold(f32::MIN, f32::max);
1051 assert!((f[5] - peak).abs() < 1e-6);
1052
1053 }
1054
1055 #[test]
1056 fn bessel_i0_matches_known_values() {
1057 for (x, want) in [(0.0, 1.0), (1.0, 1.266_065_878), (4.664, 20.204_6)] {
1060 let got = bessel_i0(x);
1061 assert!((got - want).abs() < 1e-3 * want.max(1.0), "I0({x}) = {got}");
1062 }
1063 }
1064}