Sehr kurze WAV-Files ohne Pause "dynamisch" abspielen

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Suiram
User
Beiträge: 10
Registriert: Mittwoch 31. Januar 2018, 20:30

Hallo zusammen,

ich bin noch reltaiv neu bei Python, habe aber schon allgemeine Programmiererfahrung.

Zur Problembeschreibung muss ich etwas weiter ausholen:
Mein Vater baut momentan das RC Modell und möchte dafür nun ein eigenes Sound-Modul entwickeln (um den Sound authentisch hin zu bekommen). Ich möchte ihm dabei helfen.
Das ganze soll wie folgt funktionieren:
Er hat von seinem Traktor das Motor-Geräusch in unterschiedlichen Last-Zuständen aufgezeichnet. Immer 3 Umdrehungen des Motors hat er dann in eine WAV-Datei gepackt. Diese Dateien sind etwa 230 Millisekunden lang.
Mit einem Sensor möchte er dann den aktuellen Stromverbrauch des Elektromotors im Modell messen, das passende Soundfile raussuchen und abspielen. Während dieses dann abgespielt wird muss wieder der Stromverbrauch gemessen werden, das nächste Soundfile rausgesucht und dann möglichst lückenlos nach dem ersten abgespielt werden. Dann geht es immer so weiter.
Diese Probleme sehe ich momentan dabei:
Die Pause zwischen den einzelnen Files darf allerhöchstens 10ms betragen (kürzer = besser). Ansonsten hört es sich nicht mehr "rund" an. Die Dateien dürfen sich natürlich auch nicht überschneiden ("negative" Pause).
Außerdem sind nicht alle Soundfiles gleich lang, denn wenn mehr Gas gegeben wird ändert sich die Drehzahl des Motors und damit die Länge der Datei.

Ich habe mittels pygame schon Soundfiles abgespielt:

Code: Alles auswählen

import pygame
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=128)
sound0 = pygame.mixer.Sound("T00.wav")
sound0.set_volume(1)
sound0.play()
Bei der Benutzung von pygame läuft das Programm ja weiter während der Sound gespielt wird. Dies würde mir die Möglichkeit geben meinen Sensor auszulesen und das nächste File zu bestimmen. Es gibt sogar eine Möglichkeit zu überprüfen, ob aktuell noch ein Sound gespielt wird (pygame.mixer.get_busy()). Allerdings dauert die Ausführung dieses Befehls und das starten eines neuen Files zu lange, so dass eine zu große Lücke entsteht.

Folgenden Lösungsansatz könnte ich mir jetzt vorstellen:
- Die Länge jedes Files ist bekannt und kann in einer Variablen gespeichert werden.
- Es gibt eine Unterfunktion (neuer Thread? neuer Prozess?) die einfach nur ein übergebenes File abspielen kann.
- Der Hauptprozess weiß ja, wann er die Unterfunktion gestartet hat und er weiß die Länge des Soundfiles. Also weiß er auch, wann das nächste File abgespielt werden muss.
- Gibt es eine Möglichkeit eine Funktion zu einem bestimmten Zeitpunkt ausführen zu lassen?
- Wie würdet ihr an dieses Problem rangehen?
Ich bräuchte ein paar Ansätze nach was ich googeln soll, momentan komme ich leider nicht weiter.

Gruß
Marius
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du solltest pyaudio verwenden. In dessen callback lieferst du die wav Daten aus, und kannst dann präzise umschalten. Der Callback ist in einem andern Thread, dh dein Hauptprogramm kann auf zustandsänderumgen warten.
Suiram
User
Beiträge: 10
Registriert: Mittwoch 31. Januar 2018, 20:30

Vielen Dank für deinen Beitrag. Leider habe ich Probleme dies umzusetzen, da ich noch nicht ganz durchblicke :K
Der Code für den Blocking-Mode ist verständlich, aber das mit dem Callback ist mir nicht klar.
Wenn ich den Code von der pyaudio-Dokumentation verwende kann ich problemlos eine Datei abspielen:

Code: Alles auswählen

import pyaudio
import wave
import time
import sys

