ALWOAL commited on
Commit
c1b448c
·
verified ·
1 Parent(s): 924981e

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ assets/251-118436-0003.wav filter=lfs diff=lfs merge=lfs -text
37
+ assets/5_1812_20170628135856.wav filter=lfs diff=lfs merge=lfs -text
38
+ assets/p226_006.wav filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,50 @@
1
  ---
2
- title: Focalcodec Quantization
3
- emoji: 🏆
4
- colorFrom: indigo
5
  colorTo: purple
6
  sdk: gradio
7
  sdk_version: 6.30.0
8
- python_version: '3.12'
9
  app_file: app.py
10
- pinned: false
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: FocalCodec Quantization Lab
3
+ emoji: ⚡
4
+ colorFrom: blue
5
  colorTo: purple
6
  sdk: gradio
7
  sdk_version: 6.30.0
 
8
  app_file: app.py
9
+ short_description: FocalCodec weights at int4–int32 vs fp4–fp32
10
+ python_version: "3.12"
11
+ startup_duration_timeout: 30m
12
  ---
13
 
14
+ # ⚡ FocalCodec 12.5 Hz — weight quantization lab
15
+
16
+ Interactive demo of what happens to [`lucadellalib/focalcodec_12_5hz`](https://huggingface.co/lucadellalib/focalcodec_12_5hz)
17
+ when its **145 M weights** are stored at different numeric precisions:
18
+
19
+ `int4` · `int8` · `int16` · `int32` · `fp4` · `fp8` · `fp16` · `fp32`
20
+
21
+ The token stream is untouched, so the codec still runs at **0.1625 kbps**
22
+ (12.5 tokens/s × 13 bits) at every level — only the size of the network changes
23
+ (72.6 MB at 4-bit down to 581.1 MB at fp32).
24
+
25
+ ## Three views
26
+
27
+ | Tab | What it shows |
28
+ | --- | --- |
29
+ | **Compare** | One reconstructed clip + input/reconstruction spectrograms, side by side for all 8 levels |
30
+ | **Playground** | Encode → decode a single clip at one chosen level, with its metrics row |
31
+ | **Benchmark** | SI-SDR, STOI, LSD, stored model size, bitrate and real-time factor for all 8 levels, plus a summary chart |
32
+
33
+ ## Method
34
+
35
+ Post-training **fake quantization** of every floating-point tensor: per-output-channel
36
+ absmax scale, round-to-nearest in the target format, dequantize back to fp32, then run
37
+ the untouched FocalCodec forward pass. `fp4` is IEEE-style E2M1, `fp8` is E4M3, `fp16`
38
+ and `fp32` are exact casts. Nothing is fine-tuned or calibrated on data.
39
+
40
+ ⚠️ **On SI-SDR:** it is deeply negative at every level, including `fp32`. That is
41
+ expected — at 0.16 kbps FocalCodec does not preserve waveform phase, and the
42
+ [FocalCodec paper](https://arxiv.org/abs/2502.04465) reports SI-SNR ≈ −37 dB for the
43
+ untouched model. Use **STOI** (intelligibility, higher is better) and **LSD**
44
+ (log-spectral distance, lower is better) to compare levels.
45
+
46
+ ## Credits
47
+
48
+ Model, method and example audio by
49
+ [Luca Della Libera](https://huggingface.co/lucadellalib) et al. — Apache-2.0.
50
+ [FocalCodec: Low-Bitrate Speech Coding via Focal Modulation Networks](https://arxiv.org/abs/2502.04465), NeurIPS 2025.
__pycache__/app.cpython-314.pyc ADDED
Binary file (15.5 kB). View file
 
__pycache__/core.cpython-314.pyc ADDED
Binary file (13.2 kB). View file
 
app.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FocalCodec 12.5 Hz weight-quantization lab (Gradio / ZeroGPU)."""
2
+
3
+ import hashlib
4
+ import time
5
+
6
+ import spaces # must precede torch: patches torch.cuda for ZeroGPU
7
+
8
+ import gradio as gr
9
+ import soundfile as sf
10
+ import torch
11
+
12
+ import core
13
+ from core import QUANT_LEVELS
14
+
15
+ MODEL_REPO = "lucadellalib/focalcodec_12_5hz"
16
+ HUB_REPO = "lucadellalib/focalcodec"
17
+ MAX_SECONDS = 20
18
+ HEADERS = ["Level", "SI-SDR (dB)", "STOI", "LSD (dB)", "Model size (MB)", "Bitrate (kbps)", "RTF"]
19
+ DATATYPES = ["str", "number", "number", "number", "number", "number", "number"]
20
+ EXAMPLES = [
21
+ ["assets/251-118436-0003.wav"],
22
+ ["assets/p226_006.wav"],
23
+ ["assets/5_1812_20170628135856.wav"],
24
+ ]
25
+
26
+ codec = torch.hub.load(HUB_REPO, "focalcodec", config=MODEL_REPO, trust_repo=True)
27
+ codec.eval().requires_grad_(False)
28
+ codec.to("cuda")
29
+ BASE_STATE = {k: v.detach().cpu().clone() for k, v in codec.state_dict().items()}
30
+ SIZES = {level: core.model_size_mb(BASE_STATE, level) for level in QUANT_LEVELS}
31
+ SAMPLE_RATE = codec.sample_rate_input
32
+ assert codec.sample_rate_output == SAMPLE_RATE, "demo assumes a 16/16 kHz codec"
33
+
34
+
35
+ def _estimate_duration(audio_path: str, levels: tuple) -> float:
36
+ try:
37
+ seconds = min(sf.info(audio_path).duration, MAX_SECONDS)
38
+ except Exception:
39
+ seconds = float(MAX_SECONDS)
40
+ return min(240.0, 5.0 + 2.0 * len(levels) + 2.5 * seconds)
41
+
42
+
43
+ @spaces.GPU(duration=_estimate_duration)
44
+ def _encode_decode(audio_path: str, levels: tuple) -> dict:
45
+ """Reconstruct `audio_path` once per weight-quantization level, scoring each result."""
46
+ ref, sr = core.load_audio(audio_path, SAMPLE_RATE, MAX_SECONDS)
47
+ sig = torch.from_numpy(ref).unsqueeze(0).cuda()
48
+ rows, reconstructed = [], {}
49
+ for level in levels:
50
+ codec.load_state_dict(BASE_STATE if level == "fp32" else core.quantize_state(BASE_STATE, level))
51
+ torch.cuda.synchronize()
52
+ start = time.perf_counter()
53
+ with torch.inference_mode():
54
+ rec = codec.sig_to_sig(sig).squeeze(0).float().cpu().numpy()
55
+ torch.cuda.synchronize()
56
+ rtf = (time.perf_counter() - start) / (len(ref) / sr)
57
+ rows.append(core.evaluate(ref, rec, sr, rtf, SIZES[level], level))
58
+ reconstructed[level] = (sr, rec)
59
+ codec.load_state_dict(BASE_STATE)
60
+ return {"input": (sr, ref), "rows": rows, "audio": reconstructed}
61
+
62
+
63
+ _CACHE: dict[str, dict] = {}
64
+
65
+
66
+ def run(audio_path: str, levels: list | tuple) -> dict:
67
+ """Encode and decode the audio at every requested weight-quantization level.
68
+
69
+ Results are cached by audio content and level set, so the Compare and
70
+ Benchmark tabs share a single GPU pass.
71
+ """
72
+ if not audio_path:
73
+ raise gr.Error("Provide audio first: upload, record, or pick an example.")
74
+ levels = tuple(levels)
75
+ with open(audio_path, "rb") as fh:
76
+ key = hashlib.md5(fh.read()).hexdigest() + "|" + ",".join(levels)
77
+ if key not in _CACHE:
78
+ if len(_CACHE) >= 6:
79
+ _CACHE.clear()
80
+ _CACHE[key] = _encode_decode(audio_path, levels)
81
+ return _CACHE[key]
82
+
83
+
84
+ def compare(audio_path: str):
85
+ """Rebuild the audio at all eight levels and return one player + spectrogram per level."""
86
+ result = run(audio_path, QUANT_LEVELS)
87
+ audio = [result["audio"][level] for level in QUANT_LEVELS]
88
+ plots = [
89
+ core.spectrogram_figure(result["input"][1], result["audio"][level][1], SAMPLE_RATE, level)
90
+ for level in QUANT_LEVELS
91
+ ]
92
+ return audio + plots
93
+
94
+
95
+ def playground(audio_path: str, level: str):
96
+ """Rebuild the audio at a single quantization level, with its metrics row."""
97
+ result = run(audio_path, [level])
98
+ rec = result["audio"][level]
99
+ return (
100
+ rec,
101
+ core.spectrogram_figure(result["input"][1], rec[1], SAMPLE_RATE, level),
102
+ [result["rows"][0]],
103
+ )
104
+
105
+
106
+ def benchmark(audio_path: str):
107
+ """Score every quantization level on one input: metrics table plus summary chart."""
108
+ result = run(audio_path, QUANT_LEVELS)
109
+ return result["rows"], core.metrics_figure(result["rows"])
110
+
111
+
112
+ HEADER = f"""# ⚡ FocalCodec 12.5 Hz — weight quantization lab
113
+
114
+ Same checkpoint ([`lucadellalib/focalcodec_12_5hz`](https://huggingface.co/lucadellalib/focalcodec_12_5hz)),
115
+ eight numeric formats for its **{sum(v.numel() for v in BASE_STATE.values()) / 1e6:.0f} M weights**: `int4 … int32` and `fp4 … fp32`.
116
+ The bitrate never changes — **{core.BITRATE_KBPS:g} kbps** (12.5 tokens/s × 13 bits) — only the storage precision of the network itself.
117
+
118
+ **Method:** post-training fake quantization, per-output-channel absmax scaling, round-to-nearest, dequantized back to fp32 for inference. `fp4` = E2M1, `fp8` = E4M3, `fp16`/`fp32` are exact casts. No fine-tuning.
119
+
120
+ ⚠️ *SI-SDR sits far below 0 dB at every level: at 0.16 kbps the codec does not preserve waveform phase, and the FocalCodec paper reports SI-SNR ≈ −37 dB for the untouched model. It is an anchor, not a quality score — read **STOI** (intelligibility) and **LSD** (log-spectral distance, lower is better) instead.*
121
+ """
122
+
123
+ FOOTER = """Audio examples from the [FocalCodec repository](https://github.com/lucadellalib/focalcodec)
124
+ (LibriSpeech, VoiceBank, MagicData). Model and method by Luca Della Libera et al., Apache-2.0."""
125
+
126
+ with gr.Blocks(title="FocalCodec quantization lab", theme=gr.themes.Soft()) as demo:
127
+ gr.Markdown(HEADER)
128
+
129
+ with gr.Tab("Compare"):
130
+ with gr.Row():
131
+ with gr.Column(scale=1, min_width=320):
132
+ cmp_input = gr.Audio(
133
+ sources=["upload", "microphone"], type="filepath", label="Input audio"
134
+ )
135
+ gr.Examples(examples=EXAMPLES, inputs=cmp_input, label="Examples")
136
+ cmp_button = gr.Button("Reconstruct at all 8 levels", variant="primary")
137
+ gr.Markdown(f"Input capped at {MAX_SECONDS} s.")
138
+ with gr.Column(scale=4):
139
+ cmp_audio, cmp_plot = {}, {}
140
+ for chunk in (QUANT_LEVELS[:4], QUANT_LEVELS[4:]):
141
+ with gr.Row():
142
+ for level in chunk:
143
+ with gr.Column():
144
+ gr.Markdown(f"**{level}**")
145
+ cmp_audio[level] = gr.Audio(interactive=False, label=level)
146
+ cmp_plot[level] = gr.Plot(label=f"{level} spectrogram")
147
+ cmp_button.click(
148
+ compare,
149
+ inputs=cmp_input,
150
+ outputs=[cmp_audio[level] for level in QUANT_LEVELS]
151
+ + [cmp_plot[level] for level in QUANT_LEVELS],
152
+ )
153
+
154
+ with gr.Tab("Playground"):
155
+ with gr.Row():
156
+ with gr.Column(scale=1, min_width=320):
157
+ pg_input = gr.Audio(
158
+ sources=["upload", "microphone"], type="filepath", label="Input audio"
159
+ )
160
+ gr.Examples(examples=EXAMPLES, inputs=pg_input, label="Examples")
161
+ pg_level = gr.Dropdown(
162
+ choices=list(QUANT_LEVELS), value="int8", label="Weight format"
163
+ )
164
+ pg_button = gr.Button("Encode + decode", variant="primary")
165
+ with gr.Column(scale=3):
166
+ pg_output = gr.Audio(interactive=False, label="Reconstruction")
167
+ pg_plot = gr.Plot(label="Input vs reconstruction")
168
+ pg_table = gr.Dataframe(headers=HEADERS, datatype=DATATYPES, interactive=False)
169
+ pg_button.click(
170
+ playground, inputs=[pg_input, pg_level], outputs=[pg_output, pg_plot, pg_table]
171
+ )
172
+
173
+ with gr.Tab("Benchmark"):
174
+ with gr.Row():
175
+ with gr.Column(scale=1, min_width=320):
176
+ bm_input = gr.Audio(
177
+ sources=["upload", "microphone"], type="filepath", label="Input audio"
178
+ )
179
+ gr.Examples(examples=EXAMPLES, inputs=bm_input, label="Examples")
180
+ bm_button = gr.Button("Run all 8 levels", variant="primary")
181
+ with gr.Column(scale=3):
182
+ gr.Markdown(
183
+ "**STOI** ↑ intelligibility · **LSD** ↓ spectral error · "
184
+ "**RTF** = encode+decode time ÷ audio duration · size assumes "
185
+ "the stored weight format."
186
+ )
187
+ bm_table = gr.Dataframe(headers=HEADERS, datatype=DATATYPES, interactive=False)
188
+ bm_plot = gr.Plot(label="Quality and size per level")
189
+ bm_button.click(benchmark, inputs=bm_input, outputs=[bm_table, bm_plot])
190
+
191
+ gr.Markdown(FOOTER)
192
+
193
+ demo.launch(mcp_server=True)
assets/251-118436-0003.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:340ee98cdd138b743dcf12d82bc2d83b39b72a413bb6f1d8a975d7d1ae8d7217
3
+ size 705360
assets/5_1812_20170628135856.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dc7aaa044b83ba0a88b4089d2914440f82490456ce3b294297847b60a8aa7050
3
+ size 151624
assets/p226_006.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:53b9be9b754b28c4907469333a1d938bc3d6ae7e513e7b371c999f337720d095
3
+ size 489980
core.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Quantization, audio IO, objective metrics and plots for the FocalCodec lab.
2
+
3
+ Kept free of Gradio / torch.hub so it can be exercised standalone.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import matplotlib
9
+
10
+ matplotlib.use("Agg")
11
+
12
+ import matplotlib.pyplot as plt
13
+ import numpy as np
14
+ import soundfile as sf
15
+ import torch
16
+ from pystoi import stoi
17
+ from scipy.signal import resample_poly, stft
18
+
19
+ QUANT_LEVELS = ("int4", "int8", "int16", "int32", "fp4", "fp8", "fp16", "fp32")
20
+
21
+ TOKEN_RATE_HZ = 12.5
22
+ BITS_PER_TOKEN = 13
23
+ BITRATE_KBPS = TOKEN_RATE_HZ * BITS_PER_TOKEN / 1000.0
24
+
25
+ _BITS = {"int4": 4, "int8": 8, "int16": 16, "int32": 32, "fp4": 4, "fp8": 8, "fp16": 16, "fp32": 32}
26
+ _FP_FORMATS = {"fp4": (2, 1), "fp8": (4, 3)} # (exponent bits, mantissa bits) -> E2M1 / E4M3
27
+
28
+
29
+ def _ref_scale(w: torch.Tensor) -> torch.Tensor:
30
+ """Per-output-channel absmax, per-tensor for 1-D params."""
31
+ if w.dim() > 1:
32
+ return w.abs().amax(dim=tuple(range(1, w.dim())), keepdim=True)
33
+ return w.abs().amax()
34
+
35
+
36
+ def _quant_int(w: torch.Tensor, bits: int) -> torch.Tensor:
37
+ if bits >= 32:
38
+ return w
39
+ qmax = 1 << (bits - 1)
40
+ scale = (_ref_scale(w) / qmax).clamp_min(1e-12)
41
+ return (w / scale).round().clamp(-qmax, qmax - 1) * scale
42
+
43
+
44
+ def _quant_fp(w: torch.Tensor, e_bits: int, m_bits: int) -> torch.Tensor:
45
+ bias = (1 << (e_bits - 1)) - 1
46
+ max_exp = (1 << e_bits) - 1 - bias
47
+ max_norm = (2.0 - 2.0 ** -m_bits) * 2.0**max_exp
48
+ scale = (_ref_scale(w) / max_norm).clamp_min(1e-12)
49
+ x = w / scale
50
+ ax = x.abs()
51
+ exp = torch.floor(torch.log2(ax.clamp_min(1e-30))).clamp(1 - bias, max_exp)
52
+ step = torch.pow(2.0, exp - m_bits)
53
+ return torch.sign(x) * ((ax / step).round() * step) * scale
54
+
55
+
56
+ def quantize_state(state: dict, level: str) -> dict:
57
+ """Fake-quantize every float tensor in `state` to `level` (per-channel absmax scaling)."""
58
+ if level == "fp32":
59
+ return dict(state)
60
+ out = {}
61
+ for key, value in state.items():
62
+ if not value.is_floating_point():
63
+ out[key] = value
64
+ elif level == "fp16":
65
+ out[key] = value.half().float()
66
+ elif level.startswith("int"):
67
+ out[key] = _quant_int(value, int(level[3:]))
68
+ else:
69
+ out[key] = _quant_fp(value, *_FP_FORMATS[level])
70
+ return out
71
+
72
+
73
+ def model_size_mb(state: dict, level: str) -> float:
74
+ return sum(v.numel() for v in state.values()) * _BITS[level] / 8 / 1e6
75
+
76
+
77
+ def load_audio(path: str, target_sr: int, max_seconds: float | None = None) -> tuple[np.ndarray, int]:
78
+ """Read `path` as mono float32, resample to `target_sr`, optionally truncate."""
79
+ x, sr = sf.read(path, dtype="float32", always_2d=True)
80
+ x = x.mean(axis=1)
81
+ if sr != target_sr:
82
+ g = np.gcd(sr, target_sr)
83
+ x = resample_poly(x, target_sr // g, sr // g).astype(np.float32)
84
+ sr = target_sr
85
+ if max_seconds is not None and len(x) > int(max_seconds * sr):
86
+ x = x[: int(max_seconds * sr)]
87
+ return x, sr
88
+
89
+
90
+ def si_sdr(ref: np.ndarray, rec: np.ndarray) -> float:
91
+ ref = ref - ref.mean()
92
+ rec = rec - rec.mean()
93
+ denom = float(ref @ ref) + 1e-12
94
+ target = (float(rec @ ref) / denom) * ref
95
+ noise = rec - target
96
+ return float(10 * np.log10((float(target @ target) + 1e-12) / (float(noise @ noise) + 1e-12)))
97
+
98
+
99
+ def log_spectral_distance(ref: np.ndarray, rec: np.ndarray, sr: int) -> float:
100
+ """Frame-averaged log-spectral distance (dB) between reference and reconstruction."""
101
+ _, _, R = stft(ref, fs=sr, nperseg=1024, noverlap=768)
102
+ _, _, S = stft(rec, fs=sr, nperseg=1024, noverlap=768)
103
+ diff = 20 * np.log10(np.abs(R) + 1e-8) - 20 * np.log10(np.abs(S) + 1e-8)
104
+ return float(np.mean(np.sqrt(np.mean(diff**2, axis=0))))
105
+
106
+
107
+ def evaluate(ref: np.ndarray, rec: np.ndarray, sr: int, rtf: float, size_mb: float, level: str) -> dict:
108
+ n = min(len(ref), len(rec))
109
+ ref, rec = ref[:n], rec[:n]
110
+ return {
111
+ "Level": level,
112
+ "SI-SDR (dB)": round(si_sdr(ref, rec), 2),
113
+ "STOI": round(float(stoi(ref, rec, sr, extended=False)), 3),
114
+ "LSD (dB)": round(log_spectral_distance(ref, rec, sr), 2),
115
+ "Model size (MB)": round(size_mb, 1),
116
+ "Bitrate (kbps)": round(BITRATE_KBPS, 4),
117
+ "RTF": round(rtf, 3),
118
+ }
119
+
120
+
121
+ def spectrogram_figure(ref: np.ndarray, rec: np.ndarray, sr: int, title: str):
122
+ fig, axes = plt.subplots(1, 2, figsize=(9.0, 2.6), sharey=True)
123
+ for ax, sig, label in zip(axes, (ref, rec), ("Input", "Reconstruction")):
124
+ ax.specgram(sig, NFFT=1024, noverlap=768, Fs=sr, cmap="magma")
125
+ ax.set_title(label, fontsize=9)
126
+ ax.set_xlabel("Time (s)")
127
+ axes[0].set_ylabel("Frequency (Hz)")
128
+ fig.suptitle(title, fontsize=10)
129
+ fig.tight_layout()
130
+ return fig
131
+
132
+
133
+ def metrics_figure(rows: list[dict]):
134
+ levels = [r["Level"] for r in rows]
135
+ panels = (("SI-SDR (dB)", "#4c72b0"), ("STOI", "#55a868"), ("Model size (MB)", "#c44e52"))
136
+ fig, axes = plt.subplots(1, len(panels), figsize=(11.5, 2.9))
137
+ for ax, (key, color) in zip(axes, panels):
138
+ ax.bar(levels, [r[key] for r in rows], color=color)
139
+ ax.set_title(key, fontsize=9)
140
+ ax.tick_params(axis="x", rotation=45)
141
+ ax.grid(axis="y", alpha=0.3)
142
+ fig.tight_layout()
143
+ return fig
144
+
145
+
146
+ if __name__ == "__main__":
147
+ torch.manual_seed(0)
148
+ w = (torch.randn(64, 32) * 0.05).requires_grad_(False)
149
+ for level in QUANT_LEVELS:
150
+ q = quantize_state({"w": w}, level)["w"]
151
+ assert torch.isfinite(q).all(), f"{level} not finite"
152
+ assert q.abs().max() <= w.abs().max() * 1.01 + 1e-9, f"{level} broke the magnitude bound"
153
+ x = np.random.randn(16000).astype(np.float32)
154
+ assert si_sdr(x, x) > 60, "SI-SDR of identical signals should be large"
155
+ assert si_sdr(x, 3 * x) > 60, "SI-SDR must be scale invariant"
156
+ assert abs(evaluate(x, x, 16000, 1.0, 1.0, "fp32")["STOI"] - 1.0) < 1e-3
157
+ assert evaluate(x, x, 16000, 1.0, 1.0, "fp32")["Bitrate (kbps)"] == round(BITRATE_KBPS, 4)
158
+ print("core self-check ok")
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ matplotlib
2
+ pystoi
3
+ safetensors
4
+ scipy
5
+ soundfile