from __future__ import annotations import gradio as gr try: import spaces except ImportError: # 'spaces' is only provided by Hugging Face Spaces import types as _types def _gpu(*args, **kwargs): if len(args) == 1 and callable(args[0]) and not kwargs: return args[0] def _decorator(func): return func return _decorator spaces = _types.SimpleNamespace(GPU=_gpu) from pyharp import * import tempfile from voicefixer import VoiceFixer # Initialize VoiceFixer. It handles downloading checkpoints and setting device. # The VoiceFixer class automatically detects and uses CUDA if available. voicefixer_model = VoiceFixer() model_card = ModelCard( name="VoiceFixer", description=( "VoiceFixer aims to restore human speech regardless how serious its " "degraded. It can handle noise, reverberation, low resolution " "(2kHz~44.1kHz) and clipping (0.1-1.0 threshold) effect within one " "model. The model is fixed-rate and mono, so the result is resampled " "back to the input's rate and copied across its channels: the output " "has the format it came in with, but no stereo image. Repository: " "https://github.com/haoheliu/voicefixer Paper: " "https://arxiv.org/abs/2109.13731" ), author="Haohe Liu, Qiuqiang Kong, Qiao Tian, Yan Zhao, DeLiang Wang, Chuanzeng Huang, Yuxuan Wang", tags=[Subcategory.RESTORATION, SampleRate(44100), Channels(1), 'speech'], ) @spaces.GPU def match_input_format(output_path: str, input_path: str) -> str: """ Rewrite an output file at the sample rate and channel count it came in with. The models here are fixed-rate and mono, and hand the writing to vendored upstream code, so the format is corrected afterwards rather than threaded through inference. Duplicated channels are copies, not a recovered stereo image; the model card says so. Args: output_path (str): File the model wrote. Overwritten in place. input_path (str): File the request supplied. Returns: output_path (str): The same path, for use in a return statement. """ import librosa import numpy as np import soundfile want = soundfile.info(input_path) have = soundfile.info(output_path) if have.samplerate == want.samplerate and have.channels == want.channels: return output_path data, rate = soundfile.read(output_path, always_2d=True) if rate != want.samplerate: data = librosa.resample( data.T, orig_sr=rate, target_sr=want.samplerate ).T if data.shape[1] == 1 and want.channels > 1: data = np.tile(data, (1, want.channels)) elif data.shape[1] > 1 and want.channels == 1: data = data.mean(axis=1, keepdims=True) soundfile.write(output_path, data, want.samplerate) return output_path def process_fn(input_audio, mode): output_file = tempfile.NamedTemporaryFile(suffix=".wav", delete=False).name voicefixer_model.restore(input_audio, output_file, mode=int(mode)) return match_input_format(output_file, input_audio) if __name__ == "__main__": with gr.Blocks() as demo: input_components = [ gr.Audio(type="filepath", label="Input Audio").harp_required(True).set_info("Upload an audio file to be processed by VoiceFixer."), gr.Dropdown(choices=["0", "1", "2"], value="0", label="Processing Mode", info="Select the VoiceFixer processing mode:\n0: Original Model (suggested by default)\n1: Add preprocessing module (remove higher frequency)\n2: Train mode (might work sometimes on seriously degraded real speech)"), ] output_components = [ gr.Audio(type="filepath", label="Fixed Audio").set_info("The enhanced audio output from VoiceFixer."), ] build_endpoint( model_card=model_card, input_components=input_components, output_components=output_components, process_fn=process_fn, ) demo.queue().launch(show_error=True, pwa=True)