Abfrage der Laufzeit entfernen und weiteres

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

Jetzt bin ich doch etwas verwirrt. play_leds hat doch bereits eine Zeile zur Soundwiedergabe, "pygame.mixer.Sound(random.choice(soundfiles)).play()", die aber so nicht mehr stimmen kann. Wenn ich das richtig verstanden habe, muss ja zwangsläufig "shuffle" verwendet werden, da nur so eine Wiederholung der selben Datei direkt nacheinander vermieden wird. Und welche while-true Schleife ist gemeint?
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Du willst, dass ein Song nicht mehrfach gespielt wird, also muß die Funktion `play_leds` Wissen über ihren vorherigen Aufruf haben, das geht aber nicht. Daher muß dieses Wissen von außen kommen, über ein Argument.
Ich sehe in Deinem Code nur eine while-Schleife.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Die Lösung tut übrigens nicht ganz das, was der Threadersteller gewünscht hat. Es kann noch immer dazu kommen, dass der selbe Song 2 Mal hintereinander gespielt wird. Nämlich dann, wenn er einmal einmal ans Ende der Liste gemischt wurde und bei dem nächsten Durchlauf an den Anfang. Bei dem Beispielcode mit 3 Elementen in der Liste kann man das sehr gut erkennen, weil es da relativ häufig auftritt. Natürlich wird die Wahrscheinlichkeit kleiner, je mehr Songs enthalten sind, trotzdem kann es vorkommen.

Bei Zufallswiedergaben von Liedern - oder zum Beispiel bei Fragen in einem Ratespiel - würde ich immer so vorgehen, dass ich mir die letzten x Songs / Fragen merke und eine neue ziehe, sollte die sich bereits in den letzten gespielten Elementen befinden.

Code: Alles auswählen

import collections
import random
import time


SONGS = ["a", "b", "c", "d", "e", "f"]


class RandomSongGenerator():

    def __init__(self, songs, no_repetition_before=None):
        if no_repetition_before is None:
            no_repetition_before = len(songs) // 2
        if no_repetition_before >= len(songs):
            raise Exception("Min. songs without repetition must be "
                            "less than all songs in list")
        self.songs = songs
        self.last_songs = collections.deque(maxlen=no_repetition_before)

    def get_next_song(self):
        while True:
            current_songs = list(self.songs)
            random.shuffle(current_songs)
            while current_songs:
                next_song = current_songs.pop(0)
                if next_song not in self.last_songs:
                    self.last_songs.append(next_song)
                    yield next_song
                else:
                    current_songs.append(next_song)


def main():
    rsg = RandomSongGenerator(SONGS)
    while True:
        for _ in SONGS:
            print(next(rsg.get_next_song()))
        print("-" * 20)
        time.sleep(.2)


if __name__ == '__main__':
    main()
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

