Skip to main content

candle_transformers/models/
snac.rs

1#![allow(unused)]
2//! Implementation of the Multi-Scale Neural Audio Codec (SNAC)
3//!
4//! See: [SNAC](https://github.com/hubertsiuzdak/snac)
5//!
6/// Multi-Scale Neural Audio Codec (SNAC) compresses audio into discrete codes at a low bitrate.
7/// For more information, read the paper: https://arxiv.org/abs/2410.14411
8///
9use candle::{DType, Device, IndexOp, Module, Result, Tensor, D};
10use candle_nn::{
11    linear_b, Conv1d, Conv1dConfig, ConvTranspose1d, ConvTranspose1dConfig, LayerNorm, Linear,
12    VarBuilder,
13};
14
15#[derive(serde::Deserialize, Debug, Clone)]
16pub struct Config {
17    pub sampling_rate: usize,
18    pub encoder_dim: usize,
19    pub encoder_rates: Vec<usize>,
20    pub decoder_dim: usize,
21    pub decoder_rates: Vec<usize>,
22    pub attn_window_size: Option<usize>,
23    pub codebook_size: usize,
24    pub codebook_dim: usize,
25    pub vq_strides: Vec<usize>,
26    pub noise: bool,
27    pub depthwise: bool,
28}
29
30// Equivalent to torch.repeat_interleave
31pub fn repeat_interleave<D: candle::shape::Dim>(
32    img: &Tensor,
33    repeats: usize,
34    dim: D,
35) -> Result<Tensor> {
36    if repeats == 1 {
37        return Ok(img.clone());
38    }
39    let dim = dim.to_index(img.shape(), "chunk")?;
40    let img = img.unsqueeze(dim + 1)?;
41    let mut dims = img.dims().to_vec();
42    dims[dim + 1] = repeats;
43    img.broadcast_as(dims)?.flatten(dim, dim + 1)
44}
45
46pub fn conv1d_weight_norm(
47    in_c: usize,
48    out_c: usize,
49    kernel_size: usize,
50    config: candle_nn::Conv1dConfig,
51    vb: VarBuilder,
52) -> Result<Conv1d> {
53    let weight_g = vb.get((out_c, 1, 1), "parametrizations.weight.original0")?;
54    let weight_v = {
55        let name = "parametrizations.weight.original1";
56        match vb.get((out_c, in_c, kernel_size), name) {
57            Ok(v) => v,
58            Err(_) => vb.get((out_c, 1, kernel_size), name)?,
59        }
60    };
61    let norm_v = weight_v.sqr()?.sum_keepdim((1, 2))?.sqrt()?;
62    let weight = weight_v.broadcast_mul(&weight_g)?.broadcast_div(&norm_v)?;
63    let bias = vb.get(out_c, "bias")?;
64    Ok(Conv1d::new(weight, Some(bias), config))
65}
66
67pub fn conv1d_weight_norm_no_bias(
68    in_c: usize,
69    out_c: usize,
70    kernel_size: usize,
71    config: candle_nn::Conv1dConfig,
72    vb: VarBuilder,
73) -> Result<Conv1d> {
74    let weight_g = vb.get((out_c, 1, 1), "parametrizations.weight.original0")?;
75    let weight_v = {
76        let name = "parametrizations.weight.original1";
77        match vb.get((out_c, in_c, kernel_size), name) {
78            Ok(v) => v,
79            Err(_) => vb.get((out_c, 1, kernel_size), name)?,
80        }
81    };
82    let norm_v = weight_v.sqr()?.sum_keepdim((1, 2))?.sqrt()?;
83    let weight = weight_v.broadcast_mul(&weight_g)?.broadcast_div(&norm_v)?;
84    Ok(Conv1d::new(weight, None, config))
85}
86
87pub fn conv_transpose1d_weight_norm(
88    in_c: usize,
89    out_c: usize,
90    kernel_size: usize,
91    bias: bool,
92    config: candle_nn::ConvTranspose1dConfig,
93    vb: VarBuilder,
94) -> Result<ConvTranspose1d> {
95    let weight_g = vb.get((in_c, 1, 1), "parametrizations.weight.original0")?;
96    let weight_v = vb.get(
97        (in_c, out_c, kernel_size),
98        "parametrizations.weight.original1",
99    )?;
100    let norm_v = weight_v.sqr()?.sum_keepdim((1, 2))?.sqrt()?;
101    let weight = weight_v.broadcast_mul(&weight_g)?.broadcast_div(&norm_v)?;
102    let bias = if bias {
103        Some(vb.get(out_c, "bias")?)
104    } else {
105        None
106    };
107    Ok(ConvTranspose1d::new(weight, bias, config))
108}
109
110// https://github.com/hubertsiuzdak/snac/blob/main/snac/attention.py
111#[allow(unused)]
112#[derive(Debug, Clone)]
113struct SinusoidalEmbeddings {
114    inv_freq: Tensor,
115    scale: Tensor,
116    scale_base: f32,
117    use_xpos: bool,
118}
119
120impl SinusoidalEmbeddings {
121    fn new(dim: usize, scale_base: f32, use_xpos: bool, dev: &Device) -> Result<Self> {
122        let inv_freq: Vec<_> = (0..dim)
123            .step_by(2)
124            .map(|i| 1f32 / 10_000f32.powf(i as f32 / dim as f32))
125            .collect();
126        let len = inv_freq.len();
127        let inv_freq = Tensor::from_vec(inv_freq, len, dev)?.to_dtype(DType::F32)?;
128        let scale: Vec<_> = (0..dim)
129            .step_by(2)
130            .map(|i| (i as f32 + 0.4 * dim as f32) / (1.4 * dim as f32))
131            .collect();
132        let scale = Tensor::from_vec(scale, len, dev)?.to_dtype(DType::F32)?;
133        Ok(Self {
134            inv_freq,
135            scale,
136            scale_base,
137            use_xpos,
138        })
139    }
140}
141
142#[allow(unused)]
143#[derive(Debug, Clone)]
144struct LocalMHA {
145    norm: LayerNorm,
146    to_qkv: Linear,
147    to_out: Linear,
148    num_heads: usize,
149    head_dim: usize,
150    rel_pos: Option<SinusoidalEmbeddings>,
151}
152
153impl LocalMHA {
154    fn new(
155        dim: usize,
156        window_size: usize,
157        dim_head: usize,
158        use_rotary_pos_emb: bool,
159        vb: VarBuilder,
160    ) -> Result<Self> {
161        let norm = candle_nn::layer_norm(dim, 1e-5, vb.pp("norm"))?;
162        let to_qkv = linear_b(dim, dim * 3, false, vb.pp("to_qkv"))?;
163        let to_out = linear_b(dim, dim, false, vb.pp("to_out"))?;
164        let rel_pos = if use_rotary_pos_emb {
165            let rel_pos =
166                SinusoidalEmbeddings::new(dim_head, window_size as f32 / 2.0, false, vb.device())?;
167            Some(rel_pos)
168        } else {
169            None
170        };
171        Ok(Self {
172            norm,
173            to_qkv,
174            to_out,
175            rel_pos,
176            num_heads: dim / dim_head,
177            head_dim: dim_head,
178        })
179    }
180}
181
182impl Module for LocalMHA {
183    fn forward(&self, xs: &Tensor) -> Result<Tensor> {
184        let (b, c, t) = xs.dims3()?;
185        let residual = xs.clone();
186        let xs = xs.transpose(1, 2)?.apply(&self.norm)?;
187        let qkv = xs.apply(&self.to_qkv)?;
188        let q = qkv.narrow(D::Minus1, 0, c)?;
189        let k = qkv.narrow(D::Minus1, c, c)?;
190        let v = qkv.narrow(D::Minus1, 2 * c, c)?;
191        let q = q
192            .reshape((b, t, self.num_heads, self.head_dim))?
193            .transpose(1, 2)?
194            .contiguous()?;
195        let k = k
196            .reshape((b, t, self.num_heads, self.head_dim))?
197            .transpose(1, 2)?
198            .contiguous()?;
199        let v = v
200            .reshape((b, t, self.num_heads, self.head_dim))?
201            .transpose(1, 2)?
202            .contiguous()?;
203        let (q, k) = match self.rel_pos {
204            Some(_) => todo!(),
205            None => (q, k),
206        };
207        let out = {
208            let scale = 1f64 / f64::sqrt(self.head_dim as f64);
209            let attn_weights = (q.matmul(&k.transpose(2, 3)?)? * scale)?;
210            // Non-causal attention
211            let attn_weights = candle_nn::ops::softmax_last_dim(&attn_weights)?;
212            attn_weights.matmul(&v)?
213        };
214        let out = out
215            .transpose(1, 2)?
216            .reshape((b, t, self.num_heads * self.head_dim))?
217            .apply(&self.to_out)?;
218        out.transpose(1, 2)? + residual
219    }
220}
221
222#[derive(Debug, Clone)]
223struct Snake1d {
224    alpha: Tensor,
225}
226
227impl Snake1d {
228    pub fn new(channels: usize, vb: VarBuilder) -> Result<Self> {
229        let alpha = vb.get((1, channels, 1), "alpha")?;
230        Ok(Self { alpha })
231    }
232}
233
234impl Module for Snake1d {
235    fn forward(&self, xs: &Tensor) -> Result<Tensor> {
236        let xs_shape = xs.shape();
237        let xs = xs.flatten_from(2)?;
238        let sin = self.alpha.broadcast_mul(&xs)?.sin()?;
239        let sin = (&sin * &sin)?;
240        (xs + (&self.alpha + 1e-9)?.recip()?.broadcast_mul(&sin)?)?.reshape(xs_shape)
241    }
242}
243
244#[derive(Debug, Clone)]
245struct ResidualUnit {
246    snake1: Snake1d,
247    conv1: Conv1d,
248    snake2: Snake1d,
249    conv2: Conv1d,
250}
251
252impl ResidualUnit {
253    fn new(
254        dim: usize,
255        dilation: usize,
256        kernel: usize,
257        groups: usize,
258        vb: VarBuilder,
259    ) -> Result<Self> {
260        let pad = ((kernel - 1) * dilation) / 2;
261        let vb = vb.pp("block");
262        let snake1 = Snake1d::new(dim, vb.pp(0))?;
263        let cfg1 = Conv1dConfig {
264            dilation,
265            padding: pad,
266            groups,
267            ..Default::default()
268        };
269        let conv1 = conv1d_weight_norm(dim, dim, 7, cfg1, vb.pp(1))?;
270        let snake2 = Snake1d::new(dim, vb.pp(2))?;
271        let conv2 = conv1d_weight_norm(dim, dim, 1, Default::default(), vb.pp(3))?;
272        Ok(Self {
273            snake1,
274            conv1,
275            snake2,
276            conv2,
277        })
278    }
279}
280
281impl Module for ResidualUnit {
282    fn forward(&self, xs: &Tensor) -> Result<Tensor> {
283        let ys = xs
284            .apply(&self.snake1)?
285            .apply(&self.conv1)?
286            .apply(&self.snake2)?
287            .apply(&self.conv2)?;
288        let pad = (xs.dim(D::Minus1)? - ys.dim(D::Minus1)?) / 2;
289        if pad > 0 {
290            &ys + xs.narrow(D::Minus1, pad, ys.dim(D::Minus1)?)
291        } else {
292            ys + xs
293        }
294    }
295}
296
297#[derive(Debug, Clone)]
298struct NoiseBlock {
299    linear: Conv1d,
300}
301
302impl NoiseBlock {
303    fn new(dim: usize, vb: VarBuilder) -> Result<Self> {
304        let linear = conv1d_weight_norm_no_bias(dim, dim, 1, Default::default(), vb.pp("linear"))?;
305        Ok(Self { linear })
306    }
307}
308
309impl Module for NoiseBlock {
310    fn forward(&self, xs: &Tensor) -> Result<Tensor> {
311        let (b, _c, t) = xs.dims3()?;
312        let noise = Tensor::randn(0f32, 1f32, (b, 1, t), xs.device())?;
313        let h = xs.apply(&self.linear)?;
314        let n = noise.broadcast_mul(&h)?;
315        let xs = (xs + n)?;
316        Ok(xs)
317    }
318}
319
320#[derive(Debug, Clone)]
321struct DecoderBlock {
322    snake1: Snake1d,
323    conv_tr1: ConvTranspose1d,
324    noise: Option<NoiseBlock>,
325    res1: ResidualUnit,
326    res2: ResidualUnit,
327    res3: ResidualUnit,
328}
329
330impl DecoderBlock {
331    fn new(
332        in_dim: usize,
333        out_dim: usize,
334        stride: usize,
335        noise: bool,
336        groups: usize,
337        vb: VarBuilder,
338    ) -> Result<Self> {
339        let vb = vb.pp("block");
340        let snake1 = Snake1d::new(in_dim, vb.pp(0))?;
341        let cfg = ConvTranspose1dConfig {
342            stride,
343            padding: stride.div_ceil(2),
344            output_padding: stride % 2,
345            ..Default::default()
346        };
347        let conv_tr1 =
348            conv_transpose1d_weight_norm(in_dim, out_dim, 2 * stride, true, cfg, vb.pp(1))?;
349        let (n, noise) = if noise {
350            let noise = NoiseBlock::new(out_dim, vb.pp(2))?;
351            (1, Some(noise))
352        } else {
353            (0, None)
354        };
355        let res1 = ResidualUnit::new(out_dim, 1, 7, groups, vb.pp(2 + n))?;
356        let res2 = ResidualUnit::new(out_dim, 3, 7, groups, vb.pp(3 + n))?;
357        let res3 = ResidualUnit::new(out_dim, 9, 7, groups, vb.pp(4 + n))?;
358        Ok(Self {
359            snake1,
360            conv_tr1,
361            noise,
362            res1,
363            res2,
364            res3,
365        })
366    }
367}
368
369impl Module for DecoderBlock {
370    fn forward(&self, xs: &Tensor) -> Result<Tensor> {
371        xs.apply(&self.snake1)?
372            .apply(&self.conv_tr1)?
373            .apply(&self.noise.as_ref())?
374            .apply(&self.res1)?
375            .apply(&self.res2)?
376            .apply(&self.res3)
377    }
378}
379
380#[derive(Debug, Clone)]
381struct EncoderBlock {
382    res1: ResidualUnit,
383    res2: ResidualUnit,
384    res3: ResidualUnit,
385    snake1: Snake1d,
386    conv1: Conv1d,
387}
388
389impl EncoderBlock {
390    fn new(
391        out_dim: usize,
392        in_dim: Option<usize>,
393        stride: usize,
394        groups: usize,
395        vb: VarBuilder,
396    ) -> Result<Self> {
397        let vb = vb.pp("block");
398        let in_dim = in_dim.unwrap_or(out_dim / 2);
399        let res1 = ResidualUnit::new(in_dim, 1, 7, groups, vb.pp(0))?;
400        let res2 = ResidualUnit::new(in_dim, 3, 7, groups, vb.pp(1))?;
401        let res3 = ResidualUnit::new(in_dim, 9, 7, groups, vb.pp(2))?;
402        let snake1 = Snake1d::new(in_dim, vb.pp(3))?;
403        let cfg1 = Conv1dConfig {
404            stride,
405            padding: stride.div_ceil(2),
406            ..Default::default()
407        };
408        let conv1 = conv1d_weight_norm(in_dim, out_dim, 2 * stride, cfg1, vb.pp(4))?;
409        Ok(Self {
410            res1,
411            res2,
412            res3,
413            snake1,
414            conv1,
415        })
416    }
417}
418
419impl candle::Module for EncoderBlock {
420    fn forward(&self, xs: &Tensor) -> Result<Tensor> {
421        xs.apply(&self.res1)?
422            .apply(&self.res2)?
423            .apply(&self.res3)?
424            .apply(&self.snake1)?
425            .apply(&self.conv1)
426    }
427}
428
429#[derive(Debug, Clone)]
430pub struct Encoder {
431    conv1: Conv1d,
432    blocks: Vec<EncoderBlock>,
433    local_mha: Option<LocalMHA>,
434    conv2: Conv1d,
435}
436
437impl candle::Module for Encoder {
438    fn forward(&self, xs: &Tensor) -> Result<Tensor> {
439        let mut xs = xs.apply(&self.conv1)?;
440        for block in self.blocks.iter() {
441            xs = xs.apply(block)?
442        }
443        xs.apply(&self.conv2)
444    }
445}
446
447impl Encoder {
448    fn new(
449        mut d_model: usize,
450        strides: &[usize],
451        depthwise: bool,
452        attn_window_size: Option<usize>,
453        vb: VarBuilder,
454    ) -> Result<Self> {
455        let vb = vb.pp("block");
456        let mut idx = 0;
457        let cfg1 = Conv1dConfig {
458            padding: 3,
459            ..Default::default()
460        };
461        let conv1 = conv1d_weight_norm(1, d_model, 7, cfg1, vb.pp(idx))?;
462        idx += 1;
463        let mut blocks = Vec::with_capacity(strides.len());
464        for &stride in strides.iter() {
465            d_model *= 2;
466            let groups = if depthwise { d_model / 2 } else { 1 };
467            let block = EncoderBlock::new(d_model, None, stride, groups, vb.pp(idx))?;
468            idx += 1;
469            blocks.push(block)
470        }
471        let local_mha = match attn_window_size {
472            Some(w) => {
473                let mha = LocalMHA::new(d_model, w, 64, true, vb.pp(idx))?;
474                idx += 1;
475                Some(mha)
476            }
477            None => None,
478        };
479        let groups = if depthwise { d_model } else { 1 };
480        let cfg2 = Conv1dConfig {
481            padding: 3,
482            groups,
483            ..Default::default()
484        };
485        let conv2 = conv1d_weight_norm(d_model, d_model, 7, cfg2, vb.pp(idx))?;
486        idx += 1;
487        Ok(Self {
488            conv1,
489            blocks,
490            local_mha,
491            conv2,
492        })
493    }
494}
495
496#[derive(Debug, Clone)]
497enum ConvInit {
498    Depthwise(Conv1d, Conv1d),
499    Standard(Conv1d),
500}
501
502#[derive(Debug, Clone)]
503pub struct Decoder {
504    conv1: ConvInit,
505    local_mha: Option<LocalMHA>,
506    blocks: Vec<DecoderBlock>,
507    snake1: Snake1d,
508    conv2: Conv1d,
509}
510
511impl Decoder {
512    #[allow(clippy::too_many_arguments)]
513    fn new(
514        in_c: usize,
515        mut channels: usize,
516        rates: &[usize],
517        noise: bool,
518        depthwise: bool,
519        attn_window_size: Option<usize>,
520        d_out: usize,
521        vb: VarBuilder,
522    ) -> Result<Self> {
523        let vb = vb.pp("model");
524        let mut idx = 0;
525        let pad3 = Conv1dConfig {
526            padding: 3,
527            ..Default::default()
528        };
529        let conv1 = if depthwise {
530            let cfg1 = Conv1dConfig {
531                padding: 3,
532                groups: in_c,
533                ..Default::default()
534            };
535            let conv1 = conv1d_weight_norm(in_c, in_c, 7, cfg1, vb.pp(idx))?;
536            idx += 1;
537            let conv2 = conv1d_weight_norm(in_c, channels, 1, Default::default(), vb.pp(idx))?;
538            idx += 1;
539            ConvInit::Depthwise(conv1, conv2)
540        } else {
541            let conv1 = conv1d_weight_norm(in_c, channels, 7, pad3, vb.pp(idx))?;
542            idx += 1;
543            ConvInit::Standard(conv1)
544        };
545        let mut blocks = Vec::with_capacity(rates.len());
546        let local_mha = match attn_window_size {
547            Some(w) => {
548                let mha = LocalMHA::new(channels, w, 64, true, vb.pp(idx))?;
549                idx += 1;
550                Some(mha)
551            }
552            None => None,
553        };
554        for stride in rates.iter() {
555            let groups = if depthwise { channels / 2 } else { 1 };
556            let block =
557                DecoderBlock::new(channels, channels / 2, *stride, noise, groups, vb.pp(idx))?;
558            idx += 1;
559            channels /= 2;
560            blocks.push(block)
561        }
562        let snake1 = Snake1d::new(channels, vb.pp(idx))?;
563        idx += 1;
564        let conv2 = conv1d_weight_norm(channels, d_out, 7, pad3, vb.pp(idx))?;
565        idx += 1;
566        Ok(Self {
567            conv1,
568            local_mha,
569            blocks,
570            snake1,
571            conv2,
572        })
573    }
574}
575
576impl candle::Module for Decoder {
577    fn forward(&self, xs: &Tensor) -> Result<Tensor> {
578        let mut xs = match &self.conv1 {
579            ConvInit::Standard(c) => xs.apply(c)?,
580            ConvInit::Depthwise(c1, c2) => xs.apply(c1)?.apply(c2)?,
581        };
582        for block in self.blocks.iter() {
583            xs = xs.apply(block)?
584        }
585        xs.apply(&self.snake1)?.apply(&self.conv2)
586    }
587}
588
589fn normalize(v: &Tensor) -> Result<Tensor> {
590    v.broadcast_div(&v.sqr()?.sum_keepdim(1)?.sqrt()?)
591}
592
593// https://github.com/hubertsiuzdak/snac/blob/main/snac/vq.py
594#[allow(unused)]
595#[derive(Clone, Debug)]
596struct VectorQuantizer {
597    in_proj: Conv1d,
598    out_proj: Conv1d,
599    codebook: candle_nn::Embedding,
600    stride: usize,
601}
602
603impl VectorQuantizer {
604    fn new(
605        in_dim: usize,
606        cb_size: usize,
607        cb_dim: usize,
608        stride: usize,
609        vb: VarBuilder,
610    ) -> Result<Self> {
611        let in_proj = conv1d_weight_norm(in_dim, cb_dim, 1, Default::default(), vb.pp("in_proj"))?;
612        let out_proj =
613            conv1d_weight_norm(cb_dim, in_dim, 1, Default::default(), vb.pp("out_proj"))?;
614        let codebook = candle_nn::embedding(cb_size, cb_dim, vb.pp("codebook"))?;
615        Ok(Self {
616            in_proj,
617            out_proj,
618            codebook,
619            stride,
620        })
621    }
622
623    fn decode_latents(&self, latents: &Tensor) -> Result<(Tensor, Tensor)> {
624        let (b, d, t) = latents.dims3()?;
625        let encodings = latents.transpose(1, 2)?.reshape((b * t, d))?;
626        let encodings = normalize(&encodings)?;
627        let codebook = normalize(self.codebook.embeddings())?;
628        let dist = (encodings
629            .sqr()?
630            .sum_keepdim(1)?
631            .broadcast_sub(&encodings.matmul(&codebook.t()?)?)?
632            * 2.0)?
633            .broadcast_add(&codebook.sqr()?.sum_keepdim(1)?.t()?)?;
634        let indices = dist.argmin(1)?.reshape((b, ()))?;
635        let z_q = self.decode_code(&indices)?;
636        Ok((z_q, indices))
637    }
638
639    fn encode(&self, z: &Tensor) -> Result<(Tensor, Tensor)> {
640        let z = if self.stride > 1 {
641            let (b, c, t) = z.dims3()?;
642            z.reshape((b, c, 1, t))?
643                .avg_pool2d((1, self.stride))?
644                .squeeze(2)?
645        } else {
646            z.clone()
647        };
648        let z_e = z.apply(&self.in_proj)?;
649        let (z_q, indices) = self.decode_latents(&z_e)?;
650        let z_q = z_q.apply(&self.out_proj)?;
651        let z_q = if self.stride > 1 {
652            repeat_interleave(&z_q, self.stride, D::Minus1)?
653        } else {
654            z_q
655        };
656        Ok((z_q, indices))
657    }
658
659    fn embed_code(&self, embed_id: &Tensor) -> Result<Tensor> {
660        embed_id.apply(&self.codebook)
661    }
662
663    fn decode_code(&self, embed_id: &Tensor) -> Result<Tensor> {
664        self.embed_code(embed_id)?.transpose(1, 2)
665    }
666}
667
668#[derive(Clone, Debug)]
669pub struct ResidualVectorQuantizer {
670    quantizers: Vec<VectorQuantizer>,
671}
672
673impl ResidualVectorQuantizer {
674    fn new(
675        input_dim: usize,
676        cb_size: usize,
677        cb_dim: usize,
678        vq_strides: &[usize],
679        vb: VarBuilder,
680    ) -> Result<Self> {
681        let vb = &vb.pp("quantizers");
682        let quantizers = vq_strides
683            .iter()
684            .enumerate()
685            .map(|(i, stride)| VectorQuantizer::new(input_dim, cb_size, cb_dim, *stride, vb.pp(i)))
686            .collect::<Result<Vec<_>>>()?;
687        Ok(Self { quantizers })
688    }
689
690    fn encode(&self, z: &Tensor) -> Result<(Tensor, Vec<Tensor>)> {
691        let mut residual = z.clone();
692        let mut z_q = z.zeros_like()?;
693        let mut codes = Vec::with_capacity(self.quantizers.len());
694        for quantizer in self.quantizers.iter() {
695            let (z_q_i, indices_i) = quantizer.encode(&residual)?;
696            z_q = (z_q + &z_q_i)?;
697            residual = (residual - &z_q_i)?;
698            codes.push(indices_i)
699        }
700        Ok((z_q, codes))
701    }
702
703    #[allow(clippy::wrong_self_convention)]
704    fn from_codes(&self, codes: &[&Tensor]) -> Result<Tensor> {
705        let mut sum = None;
706        for (quantizer, codes) in self.quantizers.iter().zip(codes.iter()) {
707            let z_p_i = quantizer.decode_code(codes)?;
708            let z_q_i = z_p_i.apply(&quantizer.out_proj)?;
709            let z_q_i = repeat_interleave(&z_q_i, quantizer.stride, D::Minus1)?;
710            let s = match sum {
711                None => z_q_i,
712                Some(s) => (s + z_q_i)?,
713            };
714            sum = Some(s)
715        }
716        match sum {
717            Some(s) => Ok(s),
718            None => candle::bail!("empty codebooks"),
719        }
720    }
721}
722
723fn gcd(mut a: usize, mut b: usize) -> usize {
724    while b != 0 {
725        let t = b;
726        b = a % b;
727        a = t;
728    }
729    a
730}
731
732fn lcm(a: usize, b: usize) -> usize {
733    a / gcd(a, b) * b
734}
735
736// https://github.com/hubertsiuzdak/snac/blob/main/snac/snac.py
737#[derive(Debug, Clone)]
738pub struct Model {
739    pub encoder: Encoder,
740    pub quantizer: ResidualVectorQuantizer,
741    pub decoder: Decoder,
742    pub hop_length: usize,
743    pub config: Config,
744}
745
746impl Model {
747    pub fn new(cfg: &Config, vb: VarBuilder) -> Result<Self> {
748        let encoder = Encoder::new(
749            cfg.encoder_dim,
750            &cfg.encoder_rates,
751            cfg.depthwise,
752            cfg.attn_window_size,
753            vb.pp("encoder"),
754        )?;
755        let latent_dim = cfg.encoder_dim * 2usize.pow(cfg.encoder_rates.len() as u32);
756        let quantizer = ResidualVectorQuantizer::new(
757            latent_dim,
758            cfg.codebook_size,
759            cfg.codebook_dim,
760            &cfg.vq_strides,
761            vb.pp("quantizer"),
762        )?;
763        let decoder = Decoder::new(
764            latent_dim,
765            cfg.decoder_dim,
766            &cfg.decoder_rates,
767            cfg.noise,
768            cfg.depthwise,
769            cfg.attn_window_size,
770            /* d_out */ 1,
771            vb.pp("decoder"),
772        )?;
773        let hop_length = cfg.encoder_rates.iter().product::<usize>();
774        Ok(Self {
775            encoder,
776            decoder,
777            quantizer,
778            config: cfg.clone(),
779            hop_length,
780        })
781    }
782
783    fn preprocess(&self, audio_data: &Tensor) -> Result<Tensor> {
784        let len = audio_data.dim(D::Minus1)?;
785        let lcm = lcm(
786            self.config.vq_strides[0],
787            self.config.attn_window_size.unwrap_or(1),
788        );
789        let pad_to = self.hop_length * lcm;
790        let right_pad = len.div_ceil(pad_to) * pad_to - len;
791        let audio_data = audio_data.pad_with_zeros(D::Minus1, 0, right_pad)?;
792        Ok(audio_data)
793    }
794
795    pub fn encode(&self, audio_data: &Tensor) -> Result<Vec<Tensor>> {
796        let audio_data = self.preprocess(audio_data)?;
797        let z = self.encoder.forward(&audio_data)?;
798        let (_, codes) = self.quantizer.encode(&z)?;
799        Ok(codes)
800    }
801
802    pub fn decode(&self, audio_codes: &[&Tensor]) -> Result<Tensor> {
803        let audio_values = self.quantizer.from_codes(audio_codes)?;
804        audio_values.apply(&self.decoder)
805    }
806
807    pub fn config(&self) -> &Config {
808        &self.config
809    }
810
811    pub fn num_codebooks(&self) -> usize {
812        self.quantizer.quantizers.len()
813    }
814}