1use crate::dit::Proj;
31use crate::pool::{Pool, SendMut};
32use cortiq_core::CmfModel;
33use serde_json::Value;
34use std::collections::HashMap;
35use std::path::Path;
36use std::sync::Arc;
37
38pub const AUDIO_PAD_ID: u32 = 151669;
40pub const AUDIO_START_ID: u32 = 151673;
42pub const AUDIO_END_ID: u32 = 151674;
44
45pub const SAMPLE_RATE: u32 = 24_000;
47pub const N_FFT: usize = 960;
49pub const HOP: usize = 240;
51pub const N_MELS: usize = 128;
53pub const SEGMENT_FRAMES: usize = 6000;
55pub const MEL_FLOOR: f64 = 1e-7;
57
58#[derive(Clone, Debug)]
62pub struct Wav {
63 pub sample_rate: u32,
64 pub channels: Vec<Vec<f32>>,
65}
66
67impl Wav {
68 pub fn frames(&self) -> usize {
69 self.channels.first().map_or(0, Vec::len)
70 }
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74enum SampleKind {
75 Pcm,
77 Float,
79}
80
81pub fn decode_wav(bytes: &[u8]) -> Result<Wav, String> {
91 if bytes.len() < 12 || &bytes[0..4] != b"RIFF" || &bytes[8..12] != b"WAVE" {
92 return Err("audio: not a RIFF/WAVE file (only WAV input is supported)".into());
93 }
94 let le16 = |b: &[u8], o: usize| u16::from_le_bytes([b[o], b[o + 1]]);
95 let le32 = |b: &[u8], o: usize| u32::from_le_bytes([b[o], b[o + 1], b[o + 2], b[o + 3]]);
96 let mut fmt: Option<(SampleKind, usize, u32, usize)> = None; let mut data: Option<&[u8]> = None;
98 let mut pos = 12usize;
99 while pos + 8 <= bytes.len() {
100 let id = &bytes[pos..pos + 4];
101 let size = le32(bytes, pos + 4) as usize;
102 let start = pos + 8;
103 let end = start.saturating_add(size).min(bytes.len());
104 let body = &bytes[start..end];
105 match id {
106 b"fmt " => {
107 if body.len() < 16 {
108 return Err("audio: WAV fmt chunk is shorter than 16 bytes".into());
109 }
110 let mut tag = le16(body, 0);
111 let channels = le16(body, 2) as usize;
112 let rate = le32(body, 4);
113 let block_align = le16(body, 12) as usize;
114 let bits = le16(body, 14) as usize;
115 if tag == 0xFFFE {
116 if body.len() < 40 {
117 return Err("audio: WAVE_FORMAT_EXTENSIBLE fmt chunk is truncated".into());
118 }
119 tag = le16(body, 24);
121 }
122 let kind = match tag {
123 1 => SampleKind::Pcm,
124 3 => SampleKind::Float,
125 other => {
126 return Err(format!(
127 "audio: WAV format tag {other:#06x} is not supported (PCM and IEEE float only)"
128 ));
129 }
130 };
131 if channels == 0 {
132 return Err("audio: WAV declares zero channels".into());
133 }
134 if rate == 0 {
135 return Err("audio: WAV declares a zero sample rate".into());
136 }
137 let container = if block_align > 0 && block_align % channels == 0 {
138 block_align / channels
139 } else {
140 bits.div_ceil(8)
141 };
142 let ok = match kind {
143 SampleKind::Pcm => matches!(container, 1..=4),
144 SampleKind::Float => matches!(container, 4 | 8),
145 };
146 if !ok {
147 return Err(format!(
148 "audio: WAV {kind:?} with {container}-byte samples ({bits} bits) is not supported"
149 ));
150 }
151 fmt = Some((kind, channels, rate, container));
152 }
153 b"data" => {
154 data = Some(body);
155 if fmt.is_some() {
156 break;
157 }
158 }
159 _ => {}
160 }
161 pos = start.saturating_add(size).saturating_add(size & 1);
163 }
164 let (kind, nch, rate, cb) = fmt.ok_or("audio: WAV has no fmt chunk")?;
165 let data = data.ok_or("audio: WAV has no data chunk")?;
166 let frame = nch * cb;
167 let frames = data.len() / frame;
168 let mut channels = vec![Vec::with_capacity(frames); nch];
169 for f in 0..frames {
170 for (c, ch) in channels.iter_mut().enumerate() {
171 let o = f * frame + c * cb;
172 let s = &data[o..o + cb];
173 let v = match (kind, cb) {
174 (SampleKind::Pcm, 1) => (s[0] as f32 - 128.0) / 128.0,
175 (SampleKind::Pcm, 2) => i16::from_le_bytes([s[0], s[1]]) as f32 / 32768.0,
176 (SampleKind::Pcm, 3) => {
177 let v = i32::from_le_bytes([0, s[0], s[1], s[2]]) >> 8;
178 v as f32 / 8_388_608.0
179 }
180 (SampleKind::Pcm, 4) => {
181 (i32::from_le_bytes([s[0], s[1], s[2], s[3]]) as f64 / 2_147_483_648.0) as f32
182 }
183 (SampleKind::Float, 4) => f32::from_le_bytes([s[0], s[1], s[2], s[3]]),
184 (SampleKind::Float, 8) => {
185 f64::from_le_bytes([s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]]) as f32
186 }
187 _ => unreachable!("container width validated above"),
188 };
189 ch.push(v);
190 }
191 }
192 Ok(Wav {
193 sample_rate: rate,
194 channels,
195 })
196}
197
198fn gcd(mut a: u64, mut b: u64) -> u64 {
201 while b != 0 {
202 (a, b) = (b, a % b);
203 }
204 a
205}
206
207pub fn sinc_resample_kernel(orig: usize, new: usize) -> (Vec<f32>, usize) {
218 const WIDTH: f64 = 6.0;
219 const ROLLOFF: f64 = 0.99;
220 let base_freq = (orig.min(new) as f64) * ROLLOFF;
221 let width = (WIDTH * orig as f64 / base_freq).ceil() as usize;
222 let taps = 2 * width + orig;
223 let scale = base_freq / orig as f64;
224 let mut kernel = vec![0f32; new * taps];
225 for j in 0..new {
226 let phase = (-(j as f32) / new as f32) as f64;
227 for i in 0..taps {
228 let idx = (i as f64 - width as f64) / orig as f64;
229 let mut t = (phase + idx) * base_freq;
230 t = t.clamp(-WIDTH, WIDTH);
231 let window = {
232 let c = (t * std::f64::consts::PI / WIDTH / 2.0).cos();
233 c * c
234 };
235 let t = t * std::f64::consts::PI;
236 let sinc = if t == 0.0 { 1.0 } else { t.sin() / t };
237 kernel[j * taps + i] = (sinc * (window * scale)) as f32;
238 }
239 }
240 (kernel, width)
241}
242
243pub fn resampled_len(n: usize, orig: u32, new: u32) -> usize {
248 if orig == new {
249 return n;
250 }
251 let g = gcd(orig as u64, new as u64);
252 let (o, nw) = ((orig as u64 / g) as f64, (new as u64 / g) as f64);
253 ((nw * n as f64 / o) as f32).ceil() as usize
254}
255
256pub fn resample_sinc(x: &[f32], orig: u32, new: u32) -> Vec<f32> {
261 if orig == new {
262 return x.to_vec();
263 }
264 let g = gcd(orig as u64, new as u64);
265 let o = (orig as u64 / g) as usize;
266 let nw = (new as u64 / g) as usize;
267 let (kernel, width) = sinc_resample_kernel(o, nw);
268 let taps = 2 * width + o;
269 let len = x.len();
270 let frames = len / o + 1;
271 let target = resampled_len(len, orig, new).min(frames * nw);
272 let mut out = vec![0f32; target];
273 for (n_out, dst) in out.iter_mut().enumerate() {
275 let (f, j) = (n_out / nw, n_out % nw);
276 let krow = &kernel[j * taps..(j + 1) * taps];
277 let base = (f * o) as isize - width as isize;
278 let mut acc = 0f64;
279 for (m, &kv) in krow.iter().enumerate() {
280 let p = base + m as isize;
281 if p >= 0 && (p as usize) < len {
282 acc += kv as f64 * x[p as usize] as f64;
283 }
284 }
285 *dst = acc as f32;
286 }
287 out
288}
289
290pub fn wav_to_mono_24k(wav: &Wav) -> Result<Vec<f32>, String> {
293 if wav.channels.is_empty() {
294 return Err("audio: WAV has no channels".into());
295 }
296 let chans: Vec<Vec<f32>> = wav
297 .channels
298 .iter()
299 .map(|c| resample_sinc(c, wav.sample_rate, SAMPLE_RATE))
300 .collect();
301 if chans.len() == 1 {
302 return Ok(chans.into_iter().next().unwrap());
303 }
304 let n = chans[0].len();
305 let inv = chans.len() as f32;
306 Ok((0..n)
307 .map(|i| chans.iter().map(|c| c[i]).sum::<f32>() / inv)
308 .collect())
309}
310
311struct Fft {
315 n: usize,
316 factors: Vec<usize>,
317 tw: Vec<(f64, f64)>,
319}
320
321impl Fft {
322 fn new(n: usize) -> Self {
323 let mut factors = Vec::new();
324 let mut m = n;
325 for p in [4usize, 2, 3, 5] {
326 while m % p == 0 {
327 factors.push(p);
328 m /= p;
329 }
330 }
331 let mut p = 7;
332 while m > 1 {
333 while m % p == 0 {
334 factors.push(p);
335 m /= p;
336 }
337 p += 2;
338 }
339 let tw = (0..n)
340 .map(|k| {
341 let a = -2.0 * std::f64::consts::PI * k as f64 / n as f64;
342 (a.cos(), a.sin())
343 })
344 .collect();
345 Self { n, factors, tw }
346 }
347
348 fn forward_real(&self, x: &[f64], out: &mut [(f64, f64)]) {
350 debug_assert_eq!(x.len(), self.n);
351 debug_assert_eq!(out.len(), self.n);
352 let cx: Vec<(f64, f64)> = x.iter().map(|&v| (v, 0.0)).collect();
353 self.rec(&cx, 0, 1, out, self.n, 0);
354 }
355
356 fn rec(
359 &self,
360 input: &[(f64, f64)],
361 off: usize,
362 stride: usize,
363 out: &mut [(f64, f64)],
364 n: usize,
365 level: usize,
366 ) {
367 if n == 1 {
368 out[0] = input[off];
369 return;
370 }
371 let p = self.factors[level];
372 let m = n / p;
373 for r in 0..p {
374 self.rec(
375 input,
376 off + r * stride,
377 stride * p,
378 &mut out[r * m..(r + 1) * m],
379 m,
380 level + 1,
381 );
382 }
383 let big = self.n;
387 let step_n = big / n;
388 let step_p = big / p;
389 let mut t = [(0f64, 0f64); 16];
390 let mut tmp = vec![(0f64, 0f64); if p > 16 { p } else { 0 }];
391 let buf: &mut [(f64, f64)] = if p > 16 { &mut tmp } else { &mut t[..p] };
392 for k in 0..m {
393 for r in 0..p {
394 let s = out[r * m + k];
395 let w = self.tw[(r * k * step_n) % big];
396 buf[r] = (s.0 * w.0 - s.1 * w.1, s.0 * w.1 + s.1 * w.0);
397 }
398 for q in 0..p {
399 let (mut re, mut im) = (0f64, 0f64);
400 for (r, b) in buf.iter().enumerate() {
401 let w = self.tw[(r * q * step_p) % big];
402 re += b.0 * w.0 - b.1 * w.1;
403 im += b.0 * w.1 + b.1 * w.0;
404 }
405 out[k + m * q] = (re, im);
406 }
407 }
408 }
409}
410
411pub fn mel_filterbank(n_freqs: usize, n_mels: usize, sample_rate: u32) -> Vec<f64> {
414 let f_max = (sample_rate / 2) as f64;
415 let hz_to_mel = |f: f64| 2595.0 * (1.0 + f / 700.0).log10();
416 let mel_to_hz = |m: f64| 700.0 * (10f64.powf(m / 2595.0) - 1.0);
417 let (m_min, m_max) = (hz_to_mel(0.0), hz_to_mel(f_max));
418 let f_pts: Vec<f64> = (0..n_mels + 2)
419 .map(|i| mel_to_hz(m_min + (m_max - m_min) * i as f64 / (n_mels + 1) as f64))
420 .collect();
421 let f_diff: Vec<f64> = f_pts.windows(2).map(|w| w[1] - w[0]).collect();
422 let mut fb = vec![0f64; n_freqs * n_mels];
423 for k in 0..n_freqs {
424 let freq = f_max * k as f64 / (n_freqs - 1) as f64;
425 for m in 0..n_mels {
426 let down = -(f_pts[m] - freq) / f_diff[m];
427 let up = (f_pts[m + 2] - freq) / f_diff[m + 1];
428 fb[k * n_mels + m] = down.min(up).max(0.0);
429 }
430 }
431 fb
432}
433
434const TORCH_HANN_960_FIXUPS: [(usize, u32); 27] = [
441 (30, 0x3C1D6820),
442 (42, 0x3C99C880),
443 (70, 0x3D533468),
444 (87, 0x3DA191D0),
445 (99, 0x3DCF8B30),
446 (109, 0x3DF9B6B0),
447 (125, 0x3E220032),
448 (166, 0x3E88CD7D),
449 (172, 0x3E91CA07),
450 (315, 0x3F3C56BC),
451 (331, 0x3F47CEDC),
452 (338, 0x3F4C95E8),
453 (384, 0x3F678DDE),
454 (466, 0x3F7F7689),
455 (468, 0x3F7F9AFC),
456 (522, 0x3F7B31BC),
457 (558, 0x3F6FADF2),
458 (582, 0x3F648543),
459 (639, 0x3F40B962),
460 (661, 0x3F30355E),
461 (695, 0x3F14D9C0),
462 (857, 0x3DE00070),
463 (859, 0x3DD7B41C),
464 (866, 0x3DBBC250),
465 (871, 0x3DA8DEB4),
466 (924, 0x3C625860),
467 (940, 0x3B8C2B00),
468];
469
470fn torch_hann_960() -> Vec<f64> {
474 let step = (2.0 * std::f64::consts::PI / N_FFT as f64) as f32;
475 let mut w: Vec<f32> = (0..N_FFT)
476 .map(|i| {
477 let c = ((i as f32 * step) as f64).cos() as f32;
478 c * -0.5f32 + 0.5f32
479 })
480 .collect();
481 for (i, bits) in TORCH_HANN_960_FIXUPS {
482 w[i] = f32::from_bits(bits);
483 }
484 w.into_iter().map(f64::from).collect()
485}
486
487pub fn mel_frames(n: usize) -> usize {
489 1 + n / HOP
490}
491
492pub fn log_mel(wave: &[f32], pool: Option<&Pool>) -> Result<(Vec<f32>, usize), String> {
496 let n = wave.len();
497 let pad = N_FFT / 2;
498 if n <= pad {
499 return Err(format!(
500 "audio: {n} samples at 24 kHz is too short (the STFT reflect pad needs more than {pad})"
501 ));
502 }
503 let frames = mel_frames(n);
504 let n_freqs = N_FFT / 2 + 1;
505 let fb = mel_filterbank(n_freqs, N_MELS, SAMPLE_RATE);
506 let bands: Vec<(usize, Vec<f64>)> = (0..N_MELS)
508 .map(|m| {
509 let nz: Vec<usize> = (0..n_freqs)
510 .filter(|&k| fb[k * N_MELS + m] != 0.0)
511 .collect();
512 match (nz.first(), nz.last()) {
513 (Some(&a), Some(&b)) => (a, (a..=b).map(|k| fb[k * N_MELS + m]).collect()),
514 _ => (0, Vec::new()),
515 }
516 })
517 .collect();
518 let window = torch_hann_960();
519 let reflect = |p: isize| -> f32 {
520 let last = n as isize - 1;
521 let mut i = p - pad as isize;
522 if i < 0 {
523 i = -i;
524 }
525 if i > last {
526 i = 2 * last - i;
527 }
528 wave[i as usize]
529 };
530 let fft = Fft::new(N_FFT);
531 let mut out = vec![0f32; frames * N_MELS];
532 let dst = SendMut::new(out.as_mut_ptr());
533 let run = |start: usize, end: usize| {
534 let mut buf = vec![0f64; N_FFT];
535 let mut spec = vec![(0f64, 0f64); N_FFT];
536 let mut mag = vec![0f64; n_freqs];
537 for t in start..end {
538 for (i, b) in buf.iter_mut().enumerate() {
539 *b = reflect((t * HOP + i) as isize) as f64 * window[i];
540 }
541 fft.forward_real(&buf, &mut spec);
542 for k in 0..n_freqs {
543 mag[k] = (spec[k].0 * spec[k].0 + spec[k].1 * spec[k].1).sqrt();
544 }
545 for (m, (k0, w)) in bands.iter().enumerate() {
546 let mut acc = 0f64;
547 for (i, wv) in w.iter().enumerate() {
548 acc += wv * mag[k0 + i];
549 }
550 unsafe { *dst.at(t * N_MELS + m) = acc.max(MEL_FLOOR).ln() as f32 };
551 }
552 }
553 };
554 match pool {
555 Some(p) if frames >= 64 => p.run_rows(frames, &run),
556 _ => run(0, frames),
557 }
558 Ok((out, frames))
559}
560
561pub fn segment_lengths(mel_frames: usize) -> Vec<usize> {
565 let mut v = vec![SEGMENT_FRAMES; mel_frames / SEGMENT_FRAMES];
566 if mel_frames % SEGMENT_FRAMES > 0 {
567 v.push(mel_frames % SEGMENT_FRAMES);
568 }
569 v
570}
571
572pub fn segment_codes(m: usize) -> usize {
575 m.div_ceil(2).div_ceil(2)
576}
577
578pub fn codes_for_mel(mel_frames: usize) -> usize {
580 segment_lengths(mel_frames)
581 .into_iter()
582 .map(segment_codes)
583 .sum()
584}
585
586pub fn audio_token_count(mel_frames: usize, group: usize) -> usize {
590 mel_frames.div_ceil(2).div_ceil(2).div_ceil(group)
591}
592
593pub fn audio_placeholder_ids(k: usize) -> Vec<u32> {
595 let mut v = Vec::with_capacity(k + 2);
596 v.push(AUDIO_START_ID);
597 v.extend(std::iter::repeat_n(AUDIO_PAD_ID, k));
598 v.push(AUDIO_END_ID);
599 v
600}
601
602pub fn expand_audio_placeholders(ids: &[u32], counts: &[usize]) -> Result<Vec<u32>, String> {
606 let mut out = Vec::with_capacity(ids.len() + counts.iter().sum::<usize>());
607 let mut used = 0usize;
608 let mut i = 0usize;
609 while i < ids.len() {
610 if ids[i] == AUDIO_START_ID
611 && ids.get(i + 1) == Some(&AUDIO_PAD_ID)
612 && ids.get(i + 2) == Some(&AUDIO_END_ID)
613 {
614 let k = *counts.get(used).ok_or_else(|| {
615 format!(
616 "audio: the prompt has more audio placeholders than the {} audio input(s)",
617 counts.len()
618 )
619 })?;
620 out.extend(audio_placeholder_ids(k));
621 used += 1;
622 i += 3;
623 continue;
624 }
625 if ids[i] == AUDIO_PAD_ID {
626 return Err(format!(
627 "audio: <|audio_pad|> at position {i} is not inside a <|mimo_audio_start|>…<|mimo_audio_end|> triple"
628 ));
629 }
630 out.push(ids[i]);
631 i += 1;
632 }
633 if used != counts.len() {
634 return Err(format!(
635 "audio: {} audio input(s) but {used} placeholder(s) in the prompt",
636 counts.len()
637 ));
638 }
639 Ok(out)
640}
641
642fn erfc(x: f64) -> f64 {
647 let z = x.abs();
648 let t = 1.0 / (1.0 + 0.5 * z);
649 let ans = t
650 * (-z * z - 1.265_512_23
651 + t * (1.000_023_68
652 + t * (0.374_091_96
653 + t * (0.096_784_18
654 + t * (-0.186_288_06
655 + t * (0.278_868_07
656 + t * (-1.135_203_98
657 + t * (1.488_515_87
658 + t * (-0.822_152_23 + t * 0.170_872_77)))))))))
659 .exp();
660 if x >= 0.0 { ans } else { 2.0 - ans }
661}
662
663#[inline]
666fn gelu(v: f32) -> f32 {
667 (0.5 * v as f64 * erfc(-(v as f64) * std::f64::consts::FRAC_1_SQRT_2)) as f32
668}
669
670fn gelu_inplace(x: &mut [f32]) {
671 for v in x {
672 *v = gelu(*v);
673 }
674}
675
676fn layer_norm_rows(x: &[f32], w: &[f32], b: &[f32], eps: f64, dst: &mut [f32]) {
677 let d = w.len();
678 for (xr, dr) in x.chunks_exact(d).zip(dst.chunks_exact_mut(d)) {
679 let mean = xr.iter().map(|&v| v as f64).sum::<f64>() / d as f64;
680 let var = xr.iter().map(|&v| (v as f64 - mean).powi(2)).sum::<f64>() / d as f64;
681 let inv = 1.0 / (var + eps).sqrt();
682 for i in 0..d {
683 dr[i] = ((xr[i] as f64 - mean) * inv * w[i] as f64 + b[i] as f64) as f32;
684 }
685 }
686}
687
688fn rms_norm_rows(x: &[f32], w: &[f32], eps: f64, dst: &mut [f32]) {
690 let d = w.len();
691 for (xr, dr) in x.chunks_exact(d).zip(dst.chunks_exact_mut(d)) {
692 let ms = xr.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / d as f64;
693 let inv = 1.0 / (ms + eps).sqrt();
694 for i in 0..d {
695 dr[i] = w[i] * ((xr[i] as f64 * inv) as f32);
696 }
697 }
698}
699
700fn add_bias(x: &mut [f32], b: &[f32]) {
701 for row in x.chunks_exact_mut(b.len()) {
702 for (v, bb) in row.iter_mut().zip(b) {
703 *v += bb;
704 }
705 }
706}
707
708fn add_into(x: &mut [f32], y: &[f32]) {
709 for (a, b) in x.iter_mut().zip(y) {
710 *a += b;
711 }
712}
713
714fn rope_tables(base: f64, head_dim: usize, positions: usize) -> (Vec<f32>, Vec<f32>) {
719 let half = head_dim / 2;
720 let inv: Vec<f32> = (0..half)
721 .map(|i| {
722 let e = (2 * i) as f32 / head_dim as f32;
723 1.0f32 / ((base as f32 as f64).powf(e as f64) as f32)
724 })
725 .collect();
726 let mut cos = vec![0f32; positions * half];
727 let mut sin = vec![0f32; positions * half];
728 for p in 0..positions {
729 for i in 0..half {
730 let a = (p as f32 * inv[i]) as f64;
731 cos[p * half + i] = a.cos() as f32;
732 sin[p * half + i] = a.sin() as f32;
733 }
734 }
735 (cos, sin)
736}
737
738fn apply_rope(
741 x: &mut [f32],
742 heads: usize,
743 hd: usize,
744 cos: &[f32],
745 sin: &[f32],
746 pos: impl Fn(usize) -> usize,
747) {
748 let half = hd / 2;
749 let width = heads * hd;
750 for (r, row) in x.chunks_exact_mut(width).enumerate() {
751 let p = pos(r);
752 let (c, s) = (
753 &cos[p * half..(p + 1) * half],
754 &sin[p * half..(p + 1) * half],
755 );
756 for h in 0..heads {
757 let v = &mut row[h * hd..(h + 1) * hd];
758 for i in 0..half {
759 let (a, b) = (v[i], v[i + half]);
760 v[i] = a * c[i] - b * s[i];
761 v[i + half] = b * c[i] + a * s[i];
762 }
763 }
764 }
765}
766
767#[inline]
770fn dot8(a: &[f32], b: &[f32]) -> f32 {
771 let mut acc = [0f32; 8];
772 let (ca, cb) = (a.chunks_exact(8), b.chunks_exact(8));
773 let (ra, rb) = (ca.remainder(), cb.remainder());
774 for (x, y) in ca.zip(cb) {
775 for l in 0..8 {
776 acc[l] += x[l] * y[l];
777 }
778 }
779 let mut s = ((acc[0] + acc[4]) + (acc[1] + acc[5])) + ((acc[2] + acc[6]) + (acc[3] + acc[7]));
780 for (x, y) in ra.iter().zip(rb) {
781 s += x * y;
782 }
783 s
784}
785
786#[inline]
788fn dot_f64(a: &[f32], b: &[f32]) -> f64 {
789 let mut acc = [0f64; 4];
790 let (ca, cb) = (a.chunks_exact(4), b.chunks_exact(4));
791 let (ra, rb) = (ca.remainder(), cb.remainder());
792 for (x, y) in ca.zip(cb) {
793 for l in 0..4 {
794 acc[l] += x[l] as f64 * y[l] as f64;
795 }
796 }
797 let mut s = (acc[0] + acc[2]) + (acc[1] + acc[3]);
798 for (x, y) in ra.iter().zip(rb) {
799 s += *x as f64 * *y as f64;
800 }
801 s
802}
803
804#[allow(clippy::too_many_arguments)]
807fn attention(
808 q: &[f32],
809 k: &[f32],
810 v: &[f32],
811 l: usize,
812 heads: usize,
813 hd: usize,
814 range: &(dyn Fn(usize) -> (usize, usize) + Sync),
815 out: &mut [f32],
816 pool: Option<&Pool>,
817) {
818 let width = heads * hd;
819 let scale = 1.0 / (hd as f32).sqrt();
820 let dst = SendMut::new(out.as_mut_ptr());
821 let run = |start: usize, end: usize| {
822 let mut sc: Vec<f32> = Vec::new();
823 let mut acc = vec![0f32; hd];
824 for unit in start..end {
825 let (i, h) = (unit / heads, unit % heads);
826 let (lo, hi) = range(i);
827 let qi = &q[i * width + h * hd..i * width + (h + 1) * hd];
828 sc.clear();
829 let mut mx = f32::NEG_INFINITY;
830 for j in lo..hi {
831 let kj = &k[j * width + h * hd..j * width + (h + 1) * hd];
832 let s = dot8(qi, kj) * scale;
833 mx = mx.max(s);
834 sc.push(s);
835 }
836 let mut sum = 0f64;
837 for s in sc.iter_mut() {
838 *s = (*s - mx).exp();
839 sum += *s as f64;
840 }
841 acc.iter_mut().for_each(|a| *a = 0.0);
842 for (jj, &p) in sc.iter().enumerate() {
843 let j = lo + jj;
844 let vj = &v[j * width + h * hd..j * width + (h + 1) * hd];
845 for t in 0..hd {
846 acc[t] += p * vj[t];
847 }
848 }
849 let inv = (1.0 / sum) as f32;
850 for t in 0..hd {
851 unsafe { *dst.at(i * width + h * hd + t) = acc[t] * inv };
852 }
853 }
854 };
855 let units = l * heads;
856 match pool {
857 Some(p) if units >= 64 => p.run_rows(units, &run),
858 _ => run(0, units),
859 }
860}
861
862struct Mat {
864 p: Proj,
865 name: String,
866}
867
868fn linear(m: &Mat, bias: Option<&[f32]>, x: &[f32], b: usize, pool: Option<&Pool>) -> Vec<f32> {
873 let p = &m.p;
874 if b > 0 && crate::gptq_capture::capturing() && matches!(p, Proj::F32 { .. }) {
875 crate::gptq_capture::accumulate(&m.name, x, b, p.cols());
876 }
877 let mut y = vec![0f32; b * p.rows()];
878 if b > 0 {
879 p.matmat(x, b, &mut y, pool);
880 }
881 if let Some(bias) = bias {
882 add_bias(&mut y, bias);
883 }
884 y
885}
886
887trait WeightSource {
892 fn has(&self, name: &str) -> bool;
893 fn dense(&mut self, name: &str) -> Result<(Vec<f32>, Vec<usize>), String>;
895 fn proj(&mut self, name: &str) -> Result<Proj, String>;
898 fn config_blob(&self, name: &str) -> Option<Vec<u8>>;
899}
900
901struct CmfSource(Arc<CmfModel>);
902
903impl WeightSource for CmfSource {
904 fn has(&self, name: &str) -> bool {
905 self.0.tensor_index(name).is_some()
906 }
907 fn dense(&mut self, name: &str) -> Result<(Vec<f32>, Vec<usize>), String> {
908 let idx = self
909 .0
910 .tensor_index(name)
911 .ok_or_else(|| format!("mimo audio: missing tensor '{name}'"))?;
912 let entry = &self.0.tensors[idx];
913 let mut dst = vec![0f32; entry.n_elems()];
914 cortiq_core::quant::dequant_tensor(entry, self.0.entry_bytes(entry), &mut dst)
915 .map_err(|e| format!("mimo audio: tensor '{name}': {e}"))?;
916 Ok((dst, entry.shape.clone()))
917 }
918 fn proj(&mut self, name: &str) -> Result<Proj, String> {
928 let entry = self
929 .0
930 .tensor(name)
931 .ok_or_else(|| format!("mimo audio: missing tensor '{name}'"))?;
932 if entry.shape.len() == 2 {
933 let p = Proj::from_model(&self.0, name)?;
934 let keep_mapped = std::env::var("CMF_MIMO_AUDIO_MAPPED").is_ok_and(|v| v == "1");
935 if matches!(p, Proj::F32 { .. }) || keep_mapped {
936 return Ok(p);
937 }
938 }
939 let (w, shape) = self.dense(name)?;
940 let cols = shape[1..].iter().product::<usize>();
941 Ok(Proj::f32(w, cols))
942 }
943 fn config_blob(&self, name: &str) -> Option<Vec<u8>> {
944 let e = self.0.tensor(name)?;
945 Some(self.0.entry_bytes(e).to_vec())
946 }
947}
948
949struct HfSource {
952 tensors: HashMap<String, (Vec<f32>, Vec<usize>)>,
953 blobs: HashMap<String, Vec<u8>>,
954}
955
956fn read_safetensors_selected(
959 path: &Path,
960 rename: &dyn Fn(&str) -> Option<String>,
961 out: &mut Vec<RawTensor>,
962) -> Result<(), String> {
963 use std::io::{Read, Seek, SeekFrom};
964 let mut f = std::fs::File::open(path).map_err(|e| format!("{}: {e}", path.display()))?;
965 let mut len8 = [0u8; 8];
966 f.read_exact(&mut len8)
967 .map_err(|e| format!("{}: {e}", path.display()))?;
968 let hlen = u64::from_le_bytes(len8) as usize;
969 let mut hbytes = vec![0u8; hlen];
970 f.read_exact(&mut hbytes)
971 .map_err(|e| format!("{}: {e}", path.display()))?;
972 let header: Value = serde_json::from_slice(&hbytes)
973 .map_err(|e| format!("{}: safetensors header: {e}", path.display()))?;
974 let obj = header
975 .as_object()
976 .ok_or_else(|| format!("{}: safetensors header is not an object", path.display()))?;
977 let base = 8 + hlen as u64;
978 let mut picks: Vec<(u64, u64, String, String, Vec<usize>)> = Vec::new();
979 for (name, meta) in obj {
980 if name == "__metadata__" {
981 continue;
982 }
983 let Some(canon) = rename(name) else { continue };
984 let dtype = meta["dtype"].as_str().unwrap_or("").to_string();
985 let shape: Vec<usize> = meta["shape"]
986 .as_array()
987 .map(|a| a.iter().map(|v| v.as_u64().unwrap_or(0) as usize).collect())
988 .unwrap_or_default();
989 let offs = meta["data_offsets"]
990 .as_array()
991 .ok_or_else(|| format!("{name}: no data_offsets"))?;
992 let (s, e) = (offs[0].as_u64().unwrap_or(0), offs[1].as_u64().unwrap_or(0));
993 picks.push((s, e, canon, dtype, shape));
994 }
995 picks.sort_by_key(|p| p.0);
996 for (s, e, name, dtype, shape) in picks {
997 let mut bytes = vec![0u8; (e - s) as usize];
998 f.seek(SeekFrom::Start(base + s))
999 .and_then(|_| f.read_exact(&mut bytes))
1000 .map_err(|err| format!("{}: {name}: {err}", path.display()))?;
1001 out.push(RawTensor {
1002 name,
1003 dtype,
1004 shape,
1005 bytes,
1006 });
1007 }
1008 Ok(())
1009}
1010
1011#[derive(Clone, Debug)]
1013pub struct RawTensor {
1014 pub name: String,
1015 pub dtype: String,
1017 pub shape: Vec<usize>,
1018 pub bytes: Vec<u8>,
1019}
1020
1021impl RawTensor {
1022 pub fn to_f32(&self) -> Result<Vec<f32>, String> {
1023 let data: Vec<f32> = match self.dtype.as_str() {
1024 "F32" => self
1025 .bytes
1026 .chunks_exact(4)
1027 .map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]]))
1028 .collect(),
1029 "BF16" => self
1030 .bytes
1031 .chunks_exact(2)
1032 .map(|c| f32::from_bits((u16::from_le_bytes([c[0], c[1]]) as u32) << 16))
1033 .collect(),
1034 "F16" => self
1035 .bytes
1036 .chunks_exact(2)
1037 .map(|c| cortiq_core::quant::f16_to_f32(u16::from_le_bytes([c[0], c[1]])))
1038 .collect(),
1039 other => {
1040 return Err(format!(
1041 "{}: unsupported safetensors dtype {other}",
1042 self.name
1043 ));
1044 }
1045 };
1046 if data.len() != self.shape.iter().product::<usize>().max(1) {
1047 return Err(format!(
1048 "{}: {} values for shape {:?}",
1049 self.name,
1050 data.len(),
1051 self.shape
1052 ));
1053 }
1054 Ok(data)
1055 }
1056}
1057
1058fn tokenizer_tensor_unused(name: &str) -> bool {
1060 name.ends_with("._codebook.cluster_size")
1061 || name.ends_with("._codebook.embed_avg")
1062 || name.ends_with("._codebook.inited")
1063}
1064
1065pub fn read_hf_audio_tensors(
1071 dir: &Path,
1072) -> Result<(Vec<RawTensor>, Vec<(String, Vec<u8>)>), String> {
1073 let mut tensors = Vec::new();
1074 let at_dir = dir.join("audio_tokenizer");
1075 read_safetensors_selected(
1076 &at_dir.join("model.safetensors"),
1077 &|n| {
1078 (n.starts_with("encoder.") && !tokenizer_tensor_unused(n))
1079 .then(|| format!("audio_tokenizer.{n}"))
1080 },
1081 &mut tensors,
1082 )?;
1083 let index_path = dir.join("model.safetensors.index.json");
1085 let index: Value = serde_json::from_slice(
1086 &std::fs::read(&index_path).map_err(|e| format!("{}: {e}", index_path.display()))?,
1087 )
1088 .map_err(|e| format!("{}: {e}", index_path.display()))?;
1089 let map = index["weight_map"]
1090 .as_object()
1091 .ok_or("model.safetensors.index.json: no weight_map")?;
1092 let keep = |k: &str| k.starts_with("audio_encoder.") || k.starts_with("speech_embeddings.");
1093 let mut files: Vec<String> = map
1094 .iter()
1095 .filter(|(k, _)| keep(k))
1096 .filter_map(|(_, v)| v.as_str().map(str::to_string))
1097 .collect();
1098 files.sort();
1099 files.dedup();
1100 for file in files {
1101 read_safetensors_selected(
1102 &dir.join(&file),
1103 &|n| keep(n).then(|| n.to_string()),
1104 &mut tensors,
1105 )?;
1106 }
1107 let mut blobs = Vec::new();
1108 for (blob, file) in [
1109 ("mm.config_json", dir.join("config.json")),
1110 ("audio_tokenizer.config_json", at_dir.join("config.json")),
1111 ] {
1112 if let Ok(b) = std::fs::read(&file) {
1113 blobs.push((blob.to_string(), b));
1114 }
1115 }
1116 Ok((tensors, blobs))
1117}
1118
1119impl HfSource {
1120 fn open(dir: &Path) -> Result<Self, String> {
1121 let (raw, blobs) = read_hf_audio_tensors(dir)?;
1122 let mut tensors = HashMap::with_capacity(raw.len());
1123 for t in raw {
1124 let data = t.to_f32()?;
1125 tensors.insert(t.name, (data, t.shape));
1126 }
1127 Ok(Self {
1128 tensors,
1129 blobs: blobs.into_iter().collect(),
1130 })
1131 }
1132}
1133
1134impl WeightSource for HfSource {
1135 fn has(&self, name: &str) -> bool {
1136 self.tensors.contains_key(name)
1137 }
1138 fn dense(&mut self, name: &str) -> Result<(Vec<f32>, Vec<usize>), String> {
1139 self.tensors
1140 .remove(name)
1141 .ok_or_else(|| format!("mimo audio: missing tensor '{name}'"))
1142 }
1143 fn proj(&mut self, name: &str) -> Result<Proj, String> {
1144 let (w, shape) = self.dense(name)?;
1145 if shape.len() < 2 {
1146 return Err(format!("mimo audio: '{name}' is not a matrix: {shape:?}"));
1147 }
1148 let cols = shape[1..].iter().product::<usize>();
1149 Ok(Proj::f32(w, cols))
1150 }
1151 fn config_blob(&self, name: &str) -> Option<Vec<u8>> {
1152 self.blobs.get(name).cloned()
1153 }
1154}
1155
1156fn take_vec(src: &mut dyn WeightSource, name: &str, len: usize) -> Result<Vec<f32>, String> {
1157 let (v, shape) = src.dense(name)?;
1158 if v.len() != len {
1159 return Err(format!(
1160 "mimo audio: '{name}' has shape {shape:?}, expected {len} values"
1161 ));
1162 }
1163 Ok(v)
1164}
1165
1166fn take_proj(
1167 src: &mut dyn WeightSource,
1168 name: &str,
1169 rows: usize,
1170 cols: usize,
1171) -> Result<Mat, String> {
1172 let p = src.proj(name)?;
1173 if p.rows() != rows || p.cols() != cols {
1174 return Err(format!(
1175 "mimo audio: '{name}' is [{}, {}], expected [{rows}, {cols}]",
1176 p.rows(),
1177 p.cols()
1178 ));
1179 }
1180 Ok(Mat {
1181 p,
1182 name: name.to_string(),
1183 })
1184}
1185
1186#[derive(Clone, Debug)]
1190pub struct TokenizerConfig {
1191 pub d_model: usize,
1192 pub layers: usize,
1193 pub heads: usize,
1194 pub ffn: usize,
1195 pub n_mels: usize,
1196 pub skip_layer_id: Option<usize>,
1197 pub causal: bool,
1198 pub window: usize,
1201 pub hybrid: bool,
1202 pub swa_per_block: usize,
1203 pub rope_theta: f64,
1204 pub codebook_sizes: Vec<usize>,
1205 pub ln_eps: f64,
1206}
1207
1208impl TokenizerConfig {
1209 pub fn mimo_default() -> Self {
1211 let mut books = vec![1024, 1024, 256];
1212 books.extend(std::iter::repeat_n(128, 17));
1213 Self {
1214 d_model: 1024,
1215 layers: 24,
1216 heads: 16,
1217 ffn: 4096,
1218 n_mels: 128,
1219 skip_layer_id: Some(3),
1220 causal: true,
1221 window: 128,
1222 hybrid: true,
1223 swa_per_block: 2,
1224 rope_theta: 10_000.0,
1225 codebook_sizes: books,
1226 ln_eps: 1e-5,
1227 }
1228 }
1229
1230 pub fn from_json(bytes: &[u8]) -> Result<Self, String> {
1231 let v: Value =
1232 serde_json::from_slice(bytes).map_err(|e| format!("audio_tokenizer config: {e}"))?;
1233 let d = Self::mimo_default();
1234 let us = |k: &str, dflt: usize| {
1235 v.get(k)
1236 .and_then(Value::as_u64)
1237 .map_or(dflt, |x| x as usize)
1238 };
1239 let bl = |k: &str, dflt: bool| v.get(k).and_then(Value::as_bool).unwrap_or(dflt);
1240 for (k, want) in [
1242 ("kernel_size", 3u64),
1243 ("stride_size", 2),
1244 ("avg_pooler", 2),
1245 ("sampling_rate", SAMPLE_RATE as u64),
1246 ("hop_length", HOP as u64),
1247 ("nfft", N_FFT as u64),
1248 ("window_size", N_FFT as u64),
1249 ] {
1250 if let Some(got) = v.get(k).and_then(Value::as_u64) {
1251 if got != want {
1252 return Err(format!(
1253 "audio_tokenizer config: {k} = {got}, only {want} is supported"
1254 ));
1255 }
1256 }
1257 }
1258 if bl("scale_embedding", false) {
1259 return Err("audio_tokenizer config: scale_embedding = true is not supported".into());
1260 }
1261 if let Some(ln) = v.get("ln_type").and_then(Value::as_str) {
1262 if ln != "LayerNorm" {
1263 return Err(format!(
1264 "audio_tokenizer config: ln_type {ln} is not supported"
1265 ));
1266 }
1267 }
1268 let n_q = us("num_quantizers", 20);
1269 let mut books: Vec<usize> = match v.get("codebook_size") {
1270 Some(Value::Array(a)) => a
1271 .iter()
1272 .filter_map(Value::as_u64)
1273 .map(|x| x as usize)
1274 .collect(),
1275 Some(x) => vec![x.as_u64().unwrap_or(1024) as usize],
1276 None => d.codebook_sizes.clone(),
1277 };
1278 if books.is_empty() {
1279 return Err("audio_tokenizer config: empty codebook_size".into());
1280 }
1281 while books.len() < n_q {
1282 books.push(*books.last().unwrap());
1283 }
1284 books.truncate(n_q);
1285 let window = v
1286 .get("encoder_attn_window_size")
1287 .and_then(Value::as_array)
1288 .and_then(|a| a.first())
1289 .and_then(Value::as_i64)
1290 .map_or(d.window as i64, |x| x)
1291 .max(0) as usize;
1292 Ok(Self {
1293 d_model: us("d_model", d.d_model),
1294 layers: us("encoder_layers", d.layers),
1295 heads: us("encoder_attention_heads", d.heads),
1296 ffn: us("encoder_ffn_dim", d.ffn),
1297 n_mels: us("n_mels", d.n_mels),
1298 skip_layer_id: match v.get("encoder_skip_layer_id") {
1299 Some(Value::Null) => None,
1300 Some(x) => x.as_u64().map(|x| x as usize),
1301 None => d.skip_layer_id,
1302 },
1303 causal: bl("encoder_causal", d.causal),
1304 window,
1305 hybrid: bl("hybrid_attention", d.hybrid),
1306 swa_per_block: us("swa_per_block", d.swa_per_block).max(1),
1307 rope_theta: v
1308 .get("rope_theta")
1309 .and_then(Value::as_f64)
1310 .unwrap_or(d.rope_theta),
1311 codebook_sizes: books,
1312 ln_eps: d.ln_eps,
1313 })
1314 }
1315
1316 pub fn windowed(&self, i: usize) -> bool {
1319 self.window > 0 && (!self.hybrid || i % self.swa_per_block < self.swa_per_block - 1)
1320 }
1321}
1322
1323#[derive(Clone, Debug)]
1325pub struct EncoderConfig {
1326 pub channels: usize,
1327 pub group: usize,
1328 pub dim: usize,
1329 pub layers: usize,
1330 pub heads: usize,
1331 pub head_dim: usize,
1332 pub ffn: usize,
1333 pub rope_theta: f64,
1334 pub out_dim: usize,
1335 pub vocab: usize,
1336 pub bidirectional: bool,
1337 pub post_norm: bool,
1338 pub projection_layers: usize,
1339 pub rms_eps: f64,
1340}
1341
1342impl EncoderConfig {
1343 pub fn mimo_default() -> Self {
1344 Self {
1345 channels: 20,
1346 group: 4,
1347 dim: 1024,
1348 layers: 6,
1349 heads: 16,
1350 head_dim: 64,
1351 ffn: 4096,
1352 rope_theta: 640_000.0,
1353 out_dim: 4096,
1354 vocab: 1280,
1355 bidirectional: true,
1356 post_norm: true,
1357 projection_layers: 2,
1358 rms_eps: 1e-6,
1359 }
1360 }
1361
1362 pub fn from_main_config(bytes: &[u8]) -> Result<Self, String> {
1364 let v: Value =
1365 serde_json::from_slice(bytes).map_err(|e| format!("mimo config.json: {e}"))?;
1366 let a = v
1367 .get("audio_config")
1368 .filter(|a| a.is_object())
1369 .ok_or("mimo config.json: no audio_config")?;
1370 let d = Self::mimo_default();
1371 let us = |k: &str, dflt: usize| {
1372 a.get(k)
1373 .and_then(Value::as_u64)
1374 .map_or(dflt, |x| x as usize)
1375 };
1376 let first_num = |k: &str, dflt: usize| -> usize {
1377 match a.get(k) {
1378 Some(Value::String(s)) => s
1379 .split('-')
1380 .next()
1381 .and_then(|x| x.trim().parse().ok())
1382 .unwrap_or(dflt),
1383 Some(x) => x.as_u64().map_or(dflt, |x| x as usize),
1384 None => dflt,
1385 }
1386 };
1387 if let Some(Value::String(s)) = a.get("speech_vocab_size") {
1388 if s.contains('-') {
1389 return Err(format!(
1390 "mimo audio_config: per-channel speech_vocab_size '{s}' is not supported"
1391 ));
1392 }
1393 }
1394 let prf = a
1395 .get("partial_rotary_factor")
1396 .and_then(Value::as_f64)
1397 .unwrap_or(1.0);
1398 if (prf - 1.0).abs() > 1e-9 {
1399 return Err(format!(
1400 "mimo audio_config: partial_rotary_factor {prf} is not supported"
1401 ));
1402 }
1403 let dim = us("input_local_dim", d.dim);
1404 let heads = us("input_local_attn_heads", d.heads);
1405 Ok(Self {
1406 channels: us("audio_channels", d.channels),
1407 group: us("group_size", d.group),
1408 dim,
1409 layers: us("input_local_layers", d.layers),
1410 heads,
1411 head_dim: dim / heads.max(1),
1412 ffn: us("input_local_intermediate_size", d.ffn),
1413 rope_theta: a
1414 .get("rope_theta")
1415 .and_then(Value::as_f64)
1416 .unwrap_or(d.rope_theta),
1417 out_dim: us("out_hidden_size", d.out_dim),
1418 vocab: first_num("speech_vocab_size", d.vocab),
1419 bidirectional: a
1420 .get("input_full_attention")
1421 .and_then(Value::as_bool)
1422 .unwrap_or(true),
1423 post_norm: a
1424 .get("add_post_norm")
1425 .and_then(Value::as_bool)
1426 .unwrap_or(true),
1427 projection_layers: us("projection_layers", d.projection_layers),
1428 rms_eps: d.rms_eps,
1429 })
1430 }
1431}
1432
1433struct TokLayer {
1436 ln1_w: Vec<f32>,
1437 ln1_b: Vec<f32>,
1438 q: Mat,
1439 q_b: Vec<f32>,
1440 k: Mat,
1441 v: Mat,
1442 v_b: Vec<f32>,
1443 o: Mat,
1444 o_b: Vec<f32>,
1445 ln2_w: Vec<f32>,
1446 ln2_b: Vec<f32>,
1447 fc1: Mat,
1448 fc1_b: Vec<f32>,
1449 fc2: Mat,
1450 fc2_b: Vec<f32>,
1451}
1452
1453pub struct AudioTokenizer {
1455 pub cfg: TokenizerConfig,
1456 conv1: Mat, conv1_b: Vec<f32>,
1458 conv2: Mat, conv2_b: Vec<f32>,
1460 layers: Vec<TokLayer>,
1461 ln_w: Vec<f32>,
1462 ln_b: Vec<f32>,
1463 pool_w: Mat, pool_ln_w: Vec<f32>,
1465 pool_ln_b: Vec<f32>,
1466 books: Vec<Vec<f32>>,
1468 books_bf16: Vec<Vec<f32>>,
1470}
1471
1472fn bf16_round(x: f32) -> f32 {
1473 let b = x.to_bits();
1474 if x.is_nan() {
1475 return x;
1476 }
1477 let lsb = (b >> 16) & 1;
1478 let r = b.wrapping_add(0x7FFF + lsb) & 0xFFFF_0000;
1479 f32::from_bits(r)
1480}
1481
1482impl AudioTokenizer {
1483 fn load(src: &mut dyn WeightSource, cfg: TokenizerConfig) -> Result<Self, String> {
1484 let p = "audio_tokenizer.encoder";
1485 let d = cfg.d_model;
1486 if d % cfg.heads != 0 || (d / cfg.heads) % 2 != 0 {
1487 return Err(format!(
1488 "audio tokenizer: d_model {d} / heads {} is not an even head width",
1489 cfg.heads
1490 ));
1491 }
1492 let mut layers = Vec::with_capacity(cfg.layers);
1493 for i in 0..cfg.layers {
1494 let l = format!("{p}.layers.{i}");
1495 if src.has(&format!("{l}.self_attn.k_proj.bias")) {
1496 return Err(format!(
1497 "audio tokenizer: unexpected {l}.self_attn.k_proj.bias (k has no bias)"
1498 ));
1499 }
1500 layers.push(TokLayer {
1501 ln1_w: take_vec(src, &format!("{l}.self_attn_layer_norm.weight"), d)?,
1502 ln1_b: take_vec(src, &format!("{l}.self_attn_layer_norm.bias"), d)?,
1503 q: take_proj(src, &format!("{l}.self_attn.q_proj.weight"), d, d)?,
1504 q_b: take_vec(src, &format!("{l}.self_attn.q_proj.bias"), d)?,
1505 k: take_proj(src, &format!("{l}.self_attn.k_proj.weight"), d, d)?,
1506 v: take_proj(src, &format!("{l}.self_attn.v_proj.weight"), d, d)?,
1507 v_b: take_vec(src, &format!("{l}.self_attn.v_proj.bias"), d)?,
1508 o: take_proj(src, &format!("{l}.self_attn.out_proj.weight"), d, d)?,
1509 o_b: take_vec(src, &format!("{l}.self_attn.out_proj.bias"), d)?,
1510 ln2_w: take_vec(src, &format!("{l}.final_layer_norm.weight"), d)?,
1511 ln2_b: take_vec(src, &format!("{l}.final_layer_norm.bias"), d)?,
1512 fc1: take_proj(src, &format!("{l}.fc1.weight"), cfg.ffn, d)?,
1513 fc1_b: take_vec(src, &format!("{l}.fc1.bias"), cfg.ffn)?,
1514 fc2: take_proj(src, &format!("{l}.fc2.weight"), d, cfg.ffn)?,
1515 fc2_b: take_vec(src, &format!("{l}.fc2.bias"), d)?,
1516 });
1517 }
1518 let mut books = Vec::with_capacity(cfg.codebook_sizes.len());
1519 for (i, &size) in cfg.codebook_sizes.iter().enumerate() {
1520 books.push(take_vec(
1521 src,
1522 &format!("{p}.quantizer.vq.layers.{i}._codebook.embed"),
1523 size * d,
1524 )?);
1525 }
1526 let books_bf16 = books
1527 .iter()
1528 .map(|b| b.iter().map(|&v| bf16_round(v)).collect())
1529 .collect();
1530 Ok(Self {
1531 conv1: take_proj(src, &format!("{p}.conv1.weight"), d, cfg.n_mels * 3)?,
1532 conv1_b: take_vec(src, &format!("{p}.conv1.bias"), d)?,
1533 conv2: take_proj(src, &format!("{p}.conv2.weight"), d, d * 3)?,
1534 conv2_b: take_vec(src, &format!("{p}.conv2.bias"), d)?,
1535 layers,
1536 ln_w: take_vec(src, &format!("{p}.layer_norm.weight"), d)?,
1537 ln_b: take_vec(src, &format!("{p}.layer_norm.bias"), d)?,
1538 pool_w: take_proj(src, &format!("{p}.down_sample_layer.0.weight"), d, d * 2)?,
1539 pool_ln_w: take_vec(src, &format!("{p}.down_sample_norm.weight"), d)?,
1540 pool_ln_b: take_vec(src, &format!("{p}.down_sample_norm.bias"), d)?,
1541 books,
1542 books_bf16,
1543 cfg,
1544 })
1545 }
1546
1547 pub fn features(&self, mel: &[f32], m: usize, pool: Option<&Pool>) -> Result<Vec<f32>, String> {
1550 let cfg = &self.cfg;
1551 let (d, nm) = (cfg.d_model, cfg.n_mels);
1552 if m == 0 || mel.len() != m * nm {
1553 return Err(format!(
1554 "audio tokenizer: mel has {} values for {m} frames",
1555 mel.len()
1556 ));
1557 }
1558 let mut col = vec![0f32; m * nm * 3];
1560 for t in 0..m {
1561 for k in 0..3 {
1562 let s = t as isize + k as isize - 1;
1563 if s < 0 || s >= m as isize {
1564 continue;
1565 }
1566 let src = &mel[s as usize * nm..(s as usize + 1) * nm];
1567 let row = &mut col[t * nm * 3..(t + 1) * nm * 3];
1568 for c in 0..nm {
1569 row[c * 3 + k] = src[c];
1570 }
1571 }
1572 }
1573 let mut h1 = linear(&self.conv1, Some(&self.conv1_b), &col, m, pool);
1574 gelu_inplace(&mut h1);
1575 let l1 = m.div_ceil(2);
1577 let mut col = vec![0f32; l1 * d * 3];
1578 for t in 0..l1 {
1579 for k in 0..3 {
1580 let s = 2 * t as isize + k as isize - 1;
1581 if s < 0 || s >= m as isize {
1582 continue;
1583 }
1584 let src = &h1[s as usize * d..(s as usize + 1) * d];
1585 let row = &mut col[t * d * 3..(t + 1) * d * 3];
1586 for c in 0..d {
1587 row[c * 3 + k] = src[c];
1588 }
1589 }
1590 }
1591 drop(h1);
1592 let mut h = linear(&self.conv2, Some(&self.conv2_b), &col, l1, pool);
1593 drop(col);
1594 gelu_inplace(&mut h);
1595
1596 let heads = cfg.heads;
1597 let hd = d / heads;
1598 let (cos, sin) = rope_tables(cfg.rope_theta, hd, l1);
1599 let mut skip: Option<Vec<f32>> = None;
1600 let mut n = vec![0f32; l1 * d];
1601 let mut att = vec![0f32; l1 * d];
1602 for (li, layer) in self.layers.iter().enumerate() {
1603 layer_norm_rows(&h, &layer.ln1_w, &layer.ln1_b, cfg.ln_eps, &mut n);
1604 let mut q = linear(&layer.q, Some(&layer.q_b), &n, l1, pool);
1605 let mut k = linear(&layer.k, None, &n, l1, pool);
1606 let v = linear(&layer.v, Some(&layer.v_b), &n, l1, pool);
1607 apply_rope(&mut q, heads, hd, &cos, &sin, |r| r);
1608 apply_rope(&mut k, heads, hd, &cos, &sin, |r| r);
1609 let causal = cfg.causal;
1610 let window = if cfg.windowed(li) { cfg.window } else { 0 };
1611 let range = move |i: usize| -> (usize, usize) {
1612 let hi = if causal { i + 1 } else { l1 };
1613 let (lo, hi) = if window > 0 {
1614 (i.saturating_sub(window), hi.min(i + window + 1))
1615 } else {
1616 (0, hi)
1617 };
1618 (lo, hi)
1619 };
1620 attention(&q, &k, &v, l1, heads, hd, &range, &mut att, pool);
1621 let o = linear(&layer.o, Some(&layer.o_b), &att, l1, pool);
1622 add_into(&mut h, &o);
1623 layer_norm_rows(&h, &layer.ln2_w, &layer.ln2_b, cfg.ln_eps, &mut n);
1624 let mut f = linear(&layer.fc1, Some(&layer.fc1_b), &n, l1, pool);
1625 gelu_inplace(&mut f);
1626 let f2 = linear(&layer.fc2, Some(&layer.fc2_b), &f, l1, pool);
1627 add_into(&mut h, &f2);
1628 if cfg.skip_layer_id.is_some_and(|s| s >= 1 && li == s - 1) {
1629 skip = Some(h.clone());
1630 }
1631 }
1632 if let Some(s) = skip {
1633 add_into(&mut h, &s);
1634 }
1635 layer_norm_rows(&h, &self.ln_w, &self.ln_b, cfg.ln_eps, &mut n);
1636 let l2 = l1.div_ceil(2);
1638 let mut col = vec![0f32; l2 * d * 2];
1639 for t in 0..l2 {
1640 for k in 0..2 {
1641 let s = 2 * t + k;
1642 if s >= l1 {
1643 continue;
1644 }
1645 let src = &n[s * d..(s + 1) * d];
1646 let row = &mut col[t * d * 2..(t + 1) * d * 2];
1647 for c in 0..d {
1648 row[c * 2 + k] = src[c];
1649 }
1650 }
1651 }
1652 let mut p = linear(&self.pool_w, None, &col, l2, pool);
1653 gelu_inplace(&mut p);
1654 let mut out = vec![0f32; l2 * d];
1655 layer_norm_rows(&p, &self.pool_ln_w, &self.pool_ln_b, cfg.ln_eps, &mut out);
1656 Ok(out)
1657 }
1658
1659 pub fn quantize(
1664 &self,
1665 feats: &[f32],
1666 rows: usize,
1667 bf16_books: bool,
1668 pool: Option<&Pool>,
1669 ) -> Vec<u32> {
1670 let d = self.cfg.d_model;
1671 let books = if bf16_books {
1672 &self.books_bf16
1673 } else {
1674 &self.books
1675 };
1676 let nq = books.len();
1677 let norms: Vec<Vec<f64>> = books
1678 .iter()
1679 .map(|b| {
1680 b.chunks_exact(d)
1681 .map(|e| e.iter().map(|&v| v as f64 * v as f64).sum())
1682 .collect()
1683 })
1684 .collect();
1685 let mut codes = vec![0u32; rows * nq];
1686 let dst = crate::pool::SendMutT::new(codes.as_mut_ptr());
1687 let run = |start: usize, end: usize| {
1688 let mut r = vec![0f32; d];
1689 for row in start..end {
1690 r.copy_from_slice(&feats[row * d..(row + 1) * d]);
1691 for (lvl, book) in books.iter().enumerate() {
1692 let mut best = (f64::INFINITY, 0usize);
1693 for (ci, e) in book.chunks_exact(d).enumerate() {
1694 let dist = norms[lvl][ci] - 2.0 * dot_f64(&r, e);
1695 if dist < best.0 {
1696 best = (dist, ci);
1697 }
1698 }
1699 let e = &book[best.1 * d..(best.1 + 1) * d];
1700 for t in 0..d {
1701 r[t] -= e[t];
1702 }
1703 unsafe { *dst.at(row * nq + lvl) = best.1 as u32 };
1704 }
1705 }
1706 };
1707 match pool {
1708 Some(p) if rows >= 8 => p.run_rows(rows, &run),
1709 _ => run(0, rows),
1710 }
1711 codes
1712 }
1713}
1714
1715struct LocalLayer {
1718 ln1: Vec<f32>,
1719 q: Mat,
1720 q_b: Vec<f32>,
1721 k: Mat,
1722 k_b: Vec<f32>,
1723 v: Mat,
1724 v_b: Vec<f32>,
1725 o: Mat,
1726 ln2: Vec<f32>,
1727 gate: Mat,
1728 up: Mat,
1729 down: Mat,
1730}
1731
1732pub struct AudioEncoder {
1734 pub cfg: EncoderConfig,
1735 speech_emb: Vec<Vec<f32>>,
1737 layers: Vec<LocalLayer>,
1738 norm: Option<Vec<f32>>,
1739 proj0: Mat,
1740 proj2: Option<Mat>,
1741}
1742
1743impl AudioEncoder {
1744 fn load(src: &mut dyn WeightSource, cfg: EncoderConfig) -> Result<Self, String> {
1745 let (d, f) = (cfg.dim, cfg.ffn);
1746 let qd = cfg.heads * cfg.head_dim;
1747 let mut speech_emb = Vec::with_capacity(cfg.channels);
1748 for c in 0..cfg.channels {
1749 speech_emb.push(take_vec(
1750 src,
1751 &format!("speech_embeddings.{c}.weight"),
1752 cfg.vocab * d,
1753 )?);
1754 }
1755 let p = "audio_encoder.input_local_transformer";
1756 let mut layers = Vec::with_capacity(cfg.layers);
1757 for i in 0..cfg.layers {
1758 let l = format!("{p}.layers.{i}");
1759 layers.push(LocalLayer {
1760 ln1: take_vec(src, &format!("{l}.input_layernorm.weight"), d)?,
1761 q: take_proj(src, &format!("{l}.self_attn.q_proj.weight"), qd, d)?,
1762 q_b: take_vec(src, &format!("{l}.self_attn.q_proj.bias"), qd)?,
1763 k: take_proj(src, &format!("{l}.self_attn.k_proj.weight"), qd, d)?,
1764 k_b: take_vec(src, &format!("{l}.self_attn.k_proj.bias"), qd)?,
1765 v: take_proj(src, &format!("{l}.self_attn.v_proj.weight"), qd, d)?,
1766 v_b: take_vec(src, &format!("{l}.self_attn.v_proj.bias"), qd)?,
1767 o: take_proj(src, &format!("{l}.self_attn.o_proj.weight"), d, qd)?,
1768 ln2: take_vec(src, &format!("{l}.post_attention_layernorm.weight"), d)?,
1769 gate: take_proj(src, &format!("{l}.mlp.gate_proj.weight"), f, d)?,
1770 up: take_proj(src, &format!("{l}.mlp.up_proj.weight"), f, d)?,
1771 down: take_proj(src, &format!("{l}.mlp.down_proj.weight"), d, f)?,
1772 });
1773 }
1774 let norm = if cfg.post_norm {
1775 Some(take_vec(src, &format!("{p}.norm.weight"), d)?)
1776 } else {
1777 None
1778 };
1779 let flat = d * cfg.group;
1780 let (proj0, proj2) = match cfg.projection_layers {
1781 2 => (
1782 take_proj(src, "audio_encoder.projection.mlp.0.weight", flat * 4, flat)?,
1783 Some(take_proj(
1784 src,
1785 "audio_encoder.projection.mlp.2.weight",
1786 cfg.out_dim,
1787 flat * 4,
1788 )?),
1789 ),
1790 1 => (
1791 take_proj(src, "audio_encoder.projection.weight", cfg.out_dim, flat)?,
1792 None,
1793 ),
1794 n => {
1795 return Err(format!(
1796 "mimo audio_config: projection_layers = {n} is not supported"
1797 ));
1798 }
1799 };
1800 Ok(Self {
1801 cfg,
1802 speech_emb,
1803 layers,
1804 norm,
1805 proj0,
1806 proj2,
1807 })
1808 }
1809
1810 pub fn group_codes(
1814 &self,
1815 codes: &[u32],
1816 t: usize,
1817 cols: usize,
1818 ) -> Result<(Vec<u32>, usize), String> {
1819 let (c, g) = (self.cfg.channels, self.cfg.group);
1820 if cols < c {
1821 return Err(format!("audio codes have {cols} channels, need {c}"));
1822 }
1823 if t == 0 || codes.len() != t * cols {
1824 return Err(format!(
1825 "audio codes: {} values for {t}×{cols}",
1826 codes.len()
1827 ));
1828 }
1829 let padded = t.div_ceil(g) * g;
1830 let mut out = Vec::with_capacity(padded * c);
1831 for r in 0..padded {
1832 let src = r.min(t - 1);
1833 out.extend_from_slice(&codes[src * cols..src * cols + c]);
1834 }
1835 Ok((out, padded / g))
1836 }
1837
1838 pub fn forward(
1840 &self,
1841 codes: &[u32],
1842 t: usize,
1843 cols: usize,
1844 pool: Option<&Pool>,
1845 ) -> Result<Vec<f32>, String> {
1846 let cfg = &self.cfg;
1847 let (grouped, groups) = self.group_codes(codes, t, cols)?;
1848 let (d, c, g) = (cfg.dim, cfg.channels, cfg.group);
1849 let rows = groups * g;
1850 let mut x = vec![0f32; rows * d];
1852 for r in 0..rows {
1853 let dst = &mut x[r * d..(r + 1) * d];
1854 for ch in 0..c {
1855 let id = grouped[r * c + ch] as usize;
1856 if id >= cfg.vocab {
1857 return Err(format!(
1858 "audio code {id} in channel {ch} is outside the {} embedding rows",
1859 cfg.vocab
1860 ));
1861 }
1862 let e = &self.speech_emb[ch][id * d..(id + 1) * d];
1863 for (a, b) in dst.iter_mut().zip(e) {
1864 *a += b;
1865 }
1866 }
1867 }
1868 let (heads, hd) = (cfg.heads, cfg.head_dim);
1869 let (cos, sin) = rope_tables(cfg.rope_theta, hd, g);
1870 let mut n = vec![0f32; rows * d];
1871 let mut att = vec![0f32; rows * heads * hd];
1872 let bidir = cfg.bidirectional;
1873 let range = move |i: usize| -> (usize, usize) {
1874 let base = i / g * g;
1875 (base, if bidir { base + g } else { i + 1 })
1876 };
1877 for layer in &self.layers {
1878 rms_norm_rows(&x, &layer.ln1, cfg.rms_eps, &mut n);
1879 let mut q = linear(&layer.q, Some(&layer.q_b), &n, rows, pool);
1880 let mut k = linear(&layer.k, Some(&layer.k_b), &n, rows, pool);
1881 let v = linear(&layer.v, Some(&layer.v_b), &n, rows, pool);
1882 apply_rope(&mut q, heads, hd, &cos, &sin, |r| r % g);
1883 apply_rope(&mut k, heads, hd, &cos, &sin, |r| r % g);
1884 attention(&q, &k, &v, rows, heads, hd, &range, &mut att, pool);
1885 let o = linear(&layer.o, None, &att, rows, pool);
1886 add_into(&mut x, &o);
1887 rms_norm_rows(&x, &layer.ln2, cfg.rms_eps, &mut n);
1888 let mut gt = linear(&layer.gate, None, &n, rows, pool);
1889 let up = linear(&layer.up, None, &n, rows, pool);
1890 for (a, b) in gt.iter_mut().zip(&up) {
1891 let s = *a / (1.0 + (-*a).exp());
1892 *a = s * b;
1893 }
1894 let dn = linear(&layer.down, None, >, rows, pool);
1895 add_into(&mut x, &dn);
1896 }
1897 if let Some(w) = &self.norm {
1898 rms_norm_rows(&x, w, cfg.rms_eps, &mut n);
1899 std::mem::swap(&mut x, &mut n);
1900 }
1901 let mut hdn = linear(&self.proj0, None, &x, groups, pool);
1903 match &self.proj2 {
1904 Some(p2) => {
1905 gelu_inplace(&mut hdn);
1906 Ok(linear(p2, None, &hdn, groups, pool))
1907 }
1908 None => Ok(hdn),
1909 }
1910 }
1911}
1912
1913#[derive(Clone, Debug)]
1917pub struct AudioCodes {
1918 pub frames: usize,
1920 pub levels: usize,
1922 pub codes: Vec<u32>,
1924}
1925
1926#[derive(Clone, Debug)]
1928pub struct AudioEmbeds {
1929 pub n_tokens: usize,
1930 pub dim: usize,
1931 pub rows: Vec<f32>,
1932}
1933
1934pub struct MimoAudio {
1936 pub tokenizer: AudioTokenizer,
1937 pub encoder: AudioEncoder,
1938 pub bf16_codebooks: bool,
1941 pool: Option<Arc<Pool>>,
1942}
1943
1944impl MimoAudio {
1945 pub fn present_in(model: &CmfModel) -> bool {
1948 model
1949 .tensor_index("audio_tokenizer.encoder.conv1.weight")
1950 .is_some()
1951 && model
1952 .tensor_index("audio_encoder.projection.mlp.0.weight")
1953 .is_some()
1954 }
1955
1956 fn configs(src: &dyn WeightSource) -> Result<(TokenizerConfig, EncoderConfig), String> {
1957 let tok = match src.config_blob("audio_tokenizer.config_json") {
1958 Some(b) => TokenizerConfig::from_json(&b)?,
1959 None => TokenizerConfig::mimo_default(),
1960 };
1961 let enc = match src.config_blob("mm.config_json") {
1962 Some(b) => EncoderConfig::from_main_config(&b)?,
1963 None => EncoderConfig::mimo_default(),
1964 };
1965 if tok.codebook_sizes.len() < enc.channels {
1966 return Err(format!(
1967 "mimo audio: {} RVQ levels but {} speech embedding channels",
1968 tok.codebook_sizes.len(),
1969 enc.channels
1970 ));
1971 }
1972 if let Some(&big) = tok.codebook_sizes.iter().max() {
1973 if big > enc.vocab {
1974 return Err(format!(
1975 "mimo audio: codebook of {big} entries exceeds the {} speech embedding rows",
1976 enc.vocab
1977 ));
1978 }
1979 }
1980 Ok((tok, enc))
1981 }
1982
1983 fn build(mut src: Box<dyn WeightSource>) -> Result<Self, String> {
1984 let (tc, ec) = Self::configs(src.as_ref())?;
1985 let tokenizer = AudioTokenizer::load(src.as_mut(), tc)?;
1986 let encoder = AudioEncoder::load(src.as_mut(), ec)?;
1987 let bf16_codebooks = std::env::var("CMF_MIMO_AUDIO_BF16_BOOKS")
1988 .map(|v| v != "0")
1989 .unwrap_or(true);
1990 Ok(Self {
1991 tokenizer,
1992 encoder,
1993 bf16_codebooks,
1994 pool: Pool::from_env(),
1995 })
1996 }
1997
1998 pub fn from_model(model: &Arc<CmfModel>) -> Result<Self, String> {
2001 Self::build(Box::new(CmfSource(model.clone())))
2002 }
2003
2004 pub fn from_hf_dir(dir: &Path) -> Result<Self, String> {
2008 Self::build(Box::new(HfSource::open(dir)?))
2009 }
2010
2011 pub fn pool(&self) -> Option<&Pool> {
2012 self.pool.as_deref()
2013 }
2014
2015 pub fn wav_to_mel(&self, bytes: &[u8]) -> Result<(Vec<f32>, usize), String> {
2017 let wav = decode_wav(bytes)?;
2018 let mono = wav_to_mono_24k(&wav)?;
2019 log_mel(&mono, self.pool())
2020 }
2021
2022 pub fn features(&self, mel: &[f32], m: usize) -> Result<Vec<f32>, String> {
2024 let nm = self.tokenizer.cfg.n_mels;
2025 let mut out = Vec::new();
2026 let mut start = 0usize;
2027 for seg in segment_lengths(m) {
2028 out.extend(self.tokenizer.features(
2029 &mel[start * nm..(start + seg) * nm],
2030 seg,
2031 self.pool(),
2032 )?);
2033 start += seg;
2034 }
2035 Ok(out)
2036 }
2037
2038 pub fn encode_mel(&self, mel: &[f32], m: usize) -> Result<AudioCodes, String> {
2040 let d = self.tokenizer.cfg.d_model;
2041 let feats = self.features(mel, m)?;
2042 let frames = feats.len() / d;
2043 debug_assert_eq!(frames, codes_for_mel(m));
2044 let codes = self
2045 .tokenizer
2046 .quantize(&feats, frames, self.bf16_codebooks, self.pool());
2047 Ok(AudioCodes {
2048 frames,
2049 levels: self.tokenizer.books.len(),
2050 codes,
2051 })
2052 }
2053
2054 pub fn embed_codes(&self, codes: &AudioCodes) -> Result<AudioEmbeds, String> {
2056 let rows = self
2057 .encoder
2058 .forward(&codes.codes, codes.frames, codes.levels, self.pool())?;
2059 let dim = self.encoder.cfg.out_dim;
2060 Ok(AudioEmbeds {
2061 n_tokens: rows.len() / dim,
2062 dim,
2063 rows,
2064 })
2065 }
2066
2067 pub fn embed_wav(&self, bytes: &[u8]) -> Result<AudioEmbeds, String> {
2069 let (mel, m) = self.wav_to_mel(bytes)?;
2070 let codes = self.encode_mel(&mel, m)?;
2071 let emb = self.embed_codes(&codes)?;
2072 let k = audio_token_count(m, self.encoder.cfg.group);
2073 if emb.n_tokens != k {
2074 return Err(format!(
2075 "mimo audio: encoder produced {} rows but the placeholder count is {k}",
2076 emb.n_tokens
2077 ));
2078 }
2079 Ok(emb)
2080 }
2081}
2082
2083#[cfg(test)]
2084mod tests {
2085 use super::*;
2086
2087 fn wav_bytes(
2088 tag: u16,
2089 channels: u16,
2090 rate: u32,
2091 bits: u16,
2092 extensible: bool,
2093 data: &[u8],
2094 ) -> Vec<u8> {
2095 let block = channels * bits.div_ceil(8);
2096 let mut fmt = Vec::new();
2097 fmt.extend_from_slice(&(if extensible { 0xFFFEu16 } else { tag }).to_le_bytes());
2098 fmt.extend_from_slice(&channels.to_le_bytes());
2099 fmt.extend_from_slice(&rate.to_le_bytes());
2100 fmt.extend_from_slice(&(rate * block as u32).to_le_bytes());
2101 fmt.extend_from_slice(&block.to_le_bytes());
2102 fmt.extend_from_slice(&bits.to_le_bytes());
2103 if extensible {
2104 fmt.extend_from_slice(&22u16.to_le_bytes());
2105 fmt.extend_from_slice(&bits.to_le_bytes());
2106 fmt.extend_from_slice(&0u32.to_le_bytes());
2107 fmt.extend_from_slice(&tag.to_le_bytes());
2108 fmt.extend_from_slice(&[0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xAA, 0, 0x38, 0x9B, 0x71]);
2109 }
2110 let mut out = Vec::new();
2111 out.extend_from_slice(b"RIFF");
2112 out.extend_from_slice(&0u32.to_le_bytes());
2113 out.extend_from_slice(b"WAVE");
2114 out.extend_from_slice(b"junk");
2116 out.extend_from_slice(&3u32.to_le_bytes());
2117 out.extend_from_slice(&[1, 2, 3, 0]);
2118 out.extend_from_slice(b"fmt ");
2119 out.extend_from_slice(&(fmt.len() as u32).to_le_bytes());
2120 out.extend_from_slice(&fmt);
2121 out.extend_from_slice(b"data");
2122 out.extend_from_slice(&(data.len() as u32).to_le_bytes());
2123 out.extend_from_slice(data);
2124 let riff = (out.len() - 8) as u32;
2125 out[4..8].copy_from_slice(&riff.to_le_bytes());
2126 out
2127 }
2128
2129 #[test]
2130 fn wav_decodes_every_supported_layout() {
2131 let pcm16: Vec<u8> = [-32768i16, 32767, 0, -16384]
2133 .iter()
2134 .flat_map(|v| v.to_le_bytes())
2135 .collect();
2136 let w = decode_wav(&wav_bytes(1, 2, 16000, 16, false, &pcm16)).unwrap();
2137 assert_eq!(w.sample_rate, 16000);
2138 assert_eq!(
2139 w.channels,
2140 vec![vec![-1.0, 0.0], vec![32767.0 / 32768.0, -0.5]]
2141 );
2142
2143 let u8d = [0u8, 128, 255];
2144 let w = decode_wav(&wav_bytes(1, 1, 8000, 8, false, &u8d)).unwrap();
2145 assert_eq!(w.channels[0], vec![-1.0, 0.0, 127.0 / 128.0]);
2146
2147 let s24: Vec<u8> = [-8_388_608i32, 4_194_304, -1]
2148 .iter()
2149 .flat_map(|v| v.to_le_bytes()[..3].to_vec())
2150 .collect();
2151 for ext in [false, true] {
2152 let w = decode_wav(&wav_bytes(1, 1, 44100, 24, ext, &s24)).unwrap();
2153 assert_eq!(w.channels[0], vec![-1.0, 0.5, -1.0 / 8_388_608.0]);
2154 }
2155
2156 let s32: Vec<u8> = [i32::MIN, 1 << 30]
2157 .iter()
2158 .flat_map(|v| v.to_le_bytes())
2159 .collect();
2160 let w = decode_wav(&wav_bytes(1, 1, 48000, 32, false, &s32)).unwrap();
2161 assert_eq!(w.channels[0], vec![-1.0, 0.5]);
2162
2163 let f32d: Vec<u8> = [0.25f32, -0.75]
2164 .iter()
2165 .flat_map(|v| v.to_le_bytes())
2166 .collect();
2167 for ext in [false, true] {
2168 let w = decode_wav(&wav_bytes(3, 1, 22050, 32, ext, &f32d)).unwrap();
2169 assert_eq!(w.channels[0], vec![0.25, -0.75]);
2170 }
2171 let f64d: Vec<u8> = [0.125f64].iter().flat_map(|v| v.to_le_bytes()).collect();
2172 assert_eq!(
2173 decode_wav(&wav_bytes(3, 1, 24000, 64, false, &f64d))
2174 .unwrap()
2175 .channels[0],
2176 vec![0.125]
2177 );
2178
2179 assert!(decode_wav(&wav_bytes(6, 1, 8000, 8, false, &[0])).is_err());
2181 assert!(decode_wav(b"OggS....").is_err());
2182 }
2183
2184 #[test]
2185 fn wav_data_size_past_the_end_reads_to_the_end() {
2186 let pcm16: Vec<u8> = [1000i16, -1000, 5]
2187 .iter()
2188 .flat_map(|v| v.to_le_bytes())
2189 .collect();
2190 let mut b = wav_bytes(1, 1, 24000, 16, false, &pcm16);
2191 let n = b.len();
2192 b[n - pcm16.len() - 4..n - pcm16.len()].copy_from_slice(&u32::MAX.to_le_bytes());
2194 assert_eq!(decode_wav(&b).unwrap().channels[0].len(), 3);
2195 }
2196
2197 #[test]
2198 fn resample_kernel_widths_match_torchaudio() {
2199 for (o, n, w) in [
2201 (2usize, 3usize, 7usize),
2202 (147, 80, 12),
2203 (147, 160, 7),
2204 (2, 1, 13),
2205 ] {
2206 let (k, width) = sinc_resample_kernel(o, n);
2207 assert_eq!(width, w, "{o}->{n}");
2208 assert_eq!(k.len(), n * (2 * w + o));
2209 }
2210 }
2211
2212 #[test]
2213 fn resample_lengths_follow_torchaudio_rounding() {
2214 assert_eq!(resampled_len(16000, 16000, 24000), 24000);
2215 assert_eq!(resampled_len(16001, 16000, 24000), 24002);
2216 assert_eq!(resampled_len(44100, 44100, 24000), 24000);
2217 assert_eq!(resample_sinc(&vec![0.0; 16001], 16000, 24000).len(), 24002);
2218 let n = 1_837_421usize;
2222 assert_eq!((80 * n) % 147, 1);
2223 assert_eq!((80 * n).div_ceil(147), 999_958);
2224 assert_eq!(resampled_len(n, 44100, 24000), 999_957);
2225 let y = resample_sinc(&vec![1.0; 4000], 16000, 24000);
2227 assert!((y[3000] - 1.0).abs() < 1e-2, "{}", y[3000]);
2228 }
2229
2230 #[test]
2231 fn fft_matches_a_direct_dft() {
2232 let n = 960;
2233 let x: Vec<f64> = (0..n)
2234 .map(|i| ((i * 7919) % 97) as f64 / 97.0 - 0.5)
2235 .collect();
2236 let mut out = vec![(0.0, 0.0); n];
2237 Fft::new(n).forward_real(&x, &mut out);
2238 for k in [0usize, 1, 37, 240, 480, 959] {
2239 let (mut re, mut im) = (0.0, 0.0);
2240 for (t, v) in x.iter().enumerate() {
2241 let a = -2.0 * std::f64::consts::PI * (k * t % n) as f64 / n as f64;
2242 re += v * a.cos();
2243 im += v * a.sin();
2244 }
2245 assert!(
2246 (re - out[k].0).abs() < 1e-9 && (im - out[k].1).abs() < 1e-9,
2247 "bin {k}"
2248 );
2249 }
2250 }
2251
2252 #[test]
2253 fn hann_fixups_are_single_ulp_corrections() {
2254 let step = (2.0 * std::f64::consts::PI / N_FFT as f64) as f32;
2255 for (i, bits) in TORCH_HANN_960_FIXUPS {
2259 let c = ((i as f32 * step) as f64).cos() as f32;
2260 let win = |c: f32| (c * -0.5f32 + 0.5f32).to_bits();
2261 assert_ne!(win(c), bits, "index {i} needs no fixup");
2262 let up = f32::from_bits(c.to_bits() + 1);
2263 let dn = f32::from_bits(c.to_bits() - 1);
2264 assert!(
2265 win(up) == bits || win(dn) == bits,
2266 "index {i}: not a one-ulp cos difference"
2267 );
2268 }
2269 let w = torch_hann_960();
2270 assert_eq!(w[0], 0.0);
2271 assert_eq!(w[480], 1.0);
2272 }
2273
2274 #[test]
2275 fn mel_shape_and_short_input() {
2276 assert!(log_mel(&vec![0.0; 480], None).is_err());
2277 let (mel, m) = log_mel(&vec![0.0; 481], None).unwrap();
2278 assert_eq!((m, mel.len()), (3, 3 * N_MELS));
2279 assert!(
2281 mel.iter()
2282 .all(|&v| (v - (1e-7f64).ln() as f32).abs() < 1e-6)
2283 );
2284 assert_eq!(mel_frames(120_000), 501);
2285 let fb = mel_filterbank(481, 128, 24000);
2287 for m in 0..128 {
2288 assert!((0..481).any(|k| fb[k * 128 + m] > 0.0), "band {m}");
2289 }
2290 }
2291
2292 #[test]
2293 fn token_counts_and_segments() {
2294 assert_eq!(audio_token_count(501, 4), 32);
2296 assert_eq!(codes_for_mel(501), 126);
2297 assert_eq!(codes_for_mel(6501), 1626);
2298 assert_eq!(audio_token_count(6501, 4), 407);
2299 assert_eq!(segment_lengths(6501), vec![6000, 501]);
2300 for n in (24_000..=65 * 24_000).step_by(240 * 7) {
2303 let m = mel_frames(n);
2304 assert_eq!(
2305 codes_for_mel(m).div_ceil(4),
2306 audio_token_count(m, 4),
2307 "M={m}"
2308 );
2309 }
2310 for m in 5990..6020 {
2311 assert_eq!(
2312 codes_for_mel(m).div_ceil(4),
2313 audio_token_count(m, 4),
2314 "M={m}"
2315 );
2316 }
2317 }
2318
2319 #[test]
2320 fn placeholder_expansion() {
2321 let ids = [
2322 1,
2323 AUDIO_START_ID,
2324 AUDIO_PAD_ID,
2325 AUDIO_END_ID,
2326 2,
2327 AUDIO_START_ID,
2328 AUDIO_PAD_ID,
2329 AUDIO_END_ID,
2330 ];
2331 let out = expand_audio_placeholders(&ids, &[2, 3]).unwrap();
2332 assert_eq!(
2333 out,
2334 vec![
2335 1,
2336 AUDIO_START_ID,
2337 AUDIO_PAD_ID,
2338 AUDIO_PAD_ID,
2339 AUDIO_END_ID,
2340 2,
2341 AUDIO_START_ID,
2342 AUDIO_PAD_ID,
2343 AUDIO_PAD_ID,
2344 AUDIO_PAD_ID,
2345 AUDIO_END_ID
2346 ]
2347 );
2348 assert!(expand_audio_placeholders(&ids, &[2]).is_err());
2349 assert!(expand_audio_placeholders(&ids, &[2, 3, 4]).is_err());
2350 assert!(expand_audio_placeholders(&[AUDIO_PAD_ID], &[]).is_err());
2351 }
2352
2353 #[test]
2354 fn gelu_and_bf16_rounding() {
2355 let want = [-0.004_049_71f32, -0.154_268_5, 0.345_731_5, 2.995_950_3];
2357 for (x, w) in [-3.0f32, -0.5, 0.5, 3.0].iter().zip(want) {
2358 assert!((gelu(*x) - w).abs() < 2e-6, "{x}: {} vs {w}", gelu(*x));
2359 }
2360 assert_eq!(bf16_round(1.0), 1.0);
2361 assert_eq!(bf16_round(f32::from_bits(0x3F80_8000)), 1.0); assert_eq!(
2363 bf16_round(f32::from_bits(0x3F81_8000)),
2364 f32::from_bits(0x3F82_0000)
2365 );
2366 }
2367}