import os import io import sys import time import gradio as gr from typing import List, Union, Any, Dict, Optional import torch import numpy as np import librosa from transformers import SpeechSynthesisPipeline, AutoTokenizer, Wav2Vec2ForCTC, Wav2Vec2Processor from pypdfium2 import PdfDocument, Page def convert_pdf_to_text(filepath): doc = PdfDocument(filepath) text = "" for page in doc.pages(): text += page.extract_text() + "\n\n" return text class QuantizedSpeechT5TTSPipe: def __init__(self): self.tokenizer = AutoTokenizer.from_pretrained('facebook/wav2vec2-base-960h') self.model = Wav2Vec2ForCTC.from_pretrained('/model/quantized_model').half().cuda() self.processor = Wav2Vec2Processor.from_pretrained("/model/quantized_vocab") def _pad_and_concatenate(self, tensor_list: List[torch.Tensor], padding_value=0): max_length = max([tensor.size(0) for tensor in tensor_list]) padded_tensors = [] for tensor in tensor_list: pad_width = max_length - tensor.size(0) if pad_width > 0: tensor_padded = torch.cat((tensor, torch.full((pad_width, tensor.size(1)), fill_value=padding_value).type_as(tensor))) else: tensor_padded = tensor padded_tensors.append(tensor_padded) return torch.stack(padded_tensors) def preprocess(self, inputs: Union[str, List[str]], **kwargs) -> dict: if isinstance(inputs, str): inputs = [inputs] batch_encodings = self.tokenizer(inputs, truncation=True, padding='longest', return_tensors="pt").input_values return {"batch_encodings": batch_encodings} def postprocess(self, outputs: Dict[str, torch.Tensor], **kwargs) -> Union[List[str], List[bytes]]: logits = outputs["logits"].cpu().detach().numpy() ids = np.argmax(logits, axis=-1) cleaned_ids = [id_seq[:np.where(np.array(id_seq) == 2)[0][0]] for id_seq in ids] # Remove CTC blanks decoded_strings = self.tokenizer.decode(cleaned_ids) audios = [] for text in decoded_strings: input_values = self.processor(text, sampling_rate=16000, return_tensors="pt").input_values input_values = input_values.cuda().unsqueeze(0) mel_outputs = self.model(input_values).mel_output _, predicted_ids = torch.topk(mel_outputs.float(), k=1, dim=-1) predicted_ids = predicted_ids.squeeze(-1).tolist()[0] raw_waveform = self.processor.post_processing(predicted_ids) waveform = raw_waveform * 32768 / max(abs(raw_waveform)) wav_data = np.int16(waveform) audio = io.BytesIO() sf.write(audio, int(44100), wav_data, format="WAV") audios.append(audio.getvalue()) return audios def generate(self, text: str): processed_inputs = self.preprocess(text) outputs = self.model(**processed_inputs) results = self.postprocess(outputs) return results if __name__ == "__main__": tts = QuantizedSpeechT5TTSPipe() sample_text = 'Hello world! This is a test.' result = tts.generate(sample_text) print(f'Generated {len(result)} audio files from "{sample_text}"') def main(pdf_file: gr.File, output_filename: str): start_time = time.time() pdf_text = convert_pdf_to_text(pdf_file) print(f'Processed PDF content in {time.time() - start_time:.4f} seconds') pipe = QuantizedSpeechT5TTSPipe() start_time = time.time() audios = pipe.generate(pdf_text) print(f'Generated {len(audios)} audio files in {time.time() - start_time:.4f} seconds') zip_buffer = BytesIO() with ZipFile(zip_buffer, mode='w') as zf: for i, audio in enumerate(audios): filename = f"{i}_{output_filename}.wav" zf.writestr(filename, audio) zip_buffer.seek(0) return {'zip': zip_buffer} iface = gr.Interface(fn=main, inputs="file", outputs="binary", input_types=['pdf'], output_types=['download']) iface.launch()