wf = wave.open("01.wav", 'rb')

p = pyaudio.PyAudio()

def callback(in_data, frame_count, time_info, status):
    data = wf.readframes(frame_count)  # <== (2)
    return (data, pyaudio.paContinue)

stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=wf.getnchannels(),
                rate=wf.getframerate(),
                output=True,
                stream_callback=callback)

stream.start_stream()

while stream.is_active():  # <== (1)
    continue

stream.stop_stream()
stream.close()
wf.close()
p.terminate()
Dazu stellen sich ein paar Fragen:
- An der Stelle (1) könnte ich nun meinen Sensor auslesen und anschließend warten, bis der stream fertig ist, oder?
- bei mir werden es viele verschiedene Sounds werden (min 20). Muss ich dann für jedes File einen Stream mit eigenem Callback anlegen? Denn an Stelle (2) wird ja das wave verwendet.

Ich habe dann versucht zur Laufzeit das wave zu ändern:

Code: Alles auswählen

import pyaudio
import wave
import time
import sys

wf0 = wave.open("00.wav", 'rb')
wf1 = wave.open("01.wav", 'rb')
wf2 = wave.open("02.wav", 'rb')
wf3 = wave.open("03.wav", 'rb')
wf4 = wave.open("04.wav", 'rb')
#...

wf = wf0

p = pyaudio.PyAudio()

def callback(in_data, frame_count, time_info, status):
    data = wf.readframes(frame_count) 
    return (data, pyaudio.paContinue)

stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=wf.getnchannels(),
                rate=wf.getframerate(),
                output=True,
                stream_callback=callback)

stream.start_stream()
while stream.is_active(): 
    continue
wf = wf1
stream.start_stream()
while stream.is_active(): 
    continue
wf = wf2
stream.start_stream()
while stream.is_active(): 
    continue
wf = wf3
stream.start_stream()
while stream.is_active(): 
    continue
wf = wf4
stream.start_stream()
while stream.is_active(): 
    continue

stream.stop_stream()
stream.close()
wf.close()
p.terminate()
Leider funktioniert dies nicht. Er spielt nur den ersten Sound und stoppt dann.
Habe ich etwas vergessen, oder das ganze total falsch verstanden?

Am besten wäre es meiner Meinung nach, wenn man dem Stream "einfach" die Daten der neuen Datei anhängen könnte, so dass es nahtlos weitergeht und der Stream überhaut nicht "merkt", dass eine neue Datei anfängt. in der Doku von pyaudio heißt es aber bei der Funktion Stream.Write:
Write samples to the stream. Do not call when using non-blocking mode.
Ich vermute ich befinde mich momentan total auf dem Holzweg und würde mich sehr über etwas Beispielcode freuen.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ja, an Stelle 1 kannst du genau das tun.

Und nein, du musst *nicht* mehrere Streams anlegen. Sondern im callback einfach aus einer anderen WAV-Datei den Buffer befuellen. Grobe Skizze:

Code: Alles auswählen

