2016-05-03 8 views
0

Ich verwende den folgenden Code, um einige sofortige Sound-Verarbeitung/Analyse zu tun. Es funktioniert, aber sehr langsam (im Vergleich zur geplanten Geschwindigkeit). Ich habe einige Zeitmarkierungen hinzugefügt, um herauszufinden, wo das Problem liegt, und ihnen zufolge sollte es keine geben. Typische Dauer (siehe unten) ist < 0,01 s für alle drei berechneten Zeiten, aber es dauert immer noch etwa eine Sekunde, um die Schleife zu vervollständigen. Wo ist das Problem?Beschleunigen Sie den Sound-Processing-Algorithmus

Edit: Bitte beachten Sie, dass die Zeitmessung hier nicht das eigentliche Problem ist. Um das zu beweisen: MyPeaks findet im Grunde nur das Maximum an ziemlich kurzer FFT - nichts teures. Und das Problem bleibt bestehen, selbst wenn diese Routinen auskommentiert sind.

  • Sollte ich etwas anderes als Lambda-Funktion verwenden, um den Zyklus zu machen?
  • Habe ich beim Starten und Aufzeichnen des Streams einen Fehler gemacht?
  • usw.

    import pyaudio 
    import struct 
    import mute_alsa 
    import time 
    import numpy as np 
    from Tkinter import * 
    
    
    def snd_process(k=0): 
    if k<1000: 
        t0=time.clock() 
    
        data = stream.read(CHUNK) 
    
        t1=time.clock() 
    
        fl=CHUNK 
        int_data = struct.unpack("%sh" %str(fl),data) 
        ft=np.fft.fft(int_data) 
        ft=np.fft.fftshift(ft) 
        ft=np.abs(ft)  
    
        t2=time.clock() 
    
        pks=MyPeaks(np.log(ft)) 
    
        freq_out.configure(text=str(pks)) 
    
        t3=time.clock() 
    
        print t1-t0, t2-t1, t3-t2  
    
        master.after(1, lambda: snd_process(k+1)) 
    
    CHUNK = 8000 
    FORMAT = pyaudio.paInt16 
    CHANNELS = 1 
    RATE = 4000 
    
    p = pyaudio.PyAudio() 
    
    stream = p.open(format=FORMAT, 
          channels=CHANNELS, 
          rate=RATE, 
          input=True, 
          frames_per_buffer=CHUNK) 
    
    #Tkinter stuff 
    master=Tk() 
    button_play=Button(master, command=snd_process, bg="yellow", text="Analyze") 
    button_play.grid(row=0, column=0) 
    freq_out = Label(master) 
    freq_out.grid(row=0, column=1) 
    freq_out.configure(text='base') 
    mainloop() 
    
+1

Beschleunigen Sie Ihre Python mit C! (oder Cython) – erip

+0

woher wissen Sie, die Zeit ist Bearbeitungszeit nicht die master.after()? Wenn ich gegen rohes C etwas numpig ausprobiert habe, scheint es ein bisschen schneller zu sein. (für dot das ist nicht überraschend) – paddyg

+0

@paddyg ich nicht, aber das ist nicht das Problem (siehe die Änderung). –

Antwort

4

Sie planen 1000 Callback in tk main thread; Für jeden Callback verwenden Sie 1 ms Verzögerung (after() erstes Argument). Das bedeutet, dass die letzte Schleife nach 1000 ms (1 Sekunde) beginnt.

Vielleicht ist die Schleife dauert noch etwa eine Sekunde, um abzuschließen.

Versuchen Sie also, after_idle() zu verwenden. Ich glaube nicht, dass Sie wirklich brauchen Beschleunigen Sie den Sound-Processing-Algorithmus, weil np ist bereits sehr effizient.

[EDIT] Überraschung !! Sie lesen vom Audiokanal bei jeder Iteration 1 Sekunde 8000 Bytes im 16-Bit-Format für eine Bildrate von 4000. Du brauchst eine Sekunde, um es zu haben.

+0

Danke, aber - leider - es funktioniert nicht. 'after_idle' dauert etwa zur selben Zeit und wenn 0ms Timeout verwendet wird, aktualisiert es nicht die Felder in Tkinter (' freq_out.configure (text = str (pks)) ') –

+0

Also vielleicht ist es besser in Paketen von 20 zu tun oder 50 Ich glaube nicht, dass Sie 1 ms benötigen UI Aktualisierungsgranularität ... –

+0

@VictorPira Nur eine Anmerkung: Wenn Ihr vollständiger Zyklus ohne UI (zum Beispiel) 200 ms dauert, fragen Sie eine UI-Aktualisierung von 5000 Bildern pro Sekunde: vielleicht TclTk ist nicht für diese Art von Aufgaben ausgelegt. –

2

Das Komprimieren von E/A und Berechnungen in die Hauptschleife, wie Sie es tun, ist die klassische Lösung. Aber es gibt Alternativen.

  1. Machen Sie die Audio-Sammlung und Berechnungen in einem zweiten Thread. Da sowohl I/O als auch Numpy die GIL freigeben sollten, könnte dies eine gute Alternative sein. Hier gibt es einen Vorbehalt. Da GUI Toolkits wie TKinter in der Regel nicht Multithread-sicher sind, sollten Sie nicht Tkinter Aufrufe vom zweiten Thread machen. Sie könnten jedoch eine Funktion einrichten, die mit after aufgerufen wird, um den Fortschritt der Berechnung zu überprüfen und die Benutzeroberfläche alle 100 ms zu aktualisieren.

  2. Tun Sie die Audio-Sammlung und Berechnungen in einem anderen multiprocessing.Process. Dies unterscheidet es vollständig von Ihrer GUI. Sie müssen einen Kommunikationskanal wie z.B. a Queue, um die pks zurück an den Hauptprozess zu senden. Sie sollten eine after Funktion verwenden, um zu überprüfen, ob die Queue Daten verfügbar hat, und um das Display zu aktualisieren, falls dies der Fall ist.