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",
52 "conv_pre",
53 "upsamples",
54 "resblocks",
55 "act_post",
56 "conv_post",
57 ];
58 let mut v: Vec<(u64, &str)> = AVAE_TIME
59 .iter()
60 .map(|a| a.load(std::sync::atomic::Ordering::Relaxed))
61 .zip(NAMES)
62 .collect();
63 let total: u64 = v.iter().map(|(u, _)| u).sum();
64 if total == 0 {
65 return None;
66 }
67 v.sort_by(|a, b| b.0.cmp(&a.0));
68 let mut out = format!("audio vae phases (total {:.1} s):\n", total as f64 / 1e6);
69 for (us, name) in v {
70 out.push_str(&format!(
71 " {name:<12} {:>6.1} s {:>5.1}%\n",
72 us as f64 / 1e6,
73 100.0 * us as f64 / total as f64
74 ));
75 }
76 Some(out)
77}
78use cortiq_core::CmfModel;
79use std::sync::Arc;
80
81const FILTER_LEN: usize = 12;
84
85struct Conv1d {
86 w: Vec<f32>, b: Option<Vec<f32>>,
88 out_ch: usize,
89 in_ch: usize,
90 k: usize,
91 pad: usize,
92 dilation: usize,
93}
94
95impl Conv1d {
96 fn load(
97 model: &Arc<CmfModel>,
98 name: &str,
99 pad: usize,
100 dilation: usize,
101 ) -> Result<Self, String> {
102 let e = model
103 .tensor(&format!("{name}.weight"))
104 .ok_or_else(|| format!("missing {name}.weight"))?;
105 let w = crate::dit::cmf_f32(model, &format!("{name}.weight"))?;
106 let b = crate::dit::cmf_f32(model, &format!("{name}.bias")).ok();
107 Ok(Self {
108 out_ch: e.shape[0],
109 in_ch: e.shape[1],
110 k: e.shape[2],
111 w,
112 b,
113 pad,
114 dilation,
115 })
116 }
117
118 fn apply(&self, x: &[f32], n: usize, pool: Option<&Pool>) -> Vec<f32> {
121 let out_n = (n + 2 * self.pad).saturating_sub(self.dilation * (self.k - 1));
122 let mut out = vec![0f32; self.out_ch * out_n];
123 let ptr = SendPtr(out.as_mut_ptr());
124 if std::env::var("CMF_AVAE_CONV_GPU").as_deref() != Ok("0") && crate::gpu::enabled_here() {
158 let kk = self.in_ch * self.k;
159 {
166 let mut yt = vec![0f32; out_n * self.out_ch];
167 if crate::gpu::conv1d_gemm(
168 x,
169 &self.w,
170 self.in_ch,
171 self.out_ch,
172 n,
173 self.k,
174 self.pad,
175 self.dilation,
176 out_n,
177 &mut yt,
178 ) {
179 for o in 0..self.out_ch {
180 let bias = self.b.as_ref().map_or(0.0, |b| b[o]);
181 let dst = unsafe { ptr.row(o * out_n, out_n) };
183 for (t, d) in dst.iter_mut().enumerate() {
184 *d = yt[t * self.out_ch + o] + bias;
185 }
186 }
187 return out;
188 }
189 }
190 const MAX_COL_FLOATS: usize = 96 << 20; let span = (MAX_COL_FLOATS / kk.max(1)).max(1).min(out_n);
200 if span < out_n {
201 let mut done = 0usize;
202 while done < out_n {
203 let w = span.min(out_n - done);
204 let need = w + self.dilation * (self.k - 1);
210 let mut sub = vec![0f32; self.in_ch * need];
211 for i in 0..self.in_ch {
212 let src = &x[i * n..(i + 1) * n];
213 let dst = &mut sub[i * need..(i + 1) * need];
214 for (u, d) in dst.iter_mut().enumerate() {
215 let p = (done + u) as isize - self.pad as isize;
216 if p >= 0 && (p as usize) < n {
217 *d = src[p as usize];
218 }
219 }
220 }
221 let piece = Self {
222 w: self.w.clone(),
223 b: self.b.clone(),
224 out_ch: self.out_ch,
225 in_ch: self.in_ch,
226 k: self.k,
227 pad: 0,
228 dilation: self.dilation,
229 }
230 .apply(&sub, need, pool);
231 for o in 0..self.out_ch {
232 let dst = unsafe { ptr.row(o * out_n + done, w) };
234 dst.copy_from_slice(&piece[o * w..(o + 1) * w]);
235 }
236 done += w;
237 }
238 return out;
239 }
240 if let Some(col_len) = kk.checked_mul(out_n) {
241 let mut col = vec![0f32; col_len];
242 let pc = SendPtr(col.as_mut_ptr());
243 let fill = |lo: usize, hi: usize| {
244 for r in lo..hi {
245 let (i, j) = (r / self.k, r % self.k);
246 let src = &x[i * n..(i + 1) * n];
247 let dst = unsafe { pc.row(r * out_n, out_n) };
249 for (t, d) in dst.iter_mut().enumerate() {
250 let p = (t + j * self.dilation) as isize - self.pad as isize;
251 *d = if p >= 0 && (p as usize) < n {
252 src[p as usize]
253 } else {
254 0.0
255 };
256 }
257 }
258 };
259 match pool {
260 Some(p) => p.run_rows(kk, &fill),
261 None => fill(0, kk),
262 }
263 let mut colt = vec![0f32; col_len];
267 for r in 0..kk {
268 for t in 0..out_n {
269 colt[t * kk + r] = col[r * out_n + t];
270 }
271 }
272 let mut yt = vec![0f32; out_n * self.out_ch];
273 if crate::gpu::gemm_nt_f32(&colt, &self.w, &mut yt, out_n, kk, self.out_ch) {
274 for o in 0..self.out_ch {
275 let bias = self.b.as_ref().map_or(0.0, |b| b[o]);
276 let dst = unsafe { ptr.row(o * out_n, out_n) };
278 for (t, d) in dst.iter_mut().enumerate() {
279 *d = yt[t * self.out_ch + o] + bias;
280 }
281 }
282 return out;
283 }
284 }
285 }
286 let tiles = (128 / self.out_ch.max(1)).max(1);
287 let tile = out_n.div_ceil(tiles.max(1));
288 let rows = self.out_ch * tiles;
289 let work = |lo: usize, hi: usize| {
290 for r in lo..hi {
291 let o = r / tiles;
292 let t0 = (r - o * tiles) * tile;
293 if t0 >= out_n {
294 continue;
295 }
296 let len = tile.min(out_n - t0);
297 let dst = unsafe { ptr.row(o * out_n + t0, len) };
299 let bias = self.b.as_ref().map_or(0.0, |b| b[o]);
300 dst.fill(bias);
301 for i in 0..self.in_ch {
302 let ker =
303 &self.w[(o * self.in_ch + i) * self.k..(o * self.in_ch + i + 1) * self.k];
304 let src = &x[i * n..(i + 1) * n];
305 for (tt, d) in dst.iter_mut().enumerate() {
306 let t = t0 + tt;
307 let mut acc = 0f32;
308 for (j, &kv) in ker.iter().enumerate() {
309 let p = (t + j * self.dilation) as isize - self.pad as isize;
310 if p >= 0 && (p as usize) < n {
311 acc += kv * src[p as usize];
312 }
313 }
314 *d += acc;
315 }
316 }
317 }
318 };
319 match pool {
320 Some(p) => p.run_rows(rows, &work),
321 None => work(0, rows),
322 }
323 out
324 }
325}
326
327struct ConvT1d {
328 w: Vec<f32>, b: Vec<f32>,
330 in_ch: usize,
331 out_ch: usize,
332 k: usize,
333 stride: usize,
334 pad: usize,
335}
336
337impl ConvT1d {
338 fn load(model: &Arc<CmfModel>, name: &str, stride: usize) -> Result<Self, String> {
339 let e = model
340 .tensor(&format!("{name}.weight"))
341 .ok_or_else(|| format!("missing {name}.weight"))?;
342 let (in_ch, out_ch, k) = (e.shape[0], e.shape[1], e.shape[2]);
343 Ok(Self {
344 w: crate::dit::cmf_f32(model, &format!("{name}.weight"))?,
345 b: crate::dit::cmf_f32(model, &format!("{name}.bias"))?,
346 in_ch,
347 out_ch,
348 k,
349 stride,
350 pad: (k - stride) / 2,
351 })
352 }
353
354 fn apply(&self, x: &[f32], n: usize, pool: Option<&Pool>) -> Vec<f32> {
355 let full = (n - 1) * self.stride + self.k;
356 let out_n = full - 2 * self.pad;
357 let mut out = vec![0f32; self.out_ch * out_n];
358 let ptr = SendPtr(out.as_mut_ptr());
359 let work = |lo: usize, hi: usize| {
360 for o in lo..hi {
361 let dst = unsafe { ptr.row(o * out_n, out_n) };
363 dst.fill(self.b[o]);
364 for i in 0..self.in_ch {
365 let ker =
366 &self.w[(i * self.out_ch + o) * self.k..(i * self.out_ch + o + 1) * self.k];
367 let src = &x[i * n..(i + 1) * n];
368 for (t, &sv) in src.iter().enumerate() {
369 if sv == 0.0 {
370 continue;
371 }
372 let base = t * self.stride;
373 for (j, &kv) in ker.iter().enumerate() {
374 let p = base + j;
375 if p >= self.pad && p - self.pad < out_n {
376 dst[p - self.pad] += sv * kv;
377 }
378 }
379 }
380 }
381 }
382 };
383 match pool {
384 Some(p) => p.run_rows(self.out_ch, &work),
385 None => work(0, self.out_ch),
386 }
387 out
388 }
389}
390
391struct Snake {
400 alpha: Vec<f32>,
401}
402
403impl Snake {
404 fn load(model: &Arc<CmfModel>, name: &str) -> Result<Self, String> {
405 Ok(Self {
406 alpha: crate::dit::cmf_f32(model, &format!("{name}.alpha"))?,
407 })
408 }
409
410 fn apply(&self, x: &mut [f32], n: usize) {
411 for (c, row) in x.chunks_exact_mut(n).enumerate() {
412 let a = self.alpha[c];
413 let inv = 1.0 / (a + 1e-9);
414 for v in row.iter_mut() {
415 let s = (a * *v).sin();
416 *v += s * s * inv;
417 }
418 }
419 }
420}
421
422struct DavUnit {
424 a1: Snake,
425 c1: Conv1d,
426 a2: Snake,
427 c2: Conv1d,
428}
429
430impl DavUnit {
431 fn load(model: &Arc<CmfModel>, p: &str, dilation: usize) -> Result<Self, String> {
432 Ok(Self {
433 a1: Snake::load(model, &format!("{p}.block.0"))?,
434 c1: Conv1d::load(model, &format!("{p}.block.1"), 3 * dilation, dilation)?,
435 a2: Snake::load(model, &format!("{p}.block.2"))?,
436 c2: Conv1d::load(model, &format!("{p}.block.3"), 0, 1)?,
437 })
438 }
439
440 fn apply(&self, x: &[f32], n: usize, pool: Option<&Pool>) -> Vec<f32> {
441 let mut h = x.to_vec();
442 self.a1.apply(&mut h, n);
443 let mut h = self.c1.apply(&h, n, pool);
444 self.a2.apply(&mut h, n);
445 let r = self.c2.apply(&h, n, pool);
446 debug_assert_eq!(r.len(), x.len());
449 x.iter().zip(&r).map(|(a, b)| a + b).collect()
450 }
451}
452
453struct DavStage {
456 act: Snake,
457 up: ConvT1d,
458 units: Vec<DavUnit>,
459}
460
461impl DavStage {
462 fn load(model: &Arc<CmfModel>, p: &str, stride: usize) -> Result<Self, String> {
463 Ok(Self {
464 act: Snake::load(model, &format!("{p}.block.0"))?,
465 up: ConvT1d::load(model, &format!("{p}.block.1"), stride)?,
466 units: [1usize, 3, 9]
467 .iter()
468 .enumerate()
469 .map(|(i, &d)| DavUnit::load(model, &format!("{p}.block.{}", i + 2), d))
470 .collect::<Result<_, _>>()?,
471 })
472 }
473
474 fn apply(&self, x: &[f32], n: usize, pool: Option<&Pool>) -> (Vec<f32>, usize) {
475 let mut h = x.to_vec();
476 self.act.apply(&mut h, n);
477 let mut h = self.up.apply(&h, n, pool);
478 let n = n * self.up.stride;
479 for u in &self.units {
480 h = u.apply(&h, n, pool);
481 }
482 (h, n)
483 }
484}
485
486pub struct Music3Dav {
493 dec_in: Conv1d,
494 conv_pre: Conv1d,
495 stages: Vec<DavStage>,
496 act_post: Snake,
497 conv_post: Conv1d,
498}
499
500impl Music3Dav {
501 pub const STRIDES: [usize; 4] = [8, 8, 4, 2];
502 pub const HOP: usize = 512;
504 pub const SAMPLE_RATE: usize = 44100;
505
506 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
507 Ok(Self {
508 dec_in: Conv1d::load(model, "mvae.dec_in_proj", 0, 1)?,
509 conv_pre: Conv1d::load(model, "mvae.decoder.model.0", 3, 1)?,
510 stages: Self::STRIDES
511 .iter()
512 .enumerate()
513 .map(|(i, &s)| DavStage::load(model, &format!("mvae.decoder.model.{}", i + 1), s))
514 .collect::<Result<_, _>>()?,
515 act_post: Snake::load(model, "mvae.decoder.model.5")?,
516 conv_post: Conv1d::load(model, "mvae.decoder.model.6", 3, 1)?,
517 })
518 }
519
520 pub fn decode(&self, latent: &[f32], frames: usize, pool: Option<&Pool>) -> Vec<f32> {
523 let mut chans: Vec<Vec<f32>> = Vec::with_capacity(2);
524 for half in 0..2 {
525 let src = &latent[half * 64 * frames..(half + 1) * 64 * frames];
526 let mut h = self.dec_in.apply(src, frames, pool);
527 h = self.conv_pre.apply(&h, frames, pool);
528 let mut n = frames;
529 for st in &self.stages {
530 let (nh, nn) = st.apply(&h, n, pool);
531 h = nh;
532 n = nn;
533 }
534 self.act_post.apply(&mut h, n);
535 let w = self.conv_post.apply(&h, n, pool);
536 chans.push(w.iter().map(|v| v.tanh()).collect());
539 }
540 let n = chans[0].len();
541 let mut out = vec![0f32; n * 2];
542 for (i, o) in out.chunks_exact_mut(2).enumerate() {
543 o[0] = chans[0][i];
544 o[1] = chans[1][i];
545 }
546 out
547 }
548}
549
550struct SnakeBeta {
552 alpha: Vec<f32>,
553 beta: Vec<f32>,
554}
555
556impl SnakeBeta {
557 fn load(model: &Arc<CmfModel>, name: &str) -> Result<Self, String> {
558 Ok(Self {
559 alpha: crate::dit::cmf_f32(model, &format!("{name}.alpha"))?
560 .iter()
561 .map(|v| v.exp())
562 .collect(),
563 beta: crate::dit::cmf_f32(model, &format!("{name}.beta"))?
564 .iter()
565 .map(|v| v.exp())
566 .collect(),
567 })
568 }
569
570 fn apply(&self, x: &mut [f32], n: usize) {
571 for (c, row) in x.chunks_exact_mut(n).enumerate() {
572 let (a, b) = (self.alpha[c], 1.0 / (self.beta[c] + 1e-9));
573 for v in row.iter_mut() {
574 let s = (a * *v).sin();
575 *v += s * s * b;
576 }
577 }
578 }
579}
580
581fn bessel_i0(x: f64) -> f64 {
582 let mut sum = 1.0;
585 let mut term = 1.0;
586 for k in 1..40 {
587 term *= (x / (2.0 * k as f64)).powi(2);
588 sum += term;
589 if term < 1e-18 * sum {
590 break;
591 }
592 }
593 sum
594}
595
596fn sinc(x: f64) -> f64 {
597 if x == 0.0 {
598 1.0
599 } else {
600 (std::f64::consts::PI * x).sin() / (std::f64::consts::PI * x)
601 }
602}
603
604fn kaiser_sinc(cutoff: f64, half_width: f64, k: usize) -> Vec<f32> {
606 let half = k / 2;
607 let delta_f = 4.0 * half_width;
608 let a = 2.285 * (half as f64 - 1.0) * std::f64::consts::PI * delta_f + 7.95;
609 let beta = if a > 50.0 {
610 0.1102 * (a - 8.7)
611 } else if a >= 21.0 {
612 0.5842 * (a - 21.0).powf(0.4) + 0.078_86 * (a - 21.0)
613 } else {
614 0.0
615 };
616 let denom = bessel_i0(beta);
617 let n = k as f64 - 1.0;
618 let mut f: Vec<f64> = (0..k)
619 .map(|i| {
620 let r = (2.0 * i as f64 / n) - 1.0;
621 let win = bessel_i0(beta * (1.0 - r * r).max(0.0).sqrt()) / denom;
622 let t = -(half as f64) + i as f64 + 0.5;
624 2.0 * cutoff * win * sinc(2.0 * cutoff * t)
625 })
626 .collect();
627 let s: f64 = f.iter().sum();
628 for v in f.iter_mut() {
629 *v /= s;
630 }
631 f.into_iter().map(|v| v as f32).collect()
632}
633
634#[allow(clippy::too_many_arguments)]
659fn fir_pad(
660 x: &[f32],
661 ch: usize,
662 n: usize,
663 f: &[f32],
664 pad_l: usize,
665 pad_r: usize,
666 stride: usize,
667 pool: Option<&Pool>,
668) -> (Vec<f32>, usize) {
669 let padded = n + pad_l + pad_r;
670 let out_n = (padded - f.len()) / stride + 1;
671 let mut out = vec![0f32; ch * out_n];
672 struct P(*mut f32);
673 unsafe impl Send for P {}
676 unsafe impl Sync for P {}
677 impl P {
678 #[allow(clippy::mut_from_ref)]
682 pub(crate) unsafe fn row(&self, off: usize, len: usize) -> &mut [f32] {
683 unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
684 }
685 }
686 let po = P(out.as_mut_ptr());
687 let work = |lo: usize, hi: usize| {
688 let mut buf = vec![0f32; padded];
691 for c in lo..hi {
692 let src = &x[c * n..(c + 1) * n];
693 for (i, b) in buf.iter_mut().enumerate() {
694 let p = i as isize - pad_l as isize;
695 *b = src[p.clamp(0, n as isize - 1) as usize];
696 }
697 let dst = unsafe { po.row(c * out_n, out_n) };
698 for (t, d) in dst.iter_mut().enumerate() {
699 let mut acc = 0f32;
700 for (j, &kv) in f.iter().enumerate() {
701 acc += kv * buf[t * stride + j];
702 }
703 *d = acc;
704 }
705 }
706 };
707 match pool {
708 Some(p) => p.run_rows(ch, &work),
709 None => work(0, ch),
710 }
711 (out, out_n)
712}
713
714struct Activation1d {
716 act: SnakeBeta,
717 up: Vec<f32>,
718 down: Vec<f32>,
719}
720
721impl Activation1d {
722 fn load(model: &Arc<CmfModel>, name: &str) -> Result<Self, String> {
727 let designed = || kaiser_sinc(0.25, 0.3, FILTER_LEN);
728 Ok(Self {
729 act: SnakeBeta::load(model, &format!("{name}.act"))?,
730 up: crate::dit::cmf_f32(model, &format!("{name}.upsample.filter"))
731 .unwrap_or_else(|_| designed()),
732 down: crate::dit::cmf_f32(model, &format!("{name}.downsample.lowpass.filter"))
733 .unwrap_or_else(|_| designed()),
734 })
735 }
736
737 fn apply(&self, x: &[f32], ch: usize, n: usize, pool: Option<&Pool>) -> (Vec<f32>, usize) {
738 let pad = FILTER_LEN / 2 - 1;
741 let pad_l = pad * 2 + (FILTER_LEN - 2) / 2;
742 let pad_r = pad * 2 + (FILTER_LEN - 2).div_ceil(2);
743 let pn = n + 2 * pad;
744 let full = (pn - 1) * 2 + FILTER_LEN;
745 let mut up = vec![0f32; ch * full];
746 for c in 0..ch {
747 let src = &x[c * n..(c + 1) * n];
748 let dst = &mut up[c * full..(c + 1) * full];
749 for i in 0..pn {
750 let p = i as isize - pad as isize;
751 let v = src[p.clamp(0, n as isize - 1) as usize] * 2.0;
752 if v == 0.0 {
753 continue;
754 }
755 for (j, &kv) in self.up.iter().enumerate() {
756 dst[i * 2 + j] += v * kv;
757 }
758 }
759 }
760 let keep = full - pad_l - pad_r;
761 let mut mid = vec![0f32; ch * keep];
762 for c in 0..ch {
763 mid[c * keep..(c + 1) * keep]
764 .copy_from_slice(&up[c * full + pad_l..c * full + pad_l + keep]);
765 }
766 self.act.apply(&mut mid, keep);
767 fir_pad(
769 &mid,
770 ch,
771 keep,
772 &self.down,
773 FILTER_LEN / 2 - 1,
774 FILTER_LEN / 2,
775 2,
776 pool,
777 )
778 }
779}
780
781struct AmpBlock {
782 convs1: Vec<Conv1d>,
783 convs2: Vec<Conv1d>,
784 acts: Vec<Activation1d>,
785}
786
787pub struct AudioVae {
788 dec_in: Conv1d,
789 conv_pre: Conv1d,
790 ups: Vec<ConvT1d>,
791 resblocks: Vec<AmpBlock>,
792 act_post: Activation1d,
793 conv_post: Conv1d,
794 latents_mean: Vec<f32>,
795 latents_std: Vec<f32>,
796 pool: Option<Arc<Pool>>,
797 n_kernels: usize,
798 pub sample_rate: usize,
799}
800
801fn get_padding(k: usize, d: usize) -> usize {
802 (k * d - d) / 2
803}
804
805impl AudioVae {
806 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
807 let cfg: serde_json::Value = serde_json::from_slice(
808 model
809 .tensor_bytes("avae.config_json")
810 .map_err(|e| e.to_string())?,
811 )
812 .map_err(|e| format!("avae.config_json: {e}"))?;
813 let rates: Vec<usize> = cfg["upsample_rates"]
814 .as_array()
815 .ok_or("upsample_rates")?
816 .iter()
817 .map(|v| v.as_u64().unwrap_or(1) as usize)
818 .collect();
819 let rk: Vec<usize> = cfg["resblock_kernel_sizes"]
820 .as_array()
821 .ok_or("resblock_kernel_sizes")?
822 .iter()
823 .map(|v| v.as_u64().unwrap_or(3) as usize)
824 .collect();
825 let rd: Vec<Vec<usize>> = cfg["resblock_dilation_sizes"]
826 .as_array()
827 .ok_or("resblock_dilation_sizes")?
828 .iter()
829 .map(|a| {
830 a.as_array()
831 .unwrap()
832 .iter()
833 .map(|v| v.as_u64().unwrap_or(1) as usize)
834 .collect()
835 })
836 .collect();
837
838 let mut ups = Vec::new();
839 for (i, &u) in rates.iter().enumerate() {
840 ups.push(ConvT1d::load(model, &format!("avae.decoder.ups.{i}.0"), u)?);
841 }
842 let mut resblocks = Vec::new();
843 for i in 0..rates.len() {
844 for (j, (&k, d)) in rk.iter().zip(&rd).enumerate() {
845 let p = format!("avae.decoder.resblocks.{}", i * rk.len() + j);
846 let convs1 = (0..d.len())
847 .map(|q| {
848 Conv1d::load(
849 model,
850 &format!("{p}.convs1.{q}"),
851 get_padding(k, d[q]),
852 d[q],
853 )
854 })
855 .collect::<Result<Vec<_>, _>>()?;
856 let convs2 = (0..d.len())
857 .map(|q| Conv1d::load(model, &format!("{p}.convs2.{q}"), get_padding(k, 1), 1))
858 .collect::<Result<Vec<_>, _>>()?;
859 let acts = (0..convs1.len() + convs2.len())
860 .map(|q| Activation1d::load(model, &format!("{p}.activations.{q}")))
861 .collect::<Result<Vec<_>, _>>()?;
862 resblocks.push(AmpBlock {
863 convs1,
864 convs2,
865 acts,
866 });
867 }
868 }
869 Ok(Self {
870 dec_in: Conv1d::load(model, "avae.dec_in_proj", 0, 1)?,
871 conv_pre: Conv1d::load(model, "avae.decoder.conv_pre", 3, 1)?,
872 ups,
873 resblocks,
874 act_post: Activation1d::load(model, "avae.decoder.activation_post")?,
875 conv_post: Conv1d::load(model, "avae.decoder.conv_post", 3, 1)?,
876 latents_mean: crate::dit::cmf_f32(model, "avae.latents_mean")?,
877 latents_std: crate::dit::cmf_f32(model, "avae.latents_std")?,
878 pool: Pool::from_env(),
879 n_kernels: rk.len(),
880 sample_rate: cfg["sample_rate"].as_u64().unwrap_or(32000) as usize,
881 })
882 }
883
884 pub fn decode(&self, z: &[f32], c: usize, t: usize) -> (Vec<f32>, usize) {
911 let pool = self.pool.as_deref();
912 let mut chans: Vec<Vec<f32>> = Vec::with_capacity(2);
913 for ch in 0..2 {
914 let mut lat = vec![0f32; c * t];
915 for ci in 0..c {
916 let (m, s) = (self.latents_mean[ci], self.latents_std[ci]);
917 for ti in 0..t {
918 lat[ci * t + ti] = z[(ci * 2 + ch) * t + ti] * s + m;
919 }
920 }
921 let prof = std::env::var_os("CMF_AVAE_PROF").is_some();
924 let rms = |x: &[f32]| {
925 (x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / x.len() as f64).sqrt()
926 };
927 let tt = std::time::Instant::now();
928 let mut x = self.dec_in.apply(&lat, t, pool);
929 atime(0, tt);
930 let mut n = t;
931 if prof {
932 eprintln!("ch{ch} dec_in rms {:.6e} n {n}", rms(&x));
933 }
934 let tt = std::time::Instant::now();
935 x = self.conv_pre.apply(&x, n, pool);
936 atime(1, tt);
937 if prof {
938 eprintln!("ch{ch} conv_pre rms {:.6e} n {n}", rms(&x));
939 }
940 for i in 0..self.ups.len() {
941 let up = &self.ups[i];
942 let tt = std::time::Instant::now();
943 x = up.apply(&x, n, pool);
944 atime(2, tt);
945 n = (n - 1) * up.stride + up.k - 2 * up.pad;
946 let ch_n = up.out_ch;
947 let mut acc = vec![0f32; ch_n * n];
948 for j in 0..self.n_kernels {
949 let tt = std::time::Instant::now();
950 let r = self.resblocks[i * self.n_kernels + j].apply(&x, ch_n, n, pool);
951 atime(3, tt);
952 for (a, b) in acc.iter_mut().zip(&r) {
953 *a += b;
954 }
955 }
956 let inv = 1.0 / self.n_kernels as f32;
957 for v in acc.iter_mut() {
958 *v *= inv;
959 }
960 x = acc;
961 if prof {
962 eprintln!("ch{ch} up{i} rms {:.6e} ch {ch_n} n {n}", rms(&x));
963 }
964 }
965 let last_ch = self.ups[self.ups.len() - 1].out_ch;
966 let tt = std::time::Instant::now();
967 let (mut y, yn) = self.act_post.apply(&x, last_ch, n, pool);
968 atime(4, tt);
969 let tt = std::time::Instant::now();
970 y = self.conv_post.apply(&y, yn, pool);
971 atime(5, tt);
972 for v in y.iter_mut() {
973 *v = v.clamp(-1.0, 1.0);
974 }
975 chans.push(y);
976 n = yn;
977 let _ = n;
978 }
979 let len = chans[0].len().min(chans[1].len());
980 let mut out = vec![0f32; 2 * len];
981 for (ch, c) in chans.iter().enumerate() {
982 out[ch * len..(ch + 1) * len].copy_from_slice(&c[..len]);
983 }
984 (out, len)
985 }
986}
987
988impl AmpBlock {
989 fn apply(&self, x: &[f32], ch: usize, n: usize, pool: Option<&Pool>) -> Vec<f32> {
990 let mut cur = x.to_vec();
991 for i in 0..self.convs1.len() {
992 let (a1, a2) = (&self.acts[i * 2], &self.acts[i * 2 + 1]);
993 let (xt, tn) = a1.apply(&cur, ch, n, pool);
994 let xt = self.convs1[i].apply(&xt, tn, pool);
995 let (xt, tn2) = a2.apply(&xt, ch, tn, pool);
996 let xt = self.convs2[i].apply(&xt, tn2, pool);
997 for (a, b) in cur.iter_mut().zip(&xt) {
998 *a += b;
999 }
1000 }
1001 cur
1002 }
1003}
1004
1005#[doc(hidden)]
1007pub fn kaiser_sinc_for_test() -> Vec<f32> {
1008 kaiser_sinc(0.25, 0.3, FILTER_LEN)
1009}
1010
1011pub(crate) struct SendPtr(pub *mut f32);
1012unsafe impl Send for SendPtr {}
1013unsafe impl Sync for SendPtr {}
1014impl SendPtr {
1015 #[allow(clippy::mut_from_ref)]
1017 pub(crate) unsafe fn row(&self, off: usize, len: usize) -> &mut [f32] {
1018 unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
1019 }
1020}
1021
1022#[cfg(test)]
1023mod tests {
1024 use super::*;
1025
1026 #[test]
1031 fn music3_dav_decodes_to_the_reference_geometry() {
1032 let Ok(p) = std::env::var("CMF_MUSIC3_VAE") else {
1033 eprintln!("CMF_MUSIC3_VAE unset — skipping Music-3 vocoder test");
1034 return;
1035 };
1036 let model = Arc::new(CmfModel::open(&p).expect("open packed vocoder"));
1037 let dav = Music3Dav::from_cmf(&model).expect("load DAV");
1038 let frames = 12usize;
1039 let latent: Vec<f32> = (0..128 * frames)
1042 .map(|i| {
1043 let (c, t) = (i / frames, i % frames);
1044 0.4 * ((c as f32 * 0.13 + t as f32 * 0.7).sin())
1045 })
1046 .collect();
1047 let pcm = dav.decode(&latent, frames, None);
1048 assert_eq!(
1049 pcm.len(),
1050 frames * Music3Dav::HOP * 2,
1051 "512 samples a frame, two sides interleaved"
1052 );
1053 assert!(pcm.iter().all(|v| v.is_finite()), "non-finite sample");
1054 assert!(
1055 pcm.iter().all(|v| v.abs() <= 1.0),
1056 "tanh range violated: {}",
1057 pcm.iter().fold(0f32, |m, v| m.max(v.abs()))
1058 );
1059 let rms = (pcm.iter().map(|v| v * v).sum::<f32>() / pcm.len() as f32).sqrt();
1061 assert!(rms > 1e-4, "decoded to near-silence, rms {rms}");
1062 let (l, r): (Vec<f32>, Vec<f32>) = pcm.chunks_exact(2).map(|c| (c[0], c[1])).unzip();
1063 let d = l
1064 .iter()
1065 .zip(&r)
1066 .map(|(a, b)| (a - b).abs())
1067 .fold(0f32, f32::max);
1068 assert!(
1069 d > 0.0,
1070 "both sides identical — the 128 latent was not split"
1071 );
1072 eprintln!(
1073 "music3 dav: {} samples/side, rms {rms:.4}, L-R max {d:.4}",
1074 l.len()
1075 );
1076 }
1077
1078 #[test]
1079 fn the_resampling_filter_is_the_references() {
1080 let f = kaiser_sinc(0.25, 0.3, FILTER_LEN);
1081 assert_eq!(f.len(), FILTER_LEN);
1082 assert!((f.iter().sum::<f32>() - 1.0).abs() < 1e-6);
1085 for i in 0..FILTER_LEN / 2 {
1087 assert!(
1088 (f[i] - f[FILTER_LEN - 1 - i]).abs() < 1e-6,
1089 "asymmetric at {i}"
1090 );
1091 }
1092 let peak = f.iter().cloned().fold(f32::MIN, f32::max);
1093 assert!((f[5] - peak).abs() < 1e-6);
1094 }
1095
1096 #[test]
1097 fn bessel_i0_matches_known_values() {
1098 for (x, want) in [(0.0, 1.0), (1.0, 1.266_065_878), (4.664, 20.204_6)] {
1101 let got = bessel_i0(x);
1102 assert!((got - want).abs() < 1e-3 * want.max(1.0), "I0({x}) = {got}");
1103 }
1104 }
1105}