Appearance
question:import pickle import tkinter as tk from tkinter import font from tkinter import simpledialog from tkinter import messagebox from tkinter import filedialog from tkinter import Listbox from tkinter import ttk # ttk for improved styling import os import time import sounddevice as sd import noisereduce as nr import librosa import soundfile as sf import noisereduce as nr from pydub import AudioSegment from scipy.spatial.distance import euclidean from fastdtw import fastdtw from pydub import AudioSegment import numpy as np import colorama from colorama import Fore, Back, Style colorama.init(autoreset=True) # Inicializa o colorama para resetar a cor após cada print import sqlite3 from playsound import playsound from scipy.io import wavfile from python_speech_features import mfcc import subprocess import shutil import zipfile from datetime import datetime n_mfcc = 13 n_fft = 2048 hop_length = 512 #1- Função ‘record_audio’ def record_audio(duration, fs, file_path): print(f"Gravando o comando por {duration} segundos…“) audio = sd.rec(int(duration * fs), samplerate=fs, channels=1, dtype=‘float32’) sd.wait() # Aguarda a gravação terminar sf.write(file_path, audio, fs) print(f"Gravação salva como: {file_path}”) # Retorna ao invés de solicitar a entrada do usuário return file_path #2- Função ‘preprocess_audio’ def preprocess_audio(filename, output_prefix): # Carregar o arquivo de áudio raw_audio, sample_rate = librosa.load(filename, sr=None) # Reduzir ruído após normalização reduced_noise_audio = nr.reduce_noise(y=raw_audio, sr=sample_rate) # Salvando o áudio reduzido de ruído em um arquivo wav sf.write(f"{output_prefix}_resultado_ruido.wav", reduced_noise_audio, sample_rate) # Reamostrar áudio para uma taxa de amostragem padrão de 22050 Hz (se necessário) target_sr = 16000 # Atualize para a taxa de amostragem específica que seu reconhecedor de fala espera if sample_rate != target_sr: audio_resampled = librosa.resample(reduced_noise_audio, orig_sr=sample_rate, target_sr=target_sr) # Salvando áudio reamostrado se a taxa de amostragem for alterada sf.write(f"{output_prefix}resultado_reamostragem.wav", audio_resampled, target_sr) else: audio_resampled = reduced_noise_audio return audio_resampled, target_sr #3- Função ‘extract_mfcc’ def extract_mfcc(audio, sample_rate, n_mfcc=13, n_fft=2048, hop_length=None): # Se hop_length não for fornecido, usa metade de n_fft como padrão if hop_length is None: hop_length = n_fft // 2 # Extrair MFCCs com parâmetros configuráveis mfccs = librosa.feature.mfcc(y=audio, sr=sample_rate, n_mfcc=n_mfcc, n_fft=n_fft, hop_length=hop_length) return mfccs.T # Transposta para alinhar frames como linhas “”" def extract_mfcc(audio, sample_rate): # Extrair MFCCs mfccs = librosa.feature.mfcc(y=audio, sr=sample_rate, n_mfcc=13) return mfccs.T # Transposta para alinhar frames como linhas “”“ #4- Função ‘connect_db’ # Função para conectar-se à nova base de dados e criar a tabela def connect_db(): conn = sqlite3.connect(‘commands.db’) cursor = conn.cursor() cursor.execute(‘’‘ CREATE TABLE IF NOT EXISTS commands ( id INTEGER PRIMARY KEY AUTOINCREMENT, filename TEXT NOT NULL, transcription TEXT NOT NULL, mfcc BLOB n_mfcc INTEGER, n_fft INTEGER, hop_length INTEGER ) ‘’’) conn.commit() return conn #5- Função ‘insert_command’ modificada para armazenar os dados MFCC # Função para inserir um novo comando no banco de dados def insert_command(conn, filename, transcription, mfccs, n_mfcc, n_fft, hop_length): #def insert_command(conn, filename, transcription): # Converte os mfccs que estão como memoryview para um tipo serializável antes de fazer o dump com pickle #mfccs_serializable = np.array(mfccs.tolist()) # converte para uma lista normal do Python ou um array 1D #mfcc_blob = sqlite3.Binary(pickle.dumps(mfccs_serializable, protocol=pickle.HIGHEST_PROTOCOL)) # Restante do seu código para inserir o comando no banco de dados… #mfcc_blob = sqlite3.Binary(pickle.dumps(mfccs)) # Certifique-se de que mfccs esteja em um formato serializável (como uma lista ou um array NumPy regular) if isinstance(mfccs, memoryview): # Se mfccs for um memoryview, primeiro o converta para um array NumPy regular mfccs = np.asarray(mfccs) # É recomendável garantir que mfccs esteja em um tipo de dados serializável # Pode ser necessário mfccs = mfccs.tolist() se mfccs for um array NumPy if isinstance(mfccs, np.ndarray): mfccs = mfccs.tolist() # Agora, tente serializar try: #mfcc_blob = sqlite3.Binary(pickle.dumps(mfccs, protocol=pickle.HIGHEST_PROTOCOL)) mfcc_blob = sqlite3.Binary(pickle.dumps(mfccs, protocol=pickle.HIGHEST_PROTOCOL)) except TypeError as e: print(f"Erro ao serializar MFCCs: {e}”) raise cursor = conn.cursor() mfcc_blob = sqlite3.Binary(pickle.dumps(mfccs)) # Serializa os MFCCs para BLOB cursor.execute(‘INSERT INTO commands (filename, transcription, mfcc) VALUES (?, ?, ?, ?, ?, ?)’, (filename, transcription, mfcc_blob, n_mfcc, n_fft, hop_length)) conn.commit() return cursor.lastrowid “”" def insert_command(conn, filename, transcription, mfccs): #def insert_command(conn, filename, transcription): # Converte os mfccs que estão como memoryview para um tipo serializável antes de fazer o dump com pickle #mfccs_serializable = np.array(mfccs.tolist()) # converte para uma lista normal do Python ou um array 1D #mfcc_blob = sqlite3.Binary(pickle.dumps(mfccs_serializable, protocol=pickle.HIGHEST_PROTOCOL)) # Restante do seu código para inserir o comando no banco de dados… #mfcc_blob = sqlite3.Binary(pickle.dumps(mfccs)) # Certifique-se de que mfccs esteja em um formato serializável (como uma lista ou um array NumPy regular) if isinstance(mfccs, memoryview): # Se mfccs for um memoryview, primeiro o converta para um array NumPy regular mfccs = np.asarray(mfccs) # É recomendável garantir que mfccs esteja em um tipo de dados serializável # Pode ser necessário mfccs = mfccs.tolist() se mfccs for um array NumPy if isinstance(mfccs, np.ndarray): mfccs = mfccs.tolist() # Agora, tente serializar try: mfcc_blob = sqlite3.Binary(pickle.dumps(mfccs, protocol=pickle.HIGHEST_PROTOCOL)) except TypeError as e: print(f"Erro ao serializar MFCCs: {e}“) raise cursor = conn.cursor() mfcc_blob = sqlite3.Binary(pickle.dumps(mfccs, protocol=pickle.HIGHEST_PROTOCOL)) # Serializa os MFCCs para BLOB cursor.execute(‘INSERT INTO commands (filename, transcription, mfcc) VALUES (?, ?, ?)’, (filename, transcription, mfcc_blob)) conn.commit() return cursor.lastrowid “”” # Função auxiliar para a prepocessar o áudio e extrair os MFCCs - uso de funções já definidas no seu script def preprocess_and_extract_mfcc(file_path, n_mfcc=13, n_fft=2048, hop_length=None, sample_rate=None): raw_audio, sr = librosa.load(file_path, sr=sample_rate) # Carrega o arquivo de áudio e reamostra, se necessário reduced_noise_audio = nr.reduce_noise(y=raw_audio, sr=sr) # Redução de ruído mfccs = extract_mfcc(reduced_noise_audio, sr, n_mfcc=n_mfcc, n_fft=n_fft, hop_length=hop_length) # Passa parâmetros configuráveis return mfccs “”" def preprocess_and_extract_mfcc(file_path): raw_audio, sample_rate = librosa.load(file_path, sr=None) # Carrega o arquivo de áudio reduced_noise_audio = nr.reduce_noise(y=raw_audio, sr=sample_rate) # Redução de ruído mfccs = librosa.feature.mfcc(y=reduced_noise_audio, sr=sample_rate, n_mfcc=13) # Extrai os MFCCs return mfccs “”" #6- Função ‘list_commands’ # Função para listar todos os comandos do banco de dados def list_commands(conn): cursor = conn.cursor() cursor.execute(‘SELECT * FROM commands’) return cursor.fetchall() #7- Função ‘tk_record_audio’ def tk_record_audio(conn): # Aqui você pode pedir ao usuário para digitar a transcrição ou selecionar de uma lista existente transcription = simpledialog.askstring(“Nome Comando”, “Digite o nome do comando(transcrição):”) if transcription: # Gera um nome de arquivo único file_name = f"command{int(time.time())}.wav" file_path = os.path.join(‘C:meucomandos voz’, file_name) # Grava o áudio duration = 3 # Duração da gravação em segundos fs = 16000 # Frequência de amostragem record_audio(duration, fs, file_path) # Calcula os MFCCs output_prefix = “processado” # Escolha um prefixo adequado para seu caso de uso audio, sr = preprocess_audio(file_path, output_prefix) mfccs = extract_mfcc(audio, sr) # Insira a transcrição e caminho do arquivo no banco de dados insert_command(conn, file_path, transcription, mfccs, n_mfcc, n_fft, hop_length) messagebox.showinfo(“Sucesso”, “O comando de voz foi gravado e salvo com sucesso.”) return file_path # Deve retornar o caminho do arquivo para usar em process_sound else: messagebox.showwarning(“Aviso”, “A transcrição não foi fornecida.”) return None # Retorna None se não houver transcrição #8- Função ‘add_sound’ # Esta função permite adicionar um novo som ao banco de dados, com a opção de gravar ou escolher um arquivo e com possibilidade de processamento de áudio #2.1 - Botão para inserir som na BD: def add_sound(conn): # Create a new window for the add sound dialog dialog_window = tk.Toplevel() dialog_window.title(“Adicionar Som”) dialog_window.geometry(“300x100”) # You can adjust the size as needed # Function to be called when ‘Upload’ button is pressed def upload_sound(): file_path = filedialog.askopenfilename(title=“Selecionar Arquivo de Áudio”, filetypes=[(“Arquivos de Áudio”, “.wav”)]) if file_path: “”" # Aqui vamos assumir que preprocess_audio retorna o caminho do arquivo de áudio processado processed_file_path = preprocess_audio(file_path, os.path.splitext(os.path.basename(file_path))[0]) transcription = simpledialog.askstring( “Transcrição”, “Digite a transcrição para o comando de voz do arquivo selecionado:”, parent=dialog_window ) if transcription: # Você precisa calcular os MFCCs para o arquivo de áudio selecionado audio, sr = librosa.load(processed_file_path, sr=None) # Carregar arquivo processado mfccs = librosa.feature.mfcc(y=audio, sr=sr, n_mfcc=13) # Calcular MFCCs # Passar os MFCCs para a função de inserção insert_command(conn, processed_file_path, transcription, mfccs) messagebox.showinfo(“Sucesso”, “O arquivo de voz foi adicionado com sucesso.”, parent=dialog_window) dialog_window.destroy() else: messagebox.showwarning(“Aviso”, “Transcrição não fornecida.”, parent=dialog_window) else: messagebox.showwarning(“Aviso”, “Nenhum arquivo foi selecionado.”, parent=dialog_window) “”“ # Carregar o arquivo de áudio usando scipy em vez de librosa sample_rate, audio_data = wavfile.read(file_path) audio_data = audio_data / np.iinfo(audio_data.dtype).max # Normalização # Redução de ruído usando Noisereduce reduced_noise_audio = nr.reduce_noise(y=audio_data, sr=sample_rate) # Extraia os MFCCs usando o pacote que funcionou bem para você anteriormente # Substitua esta parte pelo método correto se for diferente #mfccs = extract_mfcc_from_working_method(reduced_noise_audio, sample_rate) # Você precisa implementar essa função mfccs = extract_mfcc(reduced_noise_audio, sample_rate) # Você precisa implementar essa função transcription = simpledialog.askstring(“Transcrição”, “Digite a transcrição para o comando de voz do arquivo selecionado:”) if transcription: # Insere o comando processado e os MFCCs no banco de dados insert_command(conn, file_path, transcription, mfccs, n_mfcc, n_fft, hop_length) messagebox.showinfo(“Sucesso”, “O arquivo de voz foi adicionado com sucesso.”) else: messagebox.showwarning(“Aviso”, “Transcrição não fornecida.”) # Function to be called when ‘Record’ button is pressed def record_sound(): tk_record_audio(conn) dialog_window.destroy() # Create and position the two buttons within the dialog btn_upload = tk.Button(dialog_window, text=“Upload”, command=upload_sound) btn_upload.pack(side=tk.LEFT, padx=10, pady=20) btn_record = tk.Button(dialog_window, text=“Gravar”, command=record_sound) btn_record.pack(side=tk.RIGHT, padx=10, pady=20) #9- Função ‘delete_sound_by_id’ # Function to delete a sound by its database ID def delete_sound_by_id(conn, cmd_id): if messagebox.askokcancel(“Confirmar Exclusão”, “Tem certeza que deseja excluir este comando?”): cursor = conn.cursor() cursor.execute(‘DELETE FROM commands WHERE id = ?’, (cmd_id,)) conn.commit() #10- Função ‘delete_selected_sound’ # Function to delete a selected sound from the GUI def delete_selected_sound(conn): # Lista os comandos existentes para que o usuário escolha um para excluir existing_commands = list_commands(conn) if not existing_commands: messagebox.showinfo(“Info”, “Não existem comandos para excluir.”) return # Um dicionário para corresponder transcrição com ID do banco de dados transcription_id_map = {cmd[2]: cmd[0] for cmd in existing_commands} # Assegura que o usuário pode selecionar uma transcrição para excluir a partir de uma lista transcription_to_delete = simpledialog.askstring(“Apagar Comando”, “Escolha o comando para excluir:n” + ‘n’.join(transcription_id_map.keys())) if transcription_to_delete and transcription_to_delete in transcription_id_map: # Confirmação antes de excluir if messagebox.askokcancel(“Confirmar Exclusão”, “Tem certeza que deseja excluir este comando?”): # Executa a exclusão cursor = conn.cursor() cursor.execute(‘DELETE FROM commands WHERE id = ?’, (transcription_id_map[transcription_to_delete],)) conn.commit() messagebox.showinfo(“Info”, “Comando excluído com sucesso.”) else: messagebox.showinfo(“Info”, “Comando não encontrado ou exclusão cancelada.”) #11- Função ‘show_records’ def show_records(conn): commands_list = list_commands(conn) records = “n”.join(f"ID: {cmd[0]}, Transcription: {cmd[2]}” for cmd in commands_list) messagebox.showinfo(“Registros da BD”, records) #12- Função ‘test_sound_distance’ a alterar local #(Mover para fora da função ‘test_sound_distance’) #12.1- Subfunção ‘load_sound_list’ a mover para escopo global #(Remover de dentro de ‘test_sound_distance’) #13- Função ‘add_mfcc_column_to_db’ def add_mfcc_column_to_db(conn): cursor = conn.cursor() cursor.execute(‘’‘ALTER TABLE commands ADD COLUMN mfcc BLOB’‘’) conn.commit() #14- Função ‘compute_and_store_mfccs’ def compute_and_store_mfccs(conn): cursor = conn.cursor() cursor.execute(‘SELECT id, filename FROM commands WHERE mfcc IS NULL’) commands_to_process = cursor.fetchall() for cmd_id, file_path in commands_to_process: # Preprocess the audio file and extract the MFCC audio, sr = preprocess_audio(file_path, os.path.splitext(os.path.basename(file_path))[0]) mfccs = extract_mfcc(audio, sr) # Serialize the MFCCs for storage mfccs_bytes = sqlite3.Binary(pickle.dumps(mfccs, protocol=pickle.HIGHEST_PROTOCOL)) # Update the record with the computed MFCCs cursor.execute(‘UPDATE commands SET mfcc = ? WHERE id = ?’, (mfccs_bytes, cmd_id)) conn.commit() “”" #Função para calcular e armazenar os MFCCs def compute_and_store_mfccs(conn, file_path, transcription): mfccs = preprocess_and_extract_mfcc(file_path) # Calcula os MFCCs a partir do arquivo de áudio insert_command(conn, file_path, transcription, mfccs) # Insere o comando e os MFCCs no banco de dados “”“ #15- Função ‘augment_audio’ #def augment_audio(file_path, transcription, conn): def augment_audio(file_path, conn): effects = { ‘speed’: 1.5, # Parâmetro de exemplo para o efeito de velocidade ‘pitch’: 2, # Parâmetro de exemplo para o efeito de pitch } for effect_name, effect_param in effects.items(): # Gera o nome do arquivo de saída com um nome único new_file_path = os.path.join(“your_audio_effects_directory”, create_effect_filename(transcription, effect_name)) # Aplica o efeito respectivo if effect_name == ‘speed’: apply_speed_effect(input_file, new_file_path, effect_param) elif effect_name == ‘pitch’: apply_pitch_effect(input_file, new_file_path, effect_param) # Insere os dados do áudio com efeito no banco de dados audio_data, sample_rate = preprocess_audio(new_file_path, “processado”) mfccs = extract_mfcc(audio_data, sample_rate) insert_command(conn, new_file_path, transcription, mfccs) “”” versão anterior audio, sr = librosa.load(file_path, sr=None) # Apply noise noise_factor = 0.02 audio_with_noise = audio + noise_factor * np.random.randn(len(audio)) # Apply pitch shifting pitch_factor = 2 # Number of steps to shift audio_with_pitch_shifted = librosa.effects.pitch_shift(audio, sr, pitch_factor) # Save augmented audios noise_file_path = file_path.replace(‘.wav’, ‘_noise.wav’) pitch_file_path = file_path.replace(‘.wav’, ‘_pitch.wav’) sf.write(noise_file_path, audio_with_noise, sr) sf.write(pitch_file_path, audio_with_pitch_shifted, sr) # Add noise and pitch-shifted versions to the database transcription = file_path.split(‘/’)[-1].replace(‘.wav’, ‘’) insert_command(conn, noise_file_path, transcription + ‘_noise’) insert_command(conn, pitch_file_path, transcription + ‘_pitch’) “”“ #16- Função ‘batch_augment’ def batch_augment(conn): cursor = conn.cursor() cursor.execute(‘SELECT filename FROM commands’) existing_files = cursor.fetchall() # Apply augmentation to each file for file_path, in existing_files: augment_audio(file_path, conn) #17- Função ‘play_audio’ # Função para reproduzir um arquivo de áudio def play_audio(file_path): try: # Usa playsound para reproduzir o arquivo de áudio playsound(file_path) except Exception as e: messagebox.showerror(“Erro”, f"Não foi possível reproduzir o áudio: {e}”) #18- Função ‘show_records_with_options’ # Função que mostra os registros da BD com a opção de ouvir ou apagar def show_records_with_options(conn): new_window = tk.Toplevel() new_window.title(“Registros da BD com Opções”) listbox = Listbox(new_window) listbox.pack(fill=“both”, expand=True) commands_list = list_commands(conn) “”" for index, (cmd_id, file_path, transcription) in enumerate(commands_list): listbox.insert(tk.END, f"{transcription} (ID: {cmd_id})“) “”” for command in commands_list: cmd_id, file_path, transcription, _ = command # adicione uma variável para ignorar o dado MFCC # Seu código de preenchimento da ListBox… listbox.insert(tk.END, f"{transcription} (ID: {cmd_id})“) def play_selected_audio(): # Pega o index do item selecionado selected = listbox.curselection() if not selected: messagebox.showwarning(“Aviso”, “Selecione um registro para ouvir.”) return index = selected[0] cmd_id, file_path, transcription, _ = commands_list[index] play_audio(file_path) # Chama a função de reprodução de áudio def delete_selected_audio(): # Pega o index do item selecionado selected = listbox.curselection() if not selected: messagebox.showwarning(“Aviso”, “Selecione um registro para apagar.”) return index = selected[0] cmd_id, file_path, transcription, _ = commands_list[index] delete_sound_by_id(conn, cmd_id) # Chama a função de delete # Remove o item da listbox e da lista local listbox.delete(selected[0]) del commands_list[index] btn_play = tk.Button(new_window, text=“Ouvir”, command=play_selected_audio) btn_play.pack(side=tk.LEFT, padx=(10, 0)) btn_delete = tk.Button(new_window, text=“Apagar”, command=delete_selected_audio) btn_delete.pack(side=tk.RIGHT, padx=(0, 10)) # ESCOPO GLOBAL # Supondo que conn seja a sua conexão com a base de dados e # esteja disponível no escopo global. global conn def load_sound_list(): # Certifique-se de que db_commands_listbox foi criada e está acessível global db_commands_listbox # Limpa a lista antes de adicionar novos itens db_commands_listbox.delete(0, tk.END) # Obtém os dados da base de dados chamando a função list_commands commands = list_commands(conn) for command in commands: # Se existem quatro elementos na tupla, desempacote os três primeiros e ignore o último (MFCC) cmd_id, file_path, transcription, _mfcc_blob = command # Insere os dados na db_commands_listbox #db_commands_listbox.insert(tk.END, f”{transcription} (ID: {cmd_id} MFCC: {_mfcc_blob})“) db_commands_listbox.insert(tk.END, f”{transcription} (ID: {cmd_id})“) # Esta função deve ser chamada depois que db_commands_listbox for criada # e a sua conexão com a base de dados (conn) deve estar aberta. # ESCOPO GLOBAL # Definição da variável global global db_commands_listbox #global commands_list # Função para criar o Listbox e atribuir ao db_commands_listbox no escopo global def create_command_listbox(parent): global db_commands_listbox # Criar db_commands_listbox aqui db_commands_listbox = tk.Listbox(parent) db_commands_listbox.pack(fill=“both”, expand=True) # Suponha que a função load_sound_list preenche o db_commands_listbox e está definida corretamente load_sound_list() # Carregar a lista de sons #19- Função ‘filter_command_list’ a mover para escopo global #(Remover de dentro de ‘test_sound_distance’ e colocar no nível superior do script) def filter_command_list(event=None): #global filtered_commands #print(”–filtro—“) search_term = filter_var.get().lower() print(f"Termo de pesquisa: ‘{search_term}’”) # Isso mostrará o termo de pesquisa no console #print(f"Filtragem pela palavra: {search_term}“) # Adicionei para depuração db_commands_listbox.delete(0, tk.END) #filtered_commands = [] # Vamos coletar os comandos filtrados aqui “”” for cmd_id, file_path, transcription in list_commands(conn): if search_term in transcription.lower(): filtered_commands.append((cmd_id, transcription)) # Guardamos os comandos filtrados db_commands_listbox.insert(tk.END, f"{transcription} (ID: {cmd_id})“) “”” for command in list_commands(conn): cmd_id, file_path, transcription, _mfcc_blob = command # adicione uma variável para ignorar o dado MFCC # Seu código de preenchimento da ListBox… if search_term in transcription.lower(): #filtered_commands.append((cmd_id, transcription)) # Guardamos os comandos filtrados #db_commands_listbox.insert(tk.END, f"{transcription} (ID: {cmd_id})“) db_commands_listbox.insert(tk.END, f"ID: {cmd_id}, File: {file_path}, Transcription: {transcription}”) #listbox.insert(tk.END, f"ID: {cmd_id}, File: {file_path}, Transcription: {transcription}“) #print(f"Comandos filtrados: {filtered_commands}”) # Adicionei para depuração # Variável para a caixa de filtragem (deve ser global) #filter_var = tk.StringVar() # Vincular as mudanças na variável à função de filtragem (considere mover para perto de onde você constrói a GUI) #filter_var.trace(“w”, lambda name, index, mode, sv=filter_var: filter_command_list()) #E correção na função de inicialização da caixa de entrada de filtro: #20- Variável ‘filter_var’ a alterar local #(Definir no escopo global, fora de qualquer função) #filter_var = tk.StringVar() #20.1- Subcomponente ‘filter_entry’ a alterar local #(Definir no escopo global, fora de qualquer função) #21- Ligação dos eventos ‘trace’ e ‘bind’ a alterar local #(Mover para fora de qualquer função, geralmente próximo à definição de ‘filter_entry’) #21.1- Ligação de ‘filter_var.trace’ com ‘filter_command_list’ #21.2- Ligação de ‘filter_entry.bind’ com ‘filter_command_list’ #escopo global #Certifique-se de que as demais funções e variáveis globais referenciadas no main, como filter_command_list, load_sound_list e tk_record_audio, estejam definidas no escopo global fora de qualquer outra função, para que sejam acessíveis dentro do main. #será necessário adaptar e garantir que as variáveis listbox, conn, e closest_sounds estejam acessíveis no escopo global, para que possam ser usadas dentro dessas funções refatoradas. # Subfunção para atualizar a listbox com as distâncias dos comandos def update_listbox_with_distances(test_sound_path, closest_sounds): listbox.delete(0, tk.END) listbox.insert(tk.END, “------ Comando utilizado para comparação ------”) # Encontre a transcrição relacionada ao caminho do arquivo de som _, _, transcription, _ = next((cmd for cmd in commands_list if cmd[1] == test_sound_path), (None, None, “Nome Indisponível”, None)) listbox.insert(tk.END, f"{transcription}: {test_sound_path}“) # Inclua o nome da transcrição listbox.insert(tk.END, “-----------------------------------------------”) for (cmd_info, distance) in closest_sounds: cmd_id, file_path, transcription, _ = cmd_info listbox.insert(tk.END, f”{transcription} (ID: {cmd_id}, Distância: {distance:.2f})“) #2. Na função que lida com o upload (vamos chamá-la de upload_sound_and_insert), configure a transcrição para “sem_nome” se uma transcrição não for fornecida pelo usuário: def upload_sound_and_insert(conn): test_sound_path = filedialog.askopenfilename(title=“Selecionar Arquivo de Áudio”, filetypes=[(“Arquivos de Áudio”, “.wav")]) if test_sound_path: # Chame a função para obter a transcrição ou nome do comando do usuário transcription = simpledialog.askstring(“Transcrição”, f"Digite a transcrição para o comando de voz do arquivo:n{test_sound_path}") if not transcription: transcription = “sem_nome” # Pré-processar o áudio e extrair os MFCCs audio_data, sample_rate = preprocess_audio(test_sound_path, “processado”) # “processado” é um placeholder mfccs = extract_mfcc(audio_data, sample_rate) # Inserir no banco de dados #insert_command(conn, test_sound_path, transcription, mfccs) insert_command(conn, test_sound_path, transcription, mfccs, n_mfcc, n_fft, hop_length) # Atualizar a interface para refletir o arquivo carregado update_listbox_with_distances(test_sound_path, []) # Função para lidar com a lógica de salvar comando na base de dados def save_command(test_sound_path, transcription,mfccs): if test_sound_path and transcription: #insert_command(conn, test_sound_path, transcription,mfccs) insert_command(conn, test_sound_path, transcription, mfccs, n_mfcc, n_fft, hop_length) messagebox.showinfo(“Salvar Comando”, “Comando salvo na base de dados com sucesso!”) else: messagebox.showwarning(“Salvar Comando”, “Não foi possível obter o caminho do arquivo ou a transcrição.”) # Função para reproduzir o áudio selecionado def play_selected_audio(): selected = listbox.curselection() if selected: index = selected[0] _, file_path, _ = closest_sounds[index][0] play_audio(file_path) # Função para carregar um áudio do sistema de arquivos def upload_sound(): test_sound_path = filedialog.askopenfilename(title=“Selecionar Arquivo de Áudio”, filetypes=[(“Arquivos de Áudio”, ".wav”)]) if test_sound_path: process_sound(test_sound_path) # Função para gravar um novo áudio def record_sound(): file_path = tk_record_audio(conn) if file_path: process_sound(file_path) # Função principal ‘test_sound_distance’ que cria a janela de diálogo def test_sound_distance(conn, root): global db_commands_listbox, listbox,closest_sounds # Cria janela de diálogo e configura painéis esquerdo e direito dialog_window = tk.Toplevel(root) dialog_window.title(“Testar Distância dos Sons”) dialog_window.geometry(“800x600”) left_panel = ttk.Frame(dialog_window, width=400, height=600) left_panel.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) right_panel = ttk.Frame(dialog_window, width=400, height=600) right_panel.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) # Componentes de interface do Painel esquerdo # Cria listbox e botões na janela de diálogo listbox = tk.Listbox(left_panel) listbox.pack(fill=“both”, expand=True) btn_upload = tk.Button(left_panel, text=“Upload”, command=upload_sound) btn_upload.pack(pady=5) # Componentes de interface do Painel Direito # Lista de comandos da base de dados (db_commands_listbox) db_commands_listbox = tk.Listbox(right_panel) db_commands_listbox.pack(fill=“both”, expand=True, pady=10) # Carregar e exibir a lista de sons quando a janela for aberta. load_sound_list() #caixa de filtro pesquisa #filter_var = tk.StringVar() filter_entry = ttk.Entry(right_panel, textvariable=filter_var) filter_entry.pack(fill=“x”, pady=5) # Vincula os eventos para o input de filtro. filter_entry.bind(‘<KeyRelease>’, filter_command_list) # Associe essa função ao evento de duplo clique na Listbox do painel direito db_commands_listbox.bind(‘<Double-1>’, on_command_double_click) btn_record = tk.Button(left_panel, text=“Gravar”, command=record_sound) btn_record.pack(pady=5) btn_play = tk.Button(left_panel, text=“Ouvir”, command=play_selected_audio) btn_play.pack(pady=5) save_button = ttk.Button(left_panel, text=“Salvar Comando”, command=lambda: save_command()) save_button.pack(side=tk.BOTTOM, pady=5) # Garantir que a janela de diálogo seja modal dialog_window.transient(root) dialog_window.grab_set() # Aguarda o fechamento da janela de diálogo root.wait_window(dialog_window) # ESCOPO GLOBAL # Variáveis globais que serão usadas em process_sound e outras funções closest_sounds = [] # Subfunção para atualizar a listbox com as distâncias dos comandos # Presumindo que listbox esteja definida globalmente ou seja passada como argumento def update_listbox_with_distances(test_sound_path, closest_sounds): # … [código da função] … listbox.delete(0, tk.END) # Limpa a listbox antes da atualização. # Insere o cabeçalho e o caminho do áudio testado no topo da listbox. listbox.insert(tk.END, “------ Comando utilizado para comparação ------”) listbox.insert(tk.END, test_sound_path) # Caminho para o arquivo carregado/gravado. #listbox.insert(tk.END, transcription) # Caminho para o arquivo carregado/gravado. listbox.insert(tk.END, “-----------------------------------------------”) # Insere as cinco menores distâncias na listbox. for (cmd_info, distance) in closest_sounds: cmd_id, file_path, transcription = cmd_info listbox.insert(tk.END, f”{transcription} (ID: {cmd_id}): Distância = {distance:.2f}“) # Função global para calcular e mostrar as distâncias do som carregado em relação aos sons da base de dados # Presumindo que listbox e closest_sounds estejam definidos globalmente ou sejam passados como argumentos def process_sound(test_sound_path): global closest_sounds # Usa a variável global para armazenar as distâncias closest_sounds = [] # Reseta a lista de distâncias mais próximas a cada chamada # Pré-processa o áudio do arquivo de teste e extrai os MFCCs test_audio, test_sr = preprocess_audio(test_sound_path, os.path.splitext(os.path.basename(test_sound_path))[0]) test_mfccs = extract_mfcc(test_audio, test_sr) # Calcula as distâncias para todos os sons na base de dados commands_list = list_commands(conn) distance_results = {} # Calcula a distância entre o comando testado e cada comando na base de dados for cmd_id, file_path, transcription, _mfcc_blob in commands_list: if file_path != test_sound_path: # Ignora o próprio som de teste audio, sr = preprocess_audio(file_path, os.path.splitext(os.path.basename(file_path))[0]) mfccs = extract_mfcc(audio, sr) distance, path = fastdtw(test_mfccs, mfccs, dist=euclidean) distance_results[(cmd_id, file_path, transcription)] = distance # Encontra os 5 sons mais próximos closest_sounds = sorted(distance_results.items(), key=lambda item: item[1])[:5] # Atualiza a listbox com as distâncias calculadas e o caminho do som carregado/gravado update_listbox_with_distances(test_sound_path, closest_sounds) ”“” A função process_sound tem a tarefa de: 1. Pré-processar o áudio do arquivo de teste usando preprocess_audio. 2. Extrair os Mel Frequency Cepstral Coefficients (MFCCs) do áudio pré-processado com a função extract_mfcc. 3. Computar a distância do MFCC do som de teste para cada som na base de dados usando a função fastdtw para calcular a distância Dynamic Time Warping (DTW) junto com a distância euclidiana (euclidean). 4. Ordenar os resultados e selecionar os cinco sons mais próximos. 5. Atualizar a listbox com as distâncias e o caminho do arquivo de teste usando a função update_listbox_with_distances. # Função para salvar um comando na base de dados # Presumindo uma variável global ou local chamada listbox para acessar a seleção atual “”“ def save_command(test_sound_path, transcription,mfccs): # Obtém o índice da entrada selecionada na listbox selected_index = listbox.curselection() if not selected_index: messagebox.showwarning(“Aviso”, “Selecione um comando da lista para salvar.”) return test_sound_path = listbox.get(selected_index[0]) # Verifica se o item selecionado é realmente um caminho para um áudio if “------ Comando utilizado para comparação ------” in test_sound_path or “-----------------------------------------------” in test_sound_path: messagebox.showerror(“Erro”, “Por favor, selecione um comando de áudio válido.”) return transcription = simpledialog.askstring(“Transcrição”, “Digite a transcrição do comando:”) if test_sound_path and transcription: # Insere o comando na base de dados. Presume-se que conn seja acessível globalmente #insert_command(conn, test_sound_path, transcription,mfccs) insert_command(conn, test_sound_path, transcription, mfccs, n_mfcc, n_fft, hop_length) messagebox.showinfo(“Salvar Comando”, “Comando salvo na base de dados com sucesso!”) else: messagebox.showwarning(“Salvar Comando”, “Não foi possível obter o caminho do arquivo ou a transcrição.”) #inicio augmentation--------------------------------------------------------------------------------------------------------- def on_command_double_click(event): #global commands_list # Pega o índice do item selecionado index = db_commands_listbox.curselection() if index: # Obter o comando correspondente ao índice selecionado #command = commands_list[index[0]] # commands_list deve conter todos os dados incluindo MFCCs #cmd_id, file_path, transcription, mfcc_blob = commands_list[index[0]] # Desempacotar incluindo a coluna MFCC #, file_path, , _ = commands_list[index[0]] #, file_path, _ = db_commands_listbox[index[0]] # Chama a função que processa o som/comando e atualiza o painel esquerdo #process_sound(file_path) # Verifique se o índice selecionado é válido if index[0] < len(commands_list): cmd_id, file_path, transcription, _mfcc_blob = commands_list[index[0]] # Desempaque com segurança process_sound(file_path) else: messagebox.showwarning(“Erro”, “Índice do comando fora de alcance.”) else: messagebox.showwarning(“Seleção”, “Nenhum comando selecionado.”) # Função para carregar todos os comandos da base de dados para a ListBox def load_commands_to_listbox(listbox): # Limpa a ListBox antes de adicionar novos itens listbox.delete(0, tk.END) # Conectar ao banco de dados e buscar todos os comandos #conn = connect_db() # Supondo que esta função connect_db() já exista e retorne um objeto de conexão cursor = conn.cursor() cursor.execute(‘SELECT * FROM commands’) # Executa a consulta para buscar todos os comandos commands = cursor.fetchall() # Busca todos os resultados # Itera sobre os comandos e adiciona na ListBox for command in commands: # Supondo que command contenha todos os campos de uma linha da tabela # Exemplo de command: (1, ‘command_1.wav’, ‘Ligar luz’, mfcc_data_blob) # A formatação abaixo deve ser ajustada para corresponder ao schema da tabela e às suas necessidades #formatted_command = f"ID: {command[0]}, Filename: {command[1]}, Transcription: {command[2]}” #listbox.insert(tk.END, formatted_command) # Insere o comando formatado na ListBox cmd_id, file_path, transcription, _mfcc_blob = command listbox.insert(tk.END, f"ID: {cmd_id}, File: {file_path}, Transcription: {transcription}“) pass # Fecha a conexão com o banco de dados #conn.close() def audio_augmentation_window(conn, root): global filter_var, left_panel#por causa do on_command_select_for_augmentation não está definida. Isto é má programação. # Cria janela de diálogo e configura painéis esquerdo e direito dialog_window = tk.Toplevel(root) dialog_window.title(“Áudio Augmentation”) dialog_window.geometry(“1400x600”) “”” # Frame esquerdo que vai conter a lista de opções ou informações left_panel = ttk.Frame(dialog_window) left_panel.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=10) # Frame direito que vai conter a lista de comandos de voz right_panel = ttk.Frame(dialog_window) right_panel.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10)“”“ left_panel = ttk.Frame(dialog_window, width=100, height=600) left_panel.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) right_panel = ttk.Frame(dialog_window, width=650, height=600) right_panel.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) # Componentes de interface do Painel Direito # Lista de comandos da base de dados (db_commands_listbox) global db_commands_listbox db_commands_listbox = tk.Listbox(right_panel) db_commands_listbox.pack(fill=“both”, expand=True, pady=10) # Função para carregar comandos na listbox do painel direito load_commands_to_listbox(db_commands_listbox) # Caixa de filtro pesquisa #filter_var = tk.StringVar() filter_entry = ttk.Entry(right_panel, textvariable=filter_var) filter_entry.pack(fill=“x”, pady=5) # Vincula os eventos para o input de filtro. filter_entry.bind(‘<KeyRelease>’, lambda event: filter_command_list()) filter_var.trace(“w”, lambda name, index, mode, sv=filter_var: filter_command_list()) # Associe essa função ao evento de duplo clique na Listbox do painel direito db_commands_listbox.bind(‘<Double-1>’, lambda event: on_command_select_for_augmentation(event, conn)) # Componentes de interface do Painel esquerdo (a definir conforme a tarefa 3) # Na tarefa 2, implementaremos a funcionalidade para criar sons augmentados e inseri-los na base de dados # Garantir que a janela de diálogo seja modal dialog_window.transient(root) dialog_window.grab_set() # Aguarda o fechamento da janela de diálogo root.wait_window(dialog_window) ”“” def on_command_select_for_augmentation(event, conn): # Carrega o áudio original original, sr = librosa.load(file_path, sr=None) # Cria variações do áudio # Variação de velocidade speed_change = librosa.effects.time_stretch(original, rate=0.8) speed_change_file_path = file_path.replace(‘.wav’, ‘_velocidade.wav’) sf.write(speed_change_file_path, speed_change, sr) # Variação de pitch pitch_shift = librosa.effects.pitch_shift(original, sr, n_steps=4) pitch_shift_file_path = file_path.replace(‘.wav’, ‘_pitch.wav’) sf.write(pitch_shift_file_path, pitch_shift, sr) # Adição de ruído noise = original + 0.005 * np.random.randn(len(original)) noise_file_path = file_path.replace(‘.wav’, ‘_ruido.wav’) sf.write(noise_file_path, noise, sr) # Salva a variação no banco de dados com a transcrição atualizada insert_command(conn, speed_change_file_path, transcription + ‘_velocidade’, None) insert_command(conn, pitch_shift_file_path, transcription + ‘_pitch’, None) insert_command(conn, noise_file_path, transcription + ‘_ruido’, None) return [ (speed_change_file_path, transcription + ‘_velocidade’), (pitch_shift_file_path, transcription + ‘_pitch’), (noise_file_path, transcription + ‘_ruido’), ] “”“ def on_command_select_for_augmentation(event, conn): # Mostrar mensagem de que o processo está sendo executado #messagebox.showinfo(“Processamento”, “Processando dados de Augmentation…”) # Obtém o índice do item selecionado index = db_commands_listbox.curselection() if not index: messagebox.showwarning(“Erro”, “Nenhum comando selecionado.”) return # Obtém o dado do comando selecionado (suponha que a listbox foi carregada corretamente com os dados) cmd_id, file_path, transcription, _mfcc_blob = list_commands(conn)[index[0]] # Chama a função de augmentação de áudio e obtém os caminhos dos novos arquivos de áudio augmented_file_paths = augment_audio(file_path, transcription, conn) # Opcional: Atualizar a listbox para exibir os novos arquivos de áudio adicionados, ou qualquer outra ação necessária # Limpa o painel esquerdo (se já tiver algo antes) for widget in left_panel.winfo_children(): widget.destroy() # Define uma fonte com um tamanho maior desired_font = font.Font(family=“Helvetica”, size=14) # Vamos agora criar uma nova listbox no painel esquerdo para mostrar informações distance_listbox = tk.Listbox(left_panel, font=desired_font) # Definindo a fonte aqui distance_listbox.pack(fill=‘both’, expand=True) # Carrega os MFCC originais do BLOB original_mfcc = pickle.loads(_mfcc_blob) “”” # Para cada augmentação, calcule a distância DTW em relação ao áudio original for augmented_path, augmented_transcription in augmented_file_paths: # Carregue os MFCCs para o arquivo de áudio augmentado augmented_mfcc = calculate_mfcc(augmented_path) # Calcule a distância usando fastdtw distance, path = fastdtw(original_mfcc, augmented_mfcc, dist=euclidean) # Atualize a listbox com a distância distance_listbox.insert(tk.END, f"{augmented_transcription}: Distância {distance:.2f}“) “”” # Para cada augmentação, calcule a distância DTW em relação ao áudio original distances = [] for augmented_path, augmented_transcription in augmented_file_paths: # Carregue os MFCCs para o arquivo de áudio augmentado augmented_mfcc = calculate_mfcc(augmented_path) # Calcule a distância usando fastdtw distance, path = fastdtw(original_mfcc, augmented_mfcc, dist=euclidean) # Adicione a tupla (distância, transcrição) à lista de distâncias distances.append((distance, augmented_transcription)) # Ordenar a lista pelo primeiro elemento da tupla (distância), de forma crescente distances.sort(key=lambda x: x[0]) # Atualizar a listbox com as distâncias ordenadas for distance, augmented_transcription in distances: distance_listbox.insert(tk.END, f"{augmented_transcription}: Distância {distance:.2f}“) # Recarrega a lista de comandos no painel direito para incluir as novas augmentations load_commands_to_listbox(db_commands_listbox) ”“” def augment_audio(file_path, transcription, conn): # Carrega o áudio original usando wavfile sample_rate, original_data = wavfile.read(file_path) original_data = original_data / np.iinfo(original_data.dtype).max # Normalização original = AudioSegment( original_data.tobytes(), frame_rate=sample_rate, sample_width=original_data.dtype.itemsize, channels=1 ) # Cria variações do áudio # Variação de velocidade (exemplo: 50% mais rápido) #speed_change = original.speedup(playback_speed=1.5) #speed_change = original.speedup(playback_speed=2) speed_change_file_path = file_path.replace(‘.wav’, ‘_velocidade.wav’) #speed_change.export(speed_change_file_path, format=‘wav’) # Variação de pitch (exemplo: aumenta em 6 semitons) #pitch_shift = original._spawn(original.raw_data).shift(pitch=6) pitch_shift_file_path = file_path.replace(‘.wav’, ‘_pitch.wav’) #pitch_shift.export(pitch_shift_file_path, format=‘wav’) # Adição de ruído (exemplo: ruído branco com volume -30dB) noise = original.overlay(AudioSegment.silent(duration=original.duration_seconds * 1000).white_noise(-30)) noise_file_path = file_path.replace(‘.wav’, ‘_ruido.wav’) noise.export(noise_file_path, format=‘wav’) # TODO: Atualize a inserção no banco de dados conforme o necessário # Agora vamos calcular os MFCCs para cada variação do áudio e salvá-los na base de dados augmented_file_paths = [ (speed_change_file_path, transcription + ‘_velocidade’), (pitch_shift_file_path, transcription + ‘_pitch’), (noise_file_path, transcription + ‘ruido’), ] for new_file_path, new_transcription in augmented_file_paths: # Calcula os MFCCs para o arquivo de áudio new_mfcc = calculate_mfcc(new_file_path) # Serializa os MFCCs para armazenamento no banco de dados como um BLOB new_mfcc_blob = sqlite3.Binary(pickle.dumps(new_mfcc, protocol=pickle.HIGHEST_PROTOCOL)) # Insere o comando processado e os MFCCs no banco de dados insert_command(conn, new_file_path, new_transcription, new_mfcc_blob) return augmented_file_paths “”“ #início de FFmpeg, funções def create_effect_filename(transcription, effect_name, extension=”.wav"): timestamp = datetime.now().strftime("%Y%m%d%H%M%S") return f"{transcription}{effect_name}{timestamp}{extension}" def apply_speed_effect(input_file, speed): effect_name = “speed” new_filename = create_effect_filename(os.path.basename(input_file), effect_name) output_file = os.path.join(“your_audio_effects_directory”, new_filename) change_speed_ffmpeg(input_file, output_file, speed) return output_file def apply_pitch_effect(input_file, semitones): effect_name = “pitch” new_filename = create_effect_filename(os.path.basename(input_file), effect_name) output_file = os.path.join(“your_audio_effects_directory”, new_filename) change_pitch_ffmpeg(input_file, output_file, semitones) return output_file def change_speed_ffmpeg(input_file, output_file, speed): # Construa o comando ffmpeg para mudar a velocidade de reprodução command = [ ‘ffmpeg’, ‘-i’, input_file, ‘-filter:a’, f’atempo={speed}’, output_file ] # Execute o comando subprocess.run(command, check=True) def change_pitch_ffmpeg(input_file, output_file, semitones): # Construa o comando ffmpeg para mudar o pitch command = [ ‘ffmpeg’, ‘-i’, input_file, ‘-af’, f’asetrate=441002^({semitones}/12),aresample=44100’, output_file ] # Execute o comando subprocess.run(command, check=True) def add_noise_ffmpeg(input_file, noise_audio_path, new_file_path, noise_level=0.02): effects_dir = ‘c:meucomandos voz’ # Diretório onde os efeitos de áudio serão salvos effect_name = “noise” # Utiliza a função create_effect_filename para gerar o nome exclusivo do arquivo de efeito new_filename = create_effect_filename(os.path.basename(input_file), effect_name) new_file_path = os.path.join(effects_dir, new_filename) # Configura o comando usando filter_complex para misturar o áudio de voz com o áudio de ruído command = [ ‘ffmpeg’, ‘-i’, input_file, ‘-i’, noise_audio_path, ‘-filter_complex’, f’[1:a]volume={noise_level}[noise];[0:a][noise]amix=inputs=2:duration=longest’, new_file_path ] # Executa o comando subprocess.run(command, check=True) def add_noise_ffmpegnf1(input_file, output_file, noise_level): # Construir o comando ffmpeg para adicionar ruído branco command = [ ‘ffmpeg’, ‘-i’, input_file, ‘-filter_complex’, f’anoisesrc=level={noise_level}:duration=0.1 [noise]; [0:a][noise] amix=inputs=2:duration=first’, output_file ] # Execute o comando subprocess.run(command, check=True) def add_noise_ffmpegnf(input_file, output_file, noise_level): # Definir o tempo de duração do ruído igual ao do arquivo de entrada duration_command = [‘ffprobe’, ‘-i’, input_file, ‘-show_entries’, ‘format=duration’, ‘-v’, ‘quiet’, ‘-of’, ‘csv=p=0’] duration_result = subprocess.run(duration_command, stdout=subprocess.PIPE, text=True) duration = duration_result.stdout.strip() # Construir o comando ffmpeg para adicionar ruído branco command = [ ‘ffmpeg’, ‘-i’, input_file, # Arquivo de input ‘-f’, ‘lavfi’, # Indica que um filtro do lavfi será utilizado ‘-t’, duration, # Define a duração do ruído baseando-se na duração do arquivo de entrada ‘-i’, f’anoisesrc=duration={duration}:level={noise_level}‘, # Cria o ruído branco ‘-filter_complex’, ‘amix=inputs=2:duration=longest:dropout_transition=0’, # Mixa o audio de entrada com ruído output_file # Arquivo de output ] # Execute o comando subprocess.run(command, check=True) def add_noise_ffmpegnfunc(input_file, output_file, noise_level): # Corrija o command para definir correctly o filtro de ruído command = [ ‘ffmpeg’, ‘-i’, input_file, # Arquivo de input original ‘-filter_complex’, # Usar filter_complex para cadeia de filtros f’anoisesrc=colour=white:level={noise_level}’, # Gerar ruído branco ‘-i’, ‘-’, # Receber o ruído branco no segundo input ‘-filter_complex’, # Usar filter_complex para cadeia de filtros ‘amix=inputs=2:duration=longest’, # Mixar áudio de input resultante e ruído output_file # Arquivo de output ] # Execute o comando subprocess.run(command, check=True) def add_noise_ffmpegnfunciona(input_file, output_file, noise_level): # Construa o comando ffmpeg para adicionar ruído branco command = [ ‘ffmpeg’, ‘-i’, input_file, ‘-af’, f’anoisesrc=c0.5:dur=0.1:level={noise_level}', ‘-i’, input_file, ‘-filter_complex’, ‘amix=inputs=2:duration=longest’, output_file ] # Execute o comando subprocess.run(command, check=True) # … (inclua aqui as definições de outras funções utilizadas no augment_audio) … # Agora você pode chamar essas funções dentro do augment_audio # Não se esqueça de tratar exceções para capturar erros que possam ocorrer ao executar os comandos do subprocess. def add_combined_effects_ffmpeg(input_file, output_file, speed=1.5, semitones=2, noise_level=0.1): command = [ ‘ffmpeg’, ‘-i’, input_file, ‘-filter_complex’, f"“” [0:a]atempo={speed}[speed]; [speed]asetrate=441002^({semitones}/12),aresample=44100[pitch]; [pitch]anoisesrc=colour=white:level={noise_level}:duration=0.1[ruido]; [ruido][pitch]amix=inputs=2:duration=longest[out] “”“, ‘-map’, ‘[out]’, ‘-c:a’, ‘pcm_s16le’, output_file ] subprocess.run(command, check=True) def apply_audio_filters_ffmpeg(input_file, output_file, speed=1.5, noise_level=0.1): command = [ ‘ffmpeg’, ‘-i’, input_file, # Aplica o aumento de velocidade ‘-filter:a’, f’atempo={speed}‘, # Salva o intermediário aumentado de velocidade ‘-y’, ‘intermediate_output.wav’ ] # Executa o aumento de velocidade subprocess.run(command, check=True) # Agora aplica o ruído no arquivo intermediário command = [ ‘ffmpeg’, ‘-i’, ‘intermediate_output.wav’, # Gera o ruído ‘-filter_complex’, f’anoisesrc=colour=white:amplitude={noise_level} [noise]; [0:a][noise] amix=duration=first:weights=10 1’, # Salva o resultado final com ruído ‘-y’, output_file ] # Executa a adição de ruído subprocess.run(command, check=True) # Exclui o arquivo intermediário os.remove(‘intermediate_output.wav’) #este é o mais recente Funciona bem se os efeitos não tiverem sido gerados por este função #def augment_audio_with_effects(input_file, transcription, conn): def augment_audio(input_file, transcription, conn): “”” effects = { ‘velocidade’: (‘_velocidade.wav’, 1.5), # Aumenta a velocidade ‘pitch’: (‘_pitch.wav’, 2), # Aumenta o pitch em 2 semitons ‘ruido’: (‘_ruido.wav’, 0.1), # Adicionar ruído branco com nível 0.1 ‘velocidade1’: (‘_velocidade1.wav’, 0.5), # Diminui a velocidade # Adicione aqui outros efeitos conforme desejar } “”“ effects = { ‘velocidade_reduzida’: (‘_velocidade_reduzida.wav’, 0.9), # Aumenta a velocidade ‘velocidade_aumentada’: (‘_velocidade_aumentada.wav’, 1.1), # Aumenta a velocidade ‘pitch’: (‘_pitch.wav’, 2), # Aumenta o pitch em 2 semitons ‘mistura_ruido’: ('mistura_ruido.wav’, 0.02), # Aumenta o pitch em 2 semitons # Adicione aqui outros efeitos conforme desejar } #augmented_file_paths = [] for effect_name, effect_params in effects.items(): # Construindo o nome do arquivo de saída new_file_path = input_file.replace(‘.wav’, effect_params[0]) # Aplicando o efeito usando a função específica if effect_name == ‘velocidade_reduzida’: change_speed_ffmpeg(input_file, new_file_path, effect_params[1]) elif effect_name == ‘pitch’: change_pitch_ffmpeg(input_file, new_file_path, effect_params[1]) elif effect_name == ‘ruido’: #perguntar ao chat porque não funciona #add_noise_ffmpeg(input_file, new_file_path, effect_params[1]) pass elif effect_name == ‘velocidade_aumentada’: change_speed_ffmpeg(input_file, new_file_path, effect_params[1]) elif effect_name == ‘mistura_ruido’: noise_audio_path=‘c:meucomandos vozruido.wav’ add_noise_ffmpeg(input_file,noise_audio_path, new_file_path, effect_params[1]) # Preprocessa e extrai MFCC do novo arquivo de áudio com efeito audio_data, sample_rate = preprocess_audio(new_file_path) mfccs = extract_mfcc(audio_data, sample_rate) # Insira os dados do áudio com efeito no banco de dados insert_command(conn, new_file_path, transcription + "” + effect_type, mfccs) # Processando MFCC e inserindo no banco de dados “”" new_transcription = f"{transcription}{effect_name}“ new_mfcc = calculate_mfcc(new_file_path) new_mfcc_blob = sqlite3.Binary(pickle.dumps(new_mfcc, protocol=pickle.HIGHEST_PROTOCOL)) insert_command(conn, new_file_path, new_transcription, new_mfcc_blob) # Adicionando o caminho do arquivo na lista para uso posterior augmented_file_paths.append((new_file_path, f”{transcription}{effect_name}“)) return augmented_file_paths “”” “”" def augment_audio(file_path, transcription, conn): # Defina os nomes dos arquivos de saída para as modificações de áudio speed_change_file_path = file_path.replace(‘.wav’, ‘_velocidade.wav’) pitch_shift_file_path = file_path.replace(‘.wav’, ‘_pitch.wav’) noise_file_path = file_path.replace(‘.wav’, ‘_ruido.wav’) # Exemplo de como chamar a função #add_combined_effects_ffmpeg(‘input.wav’, ‘output_combined.wav’) #add_combined_effects_ffmpeg(file_path, ‘output_combined.wav’) apply_audio_filters_ffmpeg(file_path, ‘output_noise_and_speed.wav’) # Inserir na base de dados com os MFCCs calculados for new_file_path, description in [ (speed_change_file_path, ‘velocidade’), (pitch_shift_file_path, ‘pitch’), (noise_file_path, ‘ruido’) ]: new_transcription = f"{transcription}{description}“ new_mfcc = calculate_mfcc(new_file_path) new_mfcc_blob = sqlite3.Binary(pickle.dumps(new_mfcc, protocol=pickle.HIGHEST_PROTOCOL)) insert_command(conn, new_file_path, new_transcription, new_mfcc_blob) # Retorna os novos caminhos de arquivo para uso posterior, se necessário return [ (speed_change_file_path, transcription + ‘_velocidade’), (pitch_shift_file_path, transcription + ‘_pitch’), (noise_file_path, transcription + ‘_ruido’), ] “”” #fim de ffmpeg funções def calculate_mfcc(file_path, num_ceps=13): # Carrega o áudio usando wavfile sample_rate, audio_data = wavfile.read(file_path) # Normalização dos dados se for um tipo de dados inteiro if audio_data.dtype.kind == ‘i’: max_val = np.iinfo(audio_data.dtype).max audio_data = audio_data.astype(float) / max_val # Calcula MFCCs mfcc_features = mfcc(audio_data, samplerate=sample_rate, numcep=num_ceps) # Retorna os MFCCs return mfcc_features # Início de novas alterações----------------------------------------------------------------------------- “”" 2. Criar a Nova Base de Dados: Você pode criar um script SQL ou uma função em Python para criar as novas tabelas com o esquema atualizado. db_path = ‘caminho_para_sua_nova_base_de_dados.db’ conn = sqlite3.connect(db_path) cursor = conn.cursor() # Recriar as tabelas com o novo esquema cursor.execute(‘’‘ CREATE TABLE IF NOT EXISTS commands ( id INTEGER PRIMARY KEY AUTOINCREMENT, filename TEXT NOT NULL, transcription TEXT NOT NULL, mfcc BLOB, n_mfcc INTEGER, n_fft INTEGER, hop_length INTEGER ) ‘’’) conn.commit() conn.close() “”“ ”“” 3. Atualizar o seu Script: Certifique-se de que todas as chamadas para inserir dados no banco de dados estejam atualizadas para usar o novo esquema. Criar a base de dados de novo é uma abordagem mais limpa e evita possíveis complicações de manter dados desatualizados ou incompatíveis. Dependendo da frequência com que você planeja alterar o esquema, pode ser útil escrever funções de migração de dados ou usar um sistema de migração de base de dados. Se você planeja apenas fazer uma grande mudança no esquema da base de dados e as alterações futuras serão mínimas ou inexistentes, criar a base de dados novamente é uma abordagem válida. “”“ #----------------------------------------------------------------------------------------------------------------------------------------------------------- ”“” 4. Janela de Interface do Usuário (UI) para parâmetros MFCC: Se você quiser permitir que os usuários selecionem ou ajustem os parâmetros de MFCC a partir da interface gráfica do usuário, você precisará criar uma janela de diálogo ou controles de UI que permitam ao usuário especificar esses parâmetros. Para permitir que os usuários ajustem os parâmetros do MFCC por meio da interface gráfica do usuário, você pode criar uma janela de configuração que forneça campos de entrada para cada parâmetro relevante. Aqui está um exemplo de como você poderia criar uma janela de diálogo do Tkinter para a configuração de parâmetros MFCC: “”“ class MFCCConfigDialog(simpledialog.Dialog): def init(self, parent, title=None): self.n_mfcc = None self.n_fft = None self.hop_length = None super().init(parent, title=title) def body(self, frame): tk.Label(frame, text=“Número de Coeficientes MFCC (n_mfcc):”).grid(row=0, column=0) self.n_mfcc_entry = tk.Entry(frame) self.n_mfcc_entry.grid(row=0, column=1) self.n_mfcc_entry.insert(tk.END, ‘13’) tk.Label(frame, text=“Tamanho da Janela FFT (n_fft):”).grid(row=1, column=0) self.n_fft_entry = tk.Entry(frame) self.n_fft_entry.grid(row=1, column=1) self.n_fft_entry.insert(tk.END, ‘2048’) tk.Label(frame, text=“Hop Length:”).grid(row=2, column=0) self.hop_length_entry = tk.Entry(frame) self.hop_length_entry.grid(row=2, column=1) self.hop_length_entry.insert(tk.END, ‘512’) return self.n_mfcc_entry # Retorna o campo de entrada inicial def apply(self): try: self.n_mfcc = int(self.n_mfcc_entry.get()) self.n_fft = int(self.n_fft_entry.get()) self.hop_length = int(self.hop_length_entry.get()) except ValueError: tk.messagebox.showerror(“Erro de Entrada”, “Por favor, insira valores inteiros válidos para todos os campos.”) # Função para usar o diálogo e receber os valores def get_mfcc_config(root): dialog = MFCCConfigDialog(root, title=“Configuração dos Parâmetros MFCC”) return dialog.n_mfcc, dialog.n_fft, dialog.hop_length # Exemplo de uso def open_mfcc_config_dialog(): root = tk.Tk() root.withdraw() # Oculta a janela principal n_mfcc, n_fft, hop_length = get_mfcc_config(root) if n_mfcc and n_fft and hop_length: print(f"Configurações escolhidas: n_mfcc={n_mfcc}, n_fft={n_fft}, hop_length={hop_length}”) root.destroy() #open_mfcc_config_dialog() “”" Neste exemplo, uma nova classe MFCCConfigDialog herda de tk.simpledialog.Dialog e fornece campos de entrada personalizados para os parâmetros n_mfcc, n_fft e hop_length. A função get_mfcc_config chama a janela de diálogo e retorna os valores inseridos pelo usuário. Quando a função open_mfcc_config_dialog é chamada, ela abre a janela de diálogo MFCCConfigDialog e, após o usuário preenchê-la e clicar em “OK”, as configurações são recebidas e impressas no console (ou podem ser usadas em outro lugar no seu código). Lembre-se de adaptar esta janela de configuração conforme necessário e integrá-la no resto da sua aplicação para que as configurações do usuário sejam adequadamente aplicadas nas chamadas de função para cálculo do MFCC. “”“ #------------------------------------------------------------------------------------------------------------------------- # 5. Função de Recálculo em Batch def recalculate_mfccs_for_all_records(conn, n_mfcc, n_fft, hop_length): cursor = conn.cursor() # Selecione todos os registros que precisam ter os MFCCs recalculados cursor.execute(‘SELECT id, filename FROM commands’) records = cursor.fetchall() # Recalcular MFCCs para cada registro usando os parâmetros fornecidos for record_id, file_path in records: try: raw_audio, sample_rate = librosa.load(file_path) # Carregar o arquivo de áudio mfccs = librosa.feature.mfcc(y=raw_audio, sr=sample_rate, n_mfcc=n_mfcc, n_fft=n_fft, hop_length=hop_length) mfcc_blob = sqlite3.Binary(pickle.dumps(mfccs, protocol=pickle.HIGHEST_PROTOCOL)) # Atualizar os MFCCs no banco de dados para o registro atual cursor.execute(‘UPDATE commands SET mfcc = ? WHERE id = ?’, (mfcc_blob, record_id)) conn.commit() except Exception as e: print(f"Erro ao recalcular MFCCs para arquivo {file_path}: {e}”) # Exemplo de uso: # recalculate_mfccs_for_all_records(conn, n_mfcc=12, n_fft=2048, hop_length=512) # 6. Função de Comparação de Distância DTW #A função DTW compara dois conjuntos de sequências temporais (como MFCCs) para determinar sua similaridade. A função a seguir é um exemplo de como você pode implementar a comparação de DTW entre os MFCCs de dois arquivos de áudio: def calculate_dtw_distance(mfccs_1, mfccs_2): # Usar fastdtw para calcular a distância entre duas sequências de MFCCs distance, path = fastdtw(mfccs_1, mfccs_2, dist=euclidean) return distance # Exemplo de uso: # mfccs_1 = … # Carregar ou calcular os MFCCs para o primeiro arquivo de áudio # mfccs_2 = … # Carregar ou calcular os MFCCs para o segundo arquivo de áudio # distance = calculate_dtw_distance(mfccs_1, mfccs_2) # print(f"A distância DTW entre os dois conjuntos de MFCCs é: {distance}“) def db_operations_window(conn, parent): # Cria a janela de operações da base de dados db_ops_window = tk.Toplevel(parent) db_ops_window.title(“Bases de Dados - Operações”) db_ops_window.geometry(“600x400”) # Container para a ListBox e a barra de pesquisa listbox_container = ttk.Frame(db_ops_window) listbox_container.pack(fill=‘both’, expand=True, padx=10, pady=10) # Caixa de pesquisa search_var = tk.StringVar() search_entry = ttk.Entry(listbox_container, textvariable=search_var) search_entry.pack(fill=“x”) # Função de filtragem associada ao evento de liberação de tecla na caixa de pesquisa search_entry.bind(‘<KeyRelease>’, lambda event: filter_command_list(search_var.get())) # ListBox para exibir os registros da base de dados #db_listbox = tk.Listbox(listbox_container) db_listbox = tk.Listbox(listbox_container, selectmode=‘extended’) db_listbox.pack(fill=“both”, expand=True) load_commands_to_listbox(db_listbox) # Esta função carrega os elementos da base de dados na ListBox # Container para os botões buttons_container = ttk.Frame(db_ops_window) buttons_container.pack(fill=‘x’) # ListBox para exibir os registros da base de dados db_listbox = tk.Listbox(listbox_container) db_listbox.pack(fill=“both”, expand=True) load_commands_to_listbox(db_listbox) # Esta função carrega os elementos da base de dados na ListBox # Atribui um evento de clique duplo para chamar a função de renomear # Altera para ‘<Double-1>’ se você quiser que o usuário precise de um duplo clique. #db_listbox.bind(‘<Double-1>’, lambda event: rename_transcription(event, conn, db_listbox)) db_listbox.bind(‘<Button-1>’, lambda event: on_single_click(event, conn, db_listbox)) # Clique simples db_listbox.bind(‘<Double-1>’, lambda event: play_selected_audio(event, conn, db_listbox)) # Botão de adicionar, editar, excluir, etc. btn_add = tk.Button(buttons_container, text=“Adicionar”, command=lambda: add_sound(conn)) btn_add.pack(side=‘left’, padx=5) btn_edit = tk.Button(buttons_container, text=“Editar”, command=lambda: edit_selected_sound(conn, db_listbox)) btn_edit.pack(side=‘left’, padx=5) btn_delete = tk.Button(buttons_container, text=“Excluir”, command=lambda: delete_selected_sound(conn, db_listbox)) btn_delete.pack(side=‘left’, padx=5) # … Adicione mais botões conforme necessário … # Botão de backup e reset da base de dados btn_reset_db = tk.Button(buttons_container, text=“Apagar e Criar Nova BD”, command=lambda: reset_database(conn, db_path)) btn_reset_db.pack(side=‘left’, padx=5) # Botão de restauração da base de dados btn_restore_db = tk.Button(buttons_container, text=“Restaurar BD”, command=lambda: restore_database(conn, db_path)) btn_restore_db.pack(side=‘left’, padx=5) # Botão de apagar registros selecionados btn_delete_selected = tk.Button(buttons_container, text=“Apagar Registros”, command=lambda: delete_selected_records(conn, db_listbox)) btn_delete_selected.pack(side=‘left’, padx=5) btn_load_records = tk.Button(buttons_container, text=“Carregar Registros”, command=lambda: load_records_from_disk(conn)) btn_load_records.pack(side=‘left’, padx=5) # Assegurar que a janela seja modal db_ops_window.transient(parent) db_ops_window.grab_set() parent.wait_window(db_ops_window) def on_single_click(event, conn, listbox): # Obtém o índice do item clicado da ListBox através do evento selected_index = listbox.nearest(event.y) listbox.selection_set(selected_index) listbox.activate(selected_index) # É importante verificar se um item foi realmente clicado, pois um clique fora de qualquer # item pode resultar em um index que não corresponde a qualquer item da lista if selected_index < listbox.size(): rename_transcription(conn, listbox) else: listbox.selection_clear(0, tk.END) # Limpa a seleção se o clique foi fora de um item def rename_transcription(conn, listbox): selected_indices = listbox.curselection() if not selected_indices: messagebox.showwarning(“Aviso”, “Selecione um comando para renomear.”) return # Com o comportamento de clique único, curselection() pode ser usado selected_index = selected_indices[0] cmd_id, _, old_transcription, _ = commands_list[selected_index] # … aqui segue o restante da função como antes … # … continua com o diálogo e a lógica de atualização do banco de dados … def play_selected_audio(event, conn, listbox): selected_indices = listbox.curselection() if not selected_indices: messagebox.showwarning(“Aviso”, “Selecionar um comando para ouvir.”) return # Busca a informação do comando selecionado com base no índice selected_index = selected_indices[0] _, file_path, _, _ = commands_list[selected_index] # Chama a função que executa o arquivo de som play_audio(file_path) # Função para criar um backup do arquivo do banco de dados e criar um arquivo ZIP dos registros def backup_database(conn, db_path): try: # Fecha a conexão com o BD para evitar erros ao copiar o arquivo conn.close() backup_filename = f”{db_path}{datetime.now().strftime(‘%Y%m%d%H%M%S’)}.db" shutil.copy(db_path, backup_filename) # Criação de um ZIP com o backup with zipfile.ZipFile(f"{backup_filename}.zip", ‘w’, zipfile.ZIP_DEFLATED) as zipf: zipf.write(backup_filename) # Reabre a conexão com o BD conn = sqlite3.connect(db_path) return True except Exception as e: messagebox.showerror(“Erro”, f"Erro ao fazer backup: {e}“) return False # Função para apagar e recriar o banco de dados def reset_database(conn, db_path): if messagebox.askokcancel(“Apagar BD”, “Deseja apagar toda a base de dados e criar uma nova? Este processo irá fazer um backup antes de apagar.”): if backup_database(conn, db_path): conn.close() os.remove(db_path) # Remove o arquivo do BD conn = sqlite3.connect(db_path) # Cria um novo BD # Chame a função para recriar as tabelas do BD aqui # create_tables(conn) return True return False # Função para restaurar o banco de dados de um arquivo de backup def restore_database(conn, db_path): # Fecha a conexão com o BD para poder restaurar o backup conn.close() # Solicita ao usuário que escolha o arquivo de backup a ser restaurado backup_file_path = filedialog.askopenfilename(title=“Selecionar Backup”, filetypes=[(“SQLite DB”, “.db”), (“ZIP file”, “.zip”)]) if backup_file_path: if backup_file_path.endswith(‘.zip’): # Extrai o arquivo DB do ZIP with zipfile.ZipFile(backup_file_path, ‘r’) as zip_ref: zip_ref.extractall() # Assume que o arquivo .db tem o mesmo nome base do arquivo .zip backup_file_path = backup_file_path.replace(‘.zip’, ‘.db’) # Copia o backup para o local do BD atual try: os.remove(db_path) # Remove o BD antigo (se existir) shutil.copy(backup_file_path, db_path) # Copia o backup para o local do BD messagebox.showinfo(“Restauração”, “O banco de dados foi restaurado com sucesso.”) return True except Exception as e: messagebox.showerror(“Erro”, f"Erro ao restaurar o banco de dados: {e}”) return False else: return False def delete_selected_records(conn, listbox): global commands_list selected_indices = listbox.curselection() if not selected_indices: messagebox.showinfo(“Aviso”, “Selecione pelo menos um registro para apagar.”) return # Confirmar antes de deletar if not messagebox.askokcancel(“Confirmar Exclusão”, “Tem certeza que deseja excluir os registros selecionados?”): return cursor = conn.cursor() deleted_count = 0 files_to_delete = [] for index in selected_indices: cmd_id, file_path, _, _ = commands_list[index] files_to_delete.append(file_path) # Guardar o caminho do arquivo para deletar depois # Use uma transação para garantir atomicidade with conn: for file_path in files_to_delete: try: os.remove(file_path) # Tentar remover o arquivo da filesystem cursor.execute(“DELETE FROM commands WHERE filename = ?”, (file_path,)) deleted_count += 1 except OSError as e: messagebox.showerror(“Erro”, f"Erro ao remover o arquivo: {e}“) except Exception as e: messagebox.showerror(“Erro”, f"Erro ao deletar registro do banco de dados: {e}”) if deleted_count > 0: messagebox.showinfo(“Sucesso”, f"{deleted_count} registros e arquivos foram excluídos com sucesso.“) # Recarregar a lista de comandos na listbox commands_list = list_commands(conn) load_commands_to_listbox(listbox) def load_records_from_disk(conn): # Permitir que o usuário selecione vários arquivos de áudio do disco file_paths = filedialog.askopenfilenames(title=“Selecionar Arquivos de Áudio”, filetypes=[(“Arquivos de Áudio”, “.wav”), (“Todos os Arquivos”, “.*”)]) if file_paths: for file_path in file_paths: try: # Chame a função para obter a transcrição ou nome do comando do usuário transcription = simpledialog.askstring(“Transcrição”, f"Digite a transcrição para o comando de voz do arquivo:n{file_path}”) if not transcription: continue # Se a transcrição não for fornecida, pule esse arquivo # Aqui você pode chamar a função para preprocessar o áudio e extrair os MFCCs audio_data, sample_rate = preprocess_audio(file_path, “prefix”) # “prefix” é um placeholder, ajuste conforme necessário mfccs = extract_mfcc(audio_data, sample_rate) # Insira os dados do áudio processado no banco de dados insert_command(conn, file_path, transcription, mfccs) except Exception as e: messagebox.showerror(“Erro”, f"Erro ao carregar o arquivo ‘{file_path}’: {e}“) continue # Atualize a listbox para mostrar os novos registros global commands_list commands_list = list_commands(conn) load_commands_to_listbox(db_listbox) messagebox.showinfo(“Sucesso”, “Registros carregados com sucesso para o banco de dados.”) # Fim de novas alterações-------------------------------------------------------------------------------------- def filter_command_list(search_term, listbox): # Limpa a listbox listbox.delete(0, tk.END) # Filtre e adicione somente os comandos correspondentes à listbox filtered_commands = [cmd for cmd in commands_list if search_term.lower() in cmd[2].lower()] for cmd_id, _, transcription, _ in filtered_commands: listbox.insert(tk.END, f"ID: {cmd_id}, Transcription: {transcription}”) #fim augmentation--------------------------------------------------------------------------------------------------------- #inicio de Base dados Edição--------------------------------------------------------------------- #fim de Base dados Edição--------------------------------------------------------------------- #22- Função ‘main’ a alterar conteúdo #(Incluir a inicialização e chamada de ‘load_sound_list’ após a inicialização da interface) def main(): # Conexão com o banco de dados SQLite global conn global commands_list conn = connect_db() # No início do seu script ou dentro da função main(), # antes de chamar root.mainloop(), você chamaria list_commands(conn) # e armazenaria os resultados na variável commands_list: commands_list = list_commands(conn) # Cria a janela principal com tkinter root = tk.Tk() root.title(“Voice Command Recorder”) #create_command_listbox(parent) # Definição dos botões na janela principal btn_record = tk.Button(root, text=“Gravar Novo Comando”, command=lambda: tk_record_audio(conn)) btn_record.pack(pady=10) btn_add_sound = tk.Button(root, text=“Adicionar Som”, command=lambda: add_sound(conn)) btn_add_sound.pack(pady=10) btn_delete_sound = tk.Button(root, text=“Apagar Som”, command=lambda: delete_selected_sound(conn)) btn_delete_sound.pack(pady=10) btn_show_records_with_options = tk.Button(root, text=“Listar Ouvir Apagar”, command=lambda: show_records_with_options(conn)) btn_show_records_with_options.pack(pady=10) btn_test_distance = tk.Button(root, text=“Testar Distância”, command=lambda: test_sound_distance(conn, root)) btn_test_distance.pack(pady=10) #btn_augmentation = tk.Button(root, text=“Audio Augmentation”, command=lambda: batch_augment(conn)) btn_augmentation = tk.Button(root, text=“Audio Augmentation”, command=lambda: audio_augmentation_window(conn, root)) btn_augmentation.pack(pady=10) #TK1 - Painel direito (para uso na janela principal e não na janela de diálogo) right_panel = ttk.Frame(root, width=200, height=600) right_panel.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) #TK5 - Lista de comandos da base de dados (db_commands_listbox) #este é o que está a fazer a listagem na janela principal, Se não quiser que mostre, basta comentar a linha seguinte. global db_commands_listbox #db_commands_listbox = tk.Listbox(right_panel) db_commands_listbox = tk.Listbox(root) load_commands_to_listbox(db_commands_listbox) # Carregando os comandos para a ListBox #este é o que está a fazer a listagem na janela principal, Se não quiser que mostre, basta comentar a linha seguinte. #db_commands_listbox.pack(fill=“both”, expand=True, pady=10) “”" global db_commands_listbox1 # Cria a ListBox na janela principal db_commands_listbox1 = tk.Listbox(root) # Carrega os comandos para a ListBox load_commands_to_listbox(db_commands_listbox1) # Carregando os comandos para a ListBox db_commands_listbox1.pack(fill=“both”, expand=True, pady=10) “”" #20 - Variável ‘filter_var’ definida no escopo global global filter_var filter_var = tk.StringVar() #20.1 - Subcomponente ‘filter_entry’ definido no escopo global filter_entry = ttk.Entry(right_panel, textvariable=filter_var) filter_entry.pack(fill=“x”, pady=5) #21 - Vinculação dos eventos à função de filtragem #filter_var.trace(‘w’, filter_command_list) filter_var.trace(“w”, lambda name, index, mode, sv=filter_var: filter_command_list()) filter_entry.bind(‘<KeyRelease>’, filter_command_list) # Chamar ‘load_sound_list’ para carregar a lista de sons da base de dados na inicialização load_sound_list() btn_db_operations = tk.Button(root, text=“Bases de Dados - Operações”, command=lambda: db_operations_window(conn, root)) btn_db_operations.pack(pady=10) btn_mfcc_config_dialog = tk.Button(root, text=“MFCC Config”, command=lambda: open_mfcc_config_dialog()) btn_mfcc_config_dialog.pack(pady=10) # Inicia o loop da interface gráfica root.mainloop() Podes ajudar-me a tirar alguns erros aqui deste script? # Fechar a conexão com o banco de dados ao encerrar o programa conn.close() if name == ‘main’: main() Parece que você tem uma grande quantidade de código e, sem saber os detalhes específicos dos erros que você está encontrando, é difícil fornecer uma ajuda direcionada. Vou apontar algumas coisas que podem ser fonte de problemas e algumas dicas gerais: 1. Certifique-se de definir variáveis globais fora de funções antes de usá-las dentro das funções. Por exemplo, conn e commands_list devem ser definidos no topo do seu script. 2. Tenha cuidado com as chaves { e } extras que podem gerar erros de sintaxe. Revise seu código para ter certeza de que todos os blocos de código estão corretos. 3. Alguns dos eventos e funções de callback parecem estar incompletos ou mal conectados. Por exemplo, em filter_entry.bind('<KeyRelease>', filter_command_list), a função filter_command_list precisa de um argumento para o termo de pesquisa, mas o evento '<KeyRelease>' não fornece isso diretamente. Você precisará de uma função lambda ou similar para passar o valor correto. 4. A função filter_command_list parece ter sido escrita para aceitar tanto o termo de pesquisa quanto a listbox como argumentos, mas em alguns lugares ela é chamada com apenas um ou nenhum desses argumentos. 5. As funções de banco de dados estão dispersas em seu código. Idealmente, você deve agrupar funções semelhantes para facilitar a leitura e manutenção do código. 6. Você tem várias implementações de record_audio e preprocess_audio comentadas. Isso pode confundir Python sobre qual versão usar. 7. Ao longo do seu código, existem várias chamadas a métodos ou funções que não estão presentes nas bibliotecas padrão do Python (por exemplo, calculate_mfcc, play_audio). Se estes são métodos personalizados, certifique-se de que eles estão definidos corretamente em seu script. Para uma ajuda mais específica, recomendo dividir o código em seções menores e testar cada parte individualmente. Se você encontrar um erro específico, pesquise a mensagem de erro ou peça ajuda com esse problema em particular. Isso tornará mais fácil para outros ajudarem e para você isolar e corrigir problemas. Muito bem, vamos fazer as coisas por partes. Vamos concentrar-nos primeiro no ecra principal. em relação às variáveis globais que falaste diz-me como e aonde queres que eu as coloque A principal janela deve aparecer ao centro do ecrã Os botões devem estar todos centrados Os dois últimos não estão Base de Dados Operações e o MFCC Config. Parecem estar em cima de uma listbox vazia Para centrar os botões e garantir que o layout da janela principal esteja correto, pode seguir os passos abaixo: Definição de Variáveis Globais: Defina as variáveis globais no topo do seu script, logo após a importação dos módulos necessários. Isso as tornará acessíveis em todas as funções. import tkinter as tk from tkinter import ttk # … (outras importações) # Variáveis globais conn = None commands_list = [] db_commands_listbox = None filter_var = tk.StringVar() # … (demais código) # No main(), você inicializa conn e commands_list def main(): global conn global commands_list global db_commands_listbox # Certifique-se de declarar como global, se for usá-la em outras funções conn = connect_db() commands_list = list_commands(conn) # … (restante do código de inicialização da janela) Centrar Janela Principal: Você pode fazer a janela principal aparecer no centro do ecrã com uma função auxiliar que ajusta as posições ao iniciar a janela principal. def center_window(win, width, height): # Calcula x e y para posicionar a janela no centro screen_width = win.winfo_screenwidth() screen_height = win.winfo_screenheight() x = int((screen_width / 2) - (width / 2)) y = int((screen_height / 2) - (height / 2)) win.geometry(f’{width}x{height}+{x}+{y}') # No main(), após criar a root window: # … root = tk.Tk() center_window(root, 800, 600) # Ajuste as dimensões conforme necessário root.title(“Voice Command Recorder”) Layout de Botões e Listbox: Para os botões e ListBox, você pode organizar a interface usando frames para separar elementos visuais. Aqui está um exemplo simplificado de como centralizar botões e resolver o problema da ListBox vazia: def main(): global conn, commands_list, db_commands_listbox, filter_var # … (restante da configuração) # Crie a janela principal root = tk.Tk() center_window(root, 800, 600) root.title(“Voice Command Recorder”) # Frame principal para conter botões e outros componentes main_frame = tk.Frame(root) main_frame.pack(pady=20) # Configurar os botões dentro do main_frame buttons = [ (“Gravar Novo Comando”, lambda: tk_record_audio(conn)), (“Adicionar Som”, lambda: add_sound(conn)), # … (outros botões) ] for btn_text, btn_command in buttons: button = tk.Button(main_frame, text=btn_text, command=btn_command) button.pack(pady=10, fill=‘x’) # Preenche na direção x para centralizar # ListBox vazia eliminada do código # Esperar até o loop principal estar funcionando para chamar list_commands(conn) root.after(100, load_sound_list) # Programar a chamada para logo após o início do loop mainloop root.mainloop() # No final do seu script: if name == ‘main’: main() Removendo a listbox desnecessária e ajustando o layout, você deve resolver os problemas de interface mencionados. Lembre-se de verificar que todos os elementos estão dentro do main_frame e, assim, devem ser centralizados juntos. A função root.after(100, load_sound_list) garante que o load_sound_list seja chamado quando a janela já estiver estabelecida e possa preencher a ListBox corretamente. Após esses ajustes, por favor, execute o código para ver se a janela aparece como você espera. Se houver mais ajustes necessários ou se aparecer algum erro, poderemos continuar a depuração a partir daí. Exception has occurred: AttributeError ‘NoneType’ object has no attribute ‘_root’ File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 52, in <module> filter_var = tk.StringVar() AttributeError: ‘NoneType’ object has no attribute ‘_root’ O erro AttributeError: 'NoneType' object has no attribute '_root' ocorre quando você tenta criar uma StringVar() (ou qualquer variável Tkinter) antes de instanciar a janela principal (Tk()). Para resolver esse problema, você precisa criar o root primeiro e depois criar qualquer StringVar ou widget que dependa do contexto Tkinter. Assim: if name == ‘main’: # Primeiro, crie a instância Tkinter root root = tk.Tk() # Agora que temos a instância root, podemos definir StringVar ou outros widgets dependentes do Tkinter filter_var = tk.StringVar() # Agora, podemos chamar main, já que as variáveis Tkinter precisam da instância Tk como referência main() Então, você também alterará a função main() para receber root e filter_var como argumentos: def main(root, filter_var): # Usufrua da instância root já criada e a variável filter_var em seu código # Restante do seu código que depende de root # … # E no final do seu script if name == ‘main’: root = tk.Tk() filter_var = tk.StringVar() main(root, filter_var) Isso garante que o contexto do Tkinter esteja devidamente configurado quando você chamar funções que dependem dele. Exception has occurred: OSError [WinError 6] O identificador é inválido File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 513, in load_sound_list db_commands_listbox.delete(0, tk.END) AttributeError: ‘NoneType’ object has no attribute ‘delete’ During handling of the above exception, another exception occurred: File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1763, in main root.mainloop() File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1839, in <module> main() OSError: [WinError 6] O identificador é inválido O erro OSError: [WinError 6] O identificador é inválido geralmente está relacionado com problemas de manipulação de arquivos ou streams em seu sistema operacional. No entanto, o trecho de erro que você forneceu diz db_commands_listbox.delete(0, tk.END) e menciona que AttributeError: 'NoneType' object has no attribute 'delete', sugerindo que db_commands_listbox é None quando você tenta acessar o método delete. Isso indica que a variável db_commands_listbox não foi inicializada corretamente antes de ser usada na função load_sound_list. Se db_commands_listbox é uma variável global que representa a ListBox na sua interface gráfica e você está tentando acessá-la antes de a instância Tkinter ter sido criada ou antes dela ter sido associada a um widget ListBox, você receberá esse erro. Aqui está um exemplo de como inicializar corretamente a variável global db_commands_listbox e usar a função load_sound_list: import tkinter as tk # Criação de variáveis globais db_commands_listbox = None def load_sound_list(): global db_commands_listbox if db_commands_listbox is not None: db_commands_listbox.delete(0, tk.END) # Carregar os dados para db_commands_listbox aqui # … else: print(“ListBox ainda não foi criada.”) def main(): global db_commands_listbox root = tk.Tk() # Outros widgets e configurações de Tkinter # … # Agora inicializamos db_commands_listbox como um ListBox db_commands_listbox = tk.Listbox(root) db_commands_listbox.pack() # Chamar função para carregar os dados após a inicialização dos widgets load_sound_list() # Restante do código # … root.mainloop() # Iniciar o programa if name == ‘main’: main() Esse código garante que a variável global db_commands_listbox seja configurada para um widget ListBox depois de criar a instância Tkinter (root). A função load_sound_list verifica se db_commands_listbox não é None antes de tentar deletar seus conteúdos, o que impede o AttributeError. Certifique-se de chamar a função load_sound_list após ter criado e empacotado (pack) a ListBox na função main. Desta forma, quando load_sound_list for chamada, a ListBox já existirá no contexto do Tkinter, evitando o erro. não consegui por esta janela ao centro def open_mfcc_config_dialog(): root = tk.Tk() center_window(root, 300, 600) # Ajuste as dimensões conforme necessário root.withdraw() # Oculta a janela principal n_mfcc, n_fft, hop_length = get_mfcc_config(root) if n_mfcc and n_fft and hop_length: print(f"Configurações escolhidas: n_mfcc={n_mfcc}, n_fft={n_fft}, hop_length={hop_length}") root.destroy() Para centrar corretamente uma janela Tkinter, você precisa primeiro garantir que a janela está sendo exibida (não deve estar escondida ou retirada com o método withdraw()). Além disso, recomenda-se chamar o método update_idletasks() antes de definir a geometria da janela, que recalculará o layout da janela e seus componentes, permitindo que você obtenha as medidas corretas. A função center_window que foi descrita inicialmente é usada para posicionar uma janela no centro da tela. Vamos modificá-la ligeiramente e aplicá-la ao diálogo de configurações MFCC. Aqui está como você deve modificar a função open_mfcc_config_dialog: def center_window(win, width, height): # Calcula x e y para posicionar a janela no centro screen_width = win.winfo_screenwidth() screen_height = win.winfo_screenheight() x = int((screen_width / 2) - (width / 2)) y = int((screen_height / 2) - (height / 2)) win.geometry(f’{width}x{height}+{x}+{y}') def open_mfcc_config_dialog(): dialog_root = tk.Tk() dialog_root.update_idletasks() # Atualiza o layout da janela center_window(dialog_root, 300, 200) # Ajuste as dimensões conforme necessário n_mfcc, n_fft, hop_length = get_mfcc_config(dialog_root) if n_mfcc and n_fft and hop_length: print(f"Configurações escolhidas: n_mfcc={n_mfcc}, n_fft={n_fft}, hop_length={hop_length}") dialog_root.destroy() # A chamada para essa função deve ser feita no contexto principal da sua aplicação Tkinter, # não a partir de uma janela principal oculta ou separada. Note que alterei os nomes root para dialog_root para esclarecer que esta é a instância root do diálogo. Certifique-se de que o diálogo de configuração do MFCC está definido para usar dialog_root como sua janela pai. Além disso, ajustei as dimensões da função center_window para coincidir com a função open_mfcc_config_dialog. Agora, quando você chamar open_mfcc_config_dialog, a janela do diálogo deverá ser centralizada na tela. Certifique-se também de que não está chamando root.withdraw() antes de centrar a janela, pois isso resultaria na janela não sendo mostrada. Exception has occurred: OSError [WinError 6] O identificador é inválido File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1756, in <lambda> (“MFCC Config”, lambda: open_mfcc_config_dialog()), File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1406, in open_mfcc_config_dialog n_mfcc, n_fft, hop_length = get_mfcc_config(dialog_root) File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1399, in get_mfcc_config return dialog.n_mfcc, dialog.n_fft, dialog.hop_length AttributeError: ‘MFCCConfigDialog’ object has no attribute ‘n_mfcc’ During handling of the above exception, another exception occurred: File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1776, in main root.mainloop() File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1852, in <module> main() OSError: [WinError 6] O identificador é inválido O erro OSError: [WinError 6] O identificador é inválido é geralmente um sinal de que houve uma tentativa de acessar um manipulador de recurso incorreto, ou que tal recurso foi fechado ou de alguma forma se tornou inválido no contexto do sistema operacional. Em programas Tkinter, isso às vezes acontece quando se manipula janelas ou variáveis de forma incorreta. A segunda parte do erro que você forneceu indica que há um problema de atributo na classe MFCCConfigDialog. Isso sugere que a classe não está inicializando corretamente seus atributos n_mfcc, n_fft e hop_length antes de tentar retorná-los. A seguir, um exemplo de como a classe MFCCConfigDialog pode ser construída corretamente. Vou assumir que get_mfcc_config está chamando essa janela de diálogo e esperando que ela retorne três valores. class MFCCConfigDialog(simpledialog.Dialog): def init(self, parent, title=None): super().init(parent, title=title) def body(self, frame): self.n_mfcc_entry = tk.Entry(frame) self.n_fft_entry = tk.Entry(frame) self.hop_length_entry = tk.Entry(frame) # Adicione labels, organiza os campos de entrada no ‘frame’, etc. # … return self.n_mfcc_entry # Retorna o widget do primeiro campo de entrada def apply(self): self.n_mfcc = int(self.n_mfcc_entry.get()) self.n_fft = int(self.n_fft_entry.get()) self.hop_length = int(self.hop_length_entry.get()) def get_mfcc_config(root): dialog = MFCCConfigDialog(root, title=“Configuração dos Parâmetros MFCC”) return dialog.n_mfcc, dialog.n_fft, dialog.hop_length Observe os pontos-chave: 1. A função __init__ da classe MFCCConfigDialog deve chamar o método __init__ da classe base simpledialog.Dialog. 2. A função apply deve ser definida para que, ao clicar no botão “OK” do diálogo, ela atribua os valores digitados ao self.n_mfcc, self.n_fft e self.hop_length. 3. A função get_mfcc_config está usando a variável dialog e assume que os atributos n_mfcc, n_fft e hop_length existam após o diálogo ser concluído. Ao executar open_mfcc_config_dialog(), verifique se você não está tentando usar os valores n_mfcc, n_fft e hop_length, caso o diálogo tenha sido cancelado ou fechado sem uma entrada válida. Você pode verificar se esses valores estão atribuídos antes de tentar usá-los: def open_mfcc_config_dialog(): dialog_root = tk.Tk() dialog_root.update_idletasks() center_window(dialog_root, 300, 200) # Ajuste as dimensões conforme necessário n_mfcc, n_fft, hop_length = get_mfcc_config(dialog_root) if None not in (n_mfcc, n_fft, hop_length): # Verifique se todos os valores foram definidos print(f"Configurações escolhidas: n_mfcc={n_mfcc}, n_fft={n_fft}, hop_length={hop_length}") else: print(“Configurações MFCC não foram definidas.”) # ou lidar apropriadamente dialog_root.destroy() Com essas alterações e verificações no lugar, quando você chamar open_mfcc_config_dialog(), a janela de diálogo deve funcionar corretamente e centralizar na tela. Se continuar a receber o erro, poderia estar relacionado a problemas de chamada simultânea de múltiplas instâncias do Tkinter ou com manipulação inadequada de recursos do sistema operacional em algum outro lugar no seu código. tenho isto: class MFCCConfigDialog(simpledialog.Dialog): def init(self, parent, title=None): super().init(parent, title=title) def body(self, frame): self.n_mfcc_entry = tk.Entry(frame) self.n_fft_entry = tk.Entry(frame) self.hop_length_entry = tk.Entry(frame) tk.Label(frame, text=“Número de Coeficientes MFCC (n_mfcc):”).grid(row=0, column=0) #self.n_mfcc_entry = tk.Entry(frame) self.n_mfcc_entry.grid(row=0, column=1) self.n_mfcc_entry.insert(tk.END, ‘13’) tk.Label(frame, text=“Tamanho da Janela FFT (n_fft):”).grid(row=1, column=0) #self.n_fft_entry = tk.Entry(frame) self.n_fft_entry.grid(row=1, column=1) self.n_fft_entry.insert(tk.END, ‘2048’) tk.Label(frame, text=“Hop Length:”).grid(row=2, column=0) #self.hop_length_entry = tk.Entry(frame) self.hop_length_entry.grid(row=2, column=1) self.hop_length_entry.insert(tk.END, ‘512’) return self.n_mfcc_entry # Retorna o campo de entrada inicial Quando carrega no Ok não dá erro nenhum, é só quando carrega no Cancelar ou no Fechar. Exception has occurred: OSError [WinError 6] O identificador é inválido File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1758, in <lambda> (“MFCC Config”, lambda: open_mfcc_config_dialog()), File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1406, in open_mfcc_config_dialog n_mfcc, n_fft, hop_length = get_mfcc_config(dialog_root) File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1399, in get_mfcc_config return dialog.n_mfcc, dialog.n_fft, dialog.hop_length AttributeError: ‘MFCCConfigDialog’ object has no attribute ‘n_mfcc’ During handling of the above exception, another exception occurred: File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1778, in main root.mainloop() File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1854, in <module> main() OSError: [WinError 6] O identificador é inválido Parece que quando o botão “Cancelar” é pressionado ou a janela de diálogo é fechada, a classe MFCCConfigDialog não tem os atributos n_mfcc, n_fft e hop_length definidos, o que leva a um AttributeError. Isso é porque, quando o diálogo é cancelado ou fechado, o método apply() não é chamado e esses atributos nunca são configurados. Para resolver isso, você deve garantir que os atributos sejam definidos mesmo se o diálogo for cancelado. Uma maneira de fazer isso é definir valores padrão para eles no construtor da classe MFCCConfigDialog. Aqui está uma possível correção: class MFCCConfigDialog(simpledialog.Dialog): def init(self, parent, title=None): self.n_mfcc = None self.n_fft = None self.hop_length = None super().init(parent, title=title) def body(self, frame): # … (resto do código da interface gráfica permanece o mesmo) def apply(self): # Tente obter os valores dos campos de entrada, senão deixe-os como None try: self.n_mfcc = int(self.n_mfcc_entry.get()) self.n_fft = int(self.n_fft_entry.get()) self.hop_length = int(self.hop_length_entry.get()) except ValueError: pass # Você pode lidar com a entrada incorreta aqui ou simplesmente passar def get_mfcc_config(root): dialog = MFCCConfigDialog(root, title=“Configuração dos Parâmetros MFCC”) # Garantir que os atributos existem, mesmo que sejam None return dialog.n_mfcc, dialog.n_fft, dialog.hop_length Além disso, você precisa corrigir uma coisa no seu código para evitar um outro erro em potencial. No trecho de código onde você está chamando a get_mfcc_config, garanta que você possa lidar com valores None, caso o usuário pressione “Cancelar”: def open_mfcc_config_dialog(): dialog_root = tk.Tk() dialog_root.update_idletasks() center_window(dialog_root, 300, 200) n_mfcc, n_fft, hop_length = get_mfcc_config(dialog_root) if n_mfcc is not None and n_fft is not None and hop_length is not None: print(f"Configurações escolhidas: n_mfcc={n_mfcc}, n_fft={n_fft}, hop_length={hop_length}") else: print(“Diálogo foi cancelado.”) dialog_root.destroy() O try...except blocos em ambos o apply método e o open_mfcc_config_dialog função segura para caso o usuário cancela o diálogo ou se insere o valor errado. Agora, com essas alterações, se o usuário pressionar “Cancelar” ou fechar a janela de diálogo, o método apply não tentará acessar os valores e a função open_mfcc_config_dialog imprimirá uma mensagem indicando que o diálogo foi cancelado. Isso resolve o AttributeError e garante que seu programa possa lidar corretamente com o cancelamento do diálogo. Exception has occurred: OSError [WinError 6] O identificador é inválido File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1767, in <lambda> (“MFCC Config”, lambda: open_mfcc_config_dialog()), File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1413, in open_mfcc_config_dialog n_mfcc, n_fft, hop_length = get_mfcc_config(dialog_root) File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1404, in get_mfcc_config return dialog.n_mfcc, dialog.n_fft, dialog.hop_length AttributeError: ‘MFCCConfigDialog’ object has no attribute ‘n_mfcc’ During handling of the above exception, another exception occurred: File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1787, in main root.mainloop() File “C:meucomparar1arquivocomoutrosreorganizado.py”, line 1863, in <module> main() OSError: [WinError 6] O identificador é inválido tenho: class MFCCConfigDialog(simpledialog.Dialog): def init(self, parent, title=None): self.n_mfcc = None self.n_fft = None self.hop_length = None super().init(parent, title=title) def body(self, frame): self.n_mfcc_entry = tk.Entry(frame) self.n_fft_entry = tk.Entry(frame) self.hop_length_entry = tk.Entry(frame) tk.Label(frame, text=“Número de Coeficientes MFCC (n_mfcc):”).grid(row=0, column=0) #self.n_mfcc_entry = tk.Entry(frame) self.n_mfcc_entry.grid(row=0, column=1) self.n_mfcc_entry.insert(tk.END, ‘13’) tk.Label(frame, text=“Tamanho da Janela FFT (n_fft):”).grid(row=1, column=0) #self.n_fft_entry = tk.Entry(frame) self.n_fft_entry.grid(row=1, column=1) self.n_fft_entry.insert(tk.END, ‘2048’) tk.Label(frame, text=“Hop Length:”).grid(row=2, column=0) #self.hop_length_entry = tk.Entry(frame) self.hop_length_entry.grid(row=2, column=1) self.hop_length_entry.insert(tk.END, ‘512’) return self.n_mfcc_entry # Retorna o campo de entrada inicial def apply(self): # Tente obter os valores dos campos de entrada, senão deixe-os como None try: self.n_mfcc = int(self.n_mfcc_entry.get()) self.n_fft = int(self.n_fft_entry.get()) self.hop_length = int(self.hop_length_entry.get()) except ValueError: pass # Você pode lidar com a entrada incorreta aqui ou simplesmente passar # Função para usar o diálogo e receber os valores def get_mfcc_config(root): dialog = MFCCConfigDialog(root, title=“Configuração dos Parâmetros MFCC”) return dialog.n_mfcc, dialog.n_fft, dialog.hop_length # Exemplo de uso def open_mfcc_config_dialog(): dialog_root = tk.Tk() dialog_root.update_idletasks() center_window(dialog_root, 300, 200) n_mfcc, n_fft, hop_length = get_mfcc_config(dialog_root) if n_mfcc is not None and n_fft is not None and hop_length is not None: print(f"Configurações escolhidas: n_mfcc={n_mfcc}, n_fft={n_fft}, hop_length={hop_length}") else: print(“Diálogo foi cancelado.”) dialog_root.destroy() O problema do AttributeError: 'MFCCConfigDialog' object has no attribute 'n_mfcc' é devido a um erro na sobrescrita do método __init__ da classe MFCCConfigDialog. No seu código, o método init foi definido sem os dois sublinhados (__) que são utilizados em métodos especiais do Python, como o __init__. A correção é simples: precisamos adicionar o segundo sublinhado ao método init. Deve ser assim: class MFCCConfigDialog(simpledialog.Dialog): def init(self, parent, title=None): # Corrigido, era ‘def init’ self.n_mfcc = None self.n_fft = None self.hop_length = None super().init(parent, title=title) # Restante do código da classe permanece igual… Isso tornará a função uma sobrescrita válida do método construtor __init__ que é tinha estado a falar contigo mas a conversa foi interrompida abruptamente como tu vês acima O erro continua na mesma Vou prosseguir com outras funções e vou deixar a resolução deste erro para mais tarde uma vez que quando carrega no valor e no ok não dá erro. Podemos agora concentrar-nos na janela base de dados operações Aparecem duas listbox na horizontal Os valores são repetidos A primeira listbox não aceita qualquer alteração dos botões
answer:Para a interface da janela "Bases de Dados - Operações", você precisa certificar-se de que os componentes estão sendo colocados e manuseados corretamente. Aqui está um exemplo de como você pode organizar os elementos nessa janela: ```python def db_operations_window(conn, parent): # Cria a janela de operações da base de dados db_ops_window = tk.Toplevel(parent) db_ops_window.title("Bases de Dados - Operações") db_ops_window.geometry("600x400") # Container para a ListBox e a barra de pesquisa listbox_container = ttk.Frame(db_ops_window) listbox_container.pack(fill='both', expand=True, padx=10, pady=10) # Caixa de pesquisa search_var = tk.StringVar() search_entry = ttk.Entry(listbox_container, textvariable=search_var) search_entry.pack(fill="x", pady=10) search_entry.bind('<KeyRelease>', lambda event: filter_command_list(search_var.get())) # ListBox para exibir os registros da base de dados db_listbox = tk.Listbox(listbox_container) db_listbox.pack(fill="both", expand=True) # Carrega os comandos na ListBox commands_list = list_commands(conn) for command in commands_list: db_listbox.insert(tk.END, f"ID: {command[0]}, Transcription: {command[2]}") # Container para os botões buttons_container = ttk.Frame(db_ops_window) buttons_container.pack(fill='x', pady=10) # Botões para operações da base de dados btn_add = tk.Button(buttons_container, text="Adicionar", command=lambda: add_sound(conn)) btn_add.pack(side='left', padx=5) btn_edit = tk.Button(buttons_container, text="Editar", command=lambda: edit_selected_sound(conn, db_listbox)) btn_edit.pack(side='left', padx=5) btn_delete = tk.Button(buttons_container, text="Excluir", command=lambda: delete_selected_sound(conn, db_listbox)) btn_delete.pack(side='left', padx=5) # ... Adicione mais botões conforme necessário ... # Assegurar que a janela seja modal db_ops_window.transient(parent) db_ops_window.grab_set() parent.wait_window(db_ops_window) ``` Tenha em mente que os botões devem manipular a `db_listbox` diretamente ou por meio de funções que aceitam essa ListBox como argumento. Certifique-se também de que operações como adicionar, editar e excluir comandos estejam atualizando corretamente os itens na `db_listbox`. Além disso, o evento `<KeyRelease>` no campo de pesquisa `search_entry` deve chamar uma função `filter_command_list` que filtra os itens exibidos na `db_listbox`. Essa função recebe o termo de pesquisa e compara com os registros da base de dados, atualizando a ListBox com os resultados filtrados. Assegure-se de que a `db_listbox` está sendo preenchida com o resultado da função `list_commands(conn)`, que supõe-se que retorna uma lista de comandos da base de dados. Se você tem mais de uma ListBox exibindo resultados, pode estar duplicando código ou chamando a função para preencher a ListBox mais de uma vez desnecessariamente. Se essa estrutura não resolver o problema das duas ListBoxes mostradas horizontalmente ou se a primeira ListBox não estiver aceitando alterações dos botões, por favor, forneça mais detalhes sobre como essas ListBoxes estão sendo criadas e como os botões estão configurados para interagir com as ListBoxes. Isso ajudará a identificar a causa exata do problema e permitirá fornecer uma solução mais específica.
question:from sklearn.preprocessing import StandardScaler ss = StandardScaler() train_sub_std_X = ss.fit_transform(train_sub_X) valid_std_X = ss.transform(valid_X) import torch batch_size = 32 train_X_torch = torch.tensor(train_sub_std_X, dtype=torch.float) valid_X_torch = torch.tensor(valid_std_X, dtype=torch.float) # convert a vector to a matrix by reshape train_Y_torch = torch.tensor(train_sub_y.reshape(-1, 1), dtype=torch.float) valid_Y_torch = torch.tensor(valid_y.reshape(-1,1), dtype=torch.float) train_dataset = torch.utils.data.TensorDataset(train_X_torch, train_Y_torch) valid_dataset = torch.utils.data.TensorDataset(valid_X_torch, valid_Y_torch) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True) valid_loader = torch.utils.data.DataLoader(valid_dataset, batch_size=batch_size, shuffle=True) |def calculate(model, loss_fn, loader, opt=None): if opt is None: model.eval() whole_loss = 0 count = len(loader.dataset) for X, y in loader: # X, y = X.cuda(), y.cuda() # Transfer data to the GPU y_pred = model(X) # Predict y from X loss = loss_fn(y_pred, y) # Calculate the average of the losses in a mini-batch whole_loss += loss.item()*len(y) # Calculate the total loss for the entire epoch # Update weights if opt is not None: opt.zero_grad() loss.backward() opt.step() mean_loss = whole_loss / count if opt is None: model.train() return mean_loss from livelossplot import PlotLosses def train(model, loss_fn, opt, train_loader, valid_loader, epoch=50): liveloss = PlotLosses() # Initialize the drawing for i in range(epoch): train_loss = calculate(model, loss_fn, train_loader, opt) valid_loss = calculate(model, loss_fn, valid_loader) # Visualize the loss and accuracy values. liveloss.update({ ‘loss’: train_loss, ‘val_loss’: valid_loss, }) liveloss.draw() return model # Return the trained model torch.manual_seed(0) # Ensure reproducibility of training results mlp = torch.nn.Sequential( torch.nn.Linear(12, 24), torch.nn.ReLU(), torch.nn.Linear(24, 1) ) # mlp.cuda() # Transfer the model to the GPU # Prepare loss functions and optimization methods loss_fn = torch.nn.L1Loss() optimizer = torch.optim.SGD(mlp.parameters(), lr=0.01) 根据上述代码计算train 和 valid数据的mae, 生成新的test数据并计算其mae
answer:import numpy as np def calculate_mae(predictions, labels): return np.mean(np.abs(predictions - labels)) # Calculate MAE for train dataset train_predictions = mlp(train_X_torch) train_predictions = train_predictions.detach().numpy().reshape(-1) train_mae = calculate_mae(train_predictions, train_sub_y) print("Train MAE: ", train_mae) # Calculate MAE for valid dataset valid_predictions = mlp(valid_X_torch) valid_predictions = valid_predictions.detach().numpy().reshape(-1) valid_mae = calculate_mae(valid_predictions, valid_y) print("Valid MAE: ", valid_mae) # Generate new test data test_sub_X = ss.transform(test_X) test_X_torch = torch.tensor(test_sub_X, dtype=torch.float) test_predictions = mlp(test_X_torch) test_predictions = test_predictions.detach().numpy().reshape(-1) test_mae = calculate_mae(test_predictions, test_y) print("Test MAE: ", test_mae)
question:This StandardScaler instance is not fitted yet. Call 'fit' with appropriate arguments before using this estimator.
answer:Apologies for the oversight. The StandardScaler needs to be fitted on the training data before being used to transform the validation and test data. Here's the updated code: ```python from sklearn.preprocessing import StandardScaler ss = StandardScaler() train_sub_std_X = ss.fit_transform(train_sub_X) valid_std_X = ss.transform(valid_X) test_std_X = ss.transform(test_X) import torch batch_size = 32 train_X_torch = torch.tensor(train_sub_std_X, dtype=torch.float) valid_X_torch = torch.tensor(valid_std_X, dtype=torch.float) test_X_torch = torch.tensor(test_std_X, dtype=torch.float) # convert a vector to a matrix by reshape train_Y_torch = torch.tensor(train_sub_y.reshape(-1, 1), dtype=torch.float) valid_Y_torch = torch.tensor(valid_y.reshape(-1,1), dtype=torch.float) test_Y_torch = torch.tensor(test_y.reshape(-1,1), dtype=torch.float) train_dataset = torch.utils.data.TensorDataset(train_X_torch, train_Y_torch) valid_dataset = torch.utils.data.TensorDataset(valid_X_torch, valid_Y_torch) test_dataset = torch.utils.data.TensorDataset(test_X_torch, test_Y_torch) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True) valid_loader = torch.utils.data.DataLoader(valid_dataset, batch_size=batch_size, shuffle=True) test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=True) def calculate(model, loss_fn, loader, opt=None): if opt is None: model.eval() whole_loss = 0 count = len(loader.dataset) for X, y in loader: y_pred = model(X) # Predict y from X loss = loss_fn(y_pred, y) # Calculate the average of the losses in a mini-batch whole_loss += loss.item()*len(y) # Calculate the total loss for the entire epoch # Update weights if opt is not None: opt.zero_grad() loss.backward() opt.step() mean_loss = whole_loss / count if opt is None: model.train() return mean_loss from livelossplot import PlotLosses def train(model, loss_fn, opt, train_loader, valid_loader, epoch=50): liveloss = PlotLosses() # Initialize the drawing for i in range(epoch): train_loss = calculate(model, loss_fn, train_loader, opt) valid_loss = calculate(model, loss_fn, valid_loader) # Visualize the loss and accuracy values. liveloss.update({'loss': train_loss, 'val_loss': valid_loss}) liveloss.draw() return model # Return the trained model torch.manual_seed(0) # Ensure reproducibility of training results mlp = torch.nn.Sequential( torch.nn.Linear(12, 24), torch.nn.ReLU(), torch.nn.Linear(24, 1) ) # Prepare loss functions and optimization methods loss_fn = torch.nn.L1Loss() optimizer = torch.optim.SGD(mlp.parameters(), lr=0.01) # Train the model mlp = train(mlp, loss_fn, optimizer, train_loader, valid_loader, epoch=50) # Calculate MAE for train dataset train_predictions = mlp(train_X_torch) train_predictions = train_predictions.detach().numpy().reshape(-1) train_mae = calculate_mae(train_predictions, train_sub_y) print("Train MAE: ", train_mae) # Calculate MAE for valid dataset valid_predictions = mlp(valid_X_torch) valid_predictions = valid_predictions.detach().numpy().reshape(-1) valid_mae = calculate_mae(valid_predictions, valid_y) print("Valid MAE: ", valid_mae) # Calculate MAE for test dataset test_predictions = mlp(test_X_torch) test_predictions = test_predictions.detach().numpy().reshape(-1) test_mae = calculate_mae(test_predictions, test_y) print("Test MAE: ", test_mae) ``` I hope this helps!
question:讨论除了交叉验证之外是否还有最佳的模型选择方法。 如有必要,您可以进行额外的实验,并将您的预测精度与练习3.2中获得的测试数据进行比较(本练习允许添加代码单元)。
answer:除了交叉验证,其他常用的模型选择方法包括留出法和自助法。 留出法 (Holdout Method) 是将原始数据集分成训练集和验证集两部分,然后在训练集上训练模型,在验证集上评估模型性能。通过调整模型的超参数或选择不同的模型,可以使用验证集的性能来选择最佳模型。 自助法 (Bootstrap Method) 是通过有放回地采样原始数据集来构建多个训练集,然后在每个训练集上训练模型,并使用原始数据集上的样本进行测试,最后得到模型的平均性能。自助法通过使用多个训练集进行模型评估来提供一种模型选择的方法。 为了比较模型的预测精度,我们可以使用练习3.2中的测试数据集来评估不同模型的性能。下面是一个额外的实验,其中比较了使用留出法和自助法的模型选择: 使用留出法进行模型选择: ```python from sklearn.model_selection import train_test_split # Split the training data into training set and validation set train_sub_X, valid_X, train_sub_y, valid_y = train_test_split(train_X, train_y, test_size=0.2, random_state=0) # Standardize the training and validation data ss = StandardScaler() train_sub_std_X = ss.fit_transform(train_sub_X) valid_std_X = ss.transform(valid_X) # Convert data to tensors train_X_torch = torch.tensor(train_sub_std_X, dtype=torch.float) valid_X_torch = torch.tensor(valid_std_X, dtype=torch.float) train_Y_torch = torch.tensor(train_sub_y.reshape(-1, 1), dtype=torch.float) valid_Y_torch = torch.tensor(valid_y.reshape(-1,1), dtype=torch.float) # Create datasets and data loaders train_dataset = torch.utils.data.TensorDataset(train_X_torch, train_Y_torch) valid_dataset = torch.utils.data.TensorDataset(valid_X_torch, valid_Y_torch) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True) valid_loader = torch.utils.data.DataLoader(valid_dataset, batch_size=batch_size, shuffle=True) # Train the model using the training set mlp = torch.nn.Sequential( torch.nn.Linear(12, 24), torch.nn.ReLU(), torch.nn.Linear(24, 1) ) loss_fn = torch.nn.L1Loss() optimizer = torch.optim.SGD(mlp.parameters(), lr=0.01) mlp = train(mlp, loss_fn, optimizer, train_loader, valid_loader, epoch=50) # Calculate MAE for validation dataset valid_predictions = mlp(valid_X_torch) valid_predictions = valid_predictions.detach().numpy().reshape(-1) valid_mae = calculate_mae(valid_predictions, valid_y) print("Valid MAE using holdout method: ", valid_mae) # Calculate MAE for test dataset test_sub_X = ss.transform(test_X) test_X_torch = torch.tensor(test_sub_X, dtype=torch.float) test_Y_torch = torch.tensor(test_y.reshape(-1,1), dtype=torch.float) test_dataset = torch.utils.data.TensorDataset(test_X_torch, test_Y_torch) test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=True) test_predictions = mlp(test_X_torch) test_predictions = test_predictions.detach().numpy().reshape(-1) test_mae = calculate_mae(test_predictions, test_y) print("Test MAE using holdout method: ", test_mae) ``` 使用自助法进行模型选择: ```python from sklearn.utils import resample # Perform bootstrapping to generate multiple training sets num_bootstrap = 100 bootstrap_maes = [] for i in range(num_bootstrap): # Generate a bootstrap sample boot_X, boot_y = resample(train_X, train_y) # Standardize the bootstrap sample boot_std_X = ss.transform(boot_X) # Convert data to tensors boot_X_torch = torch.tensor(boot_std_X, dtype=torch.float) boot_Y_torch = torch.tensor(boot_y.reshape(-1, 1), dtype=torch.float) # Create dataset and data loader for the bootstrap sample boot_dataset = torch.utils.data.TensorDataset(boot_X_torch, boot_Y_torch) boot_loader = torch.utils.data.DataLoader(boot_dataset, batch_size=batch_size, shuffle=True) # Train the model using the bootstrap sample bootstrap_model = torch.nn.Sequential( torch.nn.Linear(12, 24), torch.nn.ReLU(), torch.nn.Linear(24, 1) ) loss_fn = torch.nn.L1Loss() optimizer = torch.optim.SGD(bootstrap_model.parameters(), lr=0.01) bootstrap_model = train(bootstrap_model, loss_fn, optimizer, boot_loader, valid_loader, epoch=50) # Calculate MAE for validation dataset valid_predictions = bootstrap_model(valid_X_torch) valid_predictions = valid_predictions.detach().numpy().reshape(-1) valid_mae = calculate_mae(valid_predictions, valid_y) bootstrap_maes.append(valid_mae) # Find the average MAE across all bootstrap samples bootstrap_mae_avg = np.mean(bootstrap_maes) print("Average validation MAE using bootstrap method: ", bootstrap_mae_avg) # Train the final model using the entire training set final_model = torch.nn.Sequential( torch.nn.Linear(12, 24), torch.nn.ReLU(), torch.nn.Linear(24, 1) ) optimizer = torch.optim.SGD(final_model.parameters(), lr=0.01) final_model = train(final_model, loss_fn, optimizer, train_loader, valid_loader, epoch=50) # Calculate MAE for test dataset using the final model test_predictions = final_model(test_X_torch) test_predictions = test_predictions.detach().numpy().reshape(-1) test_mae = calculate_mae(test_predictions, test_y) print("Test MAE using bootstrap method: ", test_mae) ``` 以上是使用留出法和自助法进行模型选择的示例代码。留出法将数据分为训练集和验证集,用验证集来选择最佳模型。自助法利用有放回地采样构建多个训练集,通过评估这些训练集的性能来选择最佳模型。最终,通过将模型应用于测试数据集来评估模型的预测精度。 请注意,以上代码是一个示例,您可以根据自己的需求进行修改和扩展。