Olá. Estou a criar um programa que permite ao usuário saber qual nota está a cantar e a sua respetiva frequência, em tempo real. No entanto, durante a atualização da janela, o programa deixa de funcionar e só atualiza ao fim de alguns segundos, acontecendo várias vezes ao longo da execução do programa. Código: # -*- coding: cp1252 -*- from numba import jit import pyximport; pyximport.install() import sys from audiolazy import (tostream, AudioIO, freq2str, sHz, chunks, lowpass, envelope, pi, thub, Stream, maverage) from numpy.fft import rfft import matplotlib import matplotlib.pyplot as plt import ttk import pyaudio import pylab import random #-----------------------------------------------------------------------------# #funcoes de calculo do pitch--------------------------------------------------# #variaveis do pyaudio f_a=440.0 chunk=2048 FORMAT=pyaudio.paInt16 CHANNELS=1 RATE=44100 RECORD_S=2. WAVE_OUTPUT_NAME="sing.wav" pa=pyaudio.PyAudio() # pylab.ioff() stream=pa.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=chunk) #calcula a frequencia def real_freq(): all_v=[] all_data="" x_array=numpy.array([],dtype=numpy.int16) n_chunks=int(RECORD_S*RATE/chunk) npts=n_chunks*chunk for i in range(0, n_chunks): try: data=stream.read(chunk) except: data=stream.read(chunk) all_v.append(data) all_data += data x=numpy.fromstring(data, dtype=numpy.int16) numpy.append(x_array,x) all_x=numpy.fromstring(all_data, dtype=numpy.int16) #p2p = 2.*numpy.sqrt(2.*all_x.var()) z = abs(pylab.fft(all_x)) max_arg = numpy.argmax(z[0:npts/2]) #peak = z[max_arg] freq = pylab.arange(npts)*1.*RATE/npts fmax = freq[max_arg] + 0.01 fmax = round(fmax, 2) return fmax def limiter(sig, threshold=.1, size=256, env=envelope.rms, cutoff=pi/2048): sig = thub(sig, 2) return sig * Stream( 1. if el <= threshold else threshold / el for el in maverage(size)(env(sig, cutoff=cutoff)) ) @tostream def dft_pitch(sig, size=2048, hop=None): for blk in Stream(sig).blocks(size=size, hop=hop): dft_data = rfft(blk) idx, vmax = max(enumerate(dft_data), key=lambda el: abs(el[1]) / (2 * el[0] / size + 1)) yield 2 * pi * idx / size def pitch_from_mic(upd_time_in_ms): rate = 44100 s, Hz = sHz(rate) api = sys.argv[1] if sys.argv[1:] else None # Choose API via command-line chunks.size = 1 if api == "jack" else 16 with AudioIO(api) as recorder: snd = recorder.record(rate=rate) sndlow = lowpass(400 * Hz)(limiter(snd, cutoff=20 * Hz)) hop = int(upd_time_in_ms * 1e-3 * s) for pitch in freq2str(dft_pitch(sndlow, size=2*hop, hop=hop) / Hz): first_cut = pitch.find('+') second_cut = pitch.find('-') if ((first_cut != -1) and (second_cut == -1)): yield pitch[0:first_cut] elif ((first_cut == -1) and (second_cut != -1)): yield pitch[0:second_cut] #funcao que define a afinacao (semelhante com a funcao pitch_from_mic()) def pitch_tune(upd_time_in_ms): #calcula o pitch rate = 44100 s, Hz = sHz(rate) api = sys.argv[1] if sys.argv[1:] else None # Choose API via command-line chunks.size = 1 if api == "jack" else 16 with AudioIO(api) as recorder: snd = recorder.record(rate=rate) sndlow = lowpass(400 * Hz)(limiter(snd, cutoff=20 * Hz)) hop = int(upd_time_in_ms * 1e-3 * s) #calculado o pitch, le o valor encontrado e retorna a afinacao for tune in freq2str(dft_pitch(sndlow, size=2*hop, hop=hop) / Hz): first_cut = tune.find('+') second_cut = tune.find('-') third_cut = tune.find('%') if ((first_cut != -1) and (second_cut == -1)): fourth_cut = tune[first_cut:third_cut] flat_one = fourth_cut[1:] flat_one = float(flat_one) #le os valores e retorna a afinacao if (flat_one <= 20): yield 'Afinado' elif ((flat_one > 20) and (flat_one <= 35)): yield 'Lig. Desafinado' elif (flat_one > 35): yield 'Muito Desafinado' elif ((first_cut == -1) and (second_cut != -1)): fifth_cut = tune[second_cut:third_cut] flat_two = fifth_cut[1:] flat_two = float(flat_two) #le os valores e retorna a afinacao if (flat_two <= 20): yield 'Afinado' elif ((flat_two > 20) and (flat_two <= 35)): yield 'Lig. Desafinado' elif (flat_two > 35): yield 'Muito Desafinado' # ---------------------- #Parte grafica do codigo # ---------------------- if __name__ == "__main__": from Tkinter import * from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import threading import re matplotlib.use('TkAgg') #comeca a janela do programa root = Tk() #define o tamanho da janela root.minsize(1200,500) root.maxsize(1200,500) root.configure(background='#d9d9d9') root.focus_force() #titulo da janela (outer widget) root.title("SingMeter") #stringvars pitch_value = StringVar() note_value = StringVar() no_tone_deaf = StringVar() #-----Frames secundaria(outer widgets) #frame do pitch sing_frame = LabelFrame(root, text='Informacão quantitativa', width=400, height=400, background='#d9d9d9') sing_frame.place(x = 10, y = 30) #frame da nota graph_frame = LabelFrame(root, text='Gráfico', width=700, height=400, background='#d9d9d9') graph_frame.place(x = 450, y = 30) #listas e dicionarios de frequencias e notas (torna a base de dados propia para leitura) database_dict = {} freq_list = [] #funcoes do grafico-------------------------------------------------------# xAchse=pylab.arange(0,100,1) yAchse=pylab.array([0]*100) fig = pylab.figure(1) ax = fig.add_subplot(111) ax.grid(True) ax.set_xlabel("Tempo") ax.set_ylabel("Frequência") ax.axis([0,100, 50, 2000]) line1=ax.plot(xAchse,yAchse,'-') canvas = FigureCanvasTkAgg(fig, master = graph_frame) canvas.show() canvas.get_tk_widget().place(x = 100, y = 50) canvas._tkcanvas.place(x = 100, y = 25) values=[] values = [0 for x in range(100)] Ta=0.01 fa=1.0/Ta fcos=3.5 Konstant=1 T0=1.0 T1=Konstant freq_list = [] def SinwaveformGenerator(): global values,T1,Konstant,T0,wScale2 #ohmegaCos=arccos(T1)/Ta #print "fcos=", ohmegaCos/(2*pi), "Hz" Tnext=((Konstant*T1)*2)-T0 if (len(values)%100>70): try: values.append(random.random()) except: raise else: values.append(Tnext) T0=T1 T1=Tnext root.after(int(wScale2['to'])-wScale2.get(),SinwaveformGenerator) def RealtimePloter(): global values,wScale,wScale2 NumberSamples=min(len(values),wScale.get()) CurrentXAxis=pylab.arange(len(values)-NumberSamples,len(values),1) line1[0].set_data(CurrentXAxis,pylab.array(values[-NumberSamples:])) ax.axis([CurrentXAxis.min(),CurrentXAxis.max(),50, 2000]) canvas.draw() root.after(25,RealtimePloter) #canvas.draw() #manager.show() wScale = Scale(master=root,label="View Width:", from_=3, to=1000,sliderlength=30, orient=HORIZONTAL) wScale2 = Scale(master=root,label="Generation Speed:", from_=1, to=200,sliderlength=30, orient=HORIZONTAL) wScale.set(100) wScale2.set(wScale2['to']-10) #-------------------------------------------------------------------------- #fecha a janela def _quit(): root.quit() root.destroy() #funcões com elementos (inner widgets) da janela @jit def entries_window(pitch, note, sing_state): #exibidor do pitch pitch_value.set(pitch) show_pitch = ttk.Entry(sing_frame, textvariable = pitch_value, state = 'readonly') show_pitch.place(x = 120, y = 58.5) #exibidor da nota note_value.set(note) show_note = ttk.Entry(sing_frame, textvariable = note_value, state = 'readonly') show_note.place(x = 120, y = 135) #exibidor da performance das notas do cantor no_tone_deaf.set(sing_state) show_tone_deaf = ttk.Entry(sing_frame, textvariable = no_tone_deaf, state = 'readonly') show_tone_deaf.place(x = 170, y = 211.5) entries_window(None, None, None) #@jit def window(): #labels #label PITCH pitch_title = ttk.Label(sing_frame, text = "Pitch:", font = "Verdana 20", background='#d9d9d9') pitch_title.place(x = 30, y = 50) #label NOTE note_title = ttk.Label(sing_frame, text = "Nota:", font = "Verdana 21", background='#d9d9d9') note_title.place(x = 30, y = 126) #label AFINACAO tune_title = ttk.Label(sing_frame, text = "Afinação", font = "Verdana 21", background='#d9d9d9') tune_title.place(x = 30, y = 202) #------------------------ #botões #botao SAIR record = ttk.Button(sing_frame, text = 'Sair', command=_quit) record.place(x = 70, y = 300) """#botao STOP stop_button = ttk.Button(sing_frame, text = 'Parar') stop_button.place(x = 160, y = 300)""" window() regex_note = re.compile(r"^([A-Gb#]*-?[0-9]*)([?+-]?)(.*?%?)$") upd_time_in_ms = 200 # atualiza as funcoes def upd_value(): pitches = iter(pitch_from_mic(upd_time_in_ms)) while not root.should_finish: root.value = next(pitches) #atualiza os valores da janela #@jit def upd_timer(): note_value.set("\n".join(regex_note.findall(root.value)[0])) tunes = iter(pitch_tune(upd_time_in_ms)) tuning = next(tunes) no_tone_deaf.set(str("\n".join(regex_note.findall(tuning)[0]))) pitch_value.set(real_freq()) root.after(upd_time_in_ms, upd_timer) root.after(1, SinwaveformGenerator) root.after(1 ,RealtimePloter) # inicia as threads root.should_finish = False root.value = freq2str(0) note_value.set(root.value) root.upd_thread = threading.Thread(target=upd_value) #acaba o programa e as threads root.protocol("WM_DELETE_WINDOW", _quit) root.after_idle(upd_timer) root.upd_thread.start() root.mainloop() root.should_finish = True root.upd_thread.join() Tentei usar o numba (from numba import jit) e o Cython para melhorar o desempenho da execução do programa, mas não tive melhorias significativas. Tentei ainda usar o PyPy, mas devido à sua incompatibilidade com o Numpy não consegui executar o código. As funções pitch_from_mic(upd_time_in_ms) e pitch_tune(upd_time_in_ms) calculam a nota da mesma forma (não consegui colocar toda esta parte do código numa única função, pois dava o seguinte erro: IOError: [Errno -9981] Input overflowed). A função real_freq() demora algum tempo a calcular a frequência, mas, se não forem chamadas as outras funções, o código possui um desempenho aceitável. Estou a usar o Windows 7 e tenho o Python 2.7.8. Continue reading...