Team Ai
Apppublic

JacobLinCool/forced-alignment

sourceHugging Facemitupdated 2y agoView on Hugging Face
0likes
app.py197 linesDownload Raw Back to root
1import spaces2import gradio as gr3import json4import torch5import numpy as np6import librosa7from accelerate.utils.imports import is_cuda_available8from iso639 import iter_langs9from ctc_forced_aligner import (10    load_alignment_model,11    generate_emissions,12    preprocess_text,13    get_alignments,14    get_spans,15    postprocess_results,16)17 18device = "cuda" if is_cuda_available() else "cpu"19dtype = torch.float16 if is_cuda_available() else torch.float3220 21 22alignment_model, alignment_tokenizer = load_alignment_model(23    device,24    dtype=dtype,25)26 27 28def process_alignment(audio_waveform, text, language="eng"):29    print(f"{audio_waveform.shape=}, {text=}, {language=}")30    # Generate emissions31    emissions, stride = generate_emissions(32        alignment_model, audio_waveform, batch_size=1633    )34 35    # Preprocess text36    tokens_starred, text_starred = preprocess_text(37        text,38        romanize=True,39        language=language,40    )41 42    # Get alignments43    segments, scores, blank_id = get_alignments(44        emissions,45        tokens_starred,46        alignment_tokenizer,47    )48 49    # Get spans and word timestamps50    spans = get_spans(tokens_starred, segments, blank_id)51    word_timestamps = postprocess_results(text_starred, spans, stride, scores)52 53    return word_timestamps54 55 56def trim_audio(audio_array, sample_rate, word_timestamps):57    start_time = int(word_timestamps[0]["start"] * sample_rate)58    end_time = int(word_timestamps[-1]["end"] * sample_rate)59    print(f"{start_time=}, {end_time=}")60    trimmed_audio = audio_array[start_time:end_time]61    return (sample_rate, trimmed_audio)62 63 64def get_language_choices():65    return [f"{lang.pt3} - {lang.name}" for lang in iter_langs() if lang.pt3]66 67 68@spaces.GPU69def align(audio, text, language="eng - English"):70    # Extract the ISO 639-3 code from the selected language71    iso_code = language.split(" - ")[0]72 73    # Convert the input audio to 16kHz mono74    sample_rate, audio_array = audio75    audio_array = (76        audio_array.astype(np.float32) / 32768.077    )  # Convert to float32 and normalize78    print(f"{sample_rate=}, {audio_array.shape=}")79 80    if len(audio_array.shape) > 1:81        audio_array = audio_array.mean(axis=1)  # Convert to mono if stereo82    audio_array = librosa.resample(audio_array, orig_sr=sample_rate, target_sr=16000)83 84    # Convert to torch tensor and move to the correct device85    audio_waveform = torch.from_numpy(audio_array).to(device=device, dtype=dtype)86 87    # Process the alignment88    word_timestamps = process_alignment(audio_waveform, text, iso_code)89 90    # Trim the audio91    trimmed_audio = trim_audio(audio_array, 16000, word_timestamps)92 93    # Create JSON output94    output_json = {95        "input_text": text,96        "word_timestamps": word_timestamps,97        "language": language,98    }99 100    return trimmed_audio, json.dumps(output_json, indent=2)101 102 103@spaces.GPU104def align_result_only(audio, text, language="eng - English"):105    # Extract the ISO 639-3 code from the selected language106    iso_code = language.split(" - ")[0]107 108    # Convert the input audio to 16kHz mono109    sample_rate, audio_array = audio110    audio_array = (111        audio_array.astype(np.float32) / 32768.0112    )  # Convert to float32 and normalize113    print(f"{sample_rate=}, {audio_array.shape=}")114 115    if len(audio_array.shape) > 1:116        audio_array = audio_array.mean(axis=1)  # Convert to mono if stereo117    audio_array = librosa.resample(audio_array, orig_sr=sample_rate, target_sr=16000)118 119    # Convert to torch tensor and move to the correct device120    audio_waveform = torch.from_numpy(audio_array).to(device=device, dtype=dtype)121 122    # Process the alignment123    word_timestamps = process_alignment(audio_waveform, text, iso_code)124 125    # Create JSON output126    output_json = {127        "input_text": text,128        "word_timestamps": word_timestamps,129        "language": language,130    }131 132    return json.dumps(output_json, indent=2)133 134 135# Create Gradio blocks136with gr.Blocks() as demo:137    gr.Markdown("# Forced Alignment")138 139    gr.Markdown(140        """141    This tool aligns audio with text and provides word-level timestamps.142    143    ## How to use:144    1. Upload an audio file or record audio145    2. Enter the corresponding text146    3. Select the language147    4. Click 'Process' to get the alignment results148    """149    )150 151    with gr.Row():152        with gr.Column():153            audio_input = gr.Audio(label="Input Audio")154            text_input = gr.Textbox(label="Input Text")155            language_input = gr.Dropdown(156                choices=get_language_choices(), label="Language", value="eng - English"157            )158            submit_button = gr.Button(159                "Get Alignment and Trimmed Audio", variant="primary"160            )161            submit_button_result_only = gr.Button(162                "Get Alignment Only", variant="secondary"163            )164 165        with gr.Column():166            audio_output = gr.Audio(label="Trimmed Output Audio")167            json_output = gr.JSON(label="Alignment Results")168 169    submit_button.click(170        fn=align,171        inputs=[audio_input, text_input, language_input],172        outputs=[audio_output, json_output],173    )174 175    submit_button_result_only.click(176        fn=align_result_only,177        inputs=[audio_input, text_input, language_input],178        outputs=[json_output],179    )180 181    gr.Markdown("## Examples")182    gr.Examples(183        examples=[184            ["examples/example1.mp3", "我們搭上公車要回台北了", "zho - Chinese"],185            [186                "examples/example2.wav",187                "ON SATURDAY MORNINGS WHEN THE SODALITY MET IN THE CHAPEL TO RECITE THE LITTLE OFFICE HIS PLACE WAS A CUSHIONED KNEELING DESK AT THE RIGHT OF THE ALTAR FROM WHICH HE LED HIS WING OF BOYS THROUGH THE RESPONSES",188                "eng - English",189            ],190        ],191        inputs=[audio_input, text_input, language_input],192    )193 194# Launch the demo195if __name__ == "__main__":196    demo.launch()197