From 7eacf6332aa913367adfb05f1451363d6d7d5a3b Mon Sep 17 00:00:00 2001 From: Kralot Date: Mon, 3 Feb 2025 23:58:35 -0300 Subject: [PATCH] Repo Begin --- Edge TTS.py | 43 ++++++ Text-to-Speech GUI v1.1.py | 198 ++++++++++++++++++++++++++ Text-to-Speech.py | 280 +++++++++++++++++++++++++++++++++++++ 3 files changed, 521 insertions(+) create mode 100755 Edge TTS.py create mode 100755 Text-to-Speech GUI v1.1.py create mode 100755 Text-to-Speech.py diff --git a/Edge TTS.py b/Edge TTS.py new file mode 100755 index 0000000..60961a3 --- /dev/null +++ b/Edge TTS.py @@ -0,0 +1,43 @@ +import asyncio, edge_tts, random +from edge_tts import VoicesManager + +# - - - - - - - - - - - - - - - - - - - + +async def generate(text, voice, rate="+0%", volume="+0%", pitch="+0Hz") -> None: + communicate = edge_tts.Communicate(f"{text}", + f"{voice}", + rate=f"{rate}", + volume=f"{volume}", + pitch=f"{pitch}" + ) + await communicate.save(OUTPUT_FILE) + +async def generate_select_voice(gender, language) -> None: + voices = await VoicesManager.create() + voice = voices.find(Gender=f"{gender}", Language=f"{language}") + # Also supports Locales + # voice = voices.find(Gender="Female", Locale="es-AR") + + communicate = edge_tts.Communicate(TEXTO, random.choice(voice)["Name"]) + await communicate.save(OUTPUT_FILE) + +async def stream(text, voice) -> None: + communicate = edge_tts.Communicate(f"{text}", f"{voice}") + with open(OUTPUT_FILE, "wb") as file: + async for chunk in communicate.stream(): + if chunk["type"] == "audio": + file.write(chunk["data"]) + elif chunk["type"] == "WordBoundary": + print(f"WordBoundary: {chunk}") + +# - - - - - - - - - - - - - - - - - - - + +VOICE_MALE_PTBR = "pt-BR-AntonioNeural" +VOICE_FEMALE_PTBR = "pt-BR-FranciscaNeural" +OUTPUT_FILE = "output.mp3" + +TEXTO = "Olá, seja bem vindo!" + +# - - - - - - - - - - - - - - - - - - - + +#asyncio.run(generate(TEXTO, VOICE_MALE_PTBR)) \ No newline at end of file diff --git a/Text-to-Speech GUI v1.1.py b/Text-to-Speech GUI v1.1.py new file mode 100755 index 0000000..7e1b592 --- /dev/null +++ b/Text-to-Speech GUI v1.1.py @@ -0,0 +1,198 @@ +from PySimpleGUI import PySimpleGUI as sg +import asyncio, edge_tts, os, requests + +# - - - - - - - - - - - - - - - - - - - + +sg.theme('DarkGrey1') # Tema do PySimpleGUI + +nome_app = "Text-to-Speech GUI v1.1" + +menu_bar = [ # Menu da Janela Principal + ['Arquivo',[ + '!Abrir', + '!Salvar', + '---', + '!Propriedades', + 'Sair' + ]], + ['Ajuda',[ + 'Verificar Serviço', + '---', + 'Sobre' + ] + ] +] + +NAME_SIZE = 10 +def name(name): + dots = NAME_SIZE-len(name)-2 + return sg.Text(name + ':' + ' '*dots, size=(NAME_SIZE,1), justification='left',pad=(0,0)) + +layout = [ # Layout da Janela Principal + [sg.Menu(menu_bar)], + [name('Texto'), sg.Multiline(key='TEXTO', size=(35,10), expand_x=True, border_width=(1))], + [name('Voz'), sg.Combo(['Feminina (Português Brasileiro)', 'Masculina (Português Brasileiro)'], default_value='Feminina (Português Brasileiro)', readonly=True, expand_x=True, key='VOICE')], + [sg.Sizer(0,10)], + [sg.HorizontalSeparator()], + [sg.Sizer(0,10)], + [name('Velocidade'), sg.Slider((-10, 10), orientation='horizontal', default_value='0', size=(16,15), expand_x=True, key='RATE')], + [name('Volume'), sg.Slider((-10, 10), orientation='horizontal', default_value='0', size=(16,15), expand_x=True, key='VOLUME')], + [name('Pitch'), sg.Slider((-10, 10), orientation='horizontal', default_value='0', size=(16,15), expand_x=True, key='PITCH')], + [sg.Sizer(0,10)], + [sg.HorizontalSeparator()], + [sg.Sizer(0,10)], + [sg.Text('Nome do Arquivo:'), sg.Input(key='OUTPUT_FILE', size=(20,1), expand_x=True)], + [sg.Sizer(0,5)], + [sg.Checkbox("Abrir arquivo após conclusão", key='OPEN_FILE')], + [sg.Sizer(0,10)], + [sg.Button('Gerar Áudio'), sg.Button('Abrir Arquivo'), sg.Button('Limpar Logs')], + [sg.Sizer(0,10)], + [sg.HorizontalSeparator()], + [sg.Output(size=(0,8), expand_x=True, key='output_window')] +] + +window_main = sg.Window(f'{nome_app} | by: Kralot', # Definições da Janela Principal + layout, + resizable=True, + auto_save_location=True,) + +# Janela: Ajuda → Verificar Serviço +def create_window_verificar_servico(): + + verificar_servico = [ # Layout da Janela + [sg.Text("Verificando disponibilidade do Microsoft Edge Text-to-Speech")], + [sg.Text("Aguarde, o programa não está travado!")], + [sg.HorizontalSeparator()], + [sg.Output(size=(50,10), expand_x=True)] + ] + + window_verificar_servico_location = (window_main.CurrentLocation()[0] + -10, # Localização Horizontal + window_main.CurrentLocation()[1] + 57) # Localização Vertical + + # Configuração da Janela + window_verificar_servico = sg.Window('Verificar Serviço', + verificar_servico, + location=window_verificar_servico_location, + resizable=True) + + + # Ler os Eventos da Janela e criar uma lista + eventos_verificar_servico, valores_verificar_servico = window_verificar_servico.read(timeout=0) # Gerar a Janela + + # Configuração dos Eventos da Janela + if eventos_verificar_servico == sg.TIMEOUT_EVENT: + verificar_tts = requests.get('https://speech.platform.bing.com/') + print(verificar_tts.text) + if eventos_verificar_servico == sg.WINDOW_CLOSED: # Verificar fechamento da Janela + window_verificar_servico.close() # Fechar a Janela + +# Janela: Ajuda → Sobre +def create_window_sobre(): + sobre = [ # Layout da Janela + [sg.Text('Este programa foi criado utilizando a biblioteca EdgeTTS para Python, que utiliza o serviço Text-To-Speech da Microsoft para gerar áudios a partir de textos.', + size=(51,4))], + [sg.Text('Interface criada utilizando PySimpleGUI')], + [sg.Text('Criado por: Eduardo Riguetto (Kralot)')] + ] + + window_sobre_location = (window_main.CurrentLocation()[0] + -25, # Localização Horizontal + window_main.CurrentLocation()[1] + 57) # Localização Vertical + + window_sobre = sg.Window('Sobre', + sobre, + location=window_sobre_location) + + # Ler os Eventos da Janela e criar uma lista + eventos_sobre, valores_sobre = window_sobre.read() # Gerar a Janela + + # Configuração dos Eventos da Janela + if eventos_sobre == sg.WINDOW_CLOSED: # Verificar fechamento da Janela + window_sobre.close() # Fechar a Janela + +# - - - - - - - - - - - - - - - - - - - + +def button_generate_audio(valores): # Tratar os eventos ao clicar no botão "Gerar Áudio" + + global OUTPUT_FILE + + TEXTO = valores['TEXTO'] + + if valores['VOICE'] == 'Feminina (Português Brasileiro)': + VOICE = 'pt-BR-FranciscaNeural' + if valores['VOICE'] == 'Masculina (Português Brasileiro)': + VOICE = 'pt-BR-AntonioNeural' + + RATE = int(valores['RATE'])*10 + MODIFY_RATE = f'+{RATE}%' if RATE >= 0 else f'{RATE}%' + + VOLUME = int(valores['VOLUME'])*10 + MODIFY_VOLUME = f'+{VOLUME}%' if VOLUME >= 0 else f'{VOLUME}%' + + PITCH = int(valores['PITCH'])*10 + MODIFY_PITCH = f'+{PITCH}Hz' if PITCH >= 0 else f'{PITCH}Hz' + + FILE_NAME = valores['OUTPUT_FILE'] + OUTPUT_FILE = f'{FILE_NAME}.mp3' + + asyncio.run(generate(TEXTO, VOICE, MODIFY_RATE, MODIFY_VOLUME, MODIFY_PITCH, OUTPUT_FILE)) + +async def generate(text, voice, rate, volume, pitch, output) -> None: # Função para execução do EdgeTTS + communicate = edge_tts.Communicate(text, voice, rate=rate, volume=volume, pitch=pitch) + await communicate.save(output) + +# - - - - - - - - - - - - - - - - - - - + +program_running = True + +while program_running: # Loop de Execução do Programa + + eventos, valores = window_main.read() # Listas de Eventos da Janela Principal + + if eventos == sg.WINDOW_CLOSED: # Verificar fechamento da Janela Principal + program_running = False # Fechar o Programa + break # Quebra o Loop de Execução do Programa + + # Menu: Arquivo → Sair + if eventos == 'Sair': + program_running = False # Fechar o Programa + window_main.close() + break + + # Menu: Ajuda → Verificar Serviço + if eventos == 'Verificar Serviço': + create_window_verificar_servico() + + # Menu: Ajuda → Sobre + if eventos == 'Sobre': + create_window_sobre() + + # Botões da Janela Principal + if eventos == 'Gerar Áudio': + try: + button_generate_audio(valores) + print("Arquivo gerado com sucesso!") + if valores['OPEN_FILE'] == True: + try: + print("Abrindo arquivo...") + print("- - - - - - - - - - - - - - -") + os.system(f'start {OUTPUT_FILE}') + except Exception as error: + print(f'Não foi possível abrir o arquivo:') + print("") + print(f'{error}') + except Exception as error: + print(f"Ocorreu um erro ao gerar o arquivo:") + print("") + print(f'{error}') + if eventos == 'Abrir Arquivo': + try: + print("Abrindo o ultimo arquivo gerado...") + print("- - - - - - - - - - - - - - -") + os.system(f'start {OUTPUT_FILE}') + except Exception as error: + print(f'Não foi possível abrir o arquivo:') + print("") + print(f'{error}') + if eventos == 'Limpar Logs': + window_main['output_window'].update('') + diff --git a/Text-to-Speech.py b/Text-to-Speech.py new file mode 100755 index 0000000..d9e14c2 --- /dev/null +++ b/Text-to-Speech.py @@ -0,0 +1,280 @@ +import asyncio, edge_tts, os + +# - - - - - - - - - - - - - - - - - - - +# Lista de Vozes em pt-BR + +VOICE_FEMALE_PTBR = "pt-BR-FranciscaNeural" +VOICE_MALE_PTBR = "pt-BR-AntonioNeural" + +# - - - - - - - - - - - - - - - - - - - +# Variáveis globais + +TEXTO = "" +VOICE = "" +MODIFY_RATE = "+0%" +MODIFY_VOLUME = "+0%" +MODIFY_PITCH = "+0Hz" +OUTPUT_FILE = "" + +# - - - - - - - - - - - - - - - - - - - +# Funções do Script + +async def generate(text, voice, rate=MODIFY_RATE, volume=MODIFY_VOLUME, pitch=MODIFY_PITCH) -> None: # Fução Edge TTS + communicate = edge_tts.Communicate(f"{text}", + f"{voice}", + rate=f"{rate}", + volume=f"{volume}", + pitch=f"{pitch}" + ) + await communicate.save(OUTPUT_FILE) + +def credits_header(): # Cabeçalho de Créditos ao Autor + print("Olá, seja Bem Vindo!") + print("Este é um Script Text-to-Speech. Com ele você pode converter qualquer texto em áudio de forma simples e rápida!") + print("- - - - - - - - - - - - - - - - - - - -") + print("Script criado em Python utilizando Edge-TTS + async.") + print("Créditos: Eduardo Riguetto (Kralot)") + print("- - - - - - - - - - - - - - - - - - - -") + print("") + +def clean_terminal(): # Limpa o Terminal e adiciona o cabeçalho de Créditos ao Autor + os.system('cls') # Limpa o Terminal + credits_header() # Adiciona o cabeçalho de créditos ao autor + +def input_texto(): # Inserir texto a ser convertido em áudio + global TEXTO + print("Digite o Texto a ser convertido em audio e pressione Enter:") + TEXTO = input() + +def select_voice(): # Selecionar Voz (Feminina ou Masculina) + global VOICE + while True: + print("Digite F para selecionar a voz Feminina ou M para selecionar a voz Masculina:") + VOICE_SELECT = input().upper() + if VOICE_SELECT == "F": + VOICE = "pt-BR-FranciscaNeural" + break + elif VOICE_SELECT == "M": + VOICE = "pt-BR-AntonioNeural" + break + else: + clean_terminal() # Limpa o Terminal + print("Escolha invalida, por favor tente novamente.") + +def select_rate(): # Alterar Velocidade da Voz + global MODIFY_RATE + while True: + while True: + try: + print("Digite um número de 1 a 10 com um operador (+ ou -) para definir a intensidade da alteração.") + MODIFY_RATE = input() + OPERATOR = MODIFY_RATE[0] + NUMBER = int(MODIFY_RATE[1:]) + if 0 <= NUMBER <= 10: + MODIFY_RATE = f"{OPERATOR}{NUMBER * 10}%" + break + else: + clean_terminal() # Limpa o Terminal + print("Escolha invalida, por favor tente novamente.") + print("Digite um número de 1 a 10 com um operador (+ ou -) para definir a intensidade da alteração.") + except ValueError: + clean_terminal() # Limpa o Terminal + print("Por favor, digite um número válido.") + break + +def select_volume(): # Alterar Volume da Voz + global MODIFY_VOLUME + while True: + while True: + try: + print("Digite um número de 1 a 10 com um operador (+ ou -) para definir a intensidade da alteração:") + MODIFY_VOLUME = input() + OPERATOR = MODIFY_VOLUME[0] + NUMBER = int(MODIFY_VOLUME[1:]) + if 0 <= NUMBER <= 10: + MODIFY_VOLUME = f"{OPERATOR}{NUMBER * 10}%" + break + else: + clean_terminal() # Limpa o Terminal + print("Escolha invalida, por favor tente novamente.") + print("Digite um número de 1 a 10 com um operador (+ ou -) para definir a intensidade da alteração:") + except ValueError: + clean_terminal() # Limpa o Terminal + print("Por favor, digite um número válido.") + break + +def select_pitch(): # Alterar Tom de Voz (Pitch) + global MODIFY_PITCH + while True: + while True: + try: + print("Digite um número de 1 a 10 com um operador (+ ou -) para definir a intensidade da alteração:") + MODIFY_PITCH = input() + OPERATOR = MODIFY_PITCH[0] + NUMBER = int(MODIFY_PITCH[1:]) + if 0 <= NUMBER <= 10: + MODIFY_PITCH = f"{OPERATOR}{NUMBER * 10}Hz" + break + else: + clean_terminal() # Limpa o Terminal + print("Escolha invalida, por favor tente novamente.") + print("Digite um número de 1 a 10 com um operador (+ ou -) para definir a intensidade da alteração:") + except ValueError: + clean_terminal() # Limpa o Terminal + print("Por favor, digite um número válido.") + break + +def select_name(): # Definir nome do Arquivo Final + global OUTPUT_FILE + print("Digite um nome para o arquivo MP3:") + OUTPUT_FILE = input().strip() + ".mp3" + OUTPUT_FILE = os.path.abspath(OUTPUT_FILE) + +def modifications_menu(): # Menu de Modificações + clean_terminal() # Limpa o Terminal + while True: + clean_terminal() # Limpa o Terminal + print("Menu de Seleção: ") + print("") + print("1. Modificar Texto") + print("2. Selecionar Voz") + print("3. Alterar Velocidade") + print("4. Alterar Volume") + print("5. Alterar Tom de Voz") + print("0. Finalizar e gerar novo áudio") + print("") + + SELECTED = input("Escolha uma opção: ") + match SELECTED: + case "1": + clean_terminal() # Limpa o Terminal + input_texto() + case "2": + clean_terminal() # Limpa o Terminal + select_voice() + case "3": + clean_terminal() # Limpa o Terminal + select_rate() + case "4": + clean_terminal() # Limpa o Terminal + select_volume() + case "5": + clean_terminal() # Limpa o Terminal + select_pitch() + case "0": + clean_terminal() # Limpa o Terminal + select_name() + print("- - - - - - - - - - - - - - - - - - - -") + generate_audio() + case _: + clean_terminal() # Limpa o Terminal + print("Escolha de Menu invalida, por favor tente novamente.") + +def generate_new_audio(): # Questiona se deseja gerar um novo áudio e encaminha + print("Você deseja modificar os parâmetros e gerar um novo áudio? (S/N)") + MODIFY = input().upper() + while True: + if MODIFY == "S": + clean_terminal() # Limpa o Terminal + modifications_menu() + break + elif MODIFY == "N": + clean_terminal() # Limpa o Terminal + input("Pressione enter para sair.") + exit() + else: + clean_terminal() # Limpa o Terminal + print("Escolha invalida, por favor tente novamente.") + print("Você deseja modificar os parâmetros e gerar um novo áudio? (S/N)") + +def generate_audio(): # Gerar o Audio diante parâmetros informados: + try: + asyncio.run(generate(TEXTO, VOICE, MODIFY_RATE, MODIFY_VOLUME, MODIFY_PITCH)) + print("Arquivo gerado com sucesso!") + print("Você deseja abrir o arquivo? (S/N)") + + while True: + ABRIR_OU_SAIR = input().strip().upper() + if ABRIR_OU_SAIR == "S": + print("Abrindo arquivo...") + os.system(f'start {OUTPUT_FILE}') + print("- - - - - - - - - - - - - - - - - - - -") + generate_new_audio() + break + elif ABRIR_OU_SAIR == "N": + clean_terminal() # Limpa o Terminal + generate_new_audio() + break + else: + clean_terminal() # Limpa o Terminal + print("Escolha invalida, por favor tente novamente.") + print("Você deseja abrir o arquivo? (S/N)") + + except ValueError as error: + print(f"Ocorreu um erro ao gerar o arquivo: {error}") + MODIFY = print("Deseja alterar os parâmetros e tentar novamente? (S/N)") + MODIFY = input().upper() + if MODIFY == "S": + clean_terminal() # Limpa o Terminal + modifications_menu() + elif MODIFY == "N": + clean_terminal() # Limpa o Terminal + input("Pressione enter para sair.") + +def main(): # Função Principal + credits_header() # Mostra o cabeçalho de Créditos ao Autor + input_texto() + clean_terminal() # Limpa o Terminal + print("Seleção de Voz:") + select_voice() + clean_terminal() # Limpa o Terminal + print("Você deseja alterar os parâmetros do gerador? (Velocidade, Volume, Pitch)") + while True: + print("Digite S para modificar parâmetros ou N para manter os parâmetros padrão:") + MODIFY_PARAMETERS_QUESTION = input().strip().upper() + if MODIFY_PARAMETERS_QUESTION == "S": + while True: + clean_terminal() # Limpa o Terminal + print("Menu de Seleção: ") + print("") + print("1. Modificar Velocidade") + print("2. Modificar Volume") + print("3. Modificar Tom de Voz") + print("0. Finalizar e gerar áudio") + print("") + + SELECTED = input("Escolha uma opção: ") + match SELECTED: + case "1": + clean_terminal() # Limpa o Terminal + select_rate() + case "2": + clean_terminal() # Limpa o Terminal + select_volume() + case "3": + clean_terminal() # Limpa o Terminal + select_pitch() + case "0": + clean_terminal() # Limpa o Terminal + select_name() + print("- - - - - - - - - - - - - - - - - - - -") + generate_audio() + break + case _: + clean_terminal() # Limpa o Terminal + print("Escolha de Menu invalida, por favor tente novamente.") + elif MODIFY_PARAMETERS_QUESTION == "N": + clean_terminal() # Limpa o Terminal + select_name() + print("- - - - - - - - - - - - - - - - - - - -") + generate_audio() + break + else: + clean_terminal() # Limpa o Terminal + print("Escolha invalida, por favor tente novamente.") + +# - - - - - - - - - - - - - - - - - - - +# Execução do Script + +if __name__ == "__main__": + main()