class SamplePlayer:

     def __init__(self, list_of_samples):
          self._list_of_samples = list_of_samples
          self.playing = None
          self._offset = 0
          # setup pyaudio, self._callback als callback


     def _callback(self, in_data, frame_count, time_info, status):
          if self.playing is not None:
            buffer = self._get_buffer(frame_count)
            return buffer, pyaudio.Continue
          else: 
            return self._create_empty_buffer(frame_count)


     def _get_buffer(self, frame_count):
          buffer = self._list_of_samples[self.playing][self._offset:self._offset+frame_count)
          self._offset = (self._offset + frame_count) % len(self._list_of_samples[self.playing]


def main():
      player = SamplePlayer(...)
      while True:
            player.playing = get_the_current_sample_that_should_play()
Da gibt es natuerlich noch ein paar Details auszuarbeiten: unter Umstaenden bekommst du keinen vollen Buffer mehr am Ende des Samples, und musst wieder Daten vom Anfangen, es kann beim umschalten zum knacken kommen, etc. Aber die Idee sollte so funktionieren.
Suiram
User
Beiträge: 10
Registriert: Mittwoch 31. Januar 2018, 20:30

Vielen Dank für den Code. Eine Sache ist mir aber noch unklar: wofür dient der Modulo in Zeile 20?
Außerdem habe ich noch ein Verständnisproblem mit "frame_count". In den meisten Beispielen im Internet wird diese nicht verwendet, sondern ein fester Wert verwendet (meist 1024). Könnte man dann hier nicht immer die passende Länge des aktuellen waves eintragen? dann hätte man das Problem mit dem halben Buffer am Ende nicht, und müsste die Funktion "_get_buffer" nur ein einziges mal ausführen.

Weil mir das zu viele Code-Änderungen auf einmal waren habe ich den Code etwas umgebaut, um erst mal nur eine einzige Datei abzuspielen. Ich höre allerdings nur ein kurzes knacken im Kopfhörer, vermute allerdings, dass dies das erste Teilstück des waves ist. Ich bin wohl auf dem richtigen Weg, aber es fehlt irgendwie noch die Schleife, dass es im wave weitergeht.
Hier mein Code. Was fehlt da noch?

Code: Alles auswählen

import pyaudio
import wave

class SamplePlayer:
    
    def __init__(self,data):
        self._data = data
        self._playing = 0
        self._offset = 0
        self._p = pyaudio.PyAudio()
        self._stream = self._p.open(format=8, channels=2, rate=44100, output=True, stream_callback=self._callback)
        
    def Start(self):
        self._playing = 1
        
    def GetStatus(self):
        return self._playing
    
    def _callback(self, in_data, frame_count, time_info, status):
        if self._playing == 1:
            self._get_buffer(frame_count)
            return (self._buffer, pyaudio.paContinue)
        else:
            return self._create_empty_buffer(frame_count)

    def _get_buffer(self, frame_count):
        if len(self._data)-self._offset > frame_count:
            self._buffer = self._data[self._offset:self._offset+frame_count]
            self._offset = (self._offset + frame_count)# % len(self._data)
        else:
            self._buffer = bytes(frame_count)
            self._offset = 0
            self._playing = 0
        
    def _create_empty_buffer(self, frame_count):
        self._buffer = bytes(frame_count)
        self._offset = 0

wf= wave.open("T00.wav", 'rb')
dat = wf.readframes(wf.getnframes())
player = SamplePlayer(dat)
player.Start()
print("warten")
while player.GetStatus() == 1:
    continue

wf.close()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na ultimativ willst du doch ein sample endlos abspielen. Da musst du dann schon „umschlagen“ am Ende. Dafür das Modulo.

Und wenn du Code schreibst, der Hard kodiert einen Frame count verwendet, dann machst du dir das Leben doch selbst schwerer. Denn dann kannst du nicht mit der buffer Größe spielen, und alles passt sich Automatisch an. Sondern musst mühselig sicherstellen, das du alle stellen anpasst. Das ist schlecht programmiert. In der Audio Programmierung kann es auch sein, das du eine buffer size anfragst, Aber das System dir das nicht zugesteht, sondern einen anderen wert (zB gerundet auf 2er-Potenzen) zurückgibt. Damit hilft es also nix, mit einer konstanten zu arbeiten.

Zum code: was ganz bestimmt nicht stimmt ist der empty buffer. Ein Frame ist üblicherweise 8, ggf 4 Bytes groß. Nämlich Stereo und float32, also 2*4. Oder ggf Stereo pcm16. Dann also 2*2. Ob du also Stereo bist, und welches Format ein sample hat, muss der plaYer auch berücksichtigen.

Ob das der knackser ist, kann ich nicht genau sagen. Der kann viele Ursachen haben. Wann kommt der denn? Am Ende?
Suiram
User
Beiträge: 10
Registriert: Mittwoch 31. Januar 2018, 20:30

Ich habs hinbekommen! Das Problem war tatsächlich die Größe des Buffers. Ich muss diesen 4 mal so groß wie den frame_count machen.
Er spielt jetzt das eine WAV in Dauerschleife.
Den Teil im "get_buffer" kann man sicher noch optimieren, aber so ist es schön übersichtlich und verständlich :D

Code: Alles auswählen

import pyaudio
import wave

class SamplePlayer:

    def __init__(self,data):
        self._data = data
        self._playing = 0
        self._offset = 0
        self._p = pyaudio.PyAudio()
        self._stream = self._p.open(format=8, channels=2, rate=44100, output=True, stream_callback=self._callback)
    
    def Start(self):
        self._playing = 1
    
    def GetStatus(self):
        return self._playing

    def _callback(self, in_data, frame_count, time_info, status):
        if self._playing == 1:
            self._get_buffer(frame_count)
            return (self._buffer, pyaudio.paContinue)
        else:
            return self._create_empty_buffer(frame_count)

    def _get_buffer(self, frame_count):
        self._FramesNeeded = frame_count *4
        if len(self._data)-self._offset > self._FramesNeeded:           
            self._buffer = self._data[self._offset:self._offset + self._FramesNeeded]
            self._offset = self._offset + self._FramesNeeded          
        else:
            self._buffer1 = self._data[self._offset:]
            self._FramesGot = len(self._data) - self._offset
            self._FramesMissing = self._FramesNeeded - self._FramesGot
            self._buffer2 = self._data[:self._FramesMissing]            
            self._buffer = self._buffer + self._buffer2
            self._offset = self._FramesMissing
    
    def _create_empty_buffer(self, frame_count):
        self._buffer = bytearray(frame_count *4)
        self._offset = 0

wf= wave.open("T00.wav", 'rb')
dat = wf.readframes(wf.getnframes())
player = SamplePlayer(dat)
player.Start()
print("warten")
while player.GetStatus() == 1:
    continue
print("fertig")                                 
wf.close()
Die Soundqualität ist allerdings nicht so gut. Es knackst immer wieder leicht. So eine Art knistern. Woran kann das liegen?

Als nächstes muss ich mir jetzt mal überlegen, wie ich es organisiere, dass ich die Sounddatei variieren kann.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Den leeren buffer kannst du eigentlich auch nur einmal bauen & dann imme wieder verwenden. Und knacksen kann viele Ursachen haben. ZB kann es sein, das Anfang und Ende diene Samples nicht zueinander passen. Das müsstest du zb mal mit audacity testen. Es kann auch sein, das deine GetBuffer Geschichte nicht richtig funktioniert & darum Übergänge am Sampleende verhaut. Und bitte gewöhn dir mal den Python Namens Stil an - GetBuffer sieht in den Augen erfahrener Python Programmierer schlimm aus. In Python sind Methoden klein geschrieben und mit Unterstrichen getrennt.

Und wie das variiert werden kann habe ich ja schon angedeutet. Wahrscheinlich wirst du da aber auch knackser bekommen, dann musst du überblenden. Aber eins nach dem anderen.
Suiram
User
Beiträge: 10
Registriert: Mittwoch 31. Januar 2018, 20:30

ja, sorry, ich weiß, dass ich die Stile gemischt habe. Ich programmiere aber auf der Arbeit seit Jahren in C# und mache dort alles mit CamelCase. Es ist nicht so einfach sich so schnell umzugewöhnen. Bei Gelegenheit werde ich mir mal den Style-Guide für Python zu Gemüte führen.
Habe mir aber das programmieren komplett selbst beigebracht und deshalb noch etwas Probleme, wenn es in die tiefere Theorie mit Multi-Threading usw geht. Daher können in dieser Richtung evtl später noch Fragen auftauchen.

Ich werde mal noch etwas weiter herumprobieren und mich dann wieder melden, wenn ich weitergekommen bin oder wenn neue Fragen aufgetaucht sind.

Gruß
Marius
Suiram
User
Beiträge: 10
Registriert: Mittwoch 31. Januar 2018, 20:30

Ich habe es geschafft das knacksen zu beseitigen. Woran es letztendlich gelegen hat kann ich nicht sicher sagen. Ich hab den Code etwas "optimiert" (u.a. eine feste Frame-Größe und die _get_buffer-Methode) und jetzt ist es weg. Jetzt sollte auch der Namens-Stil passen.
Hier jetzt mein Code:

Code: Alles auswählen

import pyaudio
import wave

FACTOR = 4
CHUNK = 1024
FRAMES = CHUNK * FACTOR

class SamplePlayer:

    def __init__(self,data):
        self._data = data
        self._playing = 0
        self._offset = 0
        self._p = pyaudio.PyAudio()
        self._stream = self._p.open(format=8, channels=2, rate=44100, frames_per_buffer=CHUNK, output=True, stream_callback=self._callback)
    
    def start(self):
        self._playing = 1
    
    def get_status(self):
        return self._playing

    def _callback(self, in_data, frame_count, time_info, status):
        if self._playing == 1:
            self._get_buffer()
            return (self._buffer, pyaudio.paContinue)
        else:
            return self._create_empty_buffer()

    def _get_buffer(self):
        if len(self._data)-self._offset >=FRAMES:           
            self._buffer = self._data[self._offset:self._offset + FRAMES]
            self._offset += FRAMES    
        else:
            self._buffer1 = self._data[self._offset:]       
            self._buffer = self._buffer1 + self._data[:FRAMES - len(self._buffer1)] 
            self._offset = FRAMES - len(self._buffer1)
            
    def _create_empty_buffer(self):
        self._offset = 0
        return bytes(FRAMES)

wf= wave.open("T00.wav", 'rb')
dat = wf.readframes(wf.getnframes())
player = SamplePlayer(dat)
player.start()
print("warten")
while player.get_status() == 1:
    continue
print("fertig")                                 
wf.close()
Dieser Code spielt jetzt immer einen Sound in Dauerschleife.
Als nächstes werde ich versuchen die Sache mit dem variablen Abspielen anzugehen. Dazu werde ich als Basis deinen Vorschlag benutzen und versuchen so anzupassen, dass es für mich funktioniert.
Ich melde mich dann wieder.
Suiram
User
Beiträge: 10
Registriert: Mittwoch 31. Januar 2018, 20:30

Hi, ich bins nochmals.
Habe es jetzt geschafft den Sound zu variieren. Dazu habe ich eine deque benutzt. Da bin ich zufällig im Internet drauf gestoßen und es erschien mir passend. Der Code sieht inzwischen so aus (spielt jede Stufe 10x und fängt dann wieder von vorne an - in Dauerschleife):

Code: Alles auswählen

import pyaudio
import wave
from collections import deque

FACTOR = 4
CHUNK = 1024
FRAMES = CHUNK * FACTOR
PER_STEP = 10

wf0= wave.open("T00.wav", 'rb')
wf1= wave.open("T01.wav", 'rb')
wf2= wave.open("T02.wav", 'rb')
wf3= wave.open("T03.wav", 'rb')
wf4= wave.open("T04.wav", 'rb')

data0 = wf0.readframes(wf0.getnframes())
data1 = wf1.readframes(wf1.getnframes())
data2 = wf2.readframes(wf2.getnframes())
data3 = wf3.readframes(wf3.getnframes())
data4 = wf4.readframes(wf4.getnframes())

DATA = [data0, data1, data2, data3, data4]

class SamplePlayer:

    def __init__(self):
        self._playing = 0
        self._offset = 0
        self._sound = 0
        self._p = pyaudio.PyAudio()
        self._stream = self._p.open(format=8, channels=2, rate=44100, frames_per_buffer=CHUNK, output=True, stream_callback=self._callback)
    
    def start(self):
        self._sound = Q.popleft()
        self._playing = 1
    
    def get_status(self):
        return self._playing

    def _callback(self, in_data, frame_count, time_info, status):
        if self._playing == 1:
            self._get_buffer()
            return (self._buffer, pyaudio.paContinue)
        else:
            return self._create_empty_buffer()

    def _get_buffer(self):
        if len(DATA[self._sound])-self._offset >=FRAMES:           
            self._buffer = DATA[self._sound][self._offset:self._offset + FRAMES]
            self._offset += FRAMES    
        else:
            self._buffer1 = DATA[self._sound][self._offset:]
            self._sound = Q.popleft()
            self._buffer = self._buffer1 + DATA[self._sound][:FRAMES - len(self._buffer1)] 
            self._offset = FRAMES - len(self._buffer1)
            
    def _create_empty_buffer(self):
        self._offset = 0
        return bytes(FRAMES)

player = SamplePlayer()
Q = deque()
Q.append(0)
Q.append(0)
player.start()
while True:
    for i in range (0, PER_STEP):
        while len(Q) > 1:
            continue
        Q.append(0)
    for i in range (0, PER_STEP):
        while len(Q) > 1:
            continue
        Q.append(1)
    for i in range (0, PER_STEP):
        while len(Q) > 1:
            continue
        Q.append(2)
    for i in range (0, PER_STEP):
        while len(Q) > 1:
            continue
        Q.append(3)
    for i in range (0, PER_STEP):
        while len(Q) > 1:
            continue
        Q.append(4)

Meine nächste Frage wäre jetzt, ob dies eine akzeptable Lösung ist, oder ob es da noch etwas passenderes gibt. Ich muss in dem Player ja immer einen Sound abspielen und den nächsten schon wissen. Also muss ich es im Hauptprogramm mitbekommen, wenn der Player eine neue Datei anfängt. Dies habe ich hier gelöst, indem ich die Länge der deque abfrage. Ich muss diese ja synchron mit dem SamplePlayer befüllen.
Morgen kommt mein ADC, den ich dann an den Raspi anschließen werde. Dann kann ich mal mit einem Poti rumprobieren. Wenn das dann läuft kommt noch die Kür wie z.B. maximale Gradienten für die Sound-Änderung
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die dequeu scheint ganz ok, aber es sieht so aus als ob du da ein sehr fragiles Konstrukt erzeugt hast: wehe da steht nix drin, wenn der Audio-Callback da vorbeikommt. Dann ist IndexError und damit Abbruch angesagt. Das ist eine viel zu fragile Kopplung von Verhalten und dir garantiert auch keiner, dass dein Haupt-Thread dran kommt zur rechten Zeit. Da musst du vorher mit count pruefen, und auch gleich mehrere Werte rauspulen, denn es kann ja auch sein, dass du durch schnelles drehen am Poti mehrere unterschiedliche Sounds nacheinander abspielst, bevor der Callback wieder dran ist.

Warum Q eine globale Variable ist, statt einfach als Argument an den SamplePlayer uebergeben zu werden erschliesst sich mir nicht. Globaler Zustand ist immer schlecht, auch in C#.

Wenn man namen durchnummeriert, ist das auch ein Zeichen dafuer, das man etwas falsch macht, weil man eigentlich eine Datenstruktur benutzen will. In deinem Fall eine Liste. Hast du ja auch (DATA), aber das ganze geroedel davor ist doch total unflexibel und ueberfluessig.

Code: Alles auswählen

def getframes(name):
     wf = wave.open(name, "rb")
     return wf.readframes(wf.getnframes())

NAMES = ["T00.wav", ...] # im Grunde koennte man das auch irgendwie mit glob.glob machen, aber egal
DATA = [getframes(name) for name in NAMES]
Suiram
User
Beiträge: 10
Registriert: Mittwoch 31. Januar 2018, 20:30

Dass der Code noch nicht perfekt ist ist mir durchaus klar. Mir ging es jetzt nur mal darum eine Rückmeldung zu bekommen bezüglich der deque. Ob dies eine "akzeptable" Lösung ist. Bevor ich jetzt alles optimiere und später gesagt bekomme, dass dieser Ansatz nicht gut war.
__deets__ hat geschrieben:Warum Q eine globale Variable ist, statt einfach als Argument an den SamplePlayer uebergeben zu werden erschliesst sich mir nicht. Globaler Zustand ist immer schlecht, auch in C#.
Meinst du hier nicht DATA? Weil Q muss meinem Verständnis nach global sein, damit ich im Hauptprogramm und im Callback darauf zugreifen kann.
Dass es kein guter Stil ist DATA global zu machen ist mir auch klar, ich finde es nur unübersichtlich vor jede Eigenschaft der Klasse dieses "self." zu schreiben. Das war der einzige Grund dafür.
Das einlesen werde ich in der finalen Version dann als Schleife machen, da ich mir aber noch nicht 100%ig sicher bin, wie die Dateien letztlich aussehen hab ich es erst mal so gemacht.
__deets__ hat geschrieben:Da musst du vorher mit count pruefen, und auch gleich mehrere Werte rauspulen, denn es kann ja auch sein, dass du durch schnelles drehen am Poti mehrere unterschiedliche Sounds nacheinander abspielst, bevor der Callback wieder dran ist.
In der deque sollen immer nur ein bis 2 Einträge sein, egal wie schnell ich am Poti drehe.
Folgenden Ablauf hab ich mir gedacht:
- Startsituation: es sind 2 Einträge in der deque
1. der Callback nimmt sich den linken und fängt an diesen abzuarbeiten
2. die Main bemerkt, dass nur noch ein Eintrag im deque steht
3. die Main Liest den Sensor aus
4. Die Main bestimmt welcher Sound der nächste sein muss und hängt diesen rechts an die deque
5. die Main wartet bis nur noch ein Eintrag in der deque ist.

Nr 2-4 müssen dann schneller abgearbeitet werden als der callback benötigt um einen Sound abzuspielen.
Würde ich zu viele Einträge in die deque stecken würde ich sonst ein zu großes Delay bekommen, bis der zum aktuellen Analogwert passende Sound gespielt wird.
PhysicPat
User
Beiträge: 5
Registriert: Montag 28. Mai 2018, 20:41

Ich habe den Thread gelesen und habe jetzt auch noch eine frage.
Wie lässt es sich machen, dass die *.wav files nicht in der Dauerschleife laufen?
Ich möchte mehrere *.wav-files nacheinander dynamisch abspielen ohne schleife.

In wiefern müsste ich den code dafür abändern?

Danke für die Hilfe :-)
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Einfach pygame ohnn Schnörkel benutzen. Single—Shot kann das ja problemlos.
Suiram
User
Beiträge: 10
Registriert: Mittwoch 31. Januar 2018, 20:30

