Spaces:
Sleeping
Sleeping
| """compare-codec — upload audio, pick codecs, hear reconstructions side by side.""" | |
| from __future__ import annotations | |
| import time | |
| from pathlib import Path | |
| import gradio as gr | |
| import numpy as np | |
| from compare_codec import CodecConfig, get_all | |
| from compare_codec.spectrogram import generate as make_spectrogram | |
| MAX_DURATION_S = 30.0 | |
| def _encode_decode_one( | |
| audio_path: Path, codec_name: str, cfg: CodecConfig | |
| ) -> tuple[np.ndarray, int, float]: | |
| """Run one codec config and return (audio_array, sample_rate, elapsed_seconds).""" | |
| codec = get_all()[codec_name] | |
| sr = cfg.params.get("sample_rate", codec.sample_rate) | |
| t0 = time.perf_counter() | |
| audio_out = codec.encode_decode(audio_path, cfg) | |
| elapsed = time.perf_counter() - t0 | |
| max_samples = int(MAX_DURATION_S * sr) | |
| if len(audio_out) > max_samples: | |
| audio_out = audio_out[:max_samples] | |
| return audio_out, sr, elapsed | |
| def build_ui() -> gr.Blocks: | |
| codecs = get_all() | |
| with gr.Blocks(title="compare-codec") as demo: | |
| gr.Markdown("# compare-codec") | |
| audio_in = gr.Audio( | |
| sources=["upload", "microphone"], | |
| type="filepath", | |
| label="Input audio", | |
| ) | |
| run_btn = gr.Button("Reconstruct", variant="primary") | |
| tab_components: dict[str, dict] = {} | |
| with gr.Tabs(): | |
| for codec_name, codec in codecs.items(): | |
| configs = codec.configs() | |
| config_labels = [c.name for c in configs] | |
| with gr.Tab(label=codec_name) as tab: | |
| config_dd = gr.Dropdown( | |
| choices=config_labels, | |
| value=config_labels[0], | |
| label="Configuration", | |
| ) | |
| audio_out = gr.Audio( | |
| label="Reconstructed", | |
| type="numpy", | |
| interactive=False, | |
| ) | |
| stats_md = gr.Markdown(value="*Upload audio to compare.*") | |
| spec_img = gr.Image( | |
| label="Spectrogram", | |
| type="filepath", | |
| interactive=False, | |
| ) | |
| tab_components[codec_name] = { | |
| "tab": tab, | |
| "config_dd": config_dd, | |
| "audio_out": audio_out, | |
| "stats_md": stats_md, | |
| "spec_img": spec_img, | |
| "configs": configs, | |
| "config_labels": config_labels, | |
| } | |
| active_tab = gr.State(value=list(codecs.keys())[0]) | |
| ordered_names = list(codecs.keys()) | |
| all_outputs = [] | |
| for name in ordered_names: | |
| c = tab_components[name] | |
| all_outputs.extend([c["audio_out"], c["stats_md"], c["spec_img"]]) | |
| def process_active(audio_path: str | None, current_tab: str, *dropdown_values): | |
| """Reconstruct only the active tab's codec.""" | |
| if audio_path is None: | |
| return [gr.update()] * len(all_outputs) | |
| dd_map = dict(zip(ordered_names, dropdown_values)) | |
| comps = tab_components[current_tab] | |
| cfg_label = dd_map[current_tab] | |
| cfg = next(c for c in comps["configs"] if c.name == cfg_label) | |
| audio_out, sr, elapsed = _encode_decode_one( | |
| Path(audio_path), current_tab, cfg | |
| ) | |
| spec_path = make_spectrogram(audio_out, sr) | |
| stats_text = ( | |
| f"**{elapsed:.2f}s** | " | |
| f"{sr / 1000:.0f} kHz | " | |
| f"{cfg_label}" | |
| ) | |
| flat = [] | |
| for name in ordered_names: | |
| if name == current_tab: | |
| flat.extend( | |
| [ | |
| gr.update(value=(sr, audio_out)), | |
| gr.update(value=stats_text), | |
| gr.update(value=str(spec_path)), | |
| ] | |
| ) | |
| else: | |
| flat.extend([gr.update(), gr.update(), gr.update()]) | |
| return flat | |
| all_dropdowns = [tab_components[n]["config_dd"] for n in ordered_names] | |
| btn_event = run_btn.click( | |
| fn=process_active, | |
| inputs=[audio_in, active_tab] + all_dropdowns, | |
| outputs=all_outputs, | |
| ) | |
| for codec_name, comps in tab_components.items(): | |
| comps["tab"].select( | |
| fn=lambda name=codec_name: name, | |
| inputs=[], | |
| outputs=[active_tab], | |
| ) | |
| return demo | |
| demo = build_ui() | |
| if __name__ == "__main__": | |
| demo.launch() | |