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
348fn par_channels(data: &mut [f32], t: usize, f: &(dyn Fn(usize, &mut [f32]) + Sync)) {
352 if t == 0 || data.is_empty() {
353 return;
354 }
355 let c = data.len() / t;
356 let threads = std::thread::available_parallelism().map_or(1, |n| n.get()).min(c).min(32);
357 if threads <= 1 || data.len() < (1 << 16) {
358 for (i, row) in data.chunks_mut(t).enumerate() {
359 f(i, row);
360 }
361 return;
362 }
363 let per = c.div_ceil(threads);
364 std::thread::scope(|sc| {
365 for (g, chunk) in data.chunks_mut(per * t).enumerate() {
366 sc.spawn(move || {
367 for (i, row) in chunk.chunks_mut(t).enumerate() {
368 f(g * per + i, row);
369 }
370 });
371 }
372 });
373}
374
375struct Conv1d {
376 w: Vec<f32>,
377 b: Option<Vec<f32>>,
378 c_out: usize,
379 c_in: usize,
380 k: usize,
381 dilation: usize,
382 pad: usize,
383}
384
385impl Conv1d {
386 fn load(model: &Arc<CmfModel>, name: &str, dilation: usize) -> Result<Conv1d, String> {
387 let (w, s) = tensor_f32(model, &format!("{name}.weight"))?;
388 let b = tensor_f32(model, &format!("{name}.bias")).ok().map(|x| x.0);
389 let k = s[2];
390 Ok(Conv1d {
391 w,
392 b,
393 c_out: s[0],
394 c_in: s[1],
395 k,
396 dilation,
397 pad: (k - 1) * dilation / 2,
398 })
399 }
400
401 fn forward(&self, x: &Sig, pool: Option<&Pool>) -> Sig {
402 let t = x.t;
403 let kk = self.c_in * self.k;
404 let mut ys = vec![0f32; t * self.c_out];
405 let on_device = std::env::var("CMF_LTX_CONV1D_GPU").as_deref() != Ok("0")
408 && crate::gpu::enabled_here()
409 && crate::gpu::conv1d_gemm(&x.data, &self.w, self.c_in, self.c_out, t, self.k, self.pad, self.dilation, t, &mut ys);
410 if !on_device {
411 let mut patches = vec![0f32; t * kk];
412 for p in 0..t {
413 for ci in 0..self.c_in {
414 for a in 0..self.k {
415 let s = p as isize + (a * self.dilation) as isize - self.pad as isize;
416 if s >= 0 && s < t as isize {
417 patches[p * kk + ci * self.k + a] = x.data[ci * t + s as usize];
418 }
419 }
420 }
421 }
422 crate::fcd_ops::gemm_nt(&patches, &self.w, &mut ys, t, kk, self.c_out, pool);
423 }
424 let mut out = Sig::zeros(self.c_out, t);
425 for p in 0..t {
426 for co in 0..self.c_out {
427 out.data[co * t + p] =
428 ys[p * self.c_out + co] + self.b.as_ref().map_or(0.0, |b| b[co]);
429 }
430 }
431 out
432 }
433}
434
435struct ConvT1d {
437 w: Vec<f32>,
438 b: Option<Vec<f32>>,
439 c_in: usize,
440 c_out: usize,
441 k: usize,
442 stride: usize,
443 pad: usize,
444}
445
446impl ConvT1d {
447 fn load(model: &Arc<CmfModel>, name: &str, stride: usize) -> Result<ConvT1d, String> {
448 let (w, s) = tensor_f32(model, &format!("{name}.weight"))?;
449 let b = tensor_f32(model, &format!("{name}.bias")).ok().map(|x| x.0);
450 let k = s[2];
451 Ok(ConvT1d {
452 w,
453 b,
454 c_in: s[0],
455 c_out: s[1],
456 k,
457 stride,
458 pad: (k - stride) / 2,
459 })
460 }
461
462 fn forward(&self, x: &Sig) -> Sig {
463 let t_out = (x.t - 1) * self.stride + self.k - 2 * self.pad;
464 let mut out = Sig::zeros(self.c_out, t_out);
465 par_channels(&mut out.data, t_out, &|co, row| {
468 for ci in 0..self.c_in {
469 let w = &self.w[(ci * self.c_out + co) * self.k..][..self.k];
470 for (p, &v) in x.data[ci * x.t..(ci + 1) * x.t].iter().enumerate() {
471 if v == 0.0 {
472 continue;
473 }
474 let base = p * self.stride;
475 for (a, &wa) in w.iter().enumerate() {
476 let o = base + a;
477 if o < self.pad || o - self.pad >= t_out {
478 continue;
479 }
480 row[o - self.pad] += v * wa;
481 }
482 }
483 }
484 if let Some(b) = &self.b {
485 for v in row.iter_mut() {
486 *v += b[co];
487 }
488 }
489 });
490 out
491 }
492}
493
494struct Aliasing {
497 up: Vec<f32>,
498 down: Vec<f32>,
499 ratio: usize,
500}
501
502impl Aliasing {
503 fn load(model: &Arc<CmfModel>, p: &str) -> Result<Aliasing, String> {
504 Ok(Aliasing {
505 up: tensor_f32(model, &format!("{p}.upsample.filter"))?.0,
506 down: tensor_f32(model, &format!("{p}.downsample.lowpass.filter"))?.0,
507 ratio: 2,
508 })
509 }
510
511 fn upsample(&self, x: &Sig) -> Sig {
512 let k = self.up.len();
513 let stride = self.ratio;
514 let pad = k / stride - 1;
515 let pad_left = pad * stride + (k - stride) / 2;
516 let pad_right = pad * stride + (k - stride).div_ceil(2);
517 let tp = x.t + 2 * pad;
520 let full = (tp - 1) * stride + k;
521 let (lo, hi) = (pad_left, full - pad_right);
522 let t2 = hi - lo;
523 let mut trimmed = Sig::zeros(x.c, t2);
524 par_channels(&mut trimmed.data, t2, &|c, row| {
525 let mut out = vec![0f32; full];
526 for p in 0..tp {
527 let src = (p as isize - pad as isize).clamp(0, x.t as isize - 1) as usize;
528 let v = x.data[c * x.t + src] * self.ratio as f32;
529 if v == 0.0 {
530 continue;
531 }
532 for a in 0..k {
533 out[p * stride + a] += v * self.up[a];
534 }
535 }
536 row.copy_from_slice(&out[lo..hi]);
537 });
538 trimmed
539 }
540
541 fn downsample(&self, x: &Sig) -> Sig {
542 let k = self.down.len();
543 let pad_left = k / 2 - if k % 2 == 0 { 1 } else { 0 };
544 let pad_right = k / 2;
545 let tp = x.t + pad_left + pad_right;
546 let t2 = (tp - k) / self.ratio + 1;
547 let mut out = Sig::zeros(x.c, t2);
548 par_channels(&mut out.data, t2, &|c, row| {
549 for (p, o) in row.iter_mut().enumerate() {
550 let mut acc = 0f32;
551 for a in 0..k {
552 let s = (p * self.ratio + a) as isize - pad_left as isize;
553 let s = s.clamp(0, x.t as isize - 1) as usize;
554 acc += x.data[c * x.t + s] * self.down[a];
555 }
556 *o = acc;
557 }
558 });
559 out
560 }
561}
562
563struct SnakeBeta {
565 alpha: Vec<f32>,
566 beta: Vec<f32>,
567 aa: Aliasing,
568}
569
570impl SnakeBeta {
571 fn load(model: &Arc<CmfModel>, p: &str) -> Result<SnakeBeta, String> {
572 Ok(SnakeBeta {
573 alpha: tensor_f32(model, &format!("{p}.act.alpha"))?.0,
574 beta: tensor_f32(model, &format!("{p}.act.beta"))?.0,
575 aa: Aliasing::load(model, p)?,
576 })
577 }
578
579 fn forward(&self, x: &Sig) -> Sig {
580 let mut up = self.aa.upsample(x);
581 let t = up.t;
582 par_channels(&mut up.data, t, &|c, row| {
583 let a = self.alpha[c].exp();
584 let b = self.beta[c].exp();
585 for v in row.iter_mut() {
586 let s = (*v * a).sin();
587 *v += s * s / (b + 1e-9);
588 }
589 });
590 self.aa.downsample(&up)
591 }
592}
593
594struct AmpBlock {
597 convs1: Vec<Conv1d>,
598 convs2: Vec<Conv1d>,
599 acts1: Vec<SnakeBeta>,
600 acts2: Vec<SnakeBeta>,
601}
602
603impl AmpBlock {
604 fn load(model: &Arc<CmfModel>, p: &str, dil: &[usize]) -> Result<AmpBlock, String> {
605 let mut convs1 = Vec::new();
606 let mut convs2 = Vec::new();
607 let mut acts1 = Vec::new();
608 let mut acts2 = Vec::new();
609 for (i, &d) in dil.iter().enumerate() {
610 convs1.push(Conv1d::load(model, &format!("{p}.convs1.{i}"), d)?);
611 convs2.push(Conv1d::load(model, &format!("{p}.convs2.{i}"), 1)?);
612 acts1.push(SnakeBeta::load(model, &format!("{p}.acts1.{i}"))?);
613 acts2.push(SnakeBeta::load(model, &format!("{p}.acts2.{i}"))?);
614 }
615 Ok(AmpBlock {
616 convs1,
617 convs2,
618 acts1,
619 acts2,
620 })
621 }
622
623 fn forward(&self, x: &Sig, pool: Option<&Pool>) -> Sig {
624 let mut x = x.clone();
625 for i in 0..self.convs1.len() {
626 let h = self.acts1[i].forward(&x);
627 let h = self.convs1[i].forward(&h, pool);
628 let h = self.acts2[i].forward(&h);
629 let h = self.convs2[i].forward(&h, pool);
630 for (v, &y) in x.data.iter_mut().zip(&h.data) {
631 *v += y;
632 }
633 }
634 x
635 }
636}
637
638pub struct Vocoder {
641 conv_pre: Conv1d,
642 ups: Vec<ConvT1d>,
643 blocks: Vec<AmpBlock>,
644 per_level: usize,
645 act_post: SnakeBeta,
646 conv_post: Conv1d,
647 tanh_final: bool,
648 apply_final: bool,
649}
650
651impl Vocoder {
652 fn from_cmf(
653 model: &Arc<CmfModel>,
654 p: &str,
655 rates: &[usize],
656 dils: &[Vec<usize>],
657 apply_final: bool,
658 ) -> Result<Vocoder, String> {
659 let mut ups = Vec::new();
660 for (i, &r) in rates.iter().enumerate() {
661 ups.push(ConvT1d::load(model, &format!("{p}.ups.{i}"), r)?);
662 }
663 let mut blocks = Vec::new();
664 let mut i = 0usize;
665 while model
666 .tensor(&format!("{p}.resblocks.{i}.convs1.0.weight"))
667 .is_some()
668 {
669 blocks.push(AmpBlock::load(
670 model,
671 &format!("{p}.resblocks.{i}"),
672 &dils[i % dils.len()],
673 )?);
674 i += 1;
675 }
676 let per_level = blocks.len() / rates.len().max(1);
677 Ok(Vocoder {
678 conv_pre: Conv1d::load(model, &format!("{p}.conv_pre"), 1)?,
679 ups,
680 blocks,
681 per_level,
682 act_post: SnakeBeta::load(model, &format!("{p}.act_post"))?,
683 conv_post: Conv1d::load(model, &format!("{p}.conv_post"), 1)?,
684 tanh_final: false,
685 apply_final,
686 })
687 }
688
689 fn forward(&self, mel: &Grid, pool: Option<&Pool>) -> Sig {
691 let mut x = Sig::zeros(mel.c * mel.w, mel.h);
693 for s in 0..mel.c {
694 for m in 0..mel.w {
695 let c = s * mel.w + m;
696 for t in 0..mel.h {
697 x.data[c * mel.h + t] = mel.data[(s * mel.h + t) * mel.w + m];
698 }
699 }
700 }
701 let dbg = std::env::var("CMF_LTX_VOC_DBG").is_ok();
702 let rms = |name: &str, s: &Sig| {
703 let n = s.data.len().max(1) as f64;
704 let r = (s.data.iter().map(|&x| (x as f64) * (x as f64)).sum::<f64>() / n).sqrt();
705 println!(" voc {name:<10} [{}, {}] rms {r:.6}", s.c, s.t);
706 };
707 let mut h = self.conv_pre.forward(&x, pool);
708 if dbg {
709 rms("in", &x);
710 rms("conv_pre", &h);
711 }
712 for (i, up) in self.ups.iter().enumerate() {
713 h = up.forward(&h);
714 let mut acc: Option<Sig> = None;
715 for j in 0..self.per_level {
716 let b = &self.blocks[i * self.per_level + j];
717 let o = b.forward(&h, pool);
718 match &mut acc {
719 None => acc = Some(o),
720 Some(a) => {
721 for (v, &y) in a.data.iter_mut().zip(&o.data) {
722 *v += y;
723 }
724 }
725 }
726 }
727 h = acc.unwrap();
728 let inv = 1.0 / self.per_level as f32;
729 h.data.iter_mut().for_each(|v| *v *= inv);
730 if dbg {
731 rms(&format!("level{i}"), &h);
732 }
733 }
734 let h = self.act_post.forward(&h);
735 let mut out = self.conv_post.forward(&h, pool);
736 if self.apply_final {
737 out.data.iter_mut().for_each(|v| {
738 *v = if self.tanh_final {
739 v.tanh()
740 } else {
741 v.clamp(-1.0, 1.0)
742 }
743 });
744 }
745 out
746 }
747}
748
749struct MelStft {
753 forward_basis: Vec<f32>,
754 mel_basis: Vec<f32>,
755 n_freqs: usize,
756 filter_len: usize,
757 hop: usize,
758 n_mels: usize,
759}
760
761impl MelStft {
762 fn load(model: &Arc<CmfModel>, p: &str, hop: usize) -> Result<MelStft, String> {
763 let (fb, fs) = tensor_f32(model, &format!("{p}.stft_fn.forward_basis"))?;
764 let (mb, ms) = tensor_f32(model, &format!("{p}.mel_basis"))?;
765 Ok(MelStft {
766 n_freqs: fs[0] / 2,
767 filter_len: fs[2],
768 forward_basis: fb,
769 mel_basis: mb,
770 n_mels: ms[0],
771 hop,
772 })
773 }
774
775 fn forward(&self, x: &Sig) -> Grid {
777 let left = self.filter_len.saturating_sub(self.hop);
778 let padded = x.t + left;
779 let frames = if padded >= self.filter_len {
780 (padded - self.filter_len) / self.hop + 1
781 } else {
782 0
783 };
784 let mut out = Grid::zeros(x.c, frames, self.n_mels);
785 let rows = 2 * self.n_freqs;
786 let mut mag = vec![0f32; self.n_freqs];
787 for c in 0..x.c {
788 for f in 0..frames {
789 let start = f * self.hop;
790 for r in 0..self.n_freqs {
791 let mut re = 0f32;
792 let mut im = 0f32;
793 for j in 0..self.filter_len {
794 let idx = start + j;
795 let v = if idx < left {
796 0.0
797 } else {
798 let s = idx - left;
799 if s < x.t { x.data[c * x.t + s] } else { 0.0 }
800 };
801 re += v * self.forward_basis[r * self.filter_len + j];
802 im += v * self.forward_basis[(self.n_freqs + r) * self.filter_len + j];
803 }
804 mag[r] = (re * re + im * im).sqrt();
805 }
806 for m in 0..self.n_mels {
807 let mut acc = 0f32;
808 for r in 0..self.n_freqs {
809 acc += self.mel_basis[m * self.n_freqs + r] * mag[r];
810 }
811 out.data[(c * frames + f) * self.n_mels + m] = acc.max(1e-5).ln();
812 }
813 }
814 }
815 let _ = rows;
816 out
817 }
818}
819
820fn hann_sinc_upsample(x: &Sig, ratio: usize) -> Sig {
823 let rolloff = 0.99f64;
824 let lpw = 6f64;
825 let width = (lpw / rolloff).ceil() as usize;
826 let k = 2 * width * ratio + 1;
827 let pad = width;
828 let pad_left = 2 * width * ratio;
829 let pad_right = k - ratio;
830 let filt: Vec<f32> = (0..k)
831 .map(|i| {
832 let ta = (i as f64 / ratio as f64 - width as f64) * rolloff;
833 let tc = ta.clamp(-lpw, lpw);
834 let win = (tc * std::f64::consts::PI / lpw / 2.0).cos().powi(2);
835 let s = if ta == 0.0 {
836 1.0
837 } else {
838 (std::f64::consts::PI * ta).sin() / (std::f64::consts::PI * ta)
839 };
840 (s * win * rolloff / ratio as f64) as f32
841 })
842 .collect();
843 let tp = x.t + 2 * pad;
844 let full = (tp - 1) * ratio + k;
845 let mut acc = Sig::zeros(x.c, full);
846 for c in 0..x.c {
847 for p in 0..tp {
848 let src = (p as isize - pad as isize).clamp(0, x.t as isize - 1) as usize;
849 let v = x.data[c * x.t + src] * ratio as f32;
850 if v == 0.0 {
851 continue;
852 }
853 for a in 0..k {
854 acc.data[c * full + p * ratio + a] += v * filt[a];
855 }
856 }
857 }
858 let (lo, hi) = (pad_left, full - pad_right);
859 let t2 = hi - lo;
860 let mut out = Sig::zeros(x.c, t2);
861 for c in 0..x.c {
862 out.data[c * t2..(c + 1) * t2].copy_from_slice(&acc.data[c * full + lo..c * full + hi]);
863 }
864 out
865}
866
867pub struct AudioStack {
869 pub decoder: AudioVaeDecoder,
870 vocoder: Vocoder,
871 bwe: Vocoder,
872 mel: MelStft,
873 hop: usize,
874 in_rate: usize,
875 pub out_rate: usize,
876}
877
878impl AudioStack {
879 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<AudioStack, String> {
880 let cfg: serde_json::Value = ["avae.config_json"]
881 .iter()
882 .filter_map(|n| model.tensor(n).map(|e| model.entry_bytes(e)))
883 .filter_map(|b| serde_json::from_slice(b).ok())
884 .next()
885 .unwrap_or(serde_json::Value::Null);
886 let voc = cfg.pointer("/vocoder/vocoder").cloned().unwrap_or_default();
887 let bwe = cfg.pointer("/vocoder/bwe").cloned().unwrap_or_default();
888 let rates = |v: &serde_json::Value, d: Vec<usize>| -> Vec<usize> {
889 v.get("upsample_rates")
890 .and_then(|a| a.as_array())
891 .map(|a| {
892 a.iter()
893 .filter_map(|x| x.as_u64())
894 .map(|x| x as usize)
895 .collect()
896 })
897 .unwrap_or(d)
898 };
899 let dils = vec![vec![1usize, 3, 5], vec![1, 3, 5], vec![1, 3, 5]];
900 let hop = bwe.get("hop_length").and_then(|v| v.as_u64()).unwrap_or(80) as usize;
901 Ok(AudioStack {
902 decoder: AudioVaeDecoder::from_cmf(model)?,
903 vocoder: Vocoder::from_cmf(
907 model,
908 "avae.vocoder.vocoder",
909 &rates(&voc, vec![5, 2, 2, 2, 2, 2]),
910 &dils,
911 voc.get("apply_final_activation")
912 .and_then(|v| v.as_bool())
913 .unwrap_or(true),
914 )?,
915 bwe: Vocoder::from_cmf(
916 model,
917 "avae.vocoder.bwe_generator",
918 &rates(&bwe, vec![6, 5, 2, 2, 2]),
919 &dils,
920 bwe.get("apply_final_activation")
921 .and_then(|v| v.as_bool())
922 .unwrap_or(true),
923 )?,
924 mel: MelStft::load(model, "avae.vocoder.mel_stft", hop)?,
925 hop,
926 in_rate: bwe
927 .get("input_sampling_rate")
928 .and_then(|v| v.as_u64())
929 .unwrap_or(16000) as usize,
930 out_rate: bwe
931 .get("output_sampling_rate")
932 .and_then(|v| v.as_u64())
933 .unwrap_or(48000) as usize,
934 })
935 }
936
937 pub fn decode(&self, latent: &Grid, pool: Option<&Pool>) -> Sig {
939 let mel = self.decoder.decode(latent, pool);
940 self.decode_from_mel(&mel, pool)
941 }
942
943 pub fn decode_from_mel(&self, mel: &Grid, pool: Option<&Pool>) -> Sig {
945 let low = self.vocoder.forward(mel, pool);
946 if let Ok(p) = std::env::var("CMF_LTX_LOW_WAV") {
948 let _ = write_wav(std::path::Path::new(&p), &low, self.in_rate);
949 }
950 let out_len = low.t * self.out_rate / self.in_rate;
951 let rem = low.t % self.hop;
953 let padded = if rem == 0 {
954 low.clone()
955 } else {
956 let t2 = low.t + self.hop - rem;
957 let mut p = Sig::zeros(low.c, t2);
958 for c in 0..low.c {
959 p.data[c * t2..c * t2 + low.t]
960 .copy_from_slice(&low.data[c * low.t..(c + 1) * low.t]);
961 }
962 p
963 };
964 let m = self.mel.forward(&padded);
965 let residual = self.bwe.forward(&m, pool);
966 let skip = hann_sinc_upsample(&padded, self.out_rate / self.in_rate);
967 let t = residual.t.min(skip.t).min(out_len);
968 let mut out = Sig::zeros(skip.c, t);
969 for c in 0..skip.c {
970 for i in 0..t {
971 out.data[c * t + i] = (residual.data[c * residual.t + i]
972 + skip.data[c * skip.t + i])
973 .clamp(-1.0, 1.0);
974 }
975 }
976 out
977 }
978}
979
980pub fn write_wav(path: &std::path::Path, sig: &Sig, rate: usize) -> std::io::Result<()> {
982 use std::io::Write;
983 let n = sig.t;
984 let ch = sig.c as u16;
985 let bytes = (n * sig.c * 2) as u32;
986 let mut f = std::io::BufWriter::new(std::fs::File::create(path)?);
987 f.write_all(b"RIFF")?;
988 f.write_all(&(36 + bytes).to_le_bytes())?;
989 f.write_all(b"WAVEfmt ")?;
990 f.write_all(&16u32.to_le_bytes())?;
991 f.write_all(&1u16.to_le_bytes())?;
992 f.write_all(&ch.to_le_bytes())?;
993 f.write_all(&(rate as u32).to_le_bytes())?;
994 f.write_all(&((rate * sig.c * 2) as u32).to_le_bytes())?;
995 f.write_all(&((sig.c * 2) as u16).to_le_bytes())?;
996 f.write_all(&16u16.to_le_bytes())?;
997 f.write_all(b"data")?;
998 f.write_all(&bytes.to_le_bytes())?;
999 for i in 0..n {
1000 for c in 0..sig.c {
1001 let v = (sig.data[c * n + i].clamp(-1.0, 1.0) * 32767.0) as i16;
1002 f.write_all(&v.to_le_bytes())?;
1003 }
1004 }
1005 Ok(())
1006}
1007
1008fn mel_filterbank(sr: f64, n_fft: usize, n_mels: usize, fmin: f64, fmax: f64) -> Vec<f32> {
1014 let n_freqs = n_fft / 2 + 1;
1015 let hz_to_mel = |f: f64| 3.0 * f / 200.0;
1016 let mel_to_hz = |m: f64| m * 200.0 / 3.0;
1017 let (f_min_log, min_log_mel) = (1000.0f64, 15.0f64);
1019 let logstep = (6.4f64).ln() / 27.0;
1020 let hz_to_mel_s = |f: f64| {
1021 if f >= f_min_log {
1022 min_log_mel + (f / f_min_log).ln() / logstep
1023 } else {
1024 hz_to_mel(f)
1025 }
1026 };
1027 let mel_to_hz_s = |m: f64| {
1028 if m >= min_log_mel {
1029 f_min_log * ((m - min_log_mel) * logstep).exp()
1030 } else {
1031 mel_to_hz(m)
1032 }
1033 };
1034 let (m0, m1) = (hz_to_mel_s(fmin), hz_to_mel_s(fmax));
1035 let pts: Vec<f64> = (0..n_mels + 2)
1036 .map(|i| mel_to_hz_s(m0 + (m1 - m0) * i as f64 / (n_mels + 1) as f64))
1037 .collect();
1038 let freqs: Vec<f64> = (0..n_freqs).map(|i| sr * i as f64 / n_fft as f64).collect();
1039 let mut fb = vec![0f32; n_mels * n_freqs];
1040 for m in 0..n_mels {
1041 let (lo, ctr, hi) = (pts[m], pts[m + 1], pts[m + 2]);
1042 let enorm = 2.0 / (hi - lo);
1044 for (k, &f) in freqs.iter().enumerate() {
1045 let v = if f >= lo && f <= ctr {
1046 (f - lo) / (ctr - lo).max(1e-12)
1047 } else if f > ctr && f <= hi {
1048 (hi - f) / (hi - ctr).max(1e-12)
1049 } else {
1050 0.0
1051 };
1052 fb[m * n_freqs + k] = (v * enorm) as f32;
1053 }
1054 }
1055 fb
1056}
1057
1058pub fn waveform_to_mel(x: &Sig, sr: usize, n_fft: usize, hop: usize, n_mels: usize) -> Grid {
1062 let n_freqs = n_fft / 2 + 1;
1063 let fb = mel_filterbank(sr as f64, n_fft, n_mels, 0.0, sr as f64 / 2.0);
1064 let win: Vec<f32> = (0..n_fft)
1065 .map(|i| {
1066 let a = std::f64::consts::PI * 2.0 * i as f64 / n_fft as f64;
1067 (0.5 - 0.5 * a.cos()) as f32
1068 })
1069 .collect();
1070 let pad = n_fft / 2;
1071 let frames = x.t / hop + 1;
1072 let mut out = Grid::zeros(x.c, frames, n_mels);
1073 let mut re = vec![0f32; n_freqs];
1074 let mut im = vec![0f32; n_freqs];
1075 for c in 0..x.c {
1076 for f in 0..frames {
1077 let start = f as isize * hop as isize - pad as isize;
1078 re.iter_mut().for_each(|v| *v = 0.0);
1079 im.iter_mut().for_each(|v| *v = 0.0);
1080 for j in 0..n_fft {
1081 let mut s = start + j as isize;
1083 if s < 0 {
1084 s = -s;
1085 }
1086 if s >= x.t as isize {
1087 s = 2 * (x.t as isize - 1) - s;
1088 }
1089 let v = if s >= 0 && s < x.t as isize {
1090 x.data[c * x.t + s as usize]
1091 } else {
1092 0.0
1093 };
1094 let v = v * win[j];
1095 if v == 0.0 {
1096 continue;
1097 }
1098 for (k, (rr, ii)) in re.iter_mut().zip(im.iter_mut()).enumerate() {
1099 let a = -2.0 * std::f64::consts::PI * (k * j) as f64 / n_fft as f64;
1100 *rr += v * a.cos() as f32;
1101 *ii += v * a.sin() as f32;
1102 }
1103 }
1104 for m in 0..n_mels {
1105 let mut acc = 0f32;
1106 for k in 0..n_freqs {
1107 acc += fb[m * n_freqs + k] * (re[k] * re[k] + im[k] * im[k]).sqrt();
1108 }
1109 out.data[(c * frames + f) * n_mels + m] = acc.max(1e-5).ln();
1110 }
1111 }
1112 }
1113 out
1114}
1115
1116struct Downsample2 {
1117 conv: Conv2d,
1118}
1119
1120impl Downsample2 {
1121 fn forward(&self, x: &Grid, pool: Option<&Pool>) -> Grid {
1124 let (h, w) = (x.h + 2, x.w + 1);
1125 let mut p = Grid::zeros(x.c, h, w);
1126 for c in 0..x.c {
1127 for y in 0..x.h {
1128 for z in 0..x.w {
1129 p.data[(c * h + y + 2) * w + z] = x.data[(c * x.h + y) * x.w + z];
1130 }
1131 }
1132 }
1133 self.conv.forward_strided(&p, 2, pool)
1134 }
1135}
1136
1137impl Conv2d {
1138 fn forward_strided(&self, x: &Grid, stride: usize, pool: Option<&Pool>) -> Grid {
1141 let (oh, ow) = ((x.h - self.kh) / stride + 1, (x.w - self.kw) / stride + 1);
1142 let npos = oh * ow;
1143 let k = self.c_in * self.kh * self.kw;
1144 let mut patches = vec![0f32; npos * k];
1145 for i in 0..npos {
1146 let (pw, ph) = (i % ow, i / ow);
1147 for ci in 0..self.c_in {
1148 for a in 0..self.kh {
1149 for b in 0..self.kw {
1150 patches[i * k + (ci * self.kh + a) * self.kw + b] =
1151 x.data[(ci * x.h + ph * stride + a) * x.w + pw * stride + b];
1152 }
1153 }
1154 }
1155 }
1156 let mut ys = vec![0f32; npos * self.c_out];
1157 crate::fcd_ops::gemm_nt(&patches, &self.w, &mut ys, npos, k, self.c_out, pool);
1158 let mut out = Grid::zeros(self.c_out, oh, ow);
1159 for i in 0..npos {
1160 for co in 0..self.c_out {
1161 out.data[co * npos + i] = ys[i * self.c_out + co] + self.b[co];
1162 }
1163 }
1164 out
1165 }
1166}
1167
1168pub struct AudioVaeEncoder {
1170 conv_in: Conv2d,
1171 levels: Vec<(Vec<ResnetBlock>, Option<Downsample2>)>,
1172 mid: Vec<ResnetBlock>,
1173 conv_out: Conv2d,
1174 mean: Vec<f32>,
1175 std: Vec<f32>,
1176 z: usize,
1177}
1178
1179impl AudioVaeEncoder {
1180 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<AudioVaeEncoder, String> {
1181 let mut levels = Vec::new();
1182 let mut lv = 0usize;
1183 while model
1184 .tensor(&format!("avae.encoder.down.{lv}.block.0.conv1.conv.weight"))
1185 .is_some()
1186 {
1187 let mut blocks = Vec::new();
1188 let mut bi = 0usize;
1189 while model
1190 .tensor(&format!(
1191 "avae.encoder.down.{lv}.block.{bi}.conv1.conv.weight"
1192 ))
1193 .is_some()
1194 {
1195 blocks.push(ResnetBlock::load(
1196 model,
1197 &format!("avae.encoder.down.{lv}.block.{bi}"),
1198 )?);
1199 bi += 1;
1200 }
1201 let down = match model.tensor(&format!("avae.encoder.down.{lv}.downsample.conv.weight"))
1202 {
1203 Some(_) => Some(Downsample2 {
1206 conv: Conv2d::load(model, &format!("avae.encoder.down.{lv}.downsample.conv"))?,
1207 }),
1208 None => None,
1209 };
1210 levels.push((blocks, down));
1211 lv += 1;
1212 }
1213 let out = Conv2d::load(model, "avae.encoder.conv_out.conv")?;
1214 let z = out.c_out / 2;
1215 Ok(AudioVaeEncoder {
1216 conv_in: Conv2d::load(model, "avae.encoder.conv_in.conv")?,
1217 levels,
1218 mid: vec![
1219 ResnetBlock::load(model, "avae.encoder.mid.block_1")?,
1220 ResnetBlock::load(model, "avae.encoder.mid.block_2")?,
1221 ],
1222 conv_out: out,
1223 mean: tensor_f32(model, "avae.per_channel_statistics.mean-of-means")?.0,
1224 std: tensor_f32(model, "avae.per_channel_statistics.std-of-means")?.0,
1225 z,
1226 })
1227 }
1228
1229 pub fn encode(&self, mel: &Grid, pool: Option<&Pool>) -> Grid {
1231 let mut h = self.conv_in.forward(mel, pool);
1232 for (blocks, down) in &self.levels {
1233 for b in blocks {
1234 h = b.forward(&h, pool);
1235 }
1236 if let Some(d) = down {
1237 h = d.forward(&h, pool);
1238 }
1239 }
1240 for b in &self.mid {
1241 h = b.forward(&h, pool);
1242 }
1243 pixel_norm(&mut h);
1244 h.data.iter_mut().for_each(|v| *v = silu(*v));
1245 let out = self.conv_out.forward(&h, pool);
1246 let npos = out.n();
1249 let mut lat = Grid::zeros(self.z, out.h, out.w);
1250 for c in 0..self.z {
1251 for hi in 0..out.h {
1252 for wi in 0..out.w {
1253 let idx = c * out.w + wi;
1254 let v = out.data[(c * out.h + hi) * out.w + wi];
1255 lat.data[(c * out.h + hi) * out.w + wi] = (v - self.mean[idx]) / self.std[idx];
1256 }
1257 }
1258 }
1259 lat
1260 }
1261}
1262
1263pub fn read_wav(path: &std::path::Path) -> Result<(Sig, usize), String> {
1265 let raw = std::fs::read(path).map_err(|e| format!("{}: {e}", path.display()))?;
1266 if raw.len() < 44 || &raw[..4] != b"RIFF" || &raw[8..12] != b"WAVE" {
1267 return Err(format!("{}: not a RIFF/WAVE file", path.display()));
1268 }
1269 let mut i = 12usize;
1270 let (mut ch, mut rate, mut bits) = (2usize, 48000usize, 16usize);
1271 let mut data: Option<(usize, usize)> = None;
1272 while i + 8 <= raw.len() {
1273 let id = &raw[i..i + 4];
1274 let len = u32::from_le_bytes(raw[i + 4..i + 8].try_into().unwrap()) as usize;
1275 let body = i + 8;
1276 if id == b"fmt " && body + 16 <= raw.len() {
1277 ch = u16::from_le_bytes(raw[body + 2..body + 4].try_into().unwrap()) as usize;
1278 rate = u32::from_le_bytes(raw[body + 4..body + 8].try_into().unwrap()) as usize;
1279 bits = u16::from_le_bytes(raw[body + 14..body + 16].try_into().unwrap()) as usize;
1280 } else if id == b"data" {
1281 data = Some((body, len.min(raw.len() - body)));
1282 break;
1283 }
1284 i = body + len + (len & 1);
1285 }
1286 let (off, len) = data.ok_or_else(|| format!("{}: no data chunk", path.display()))?;
1287 if bits != 16 {
1288 return Err(format!("{}: only 16-bit PCM is read", path.display()));
1289 }
1290 let n = len / 2 / ch.max(1);
1291 let mut sig = Sig::zeros(ch, n);
1292 for i in 0..n {
1293 for c in 0..ch {
1294 let o = off + (i * ch + c) * 2;
1295 let v = i16::from_le_bytes([raw[o], raw[o + 1]]) as f32 / 32768.0;
1296 sig.data[c * n + i] = v;
1297 }
1298 }
1299 Ok((sig, rate))
1300}
1301
1302pub fn resample(x: &Sig, from: usize, to: usize) -> Sig {
1305 if from == to {
1306 return x.clone();
1307 }
1308 let n = (x.t as f64 * to as f64 / from as f64).round() as usize;
1309 let mut out = Sig::zeros(x.c, n);
1310 for c in 0..x.c {
1311 for i in 0..n {
1312 let p = i as f64 * from as f64 / to as f64;
1313 let j = p.floor() as usize;
1314 let f = (p - j as f64) as f32;
1315 let a = x.data[c * x.t + j.min(x.t - 1)];
1316 let b = x.data[c * x.t + (j + 1).min(x.t - 1)];
1317 out.data[c * n + i] = a + (b - a) * f;
1318 }
1319 }
1320 out
1321}