Hi, ich bin es wieder.
Ich hab das Programm zwischenzeitlich an meinen Vater übergeben und er hat die passenden Soundfiles aufgenommen und den Code noch minimal angepasst.
Es funktioniert jetzt zuverlässig wenn wir den Raspberry in die GUI booten und das Programm über die Python GUI starten.
Boote ich den Raspi aber ohne GUI in den CLI-Modus und es dann starte gibt es eine Fehlermeldung:
www.friedmannweb.de/IMG_20180923.jpg
Das "warten" ganz am Ende bedeutet aber, dass das Programm trotzdem losgelaufen ist, denn dies haben wir dort als Debugausgabe drin.
Allerdings bleibt der Lautsprecher stumm.
Woran könnte das liegen?
Ist evtl das Problem, dass ich eine USB-Soundkarte verwende?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Schwer zu sagen. ALSA und JACK sind ziemliche Kotzbrocken, da kann es tausend Gruende geben, warum das in die Binsen geht. Ich wuerde mal nach den Fehlermeldungen googeln. Und ggf. diese bluetooth-soundkarte oder was da referenziert wird aus der ALSA-conf werfen. Ausserdem schauen, ob ihr auch ohne JACK auskommt, dafuer gibt es eigentlich ueberhaupt keinen Grund.
Suiram
User
Beiträge: 10
Registriert: Mittwoch 31. Januar 2018, 20:30

Es funktioniert jetzt.
Die Fehlermeldungen sind zwar immer noch vorhanden, aber es funktioniert trotzdem.

Das Problem war, dass ich das Programm mit Thonny in Python3 geschrieben und getestet habe und wenn ich es einfach mit "python" in der CLI ausführe aber Python 2 verwendet wird.
Ich habe nun einfach "python3 /home/..../programm.py" geschrieben und schon funktioniert es.

Trotzdem danke für deine Hilfe.
Antworten