2016-05-18 11 views
2

Das Problem ist ein wenig kompliziert, so muss ich Ihnen die ganze Geschichte geben:In Python: Warum muss ein in einer Methode zurückgegebenes Numpy-Array in der Instanz gespeichert werden?

I Grundsound mit dem Paket pyaudio erstellen bin versucht. Das Modul pyaudio fragt nach einer Rückruffunktion, die Audiodaten liest. Es sieht im Wesentlichen wie folgt:

def callback(): 
    return sound.readframes() 

Ich habe eine Klasse, die Audiodaten zur Verfügung stellt. Er erzeugt ein Signal, indem er ein numpiges Array indiziert, das eine Wellenlänge einer grundlegenden Wellenform enthält. Es sieht im Wesentlichen wie folgt:

class Wave(object): 
    data = [] # one wavelength of a basic waveform like a sine wave will be stored here 

    def readframes(self): 
     indices = # the indices for the next frames 
        # based on a frequency and the current position 
     return self.data[indices] 

class SineWave(Wave): 
    data = # points of a sine wave in a numpy array 

sound = SineWave() 

# pyaudio now reads audio data in a non-blocking callback mode 

Nun zum Problem:

Es gab Lärm zwischen jedem Aufruf readframes oder jedem Block von Audiodaten. Der Ton selbst war noch intakt, aber es gab ein zusätzliches Klicken. Durch Erhöhen der Größe des zurückgegebenen Datenblocks wurde auch die Zeit zwischen jedem Klicken erhöht. Ich weiß immer noch nicht genau, was das Rauschen verursacht hat, aber ich habe es behoben, indem ich das zurückgegebene numpy Array in der Instanz gespeichert habe, anstatt es direkt zurückzugeben.

Das verursacht Lärm:

return self.data[indices] 

Dies verursacht nicht Lärm:

+ self.result = self.data[indices] 
+ return self.result 

Es ist nicht eine Veränderung der Daten sein können, die den Lärm verursacht oder sonst würde dies nicht beheben . Warum muss das Ergebnis in der Instanz gespeichert werden?

Update:

Ich muss auch erwähnen, dass der Lärm nicht immer auftritt ich das Skript ausführen. Es tritt zufällig ungefähr 50% der Läufe auf. Entweder erscheint das Geräusch gleich zu Beginn und wird nicht verschwinden oder es erscheint gar nicht. Es sieht aus wie ein Problem mit der Initialisierung des Sound-Geräts, aber wie das mit meinem Fix in Verbindung stehen könnte, ist ein Geheimnis.

Hier ist Code, der das Geräuschproblem reproduziert:

import pyaudio 
import time 
from numpy import sin, linspace, arange, pi, float32, uint32 

SR = 44100 # sampling rate 
FPB = 2048 # frames per buffer 

class SineWave(object): 
    """A sine wave""" 

    data_size = 2**10 
    data = sin(linspace(0, 2*pi, data_size, endpoint=False, dtype=float32)) 

    def __init__(self, frequency): 
     self.pos = 0 
     self.fq = frequency 
     self.ppf = self.data_size * self.fq/SR # points per frame 

    def readframes(self): 
     size = FPB 
     pos = self.pos 
     indices = (arange(pos, size+pos)*self.ppf).astype(uint32) & (self.data_size-1) 
     self.pos = (self.pos + size) % SR 
     self.result = self.data[indices] 
     return self.data[indices] 

def callback(in_data, frame_count, time_info, status): 
    return sound.readframes(), pyaudio.paContinue 

p = pyaudio.PyAudio() 

stream = p.open(format=p.get_format_from_width(4), 
       channels=1, 
       rate=SR, 
       output=True, 
       stream_callback=callback, 
       frames_per_buffer=FPB) 

sound = SineWave(440) 
stream.start_stream() 
time.sleep(2) 
stream.stop_stream() 
stream.close() 
p.terminate() 

Auf meinem System das Rauschen in etwa 90% der Läufe erscheint.

Ändern

return self.data[indices] 

zu

return self.result 

behebt es.

Antwort

0

Es sollte keinen Unterschied machen, ob Sie das Zwischen-Array in der Instanz speichern oder nicht. Ich vermute, dass ein anderes Problem vorliegt, das den Fehler verursacht, den Sie haben. Können Sie Code bereitstellen, der tatsächlich ausgeführt wird und das Problem aufweist?

Wenn Sie ein (hoffentlich) sehen wollen!) Arbeitsimplementierung, werfen Sie einen Blick auf die play() Funktion des sounddevice Moduls, insbesondere bei der loop Argument. Damit sollten Sie in der Lage sein, Ihre Wellenform wiederholt zu spielen.

Das Einzige, was Sie in der Instanz speichern müssen, ist der Frame-Index, an dem Sie aufgehört haben, nur um beim nächsten Callback mit dem folgenden Frame-Index fortzufahren (und natürlich am Ende umzuspulen). Sie sollten auch den Fall berücksichtigen, bei dem Ihre Wellenform kürzer als ein Audioblock ist. Es könnte sogar viel kürzer sein. Ich habe das mit einem rekursiven Funktionsaufruf gelöst, siehe the code.


UPDATE:

Es ist in der Tat ziemlich merkwürdig, dass die Zuordnung zu self Ihr Problem "löst". Ich denke, das hat etwas damit zu tun, wie PyAudio das zurückgegebene Array als Python-Puffer interpretiert.

Sie sollten Ihren Code mit dem Modul sounddevice neu schreiben, das ordnungsgemäß NumPy-Arrays unterstützt und über eine vernünftige API für die Callback-Funktion verfügt.

Es ist einfach, nutzen Sie einfach die Callback-Funktion:

def callback(outdata, frames, time, status): 
    if status: 
     print(status) 
    outdata[:, 0] = sound.readframes() 

und den Strom beginnt mit:

import sounddevice as sd 
stream = sd.OutputStream(
    channels=1, samplerate=SR, callback=callback, blocksize=FPB) 

aus, dass abgesehen, ist der Code fehlerhaft, da es keine Interpolation nicht tun, wenn Lesen vom Wavetable.

+0

Ich fügte ein funktionierendes Codebeispiel hinzu, das das Rauschen erzeugt. Das Problem ist kein Fehler im Code, es muss etwas damit zu tun haben, was im Hintergrund passiert, da das Rauschen nicht jedes Mal auftritt, wenn ich das Skript ausführe. – uzumaki

+0

@uzumaki Ich habe meine Antwort erweitert. – Matthias