Also den Code von sparrow habe ich soweit verstanden, zumindest gehe ich davon aus. Aber ich weiß nicht wie ich den integrieren soll. Das ist mir dann doch zu hoch :-(
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Ich habe den Generator mal entfernt, weil man sich den Zustand in der Instanz merken kann, und für dich der Aufruf mit next vielleicht etwas verwirrend ist.

Code: Alles auswählen

import collections
import random
import time


SONGS = ["a", "b", "c", "d", "e", "f"]


class RandomSongGenerator():

    def __init__(self, songs, no_repetition_before=None):
        if no_repetition_before is None:
            no_repetition_before = len(songs) // 2
        if no_repetition_before >= len(songs):
            raise Exception("Min. songs without repetition must be "
                            "less than all songs in list")
        self.songs = songs
        self.last_songs = collections.deque(maxlen=no_repetition_before)
        self.next_songs = []

    def get_next_song(self):
        if not self.next_songs:
            self.next_songs = list(self.songs)
            random.shuffle(self.next_songs)
        while True:
            next_song = self.next_songs.pop(0)
            if next_song not in self.last_songs:
                self.last_songs.append(next_song)
                return next_song
            else:
                self.next_songs.append(next_song)


def main():
    rsg = RandomSongGenerator(SONGS)
    while True:
        for _ in SONGS:
            print(rsg.get_next_song())
        print("-" * 20)
        time.sleep(.2)


if __name__ == '__main__':
    main()
Was genau ist denn jetzt noch unverständlich? Das Instanzieren einer Klasse mit Werten hast du doch bereits in deinem Code und das Aufrufen einer Funktion aus der Instanz doch auch.
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sparrow: `get_next_song()` ist halt als Name falsch wenn das einen Generator liefert. Ich hätte mich da gar nicht weiter mit einer ”normalen” Methode aufgehalten und die Methode `__iter__()` genannt. Beziehungsweise verstehe ich nicht so ganz warum das überhaupt eine Klasse ist und nicht nur eine Funktion.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Verwirrend für den OP ist vielleicht, dass es in den Beispielen immer eine while-Schleife und eine for-Schleife gibt, um zu illustrieren, wann ein Zyklus durchgespielt worden ist.

Für seine Anwendung reicht aber eine einfache for-Schleife, ohne `next`-Aufruf:

Code: Alles auswählen

import collections
import random
import time


SONGS = ["a", "b", "c", "d", "e", "f"]

def iter_songs_randomly(songs, no_repetition_before=None):
    if no_repetition_before is None:
        no_repetition_before = len(songs) // 2
    if no_repetition_before >= len(songs):
        raise Exception("Min. songs without repetition must be "
                        "less than all songs in list")
    last_songs = collections.deque(maxlen=no_repetition_before)
    next_songs = []
    while True:
        if not next_songs:
            next_songs = list(songs)
            random.shuffle(next_songs)
        next_song = next_songs.pop(0)
        if next_song not in last_songs:
            last_songs.append(next_song)
            yield next_song
            break
        else:
            next_songs.append(next_song)

def main():
    random_songs = iter_songs_randomly(SONGS)
    for song in random_songs:
        play(song)

if __name__ == '__main__':
    main()
Frank R.
User
Beiträge: 38
Registriert: Montag 23. September 2019, 10:10

Ich habe den Code mal getestet und versucht, mit dem vorhandenen irgendwie zu verbinden. Aber mit dem Code kann ich leider nichts anfangen. Die pygame Funktionen sind nicht enthalten, ebenso die LEDs, der Button... play(song) wird als nicht definiert deklariert, und die Struktur mit song, songs und SONGS ist verwirrend. Unten steht z.B. "random_songs = iter_songs_randomly(SONGS)", bei def oben steht aber "def iter_songs_randomly(songs, no_repetition_before=None):"
Um Sounddateien, in dem Fall mehrere .wav abzuspielen, hatte ich ja bereits funktionierenden Code geschrieben, wo ist der hier? Damit nicht danach gesucht werden muss, hier noch mal der funktionierende Code mit Zufallswiedergabe plus Kommentare:

Code: Alles auswählen

import gpiozero
import time
import random
import pygame.mixer #ohne den wird nichts abgespielt
import glob
from itertools import repeat, chain

pygame.mixer.init() #muss drin sein, aktiviert den mixer
soundfiles = glob.glob('/home/pi/Documents/wav/*.wav') #sucht alle Dateien mit .wav Endung im angegebenen Verzeichnis

def play_leds(leds):
    pygame.mixer.Sound(random.choice(soundfiles)).play() #spielt die per glob gefundenen .wav Dateien per random.choice ab
    on_time = 0.1  # Leuchtdauer der LED
    delays = chain(repeat(on_time, 11 * len(leds) - 1), [5])
    for delay in delays:
        led = random.choice(leds)
        led.on()
        time.sleep(delay)
        led.off()

def main():
    leds = gpiozero.LEDBoard(17, 18, 27, 22, 23, 24, 25, 4, 12)
    button = gpiozero.Button(16)
    while True:
        button.wait_for_press()
        play_leds(leds)

if __name__ == '__main__':
    main()

Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Dir fehlen noch die Grundlagen, wenn Du eine Schleife die Songs ausgeben ja kann nicht mit der Schleife, die LEDs blicken lassen kann, nicht zu einer kombinieren kannst. Natürlich gibt es keine play-Funktion, weil die Du noch schreiben musst.
Antworten