1use std::collections::BTreeMap;
19use std::fmt;
20
21pub const SAMPLE_RATE_HZ: u32 = 48_000;
22
23const N_FFT: usize = 1024;
24const HOP: usize = 512;
25const FREQ: usize = N_FFT / 2;
27const CH: usize = 64;
28const STRIDE: usize = 4;
29const K0: usize = 8;
30const F_ENC: usize = FREQ / STRIDE;
31const ENC_CONVS: usize = 3;
32const ENC_K: usize = 3;
33const RF_CH: usize = 48;
34const RF_FREQ: usize = 48;
35const HEADS: usize = 4;
36const HEAD_DIM: usize = RF_CH / HEADS;
37const BLOCKS: usize = 3;
38const COMPRESSION: f32 = 0.3;
39const MAG_EPS: f32 = 1.0e-5;
40
41#[derive(Debug)]
42pub enum EnhanceError {
43 MissingTensor(String),
44 ShapeMismatch {
45 name: String,
46 expected: Vec<usize>,
47 got: Vec<usize>,
48 },
49}
50
51impl fmt::Display for EnhanceError {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match self {
54 Self::MissingTensor(name) => write!(f, "enhancer tensor {name} is missing"),
55 Self::ShapeMismatch {
56 name,
57 expected,
58 got,
59 } => {
60 write!(
61 f,
62 "enhancer tensor {name}: expected shape {expected:?}, got {got:?}"
63 )
64 }
65 }
66 }
67}
68
69impl std::error::Error for EnhanceError {}
70
71struct Conv1d {
72 weight: Vec<f32>,
74 bias: Vec<f32>,
75 out_ch: usize,
76 in_ch: usize,
77 k: usize,
78}
79
80struct Linear {
81 weight: Vec<f32>,
83}
84
85struct GruWeights {
86 weight_ih: Vec<f32>,
88 weight_hh: Vec<f32>,
89 bias_ih: Vec<f32>,
90 bias_hh: Vec<f32>,
91}
92
93struct RnnFormerBlock {
94 rnn: GruWeights,
95 rnn_fc: Conv1d,
96 qkv: Linear,
97 attn_fc: Conv1d,
98 pe: Option<Vec<f32>>,
100}
101
102pub struct Enhancer {
104 enc_pre: Conv1d,
107 encoder: Vec<Conv1d>,
108 rf_pre_lin: Linear,
109 rf_pre_conv: Conv1d,
110 blocks: Vec<RnnFormerBlock>,
111 rf_post_lin: Linear,
112 rf_post_conv: Conv1d,
113 decoder: Vec<(Conv1d, Conv1d)>,
115 dec_post_conv: Conv1d,
116 dec_post_up: Vec<f32>,
118 dec_post_up_bias: Vec<f32>,
119 window: Vec<f32>,
120 fft: Fft,
121}
122
123pub type TensorEntry = (Vec<usize>, Vec<f32>);
125
126fn take(
127 tensors: &mut BTreeMap<String, TensorEntry>,
128 name: &str,
129 expected: &[usize],
130) -> Result<Vec<f32>, EnhanceError> {
131 let (shape, data) = tensors
132 .remove(name)
133 .ok_or_else(|| EnhanceError::MissingTensor(name.to_owned()))?;
134 if shape != expected {
135 return Err(EnhanceError::ShapeMismatch {
136 name: name.to_owned(),
137 expected: expected.to_vec(),
138 got: shape,
139 });
140 }
141 Ok(data)
142}
143
144fn silu(x: f32) -> f32 {
145 x / (1.0 + (-x).exp())
146}
147
148fn sigmoid(x: f32) -> f32 {
149 1.0 / (1.0 + (-x).exp())
150}
151
152impl Enhancer {
153 pub fn load(mut tensors: BTreeMap<String, TensorEntry>) -> Result<Self, EnhanceError> {
157 let raw = take(
162 &mut tensors,
163 "enc_pre.0.weight",
164 &[CH, 2 * STRIDE, K0 / STRIDE],
165 )?;
166 let bias = take(&mut tensors, "enc_pre.0.bias", &[CH])?;
167 let mut w = vec![0.0f32; CH * 2 * K0];
168 for o in 0..CH {
169 for si in 0..STRIDE {
170 for c in 0..2 {
171 for kk in 0..K0 / STRIDE {
172 let m = kk * STRIDE + si;
173 w[(o * 2 + c) * K0 + m] =
174 raw[(o * (2 * STRIDE) + si * 2 + c) * (K0 / STRIDE) + kk];
175 }
176 }
177 }
178 }
179 let enc_pre = Conv1d {
180 weight: w,
181 bias,
182 out_ch: CH,
183 in_ch: 2,
184 k: K0,
185 };
186
187 let mut encoder = Vec::with_capacity(ENC_CONVS);
188 for i in 0..ENC_CONVS {
189 encoder.push(Conv1d {
190 weight: take(
191 &mut tensors,
192 &format!("encoder.{i}.0.weight"),
193 &[CH, CH, ENC_K],
194 )?,
195 bias: take(&mut tensors, &format!("encoder.{i}.0.bias"), &[CH])?,
196 out_ch: CH,
197 in_ch: CH,
198 k: ENC_K,
199 });
200 }
201
202 let rf_pre_lin = Linear {
203 weight: take(&mut tensors, "rf_pre.0.weight", &[RF_FREQ, F_ENC])?,
204 };
205 let rf_pre_conv = Conv1d {
206 weight: take(&mut tensors, "rf_pre.1.weight", &[RF_CH, CH, 1])?,
207 bias: take(&mut tensors, "rf_pre.1.bias", &[RF_CH])?,
208 out_ch: RF_CH,
209 in_ch: CH,
210 k: 1,
211 };
212
213 let mut blocks = Vec::with_capacity(BLOCKS);
214 for i in 0..BLOCKS {
215 let pe = if i == 0 {
216 Some(take(&mut tensors, "rf_block.0.pe", &[RF_FREQ, RF_CH])?)
217 } else {
218 None
219 };
220 blocks.push(RnnFormerBlock {
221 rnn: GruWeights {
222 weight_ih: take(
223 &mut tensors,
224 &format!("rf_block.{i}.rnn.weight_ih_l0"),
225 &[3 * RF_CH, RF_CH],
226 )?,
227 weight_hh: take(
228 &mut tensors,
229 &format!("rf_block.{i}.rnn.weight_hh_l0"),
230 &[3 * RF_CH, RF_CH],
231 )?,
232 bias_ih: take(
233 &mut tensors,
234 &format!("rf_block.{i}.rnn.bias_ih_l0"),
235 &[3 * RF_CH],
236 )?,
237 bias_hh: take(
238 &mut tensors,
239 &format!("rf_block.{i}.rnn.bias_hh_l0"),
240 &[3 * RF_CH],
241 )?,
242 },
243 rnn_fc: Conv1d {
244 weight: take(
245 &mut tensors,
246 &format!("rf_block.{i}.rnn_fc.weight"),
247 &[RF_CH, RF_CH],
248 )?,
249 bias: take(&mut tensors, &format!("rf_block.{i}.rnn_fc.bias"), &[RF_CH])?,
250 out_ch: RF_CH,
251 in_ch: RF_CH,
252 k: 1,
253 },
254 qkv: Linear {
255 weight: take(
256 &mut tensors,
257 &format!("rf_block.{i}.attn.qkv.weight"),
258 &[3 * RF_CH, RF_CH],
259 )?,
260 },
261 attn_fc: Conv1d {
262 weight: take(
263 &mut tensors,
264 &format!("rf_block.{i}.attn_fc.weight"),
265 &[RF_CH, RF_CH],
266 )?,
267 bias: take(
268 &mut tensors,
269 &format!("rf_block.{i}.attn_fc.bias"),
270 &[RF_CH],
271 )?,
272 out_ch: RF_CH,
273 in_ch: RF_CH,
274 k: 1,
275 },
276 pe,
277 });
278 }
279
280 let rf_post_lin = Linear {
281 weight: take(&mut tensors, "rf_post.0.weight", &[F_ENC, RF_FREQ])?,
282 };
283 let rf_post_conv = Conv1d {
284 weight: take(&mut tensors, "rf_post.1.weight", &[CH, RF_CH, 1])?,
285 bias: take(&mut tensors, "rf_post.1.bias", &[CH])?,
286 out_ch: CH,
287 in_ch: RF_CH,
288 k: 1,
289 };
290
291 let mut decoder = Vec::with_capacity(ENC_CONVS);
292 for i in 0..ENC_CONVS {
293 decoder.push((
294 Conv1d {
295 weight: take(
296 &mut tensors,
297 &format!("decoder.{i}.0.weight"),
298 &[CH, 2 * CH, 1],
299 )?,
300 bias: take(&mut tensors, &format!("decoder.{i}.0.bias"), &[CH])?,
301 out_ch: CH,
302 in_ch: 2 * CH,
303 k: 1,
304 },
305 Conv1d {
306 weight: take(
307 &mut tensors,
308 &format!("decoder.{i}.2.weight"),
309 &[CH, CH, ENC_K],
310 )?,
311 bias: take(&mut tensors, &format!("decoder.{i}.2.bias"), &[CH])?,
312 out_ch: CH,
313 in_ch: CH,
314 k: ENC_K,
315 },
316 ));
317 }
318
319 let dec_post_conv = Conv1d {
320 weight: take(&mut tensors, "dec_post.0.weight", &[CH, 2 * CH, 1])?,
321 bias: take(&mut tensors, "dec_post.0.bias", &[CH])?,
322 out_ch: CH,
323 in_ch: 2 * CH,
324 k: 1,
325 };
326 let dec_post_up = take(&mut tensors, "dec_post.2.weight", &[CH, 2, K0])?;
327 let dec_post_up_bias = take(&mut tensors, "dec_post.2.bias", &[2])?;
328 let window = take(&mut tensors, "buffer.stft.window", &[N_FFT])?;
329
330 Ok(Self {
331 enc_pre,
332 encoder,
333 rf_pre_lin,
334 rf_pre_conv,
335 blocks,
336 rf_post_lin,
337 rf_post_conv,
338 decoder,
339 dec_post_conv,
340 dec_post_up,
341 dec_post_up_bias,
342 window,
343 fft: Fft::new(N_FFT),
344 })
345 }
346
347 pub fn enhance_48k(&self, wav: &[f32]) -> Vec<f32> {
351 if wav.len() < HOP {
355 return Vec::new();
356 }
357 let frames = wav.len() / HOP + 1;
358 let mut state = self.new_state();
359 let mut out = vec![0.0f32; (frames - 1) * HOP + N_FFT];
361 let mut winsq = vec![0.0f32; (frames - 1) * HOP + N_FFT];
362 let mut spec = [0.0f32; 2 * (FREQ + 1)];
363 let mut scratch_time = vec![0.0f32; N_FFT];
364
365 for t in 0..frames {
366 self.frame_spectrum(wav, t, &mut spec, &mut scratch_time);
367 let mut comp = [0.0f32; 2 * FREQ];
369 for f in 0..FREQ {
370 let re = spec[2 * f];
371 let im = spec[2 * f + 1];
372 let mag = (re * re + im * im).sqrt().max(MAG_EPS);
373 let g = mag.powf(COMPRESSION - 1.0);
374 comp[2 * f] = re * g;
375 comp[2 * f + 1] = im * g;
376 }
377 let mask = self.frame_forward(&comp, &mut state);
378 let mut frame_spec = [0.0f32; 2 * (FREQ + 1)];
380 for f in 0..FREQ {
381 let (ar, ai) = (comp[2 * f], comp[2 * f + 1]);
382 let (br, bi) = (mask[2 * f], mask[2 * f + 1]);
383 let re = ar * br - ai * bi;
384 let im = ar * bi + ai * br;
385 let mag = (re * re + im * im).sqrt();
386 let g = if mag > 0.0 {
387 mag.powf(1.0 / COMPRESSION - 1.0)
388 } else {
389 0.0
390 };
391 frame_spec[2 * f] = re * g;
392 frame_spec[2 * f + 1] = im * g;
393 }
394 self.fft.irfft(&frame_spec, &mut scratch_time);
395 let base = t * HOP;
396 for i in 0..N_FFT {
397 out[base + i] += scratch_time[i] * self.window[i];
398 winsq[base + i] += self.window[i] * self.window[i];
399 }
400 }
401
402 let start = N_FFT / 2;
404 let len = (frames - 1) * HOP;
405 let mut result = Vec::with_capacity(len);
406 for i in 0..len {
407 let w = winsq[start + i];
408 result.push(if w > 1.0e-11 { out[start + i] / w } else { 0.0 });
409 }
410 result
411 }
412
413 pub fn enhance_24k(&self, wav24k: &[f32]) -> Vec<f32> {
419 let mut wav48 = resample_lanczos6(wav24k, 24_000, SAMPLE_RATE_HZ);
420 let target_len = wav48.len();
421 let padded = target_len.div_ceil(HOP) * HOP;
423 wav48.resize(padded, 0.0);
424 let mut enhanced = self.enhance_48k(&wav48);
425 enhanced.truncate(target_len);
426 let mut back = resample_lanczos6(&enhanced, SAMPLE_RATE_HZ, 24_000);
427 back.truncate(wav24k.len());
428 back
429 }
430
431 fn new_state(&self) -> Vec<Vec<f32>> {
432 vec![vec![0.0f32; RF_FREQ * RF_CH]; BLOCKS]
433 }
434
435 fn frame_spectrum(&self, wav: &[f32], t: usize, spec: &mut [f32], time: &mut [f32]) {
437 let n = wav.len() as isize;
438 let start = t as isize * HOP as isize - (N_FFT / 2) as isize;
439 for (i, slot) in time.iter_mut().enumerate() {
440 let mut idx = start + i as isize;
441 loop {
444 if idx < 0 {
445 idx = -idx;
446 } else if idx >= n {
447 idx = 2 * (n - 1) - idx;
448 } else {
449 break;
450 }
451 }
452 *slot = wav[idx as usize] * self.window[i];
453 }
454 self.fft.rfft(time, spec);
455 }
456
457 fn frame_forward(&self, comp: &[f32], state: &mut [Vec<f32>]) -> [f32; 2 * FREQ] {
459 let pad = (K0 - STRIDE) / 2;
462 let mut x = vec![0.0f32; CH * F_ENC];
463 for o in 0..CH {
464 let w = &self.enc_pre.weight[o * 2 * K0..(o + 1) * 2 * K0];
465 let b = self.enc_pre.bias[o];
466 for j in 0..F_ENC {
467 let mut acc = b;
468 for m in 0..K0 {
469 let f = (j * STRIDE + m) as isize - pad as isize;
470 if f >= 0 && (f as usize) < FREQ {
471 let f = f as usize;
472 acc += w[m] * comp[2 * f] + w[K0 + m] * comp[2 * f + 1];
473 }
474 }
475 x[o * F_ENC + j] = silu(acc);
476 }
477 }
478
479 let mut skips: Vec<Vec<f32>> = Vec::with_capacity(1 + ENC_CONVS);
481 skips.push(x.clone());
482 for conv in &self.encoder {
483 x = conv_k_same(conv, &x, F_ENC, true);
484 skips.push(x.clone());
485 }
486
487 let mut xf = vec![0.0f32; CH * RF_FREQ];
490 for c in 0..CH {
491 let row = &x[c * F_ENC..(c + 1) * F_ENC];
492 for (fr, slot) in xf[c * RF_FREQ..(c + 1) * RF_FREQ].iter_mut().enumerate() {
493 let w = &self.rf_pre_lin.weight[fr * F_ENC..(fr + 1) * F_ENC];
494 let mut acc = 0.0f32;
495 for f in 0..F_ENC {
496 acc += w[f] * row[f];
497 }
498 *slot = acc;
499 }
500 }
501 let mut tokens = vec![0.0f32; RF_FREQ * RF_CH];
503 for oc in 0..RF_CH {
504 let w = &self.rf_pre_conv.weight[oc * CH..(oc + 1) * CH];
505 let b = self.rf_pre_conv.bias[oc];
506 for fr in 0..RF_FREQ {
507 let mut acc = b;
508 for ic in 0..CH {
509 acc += w[ic] * xf[ic * RF_FREQ + fr];
510 }
511 tokens[fr * RF_CH + oc] = acc;
512 }
513 }
514
515 for (block, h) in self.blocks.iter().zip(state.iter_mut()) {
517 for fr in 0..RF_FREQ {
519 let tok = &mut tokens[fr * RF_CH..(fr + 1) * RF_CH];
520 let hcur = &mut h[fr * RF_CH..(fr + 1) * RF_CH];
521 let mut gi = [0.0f32; 3 * RF_CH];
522 let mut gh = [0.0f32; 3 * RF_CH];
523 for g in 0..3 * RF_CH {
524 let wi = &block.rnn.weight_ih[g * RF_CH..(g + 1) * RF_CH];
525 let wh = &block.rnn.weight_hh[g * RF_CH..(g + 1) * RF_CH];
526 let mut ai = block.rnn.bias_ih[g];
527 let mut ah = block.rnn.bias_hh[g];
528 for c in 0..RF_CH {
529 ai += wi[c] * tok[c];
530 ah += wh[c] * hcur[c];
531 }
532 gi[g] = ai;
533 gh[g] = ah;
534 }
535 let mut rnn_out = [0.0f32; RF_CH];
536 for c in 0..RF_CH {
537 let r = sigmoid(gi[c] + gh[c]);
538 let z = sigmoid(gi[RF_CH + c] + gh[RF_CH + c]);
539 let ncand = (gi[2 * RF_CH + c] + r * gh[2 * RF_CH + c]).tanh();
540 let hnew = (1.0 - z) * ncand + z * hcur[c];
541 hcur[c] = hnew;
542 rnn_out[c] = hnew;
543 }
544 for (c, slot) in tok.iter_mut().enumerate().take(RF_CH) {
546 let w = &block.rnn_fc.weight[c * RF_CH..(c + 1) * RF_CH];
547 let mut acc = block.rnn_fc.bias[c];
548 for i in 0..RF_CH {
549 acc += w[i] * rnn_out[i];
550 }
551 *slot += acc;
552 }
553 }
554
555 if let Some(pe) = &block.pe {
556 for (slot, p) in tokens.iter_mut().zip(pe.iter()) {
557 *slot += p;
558 }
559 }
560
561 let mut qkv = vec![0.0f32; RF_FREQ * 3 * RF_CH];
563 for fr in 0..RF_FREQ {
564 let tok = &tokens[fr * RF_CH..(fr + 1) * RF_CH];
565 for o in 0..3 * RF_CH {
566 let w = &block.qkv.weight[o * RF_CH..(o + 1) * RF_CH];
567 let mut acc = 0.0f32;
568 for c in 0..RF_CH {
569 acc += w[c] * tok[c];
570 }
571 qkv[fr * 3 * RF_CH + o] = acc;
572 }
573 }
574 let scale = 1.0 / (HEAD_DIM as f32).sqrt();
575 let mut attn_out = vec![0.0f32; RF_FREQ * RF_CH];
576 let mut scores = [0.0f32; RF_FREQ];
577 for head in 0..HEADS {
578 let base = head * 3 * HEAD_DIM;
581 for i in 0..RF_FREQ {
582 let q = &qkv[i * 3 * RF_CH + base..i * 3 * RF_CH + base + HEAD_DIM];
583 let mut max = f32::NEG_INFINITY;
584 for (j, s) in scores.iter_mut().enumerate() {
585 let k = &qkv
586 [j * 3 * RF_CH + base + HEAD_DIM..j * 3 * RF_CH + base + 2 * HEAD_DIM];
587 let mut acc = 0.0f32;
588 for d in 0..HEAD_DIM {
589 acc += q[d] * k[d];
590 }
591 *s = acc * scale;
592 max = max.max(*s);
593 }
594 let mut denom = 0.0f32;
595 for s in scores.iter_mut() {
596 *s = (*s - max).exp();
597 denom += *s;
598 }
599 let inv = 1.0 / denom;
600 let out = &mut attn_out
601 [i * RF_CH + head * HEAD_DIM..i * RF_CH + (head + 1) * HEAD_DIM];
602 for (j, s) in scores.iter().enumerate() {
603 let v = &qkv[j * 3 * RF_CH + base + 2 * HEAD_DIM
604 ..j * 3 * RF_CH + base + 3 * HEAD_DIM];
605 let p = *s * inv;
606 for d in 0..HEAD_DIM {
607 out[d] += p * v[d];
608 }
609 }
610 }
611 }
612 for fr in 0..RF_FREQ {
613 let src = &attn_out[fr * RF_CH..(fr + 1) * RF_CH];
614 let tok = &mut tokens[fr * RF_CH..(fr + 1) * RF_CH];
615 for (c, slot) in tok.iter_mut().enumerate() {
616 let w = &block.attn_fc.weight[c * RF_CH..(c + 1) * RF_CH];
617 let mut acc = block.attn_fc.bias[c];
618 for i in 0..RF_CH {
619 acc += w[i] * src[i];
620 }
621 *slot += acc;
622 }
623 }
624 }
625
626 let mut yf = vec![0.0f32; RF_CH * F_ENC];
629 for c in 0..RF_CH {
630 for f in 0..F_ENC {
631 let w = &self.rf_post_lin.weight[f * RF_FREQ..(f + 1) * RF_FREQ];
632 let mut acc = 0.0f32;
633 for fr in 0..RF_FREQ {
634 acc += w[fr] * tokens[fr * RF_CH + c];
635 }
636 yf[c * F_ENC + f] = acc;
637 }
638 }
639 let mut y = vec![0.0f32; CH * F_ENC];
640 for oc in 0..CH {
641 let w = &self.rf_post_conv.weight[oc * RF_CH..(oc + 1) * RF_CH];
642 let b = self.rf_post_conv.bias[oc];
643 for f in 0..F_ENC {
644 let mut acc = b;
645 for ic in 0..RF_CH {
646 acc += w[ic] * yf[ic * F_ENC + f];
647 }
648 y[oc * F_ENC + f] = acc;
649 }
650 }
651
652 for (mix, conv) in &self.decoder {
654 let skip = skips.pop().expect("one skip per decoder stage");
655 y = concat_mix(mix, &y, &skip, F_ENC);
656 y = conv_k_same(conv, &y, F_ENC, true);
657 }
658
659 let skip = skips.pop().expect("enc_pre skip");
661 let z = concat_mix(&self.dec_post_conv, &y, &skip, F_ENC);
662 let mut mask = [0.0f32; 2 * FREQ];
663 let pad = (K0 - STRIDE) / 2;
666 for f in 0..FREQ {
667 mask[2 * f] = self.dec_post_up_bias[0];
668 mask[2 * f + 1] = self.dec_post_up_bias[1];
669 }
670 for c in 0..CH {
671 let wrow = &self.dec_post_up[c * 2 * K0..(c + 1) * 2 * K0];
672 for j in 0..F_ENC {
673 let zv = z[c * F_ENC + j];
674 if zv == 0.0 {
675 continue;
676 }
677 let base = j * STRIDE;
678 for m in 0..K0 {
679 let p = base + m;
680 if p < pad || p - pad >= FREQ {
681 continue;
682 }
683 let p = p - pad;
684 mask[2 * p] += wrow[m] * zv;
685 mask[2 * p + 1] += wrow[K0 + m] * zv;
686 }
687 }
688 }
689 mask
690 }
691}
692
693#[must_use]
697pub fn resample_lanczos6(mono: &[f32], from_rate: u32, to_rate: u32) -> Vec<f32> {
698 if from_rate == to_rate {
699 return mono.to_vec();
700 }
701 const LOBES: f64 = 6.0;
702 let ratio = f64::from(to_rate) / f64::from(from_rate);
703 let cutoff = ratio.min(1.0);
704 let half = (LOBES / cutoff).ceil() as isize;
705 let out_len = ((mono.len() as f64) * ratio).round() as usize;
706
707 let mut out = Vec::with_capacity(out_len);
708 for index in 0..out_len {
709 let center = index as f64 / ratio;
710 let first = center.floor() as isize - half + 1;
711 let mut acc = 0.0_f64;
712 let mut norm = 0.0_f64;
713 for tap in first..first + 2 * half {
714 if tap < 0 {
715 continue;
716 }
717 let Some(sample) = mono.get(tap as usize) else {
718 break;
719 };
720 let weight = lanczos6_tap(center - tap as f64, cutoff);
721 acc += weight * f64::from(*sample);
722 norm += weight;
723 }
724 out.push(if norm.abs() > 1e-12 {
725 (acc / norm) as f32
726 } else {
727 0.0
728 });
729 }
730 out
731}
732
733fn lanczos6_tap(distance: f64, cutoff: f64) -> f64 {
734 const LOBES: f64 = 6.0;
735 let x = distance * cutoff;
736 if x.abs() >= LOBES {
737 return 0.0;
738 }
739 let sinc = |v: f64| {
740 if v.abs() < 1e-12 {
741 1.0
742 } else {
743 let p = std::f64::consts::PI * v;
744 p.sin() / p
745 }
746 };
747 sinc(x) * sinc(x / LOBES)
748}
749
750fn conv_k_same(conv: &Conv1d, x: &[f32], width: usize, act: bool) -> Vec<f32> {
752 let mut out = vec![0.0f32; conv.out_ch * width];
753 let pad = (conv.k - 1) / 2;
754 for o in 0..conv.out_ch {
755 let orow = &mut out[o * width..(o + 1) * width];
756 for slot in orow.iter_mut() {
757 *slot = conv.bias[o];
758 }
759 for c in 0..conv.in_ch {
760 let w = &conv.weight[(o * conv.in_ch + c) * conv.k..(o * conv.in_ch + c + 1) * conv.k];
761 let xrow = &x[c * width..(c + 1) * width];
762 for (m, &wv) in w.iter().enumerate() {
763 let shift = m as isize - pad as isize;
764 let (dst_start, src_start) = if shift < 0 {
765 ((-shift) as usize, 0usize)
766 } else {
767 (0usize, shift as usize)
768 };
769 let count = width - dst_start.max(src_start);
770 for i in 0..count {
771 orow[dst_start + i] += wv * xrow[src_start + i];
772 }
773 }
774 }
775 if act {
776 for slot in orow.iter_mut() {
777 *slot = silu(*slot);
778 }
779 }
780 }
781 out
782}
783
784fn concat_mix(conv: &Conv1d, x: &[f32], skip: &[f32], width: usize) -> Vec<f32> {
786 let half = conv.in_ch / 2;
787 let mut out = vec![0.0f32; conv.out_ch * width];
788 for o in 0..conv.out_ch {
789 let w = &conv.weight[o * conv.in_ch..(o + 1) * conv.in_ch];
790 let orow = &mut out[o * width..(o + 1) * width];
791 for slot in orow.iter_mut() {
792 *slot = conv.bias[o];
793 }
794 for c in 0..half {
795 let wv = w[c];
796 let xrow = &x[c * width..(c + 1) * width];
797 for (slot, xv) in orow.iter_mut().zip(xrow.iter()) {
798 *slot += wv * xv;
799 }
800 }
801 for c in 0..half {
802 let wv = w[half + c];
803 let srow = &skip[c * width..(c + 1) * width];
804 for (slot, sv) in orow.iter_mut().zip(srow.iter()) {
805 *slot += wv * sv;
806 }
807 }
808 for slot in orow.iter_mut() {
809 *slot = silu(*slot);
810 }
811 }
812 out
813}
814
815struct Fft {
821 n: usize,
822 tw_re: Vec<f32>,
824 tw_im: Vec<f32>,
825 rev: Vec<u32>,
826}
827
828impl Fft {
829 fn new(n: usize) -> Self {
830 assert!(n.is_power_of_two());
831 let mut tw_re = Vec::with_capacity(n / 2);
832 let mut tw_im = Vec::with_capacity(n / 2);
833 for k in 0..n / 2 {
834 let ang = -2.0 * std::f64::consts::PI * k as f64 / n as f64;
835 tw_re.push(ang.cos() as f32);
836 tw_im.push(ang.sin() as f32);
837 }
838 let bits = n.trailing_zeros();
839 let rev = (0..n as u32)
840 .map(|i| i.reverse_bits() >> (32 - bits))
841 .collect();
842 Self {
843 n,
844 tw_re,
845 tw_im,
846 rev,
847 }
848 }
849
850 fn fft_complex(&self, buf: &mut [f32], inverse: bool) {
853 let n = self.n;
854 for i in 0..n {
855 let j = self.rev[i] as usize;
856 if i < j {
857 buf.swap(2 * i, 2 * j);
858 buf.swap(2 * i + 1, 2 * j + 1);
859 }
860 }
861 let mut len = 2;
862 while len <= n {
863 let half = len / 2;
864 let step = n / len;
865 let mut start = 0;
866 while start < n {
867 for k in 0..half {
868 let wre = self.tw_re[k * step];
869 let wim = if inverse {
870 -self.tw_im[k * step]
871 } else {
872 self.tw_im[k * step]
873 };
874 let a = start + k;
875 let b = a + half;
876 let (bre, bim) = (buf[2 * b], buf[2 * b + 1]);
877 let tre = bre * wre - bim * wim;
878 let tim = bre * wim + bim * wre;
879 let (are, aim) = (buf[2 * a], buf[2 * a + 1]);
880 buf[2 * a] = are + tre;
881 buf[2 * a + 1] = aim + tim;
882 buf[2 * b] = are - tre;
883 buf[2 * b + 1] = aim - tim;
884 }
885 start += len;
886 }
887 len *= 2;
888 }
889 }
890
891 fn rfft(&self, time: &[f32], spec: &mut [f32]) {
893 let n = self.n;
894 let mut buf = vec![0.0f32; 2 * n];
895 for (i, &v) in time.iter().enumerate() {
896 buf[2 * i] = v;
897 }
898 self.fft_complex(&mut buf, false);
899 spec[..2 * (n / 2 + 1)].copy_from_slice(&buf[..2 * (n / 2 + 1)]);
900 }
901
902 fn irfft(&self, spec: &[f32], time: &mut [f32]) {
904 let n = self.n;
905 let mut buf = vec![0.0f32; 2 * n];
906 buf[..2 * (n / 2 + 1)].copy_from_slice(&spec[..2 * (n / 2 + 1)]);
907 for k in 1..n / 2 {
908 buf[2 * (n - k)] = spec[2 * k];
909 buf[2 * (n - k) + 1] = -spec[2 * k + 1];
910 }
911 self.fft_complex(&mut buf, true);
912 let inv = 1.0 / n as f32;
913 for (i, slot) in time.iter_mut().enumerate() {
914 *slot = buf[2 * i] * inv;
915 }
916 }
917}
918
919#[cfg(test)]
920mod tests {
921 use super::*;
922
923 fn zero_weight_enhancer() -> Enhancer {
924 let mut tensors = BTreeMap::new();
925 let mut put = |name: &str, shape: &[usize]| {
926 let count: usize = shape.iter().product();
927 tensors.insert(name.to_owned(), (shape.to_vec(), vec![0.0f32; count]));
928 };
929 put("enc_pre.0.weight", &[CH, 2 * STRIDE, K0 / STRIDE]);
930 put("enc_pre.0.bias", &[CH]);
931 for i in 0..ENC_CONVS {
932 put(&format!("encoder.{i}.0.weight"), &[CH, CH, ENC_K]);
933 put(&format!("encoder.{i}.0.bias"), &[CH]);
934 put(&format!("decoder.{i}.0.weight"), &[CH, 2 * CH, 1]);
935 put(&format!("decoder.{i}.0.bias"), &[CH]);
936 put(&format!("decoder.{i}.2.weight"), &[CH, CH, ENC_K]);
937 put(&format!("decoder.{i}.2.bias"), &[CH]);
938 }
939 put("rf_pre.0.weight", &[RF_FREQ, F_ENC]);
940 put("rf_pre.1.weight", &[RF_CH, CH, 1]);
941 put("rf_pre.1.bias", &[RF_CH]);
942 put("rf_block.0.pe", &[RF_FREQ, RF_CH]);
943 for i in 0..BLOCKS {
944 put(
945 &format!("rf_block.{i}.rnn.weight_ih_l0"),
946 &[3 * RF_CH, RF_CH],
947 );
948 put(
949 &format!("rf_block.{i}.rnn.weight_hh_l0"),
950 &[3 * RF_CH, RF_CH],
951 );
952 put(&format!("rf_block.{i}.rnn.bias_ih_l0"), &[3 * RF_CH]);
953 put(&format!("rf_block.{i}.rnn.bias_hh_l0"), &[3 * RF_CH]);
954 put(&format!("rf_block.{i}.rnn_fc.weight"), &[RF_CH, RF_CH]);
955 put(&format!("rf_block.{i}.rnn_fc.bias"), &[RF_CH]);
956 put(
957 &format!("rf_block.{i}.attn.qkv.weight"),
958 &[3 * RF_CH, RF_CH],
959 );
960 put(&format!("rf_block.{i}.attn_fc.weight"), &[RF_CH, RF_CH]);
961 put(&format!("rf_block.{i}.attn_fc.bias"), &[RF_CH]);
962 }
963 put("rf_post.0.weight", &[F_ENC, RF_FREQ]);
964 put("rf_post.1.weight", &[CH, RF_CH, 1]);
965 put("rf_post.1.bias", &[CH]);
966 put("dec_post.0.weight", &[CH, 2 * CH, 1]);
967 put("dec_post.0.bias", &[CH]);
968 put("dec_post.2.weight", &[CH, 2, K0]);
969 put("dec_post.2.bias", &[2]);
970 put("buffer.stft.window", &[N_FFT]);
971 Enhancer::load(tensors).expect("all shapes present")
972 }
973
974 #[test]
978 fn tiny_inputs_terminate_and_keep_their_length() {
979 let enhancer = zero_weight_enhancer();
980 assert!(enhancer.enhance_48k(&[]).is_empty());
981 assert!(enhancer.enhance_48k(&[0.25]).is_empty());
982 assert!(enhancer.enhance_48k(&[0.25; 100]).is_empty());
983 assert!(enhancer.enhance_24k(&[]).is_empty());
984 assert_eq!(enhancer.enhance_24k(&[0.25; 50]).len(), 50);
985 assert_eq!(enhancer.enhance_24k(&[0.25; 2_400]).len(), 2_400);
988 assert_eq!(enhancer.enhance_48k(&[0.25; 1024]).len(), 1024);
989 }
990
991 #[test]
992 fn fft_round_trip_recovers_the_signal() {
993 let fft = Fft::new(1024);
994 let time: Vec<f32> = (0..1024)
995 .map(|i| (i as f32 * 0.013).sin() + 0.3 * (i as f32 * 0.21).cos())
996 .collect();
997 let mut spec = vec![0.0f32; 2 * 513];
998 let mut back = vec![0.0f32; 1024];
999 fft.rfft(&time, &mut spec);
1000 fft.irfft(&spec, &mut back);
1001 for (a, b) in time.iter().zip(back.iter()) {
1002 assert!((a - b).abs() < 1.0e-4, "{a} vs {b}");
1003 }
1004 }
1005
1006 #[test]
1007 fn fft_matches_the_dft_definition() {
1008 let n = 16;
1009 let fft = Fft::new(n);
1010 let time: Vec<f32> = (0..n).map(|i| (i as f32 * 0.7).sin()).collect();
1011 let mut spec = vec![0.0f32; 2 * (n / 2 + 1)];
1012 fft.rfft(&time, &mut spec);
1013 for k in 0..=n / 2 {
1014 let mut re = 0.0f64;
1015 let mut im = 0.0f64;
1016 for (i, &v) in time.iter().enumerate() {
1017 let ang = -2.0 * std::f64::consts::PI * (k * i) as f64 / n as f64;
1018 re += v as f64 * ang.cos();
1019 im += v as f64 * ang.sin();
1020 }
1021 assert!((spec[2 * k] as f64 - re).abs() < 1.0e-3, "bin {k} re");
1022 assert!((spec[2 * k + 1] as f64 - im).abs() < 1.0e-3, "bin {k} im");
1023 }
1024 }
1025}