1use crate::pool::Pool;
27use cortiq_core::CmfModel;
28use std::sync::Arc;
29
30fn tensor_f32(model: &Arc<CmfModel>, name: &str) -> Result<(Vec<f32>, Vec<usize>), String> {
31 let e = model
32 .tensor(name)
33 .ok_or_else(|| format!("missing tensor {name}"))?;
34 let mut out = vec![0.0f32; e.n_elems()];
35 cortiq_core::quant::dequant_tensor(e, model.entry_bytes(e), &mut out)?;
36 Ok((out, e.shape.clone()))
37}
38
39fn silu(v: f32) -> f32 {
40 v / (1.0 + (-v).exp())
41}
42
43#[derive(Clone)]
45pub struct Grid {
46 pub c: usize,
47 pub h: usize,
48 pub w: usize,
49 pub data: Vec<f32>,
50}
51
52impl Grid {
53 fn zeros(c: usize, h: usize, w: usize) -> Grid {
54 Grid {
55 c,
56 h,
57 w,
58 data: vec![0.0; c * h * w],
59 }
60 }
61 fn n(&self) -> usize {
62 self.h * self.w
63 }
64}
65
66struct Conv2d {
70 w: Vec<f32>,
71 b: Vec<f32>,
72 c_out: usize,
73 c_in: usize,
74 kh: usize,
75 kw: usize,
76}
77
78impl Conv2d {
79 fn load(model: &Arc<CmfModel>, name: &str) -> Result<Conv2d, String> {
80 let (w, s) = tensor_f32(model, &format!("{name}.weight"))?;
81 let (b, _) = tensor_f32(model, &format!("{name}.bias"))?;
82 Ok(Conv2d {
83 w,
84 b,
85 c_out: s[0],
86 c_in: s[1],
87 kh: s[2],
88 kw: s[3],
89 })
90 }
91
92 fn forward(&self, x: &Grid, pool: Option<&Pool>) -> Grid {
93 let (h, w) = (x.h, x.w);
94 let npos = h * w;
95 let k = self.c_in * self.kh * self.kw;
96 let mut out = Grid::zeros(self.c_out, h, w);
97 let pad_h = self.kh - 1; let pad_w = (self.kw - 1) / 2;
99 const CHUNK: usize = 8192;
100 let mut patches = vec![0f32; CHUNK.min(npos) * k];
101 let mut ys = vec![0f32; CHUNK.min(npos) * self.c_out];
102 let mut p0 = 0usize;
103 while p0 < npos {
104 let n = CHUNK.min(npos - p0);
105 patches[..n * k].fill(0.0);
106 for i in 0..n {
107 let p = p0 + i;
108 let (pwi, phi) = (p % w, p / w);
109 for ci in 0..self.c_in {
110 for a in 0..self.kh {
111 let sh = phi as isize + a as isize - pad_h as isize;
112 if sh < 0 || sh >= h as isize {
113 continue;
114 }
115 for bb in 0..self.kw {
116 let sw = pwi as isize + bb as isize - pad_w as isize;
117 if sw < 0 || sw >= w as isize {
118 continue;
119 }
120 patches[i * k + (ci * self.kh + a) * self.kw + bb] =
121 x.data[(ci * h + sh as usize) * w + sw as usize];
122 }
123 }
124 }
125 }
126 crate::fcd_ops::gemm_nt(
127 &patches[..n * k],
128 &self.w,
129 &mut ys[..n * self.c_out],
130 n,
131 k,
132 self.c_out,
133 pool,
134 );
135 for i in 0..n {
136 for co in 0..self.c_out {
137 out.data[co * npos + p0 + i] = ys[i * self.c_out + co] + self.b[co];
138 }
139 }
140 p0 += n;
141 }
142 out
143 }
144}
145
146fn pixel_norm(x: &mut Grid) {
148 let n = x.n();
149 for p in 0..n {
150 let mut ss = 0f64;
151 for c in 0..x.c {
152 let v = x.data[c * n + p] as f64;
153 ss += v * v;
154 }
155 let inv = 1.0 / (ss / x.c as f64 + 1e-6).sqrt();
156 for c in 0..x.c {
157 x.data[c * n + p] = (x.data[c * n + p] as f64 * inv) as f32;
158 }
159 }
160}
161
162struct ResnetBlock {
163 conv1: Conv2d,
164 conv2: Conv2d,
165 shortcut: Option<Conv2d>,
166}
167
168impl ResnetBlock {
169 fn load(model: &Arc<CmfModel>, p: &str) -> Result<ResnetBlock, String> {
170 Ok(ResnetBlock {
171 conv1: Conv2d::load(model, &format!("{p}.conv1.conv"))?,
172 conv2: Conv2d::load(model, &format!("{p}.conv2.conv"))?,
173 shortcut: match model.tensor(&format!("{p}.nin_shortcut.conv.weight")) {
174 Some(_) => Some(Conv2d::load(model, &format!("{p}.nin_shortcut.conv"))?),
175 None => None,
176 },
177 })
178 }
179
180 fn forward(&self, x: &Grid, pool: Option<&Pool>) -> Grid {
181 let mut h = x.clone();
182 pixel_norm(&mut h);
183 h.data.iter_mut().for_each(|v| *v = silu(*v));
184 let mut h = self.conv1.forward(&h, pool);
185 pixel_norm(&mut h);
186 h.data.iter_mut().for_each(|v| *v = silu(*v));
187 let mut h = self.conv2.forward(&h, pool);
188 let res = match &self.shortcut {
189 Some(c) => c.forward(x, pool),
190 None => x.clone(),
191 };
192 for (v, &r) in h.data.iter_mut().zip(&res.data) {
193 *v += r;
194 }
195 h
196 }
197}
198
199fn upsample2(x: &Grid, conv: &Conv2d, pool: Option<&Pool>) -> Grid {
202 let (h2, w2) = (x.h * 2, x.w * 2);
203 let mut up = Grid::zeros(x.c, h2, w2);
204 for c in 0..x.c {
205 for y in 0..h2 {
206 for z in 0..w2 {
207 up.data[(c * h2 + y) * w2 + z] = x.data[(c * x.h + y / 2) * x.w + z / 2];
208 }
209 }
210 }
211 let conved = conv.forward(&up, pool);
212 let mut out = Grid::zeros(conved.c, h2 - 1, w2);
213 for c in 0..conved.c {
214 for y in 1..h2 {
215 for z in 0..w2 {
216 out.data[(c * (h2 - 1) + y - 1) * w2 + z] = conved.data[(c * h2 + y) * w2 + z];
217 }
218 }
219 }
220 out
221}
222
223pub struct AudioVaeDecoder {
224 conv_in: Conv2d,
225 mid: Vec<ResnetBlock>,
226 levels: Vec<(Vec<ResnetBlock>, Option<Conv2d>)>,
227 conv_out: Conv2d,
228 mean: Vec<f32>,
229 std: Vec<f32>,
230}
231
232impl AudioVaeDecoder {
233 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<AudioVaeDecoder, String> {
234 let mut levels = Vec::new();
235 let mut lv = 0usize;
236 while model
237 .tensor(&format!("avae.decoder.up.{lv}.block.0.conv1.conv.weight"))
238 .is_some()
239 {
240 let mut blocks = Vec::new();
241 let mut bi = 0usize;
242 while model
243 .tensor(&format!(
244 "avae.decoder.up.{lv}.block.{bi}.conv1.conv.weight"
245 ))
246 .is_some()
247 {
248 blocks.push(ResnetBlock::load(
249 model,
250 &format!("avae.decoder.up.{lv}.block.{bi}"),
251 )?);
252 bi += 1;
253 }
254 let up = match model.tensor(&format!("avae.decoder.up.{lv}.upsample.conv.conv.weight"))
255 {
256 Some(_) => Some(Conv2d::load(
257 model,
258 &format!("avae.decoder.up.{lv}.upsample.conv.conv"),
259 )?),
260 None => None,
261 };
262 levels.push((blocks, up));
263 lv += 1;
264 }
265 Ok(AudioVaeDecoder {
266 conv_in: Conv2d::load(model, "avae.decoder.conv_in.conv")?,
267 mid: vec![
268 ResnetBlock::load(model, "avae.decoder.mid.block_1")?,
269 ResnetBlock::load(model, "avae.decoder.mid.block_2")?,
270 ],
271 levels,
272 conv_out: Conv2d::load(model, "avae.decoder.conv_out.conv")?,
273 mean: tensor_f32(model, "avae.per_channel_statistics.mean-of-means")?.0,
274 std: tensor_f32(model, "avae.per_channel_statistics.std-of-means")?.0,
275 })
276 }
277
278 pub fn decode(&self, latent: &Grid, pool: Option<&Pool>) -> Grid {
280 let mut x = latent.clone();
283 let n = x.n();
284 for c in 0..x.c {
285 for wi in 0..x.w {
286 let idx = c * x.w + wi;
287 let (m, s) = (self.mean[idx], self.std[idx]);
288 for hi in 0..x.h {
289 let o = (c * x.h + hi) * x.w + wi;
290 x.data[o] = x.data[o] * s + m;
291 }
292 }
293 }
294 let _ = n;
295 let mut h = self.conv_in.forward(&x, pool);
296 for b in &self.mid {
297 h = b.forward(&h, pool);
298 }
299 for (blocks, up) in self.levels.iter().rev() {
300 for b in blocks {
301 h = b.forward(&h, pool);
302 }
303 if let Some(c) = up {
304 h = upsample2(&h, c, pool);
305 }
306 }
307 pixel_norm(&mut h);
308 h.data.iter_mut().for_each(|v| *v = silu(*v));
309 let out = self.conv_out.forward(&h, pool);
310 let target = (latent.h * 4).saturating_sub(3).max(1);
312 if out.h == target {
313 return out;
314 }
315 let mut cropped = Grid::zeros(out.c, target.min(out.h), out.w);
316 for c in 0..out.c {
317 for y in 0..cropped.h {
318 for z in 0..out.w {
319 cropped.data[(c * cropped.h + y) * out.w + z] =
320 out.data[(c * out.h + y) * out.w + z];
321 }
322 }
323 }
324 cropped
325 }
326}
327
328#[derive(Clone)]
332pub struct Sig {
333 pub c: usize,
334 pub t: usize,
335 pub data: Vec<f32>,
336}
337
338impl Sig {
339 fn zeros(c: usize, t: usize) -> Sig {
340 Sig {
341 c,
342 t,
343 data: vec![0.0; c * t],
344 }
345 }
346}
347
348struct Conv1d {
349 w: Vec<f32>,
350 b: Option<Vec<f32>>,
351 c_out: usize,
352 c_in: usize,
353 k: usize,
354 dilation: usize,
355 pad: usize,
356}
357
358impl Conv1d {
359 fn load(model: &Arc<CmfModel>, name: &str, dilation: usize) -> Result<Conv1d, String> {
360 let (w, s) = tensor_f32(model, &format!("{name}.weight"))?;
361 let b = tensor_f32(model, &format!("{name}.bias")).ok().map(|x| x.0);
362 let k = s[2];
363 Ok(Conv1d {
364 w,
365 b,
366 c_out: s[0],
367 c_in: s[1],
368 k,
369 dilation,
370 pad: (k - 1) * dilation / 2,
371 })
372 }
373
374 fn forward(&self, x: &Sig, pool: Option<&Pool>) -> Sig {
375 let t = x.t;
376 let kk = self.c_in * self.k;
377 let mut patches = vec![0f32; t * kk];
378 for p in 0..t {
379 for ci in 0..self.c_in {
380 for a in 0..self.k {
381 let s = p as isize + (a * self.dilation) as isize - self.pad as isize;
382 if s >= 0 && s < t as isize {
383 patches[p * kk + ci * self.k + a] = x.data[ci * t + s as usize];
384 }
385 }
386 }
387 }
388 let mut ys = vec![0f32; t * self.c_out];
389 crate::fcd_ops::gemm_nt(&patches, &self.w, &mut ys, t, kk, self.c_out, pool);
390 let mut out = Sig::zeros(self.c_out, t);
391 for p in 0..t {
392 for co in 0..self.c_out {
393 out.data[co * t + p] =
394 ys[p * self.c_out + co] + self.b.as_ref().map_or(0.0, |b| b[co]);
395 }
396 }
397 out
398 }
399}
400
401struct ConvT1d {
403 w: Vec<f32>,
404 b: Option<Vec<f32>>,
405 c_in: usize,
406 c_out: usize,
407 k: usize,
408 stride: usize,
409 pad: usize,
410}
411
412impl ConvT1d {
413 fn load(model: &Arc<CmfModel>, name: &str, stride: usize) -> Result<ConvT1d, String> {
414 let (w, s) = tensor_f32(model, &format!("{name}.weight"))?;
415 let b = tensor_f32(model, &format!("{name}.bias")).ok().map(|x| x.0);
416 let k = s[2];
417 Ok(ConvT1d {
418 w,
419 b,
420 c_in: s[0],
421 c_out: s[1],
422 k,
423 stride,
424 pad: (k - stride) / 2,
425 })
426 }
427
428 fn forward(&self, x: &Sig) -> Sig {
429 let t_out = (x.t - 1) * self.stride + self.k - 2 * self.pad;
430 let mut out = Sig::zeros(self.c_out, t_out);
431 for ci in 0..self.c_in {
432 for p in 0..x.t {
433 let v = &x.data[ci * x.t + p];
434 if *v == 0.0 {
435 continue;
436 }
437 let base = p * self.stride;
438 for a in 0..self.k {
439 let o = base + a;
440 if o < self.pad || o - self.pad >= t_out {
441 continue;
442 }
443 let oo = o - self.pad;
444 for co in 0..self.c_out {
445 out.data[co * t_out + oo] +=
446 v * self.w[(ci * self.c_out + co) * self.k + a];
447 }
448 }
449 }
450 }
451 if let Some(b) = &self.b {
452 for co in 0..self.c_out {
453 for v in out.data[co * t_out..(co + 1) * t_out].iter_mut() {
454 *v += b[co];
455 }
456 }
457 }
458 out
459 }
460}
461
462struct Aliasing {
465 up: Vec<f32>,
466 down: Vec<f32>,
467 ratio: usize,
468}
469
470impl Aliasing {
471 fn load(model: &Arc<CmfModel>, p: &str) -> Result<Aliasing, String> {
472 Ok(Aliasing {
473 up: tensor_f32(model, &format!("{p}.upsample.filter"))?.0,
474 down: tensor_f32(model, &format!("{p}.downsample.lowpass.filter"))?.0,
475 ratio: 2,
476 })
477 }
478
479 fn upsample(&self, x: &Sig) -> Sig {
480 let k = self.up.len();
481 let stride = self.ratio;
482 let pad = k / stride - 1;
483 let pad_left = pad * stride + (k - stride) / 2;
484 let pad_right = pad * stride + (k - stride).div_ceil(2);
485 let tp = x.t + 2 * pad;
488 let full = (tp - 1) * stride + k;
489 let mut out = Sig::zeros(x.c, full);
490 for c in 0..x.c {
491 for p in 0..tp {
492 let src = (p as isize - pad as isize).clamp(0, x.t as isize - 1) as usize;
493 let v = x.data[c * x.t + src] * self.ratio as f32;
494 if v == 0.0 {
495 continue;
496 }
497 for a in 0..k {
498 out.data[c * full + p * stride + a] += v * self.up[a];
499 }
500 }
501 }
502 let (lo, hi) = (pad_left, full - pad_right);
503 let t2 = hi - lo;
504 let mut trimmed = Sig::zeros(x.c, t2);
505 for c in 0..x.c {
506 trimmed.data[c * t2..(c + 1) * t2]
507 .copy_from_slice(&out.data[c * full + lo..c * full + hi]);
508 }
509 trimmed
510 }
511
512 fn downsample(&self, x: &Sig) -> Sig {
513 let k = self.down.len();
514 let pad_left = k / 2 - if k % 2 == 0 { 1 } else { 0 };
515 let pad_right = k / 2;
516 let tp = x.t + pad_left + pad_right;
517 let t2 = (tp - k) / self.ratio + 1;
518 let mut out = Sig::zeros(x.c, t2);
519 for c in 0..x.c {
520 for p in 0..t2 {
521 let mut acc = 0f32;
522 for a in 0..k {
523 let s = (p * self.ratio + a) as isize - pad_left as isize;
524 let s = s.clamp(0, x.t as isize - 1) as usize;
525 acc += x.data[c * x.t + s] * self.down[a];
526 }
527 out.data[c * t2 + p] = acc;
528 }
529 }
530 out
531 }
532}
533
534struct SnakeBeta {
536 alpha: Vec<f32>,
537 beta: Vec<f32>,
538 aa: Aliasing,
539}
540
541impl SnakeBeta {
542 fn load(model: &Arc<CmfModel>, p: &str) -> Result<SnakeBeta, String> {
543 Ok(SnakeBeta {
544 alpha: tensor_f32(model, &format!("{p}.act.alpha"))?.0,
545 beta: tensor_f32(model, &format!("{p}.act.beta"))?.0,
546 aa: Aliasing::load(model, p)?,
547 })
548 }
549
550 fn forward(&self, x: &Sig) -> Sig {
551 let mut up = self.aa.upsample(x);
552 for c in 0..up.c {
553 let a = self.alpha[c].exp();
554 let b = self.beta[c].exp();
555 for v in up.data[c * up.t..(c + 1) * up.t].iter_mut() {
556 let s = (*v * a).sin();
557 *v += s * s / (b + 1e-9);
558 }
559 }
560 self.aa.downsample(&up)
561 }
562}
563
564struct AmpBlock {
567 convs1: Vec<Conv1d>,
568 convs2: Vec<Conv1d>,
569 acts1: Vec<SnakeBeta>,
570 acts2: Vec<SnakeBeta>,
571}
572
573impl AmpBlock {
574 fn load(model: &Arc<CmfModel>, p: &str, dil: &[usize]) -> Result<AmpBlock, String> {
575 let mut convs1 = Vec::new();
576 let mut convs2 = Vec::new();
577 let mut acts1 = Vec::new();
578 let mut acts2 = Vec::new();
579 for (i, &d) in dil.iter().enumerate() {
580 convs1.push(Conv1d::load(model, &format!("{p}.convs1.{i}"), d)?);
581 convs2.push(Conv1d::load(model, &format!("{p}.convs2.{i}"), 1)?);
582 acts1.push(SnakeBeta::load(model, &format!("{p}.acts1.{i}"))?);
583 acts2.push(SnakeBeta::load(model, &format!("{p}.acts2.{i}"))?);
584 }
585 Ok(AmpBlock {
586 convs1,
587 convs2,
588 acts1,
589 acts2,
590 })
591 }
592
593 fn forward(&self, x: &Sig, pool: Option<&Pool>) -> Sig {
594 let mut x = x.clone();
595 for i in 0..self.convs1.len() {
596 let h = self.acts1[i].forward(&x);
597 let h = self.convs1[i].forward(&h, pool);
598 let h = self.acts2[i].forward(&h);
599 let h = self.convs2[i].forward(&h, pool);
600 for (v, &y) in x.data.iter_mut().zip(&h.data) {
601 *v += y;
602 }
603 }
604 x
605 }
606}
607
608pub struct Vocoder {
611 conv_pre: Conv1d,
612 ups: Vec<ConvT1d>,
613 blocks: Vec<AmpBlock>,
614 per_level: usize,
615 act_post: SnakeBeta,
616 conv_post: Conv1d,
617 tanh_final: bool,
618 apply_final: bool,
619}
620
621impl Vocoder {
622 fn from_cmf(
623 model: &Arc<CmfModel>,
624 p: &str,
625 rates: &[usize],
626 dils: &[Vec<usize>],
627 apply_final: bool,
628 ) -> Result<Vocoder, String> {
629 let mut ups = Vec::new();
630 for (i, &r) in rates.iter().enumerate() {
631 ups.push(ConvT1d::load(model, &format!("{p}.ups.{i}"), r)?);
632 }
633 let mut blocks = Vec::new();
634 let mut i = 0usize;
635 while model
636 .tensor(&format!("{p}.resblocks.{i}.convs1.0.weight"))
637 .is_some()
638 {
639 blocks.push(AmpBlock::load(
640 model,
641 &format!("{p}.resblocks.{i}"),
642 &dils[i % dils.len()],
643 )?);
644 i += 1;
645 }
646 let per_level = blocks.len() / rates.len().max(1);
647 Ok(Vocoder {
648 conv_pre: Conv1d::load(model, &format!("{p}.conv_pre"), 1)?,
649 ups,
650 blocks,
651 per_level,
652 act_post: SnakeBeta::load(model, &format!("{p}.act_post"))?,
653 conv_post: Conv1d::load(model, &format!("{p}.conv_post"), 1)?,
654 tanh_final: false,
655 apply_final,
656 })
657 }
658
659 fn forward(&self, mel: &Grid, pool: Option<&Pool>) -> Sig {
661 let mut x = Sig::zeros(mel.c * mel.w, mel.h);
663 for s in 0..mel.c {
664 for m in 0..mel.w {
665 let c = s * mel.w + m;
666 for t in 0..mel.h {
667 x.data[c * mel.h + t] = mel.data[(s * mel.h + t) * mel.w + m];
668 }
669 }
670 }
671 let dbg = std::env::var("CMF_LTX_VOC_DBG").is_ok();
672 let rms = |name: &str, s: &Sig| {
673 let n = s.data.len().max(1) as f64;
674 let r = (s.data.iter().map(|&x| (x as f64) * (x as f64)).sum::<f64>() / n).sqrt();
675 println!(" voc {name:<10} [{}, {}] rms {r:.6}", s.c, s.t);
676 };
677 let mut h = self.conv_pre.forward(&x, pool);
678 if dbg {
679 rms("in", &x);
680 rms("conv_pre", &h);
681 }
682 for (i, up) in self.ups.iter().enumerate() {
683 h = up.forward(&h);
684 let mut acc: Option<Sig> = None;
685 for j in 0..self.per_level {
686 let b = &self.blocks[i * self.per_level + j];
687 let o = b.forward(&h, pool);
688 match &mut acc {
689 None => acc = Some(o),
690 Some(a) => {
691 for (v, &y) in a.data.iter_mut().zip(&o.data) {
692 *v += y;
693 }
694 }
695 }
696 }
697 h = acc.unwrap();
698 let inv = 1.0 / self.per_level as f32;
699 h.data.iter_mut().for_each(|v| *v *= inv);
700 if dbg {
701 rms(&format!("level{i}"), &h);
702 }
703 }
704 let h = self.act_post.forward(&h);
705 let mut out = self.conv_post.forward(&h, pool);
706 if self.apply_final {
707 out.data.iter_mut().for_each(|v| {
708 *v = if self.tanh_final {
709 v.tanh()
710 } else {
711 v.clamp(-1.0, 1.0)
712 }
713 });
714 }
715 out
716 }
717}
718
719struct MelStft {
723 forward_basis: Vec<f32>,
724 mel_basis: Vec<f32>,
725 n_freqs: usize,
726 filter_len: usize,
727 hop: usize,
728 n_mels: usize,
729}
730
731impl MelStft {
732 fn load(model: &Arc<CmfModel>, p: &str, hop: usize) -> Result<MelStft, String> {
733 let (fb, fs) = tensor_f32(model, &format!("{p}.stft_fn.forward_basis"))?;
734 let (mb, ms) = tensor_f32(model, &format!("{p}.mel_basis"))?;
735 Ok(MelStft {
736 n_freqs: fs[0] / 2,
737 filter_len: fs[2],
738 forward_basis: fb,
739 mel_basis: mb,
740 n_mels: ms[0],
741 hop,
742 })
743 }
744
745 fn forward(&self, x: &Sig) -> Grid {
747 let left = self.filter_len.saturating_sub(self.hop);
748 let padded = x.t + left;
749 let frames = if padded >= self.filter_len {
750 (padded - self.filter_len) / self.hop + 1
751 } else {
752 0
753 };
754 let mut out = Grid::zeros(x.c, frames, self.n_mels);
755 let rows = 2 * self.n_freqs;
756 let mut mag = vec![0f32; self.n_freqs];
757 for c in 0..x.c {
758 for f in 0..frames {
759 let start = f * self.hop;
760 for r in 0..self.n_freqs {
761 let mut re = 0f32;
762 let mut im = 0f32;
763 for j in 0..self.filter_len {
764 let idx = start + j;
765 let v = if idx < left {
766 0.0
767 } else {
768 let s = idx - left;
769 if s < x.t { x.data[c * x.t + s] } else { 0.0 }
770 };
771 re += v * self.forward_basis[r * self.filter_len + j];
772 im += v * self.forward_basis[(self.n_freqs + r) * self.filter_len + j];
773 }
774 mag[r] = (re * re + im * im).sqrt();
775 }
776 for m in 0..self.n_mels {
777 let mut acc = 0f32;
778 for r in 0..self.n_freqs {
779 acc += self.mel_basis[m * self.n_freqs + r] * mag[r];
780 }
781 out.data[(c * frames + f) * self.n_mels + m] = acc.max(1e-5).ln();
782 }
783 }
784 }
785 let _ = rows;
786 out
787 }
788}
789
790fn hann_sinc_upsample(x: &Sig, ratio: usize) -> Sig {
793 let rolloff = 0.99f64;
794 let lpw = 6f64;
795 let width = (lpw / rolloff).ceil() as usize;
796 let k = 2 * width * ratio + 1;
797 let pad = width;
798 let pad_left = 2 * width * ratio;
799 let pad_right = k - ratio;
800 let filt: Vec<f32> = (0..k)
801 .map(|i| {
802 let ta = (i as f64 / ratio as f64 - width as f64) * rolloff;
803 let tc = ta.clamp(-lpw, lpw);
804 let win = (tc * std::f64::consts::PI / lpw / 2.0).cos().powi(2);
805 let s = if ta == 0.0 {
806 1.0
807 } else {
808 (std::f64::consts::PI * ta).sin() / (std::f64::consts::PI * ta)
809 };
810 (s * win * rolloff / ratio as f64) as f32
811 })
812 .collect();
813 let tp = x.t + 2 * pad;
814 let full = (tp - 1) * ratio + k;
815 let mut acc = Sig::zeros(x.c, full);
816 for c in 0..x.c {
817 for p in 0..tp {
818 let src = (p as isize - pad as isize).clamp(0, x.t as isize - 1) as usize;
819 let v = x.data[c * x.t + src] * ratio as f32;
820 if v == 0.0 {
821 continue;
822 }
823 for a in 0..k {
824 acc.data[c * full + p * ratio + a] += v * filt[a];
825 }
826 }
827 }
828 let (lo, hi) = (pad_left, full - pad_right);
829 let t2 = hi - lo;
830 let mut out = Sig::zeros(x.c, t2);
831 for c in 0..x.c {
832 out.data[c * t2..(c + 1) * t2].copy_from_slice(&acc.data[c * full + lo..c * full + hi]);
833 }
834 out
835}
836
837pub struct AudioStack {
839 pub decoder: AudioVaeDecoder,
840 vocoder: Vocoder,
841 bwe: Vocoder,
842 mel: MelStft,
843 hop: usize,
844 in_rate: usize,
845 pub out_rate: usize,
846}
847
848impl AudioStack {
849 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<AudioStack, String> {
850 let cfg: serde_json::Value = ["avae.config_json"]
851 .iter()
852 .filter_map(|n| model.tensor(n).map(|e| model.entry_bytes(e)))
853 .filter_map(|b| serde_json::from_slice(b).ok())
854 .next()
855 .unwrap_or(serde_json::Value::Null);
856 let voc = cfg.pointer("/vocoder/vocoder").cloned().unwrap_or_default();
857 let bwe = cfg.pointer("/vocoder/bwe").cloned().unwrap_or_default();
858 let rates = |v: &serde_json::Value, d: Vec<usize>| -> Vec<usize> {
859 v.get("upsample_rates")
860 .and_then(|a| a.as_array())
861 .map(|a| {
862 a.iter()
863 .filter_map(|x| x.as_u64())
864 .map(|x| x as usize)
865 .collect()
866 })
867 .unwrap_or(d)
868 };
869 let dils = vec![vec![1usize, 3, 5], vec![1, 3, 5], vec![1, 3, 5]];
870 let hop = bwe.get("hop_length").and_then(|v| v.as_u64()).unwrap_or(80) as usize;
871 Ok(AudioStack {
872 decoder: AudioVaeDecoder::from_cmf(model)?,
873 vocoder: Vocoder::from_cmf(
877 model,
878 "avae.vocoder.vocoder",
879 &rates(&voc, vec![5, 2, 2, 2, 2, 2]),
880 &dils,
881 voc.get("apply_final_activation")
882 .and_then(|v| v.as_bool())
883 .unwrap_or(true),
884 )?,
885 bwe: Vocoder::from_cmf(
886 model,
887 "avae.vocoder.bwe_generator",
888 &rates(&bwe, vec![6, 5, 2, 2, 2]),
889 &dils,
890 bwe.get("apply_final_activation")
891 .and_then(|v| v.as_bool())
892 .unwrap_or(true),
893 )?,
894 mel: MelStft::load(model, "avae.vocoder.mel_stft", hop)?,
895 hop,
896 in_rate: bwe
897 .get("input_sampling_rate")
898 .and_then(|v| v.as_u64())
899 .unwrap_or(16000) as usize,
900 out_rate: bwe
901 .get("output_sampling_rate")
902 .and_then(|v| v.as_u64())
903 .unwrap_or(48000) as usize,
904 })
905 }
906
907 pub fn decode(&self, latent: &Grid, pool: Option<&Pool>) -> Sig {
909 let mel = self.decoder.decode(latent, pool);
910 self.decode_from_mel(&mel, pool)
911 }
912
913 pub fn decode_from_mel(&self, mel: &Grid, pool: Option<&Pool>) -> Sig {
915 let low = self.vocoder.forward(mel, pool);
916 if let Ok(p) = std::env::var("CMF_LTX_LOW_WAV") {
918 let _ = write_wav(std::path::Path::new(&p), &low, self.in_rate);
919 }
920 let out_len = low.t * self.out_rate / self.in_rate;
921 let rem = low.t % self.hop;
923 let padded = if rem == 0 {
924 low.clone()
925 } else {
926 let t2 = low.t + self.hop - rem;
927 let mut p = Sig::zeros(low.c, t2);
928 for c in 0..low.c {
929 p.data[c * t2..c * t2 + low.t]
930 .copy_from_slice(&low.data[c * low.t..(c + 1) * low.t]);
931 }
932 p
933 };
934 let m = self.mel.forward(&padded);
935 let residual = self.bwe.forward(&m, pool);
936 let skip = hann_sinc_upsample(&padded, self.out_rate / self.in_rate);
937 let t = residual.t.min(skip.t).min(out_len);
938 let mut out = Sig::zeros(skip.c, t);
939 for c in 0..skip.c {
940 for i in 0..t {
941 out.data[c * t + i] = (residual.data[c * residual.t + i]
942 + skip.data[c * skip.t + i])
943 .clamp(-1.0, 1.0);
944 }
945 }
946 out
947 }
948}
949
950pub fn write_wav(path: &std::path::Path, sig: &Sig, rate: usize) -> std::io::Result<()> {
952 use std::io::Write;
953 let n = sig.t;
954 let ch = sig.c as u16;
955 let bytes = (n * sig.c * 2) as u32;
956 let mut f = std::io::BufWriter::new(std::fs::File::create(path)?);
957 f.write_all(b"RIFF")?;
958 f.write_all(&(36 + bytes).to_le_bytes())?;
959 f.write_all(b"WAVEfmt ")?;
960 f.write_all(&16u32.to_le_bytes())?;
961 f.write_all(&1u16.to_le_bytes())?;
962 f.write_all(&ch.to_le_bytes())?;
963 f.write_all(&(rate as u32).to_le_bytes())?;
964 f.write_all(&((rate * sig.c * 2) as u32).to_le_bytes())?;
965 f.write_all(&((sig.c * 2) as u16).to_le_bytes())?;
966 f.write_all(&16u16.to_le_bytes())?;
967 f.write_all(b"data")?;
968 f.write_all(&bytes.to_le_bytes())?;
969 for i in 0..n {
970 for c in 0..sig.c {
971 let v = (sig.data[c * n + i].clamp(-1.0, 1.0) * 32767.0) as i16;
972 f.write_all(&v.to_le_bytes())?;
973 }
974 }
975 Ok(())
976}
977
978fn mel_filterbank(sr: f64, n_fft: usize, n_mels: usize, fmin: f64, fmax: f64) -> Vec<f32> {
984 let n_freqs = n_fft / 2 + 1;
985 let hz_to_mel = |f: f64| 3.0 * f / 200.0;
986 let mel_to_hz = |m: f64| m * 200.0 / 3.0;
987 let (f_min_log, min_log_mel) = (1000.0f64, 15.0f64);
989 let logstep = (6.4f64).ln() / 27.0;
990 let hz_to_mel_s = |f: f64| {
991 if f >= f_min_log {
992 min_log_mel + (f / f_min_log).ln() / logstep
993 } else {
994 hz_to_mel(f)
995 }
996 };
997 let mel_to_hz_s = |m: f64| {
998 if m >= min_log_mel {
999 f_min_log * ((m - min_log_mel) * logstep).exp()
1000 } else {
1001 mel_to_hz(m)
1002 }
1003 };
1004 let (m0, m1) = (hz_to_mel_s(fmin), hz_to_mel_s(fmax));
1005 let pts: Vec<f64> = (0..n_mels + 2)
1006 .map(|i| mel_to_hz_s(m0 + (m1 - m0) * i as f64 / (n_mels + 1) as f64))
1007 .collect();
1008 let freqs: Vec<f64> = (0..n_freqs).map(|i| sr * i as f64 / n_fft as f64).collect();
1009 let mut fb = vec![0f32; n_mels * n_freqs];
1010 for m in 0..n_mels {
1011 let (lo, ctr, hi) = (pts[m], pts[m + 1], pts[m + 2]);
1012 let enorm = 2.0 / (hi - lo);
1014 for (k, &f) in freqs.iter().enumerate() {
1015 let v = if f >= lo && f <= ctr {
1016 (f - lo) / (ctr - lo).max(1e-12)
1017 } else if f > ctr && f <= hi {
1018 (hi - f) / (hi - ctr).max(1e-12)
1019 } else {
1020 0.0
1021 };
1022 fb[m * n_freqs + k] = (v * enorm) as f32;
1023 }
1024 }
1025 fb
1026}
1027
1028pub fn waveform_to_mel(x: &Sig, sr: usize, n_fft: usize, hop: usize, n_mels: usize) -> Grid {
1032 let n_freqs = n_fft / 2 + 1;
1033 let fb = mel_filterbank(sr as f64, n_fft, n_mels, 0.0, sr as f64 / 2.0);
1034 let win: Vec<f32> = (0..n_fft)
1035 .map(|i| {
1036 let a = std::f64::consts::PI * 2.0 * i as f64 / n_fft as f64;
1037 (0.5 - 0.5 * a.cos()) as f32
1038 })
1039 .collect();
1040 let pad = n_fft / 2;
1041 let frames = x.t / hop + 1;
1042 let mut out = Grid::zeros(x.c, frames, n_mels);
1043 let mut re = vec![0f32; n_freqs];
1044 let mut im = vec![0f32; n_freqs];
1045 for c in 0..x.c {
1046 for f in 0..frames {
1047 let start = f as isize * hop as isize - pad as isize;
1048 re.iter_mut().for_each(|v| *v = 0.0);
1049 im.iter_mut().for_each(|v| *v = 0.0);
1050 for j in 0..n_fft {
1051 let mut s = start + j as isize;
1053 if s < 0 {
1054 s = -s;
1055 }
1056 if s >= x.t as isize {
1057 s = 2 * (x.t as isize - 1) - s;
1058 }
1059 let v = if s >= 0 && s < x.t as isize {
1060 x.data[c * x.t + s as usize]
1061 } else {
1062 0.0
1063 };
1064 let v = v * win[j];
1065 if v == 0.0 {
1066 continue;
1067 }
1068 for (k, (rr, ii)) in re.iter_mut().zip(im.iter_mut()).enumerate() {
1069 let a = -2.0 * std::f64::consts::PI * (k * j) as f64 / n_fft as f64;
1070 *rr += v * a.cos() as f32;
1071 *ii += v * a.sin() as f32;
1072 }
1073 }
1074 for m in 0..n_mels {
1075 let mut acc = 0f32;
1076 for k in 0..n_freqs {
1077 acc += fb[m * n_freqs + k] * (re[k] * re[k] + im[k] * im[k]).sqrt();
1078 }
1079 out.data[(c * frames + f) * n_mels + m] = acc.max(1e-5).ln();
1080 }
1081 }
1082 }
1083 out
1084}
1085
1086struct Downsample2 {
1087 conv: Conv2d,
1088}
1089
1090impl Downsample2 {
1091 fn forward(&self, x: &Grid, pool: Option<&Pool>) -> Grid {
1094 let (h, w) = (x.h + 2, x.w + 1);
1095 let mut p = Grid::zeros(x.c, h, w);
1096 for c in 0..x.c {
1097 for y in 0..x.h {
1098 for z in 0..x.w {
1099 p.data[(c * h + y + 2) * w + z] = x.data[(c * x.h + y) * x.w + z];
1100 }
1101 }
1102 }
1103 self.conv.forward_strided(&p, 2, pool)
1104 }
1105}
1106
1107impl Conv2d {
1108 fn forward_strided(&self, x: &Grid, stride: usize, pool: Option<&Pool>) -> Grid {
1111 let (oh, ow) = ((x.h - self.kh) / stride + 1, (x.w - self.kw) / stride + 1);
1112 let npos = oh * ow;
1113 let k = self.c_in * self.kh * self.kw;
1114 let mut patches = vec![0f32; npos * k];
1115 for i in 0..npos {
1116 let (pw, ph) = (i % ow, i / ow);
1117 for ci in 0..self.c_in {
1118 for a in 0..self.kh {
1119 for b in 0..self.kw {
1120 patches[i * k + (ci * self.kh + a) * self.kw + b] =
1121 x.data[(ci * x.h + ph * stride + a) * x.w + pw * stride + b];
1122 }
1123 }
1124 }
1125 }
1126 let mut ys = vec![0f32; npos * self.c_out];
1127 crate::fcd_ops::gemm_nt(&patches, &self.w, &mut ys, npos, k, self.c_out, pool);
1128 let mut out = Grid::zeros(self.c_out, oh, ow);
1129 for i in 0..npos {
1130 for co in 0..self.c_out {
1131 out.data[co * npos + i] = ys[i * self.c_out + co] + self.b[co];
1132 }
1133 }
1134 out
1135 }
1136}
1137
1138pub struct AudioVaeEncoder {
1140 conv_in: Conv2d,
1141 levels: Vec<(Vec<ResnetBlock>, Option<Downsample2>)>,
1142 mid: Vec<ResnetBlock>,
1143 conv_out: Conv2d,
1144 mean: Vec<f32>,
1145 std: Vec<f32>,
1146 z: usize,
1147}
1148
1149impl AudioVaeEncoder {
1150 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<AudioVaeEncoder, String> {
1151 let mut levels = Vec::new();
1152 let mut lv = 0usize;
1153 while model
1154 .tensor(&format!("avae.encoder.down.{lv}.block.0.conv1.conv.weight"))
1155 .is_some()
1156 {
1157 let mut blocks = Vec::new();
1158 let mut bi = 0usize;
1159 while model
1160 .tensor(&format!(
1161 "avae.encoder.down.{lv}.block.{bi}.conv1.conv.weight"
1162 ))
1163 .is_some()
1164 {
1165 blocks.push(ResnetBlock::load(
1166 model,
1167 &format!("avae.encoder.down.{lv}.block.{bi}"),
1168 )?);
1169 bi += 1;
1170 }
1171 let down = match model.tensor(&format!("avae.encoder.down.{lv}.downsample.conv.weight"))
1172 {
1173 Some(_) => Some(Downsample2 {
1176 conv: Conv2d::load(model, &format!("avae.encoder.down.{lv}.downsample.conv"))?,
1177 }),
1178 None => None,
1179 };
1180 levels.push((blocks, down));
1181 lv += 1;
1182 }
1183 let out = Conv2d::load(model, "avae.encoder.conv_out.conv")?;
1184 let z = out.c_out / 2;
1185 Ok(AudioVaeEncoder {
1186 conv_in: Conv2d::load(model, "avae.encoder.conv_in.conv")?,
1187 levels,
1188 mid: vec![
1189 ResnetBlock::load(model, "avae.encoder.mid.block_1")?,
1190 ResnetBlock::load(model, "avae.encoder.mid.block_2")?,
1191 ],
1192 conv_out: out,
1193 mean: tensor_f32(model, "avae.per_channel_statistics.mean-of-means")?.0,
1194 std: tensor_f32(model, "avae.per_channel_statistics.std-of-means")?.0,
1195 z,
1196 })
1197 }
1198
1199 pub fn encode(&self, mel: &Grid, pool: Option<&Pool>) -> Grid {
1201 let mut h = self.conv_in.forward(mel, pool);
1202 for (blocks, down) in &self.levels {
1203 for b in blocks {
1204 h = b.forward(&h, pool);
1205 }
1206 if let Some(d) = down {
1207 h = d.forward(&h, pool);
1208 }
1209 }
1210 for b in &self.mid {
1211 h = b.forward(&h, pool);
1212 }
1213 pixel_norm(&mut h);
1214 h.data.iter_mut().for_each(|v| *v = silu(*v));
1215 let out = self.conv_out.forward(&h, pool);
1216 let npos = out.n();
1219 let mut lat = Grid::zeros(self.z, out.h, out.w);
1220 for c in 0..self.z {
1221 for hi in 0..out.h {
1222 for wi in 0..out.w {
1223 let idx = c * out.w + wi;
1224 let v = out.data[(c * out.h + hi) * out.w + wi];
1225 lat.data[(c * out.h + hi) * out.w + wi] = (v - self.mean[idx]) / self.std[idx];
1226 }
1227 }
1228 }
1229 lat
1230 }
1231}
1232
1233pub fn read_wav(path: &std::path::Path) -> Result<(Sig, usize), String> {
1235 let raw = std::fs::read(path).map_err(|e| format!("{}: {e}", path.display()))?;
1236 if raw.len() < 44 || &raw[..4] != b"RIFF" || &raw[8..12] != b"WAVE" {
1237 return Err(format!("{}: not a RIFF/WAVE file", path.display()));
1238 }
1239 let mut i = 12usize;
1240 let (mut ch, mut rate, mut bits) = (2usize, 48000usize, 16usize);
1241 let mut data: Option<(usize, usize)> = None;
1242 while i + 8 <= raw.len() {
1243 let id = &raw[i..i + 4];
1244 let len = u32::from_le_bytes(raw[i + 4..i + 8].try_into().unwrap()) as usize;
1245 let body = i + 8;
1246 if id == b"fmt " && body + 16 <= raw.len() {
1247 ch = u16::from_le_bytes(raw[body + 2..body + 4].try_into().unwrap()) as usize;
1248 rate = u32::from_le_bytes(raw[body + 4..body + 8].try_into().unwrap()) as usize;
1249 bits = u16::from_le_bytes(raw[body + 14..body + 16].try_into().unwrap()) as usize;
1250 } else if id == b"data" {
1251 data = Some((body, len.min(raw.len() - body)));
1252 break;
1253 }
1254 i = body + len + (len & 1);
1255 }
1256 let (off, len) = data.ok_or_else(|| format!("{}: no data chunk", path.display()))?;
1257 if bits != 16 {
1258 return Err(format!("{}: only 16-bit PCM is read", path.display()));
1259 }
1260 let n = len / 2 / ch.max(1);
1261 let mut sig = Sig::zeros(ch, n);
1262 for i in 0..n {
1263 for c in 0..ch {
1264 let o = off + (i * ch + c) * 2;
1265 let v = i16::from_le_bytes([raw[o], raw[o + 1]]) as f32 / 32768.0;
1266 sig.data[c * n + i] = v;
1267 }
1268 }
1269 Ok((sig, rate))
1270}
1271
1272pub fn resample(x: &Sig, from: usize, to: usize) -> Sig {
1275 if from == to {
1276 return x.clone();
1277 }
1278 let n = (x.t as f64 * to as f64 / from as f64).round() as usize;
1279 let mut out = Sig::zeros(x.c, n);
1280 for c in 0..x.c {
1281 for i in 0..n {
1282 let p = i as f64 * from as f64 / to as f64;
1283 let j = p.floor() as usize;
1284 let f = (p - j as f64) as f32;
1285 let a = x.data[c * x.t + j.min(x.t - 1)];
1286 let b = x.data[c * x.t + (j + 1).min(x.t - 1)];
1287 out.data[c * n + i] = a + (b - a) * f;
1288 }
1289 }
1290 out
1291}