Einfachste Klasse zur Soundausgabe

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Moin,

ich habe folgendes Problem. Vielleicht kann mir ja jemand von euch hier kurz weiterhelfen, ich durchblick das mit den Klassen irgendwie noch nicht so ganz...

Ich möchte eine Klasse "Sound" erstellen, welche div. innerhalb der Klasse definierte Attribute (Soundfiles) über eine Methode "play" wiedergeben kann.
Der Einfachheit halber nutze ich dafür PyGame. Über Alternativen welche in der Standard-lib von Python(3) sind, freue ich mich natürlich. Auch wenn sie nur ein bestimmtes Dateiformat (.wav zB) außer FLAC (=zu groß) abspielen können.

Ich habe folgenden Code geschrieben, und Python gibt mir immer wieder einen NameError - "SoundfileX is not defined".

Code: Alles auswählen

    Sound.play(check_snd)
NameError: name 'check_snd' is not defined
Pygame ist natürlich importiert und initiiert.

Code: Alles auswählen

class Sound:
    '''Die Sound-Klasse soll bei Aufruf eine Sounddatei abspielen.'''

    def __init__(self, soundfiles):
        self.play = play
        self.soundfile = soundfiles

    def soundfiles(self):
        noticed_snd = 'signatur-Dil-7491_hifi.mp3'
        bkgrnd_people_snd = 'People_T-Doogens-8720_hifi.mp3'
        win_snd = 'Applause-SFXsourc-9027_hifi.mp3'
        check_snd = 'plim-Gato_pre-7790_hifi.mp3'

    def play(self, soundfile):
        pygame.mixer.music.load(soundfile)
        pygame.mixer.music.play() 

Sound.play(check_snd)
Wen es wundert, warum bei "pygame.mixer.music.play()" kein Attribut eingeklammert ist: ich habe es mit und ohne "...(soundfile)" probiert, check_snd bleibt weiterhin unbekannt und bei normaler Verwendung in einer Funktion (oder zu Testzwecken im Hauptstrang des Codes) muss bei pygame.mixer.music.load() zwar die Variable eingetragen werden, nicht jedoch bei pygame.mixer.music.play()

Ich denke, ich initiiere die Klasse entweder falsch. Ausprobiert habe ich jedoch auch, die vier Soundvaraiblen direkt unter den DocString zu platzieren, ebenso sie in der __init__ zu benennen - alles führte bisher aufs gleiche Ergebnis raus... :K
Oftmals beschleicht mich hier im Forum das heimliche Gefühl, an verschiedenen Stellen mal ein einfaches "Bahnhof" zu posten.

Wann du den Fisch auch fängst, er ist frisch. Sprichwort
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Weder erstellst du eine Instanz der Klasse Sound, noch hast du check_snd dort definiert wo du es brauchst.

Vielleicht beschreibst du mal warum du glaubst, den Code korrekt geschrieben zu haben und wir können dann gezielt erklären warum das nicht so ist.
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Ich glaube (=behaupte) gar nicht, den Code korrekt geschrieben zu haben. Wie schon gesagt, habe ich drei verschiedene Varianten ausprobiert, und alle drei haben bisher nicht funktioniert.


Variante 1:

Code: Alles auswählen

class Game:
  '''DocString.'''

  noticed_snd = 'signatur-Dil-7491_hifi.mp3'
  bkgrnd_people_snd = 'People_T-Doogens-8720_hifi.mp3'
  win_snd = 'Applause-SFXsourc-9027_hifi.mp3'
  check_snd = 'plim-Gato_pre-7790_hifi.mp3'
  
  def __init__ (self):
    self.load = pygame.mixer.music.load()
    self.play = pygame.mixer.music.play()

  def play(self, soundfile):
    pygame.mixer.music.load(soundfile)
    pygame.mixer.music.play()

Sound.play(noticed_snd)

Variante 2:

Code: Alles auswählen

class Game:
  '''DocString.'''

  noticed_snd = 'signatur-Dil-7491_hifi.mp3'
  bkgrnd_people_snd = 'People_T-Doogens-8720_hifi.mp3'
  win_snd = 'Applause-SFXsourc-9027_hifi.mp3'
  check_snd = 'plim-Gato_pre-7790_hifi.mp3'

  def __init__(self):
    self.play = play

  def play(self, soundfile):
    pygame.mixer.music.load(soundfile)
    pygame.mixer.music.play()

Die dritte steht ja oben.

Eben habe ich noch eine vierte Variante probiert. Hier der Code:

Variante 4)

Code: Alles auswählen

import pygame
from pygame.locals import *
pygame.init()


class Sound(object):
    noticed_snd = 'signatur-Dil-7491_hifi.mp3'
    bkgrnd_people_snd = 'People_T-Doogens-8720_hifi.mp3'
    win_snd = 'Applause-SFXsourc-9027_hifi.mp3'
    check_snd = 'plim-Gato_pre-7790_hifi.mp3'

    def play(self, soundfile):
        pygame.mixer.music.load(soundfile)
        pygame.mixer.music.play()


snd = Sound()
snd.play(check_snd)
(frei nach einem Bsp. aus dem Wiki)


Es ist ja nicht so, dass ich Dich oder euch meinen Code schreiben lassen möchte, auch bin ich kein Student und noch weniger arbeite ich mit irgendetwas das mit Programmierung zu tun hat (von ab und an excel vielleicht mal abgesehen...)
Ich steige einfach nur nicht durch, wieso meine Klasse nicht funktioniert bzw. wo der Fehler liegt. Daher habe ich hier nachgefragt. ;-)

Ich muss nicht unbedingt mit einer __init__ arbeiten, nur ohne scheint es auch nicht zu funktionieren...und das durchkreuzt leicht meine Pläne, da ich in einem Projekt von mir dies alles noch zuvor im Hauptstrang ablaufen ließ, im Hauptstrang aber nun gar nix mehr steht außer "if name == main: -> main()" und ich daher für die Soundausgabe zumindest eine Klasse zu benötigen meine.

Um deine Frage jedoch konkret zu beantworten:

Auf die letzte (einfachste) Variante, also Nr. 4, bezogen:
Die Klasse sollte mMn erstellt sein. Ihr habe ich vier Attribute (Variablen) direkt zugeordnet, diese sollten also in der gesamten Klasse les- und beschreibbar sein, sofern ich das richtig verstanden habe. Sie unterstehen keiner Instanz, sondern direkt der Klasse.
Dann habe ich eine Methode "play()" definiert, in der die einzelnen Schritte welche auch in einer Funktion so geschrieben werden, je 1x ablaufen sollen, konkret sind dies "pygame.mixer.music.load(variable)" und "pygame.mixer.music.play()"

Im Hauptstrang erstelle ich die Variable "snd" und weise ihr die Klasse "Sound" zu. Aufrufen tue ich das ganze folgendermaßen: "snd.play(attribut)"

Laufen tut es trotzdem nicht.
Oftmals beschleicht mich hier im Forum das heimliche Gefühl, an verschiedenen Stellen mal ein einfaches "Bahnhof" zu posten.

Wann du den Fisch auch fängst, er ist frisch. Sprichwort
BlackJack

@BSA: Doch Du musst unbedingt mit einer `__init__()` arbeiten, zumindest wenn die Klasse irgendwie Sinn machen soll. Es sieht aus aus sollte hier etwas künstlich verkompliziert werden was eigentlich einfach nur eine Funktion und ein paar Konstanten mit Dateinamen sind. Und das nur um das Schlüsselwort ``class`` irgendwie in das Programm zu bringen.

Man kann da irgendwie nicht wirklich Verbesserungsvorschläge bezüglich der Klasse machen, denn das einzige was mir einfällt ist: Lass das mit der Klasse bleiben. ;-)
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@BSA: Der Compiler kann nicht raten, was Du eigentlich meinst, und Raten allgemein hilft beim Programmieren nicht viel weiter. Jede der Varianten erzeugt einen Fehler (es sind mehrere, aber nur der erste wird angezeigt :-) Aber bevor ich dazu komme, zuerst die Frage: Welchen Mehrwert hast Du, wenn Du eine Klasse erzeugst im Vergleich zu einer einfachen Funktion »play(soundfile)«? Und die Antwort darauf lautet: keinen. Also ist das Erstellen einer Klasse nur mehr Schreibaufwand und höhere Komplexität. Der Vorteil von Klassen ist ja, dass sie Variablen kapseln. Deine Klasse kapselt aber nur ein paar Konstanten. Das »self«, das ja eine Referenz auf eine Instanz der Klasse ist, benutzt Du ja nie (sinnvoll). Varianten 1 bis 3 sind Quatsch bis grob falsch. Variante 4 hat potential, wenn Du die Klasse wegschmeißt:

Code: Alles auswählen

import pygame
from pygame.locals import *
pygame.init()

SOUND_FILES = {
    'noticed': 'signatur-Dil-7491_hifi.mp3',
    'bkgrnd_people': 'People_T-Doogens-8720_hifi.mp3',
    'win': 'Applause-SFXsourc-9027_hifi.mp3',
    'check': 'plim-Gato_pre-7790_hifi.mp3'
}

def play(sound_name):
    pygame.mixer.music.load(SOUND_FILES[sound_name])
    pygame.mixer.music.play()

play('check')
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

@ BlackJack: Naja, ich würde das ganze mit der Klasse ja auch gerne sein lassen. Deshalb hab ich ja auch 454 Zeilen in 10 Funktionen plus zwei Zeilen Hauptstrang... ;-)
Das Problem ist nur, dass ich über meine mainloop() momentan um die...ziemlich genau 9 Variablen an die jeweiligen von ihr, der mainloop(), aufgerufenen Funktionen übergebe.

Wen es interessiert, hier mal die letzten Zeilen meines Codes, das sind nur die beiden Hauptfunktionen main() und mainloop().
Der gesamte Teil der nun in der main() steckt, war vorher im Hauptstrang, da waren also u.a. die Soundfiles global, was sie jetzt nicht mehr sind. Und da ich, sollten mal, rein hypothetisch weitere Soundfiles hinzukommen, nicht auch noch diese Variablen jeweils an die Funktionen übermitteln möchte, dachte ich mir, wäre eine Klasse, die die Sounds ausgibt, vielleicht hilfreich..

Hier mal die letzten beiden Funktionen main() und mainloop(). :-)

Code: Alles auswählen

def mainloop(sound_on, fast_text, player_name):
    '''Welcome to BrainBug ver. 0.0.

    Where all is working and nothing is laggy.
    If you arrived, you will recognize now, that the answer for everything,
    nothing and the big whole everything is simple.
    It's just: 42.
    So we hope you have enjoyed this journey and that you had a nice ride
    with _guessy_ and her HonkyTonky, rarely faboulous, alltimed famous and
    incredibly unbelievable...

    PHYSICA:LL:INES.'''

    menu = 1
    sound_sett = 1
    cipher_band = 1
    difficult = 1
    while True:
        if menu == 0:
            mainmenue(menu, cipher_band, difficult, player_name)
            menu = play_game(cipher_band, trials, fast_text, sound_on, menu,
            player_name)
        else:
            mainmenue(menu, cipher_band, difficult, player_name)
            cipher_band = set_range(fast_text, sound_on)
            trials = bisect(cipher_band)
            difficult = difficulty(cipher_band, trials, fast_text, sound_on)
            sound_sett = sound_setting(sound_sett, sound_on)
            menu = play_game(cipher_band, trials, fast_text, sound_on, menu,
            player_name)

def main():
    print('\n\
    *********************************************\n\
    ******************* GUESS *******************\n\
    *********************************************')
    time.sleep(0.2)
    player_name = input('>> Bitte geben Sie Ihren Namen ein: ')
    print('Hallo', player_name, '!')
    if player_name == 'TESTER':
        fast_text = 1
    else:
        fast_text = 0
    time.sleep(1.2)
    while True:
        try:
            response_snd = int(input('Sound aktivieren? [1] JA  |  [0] NEIN '))
            if response_snd == 1:
                try:
                    noticed_snd = 'signatur-Dil-7491_hifi.mp3'
                    pygame.mixer.music.load(noticed_snd)
                    bkgrnd_people_snd = 'People_T-Doogens-8720_hifi.mp3'
                    pygame.mixer.music.load(bkgrnd_people_snd)
                    win_snd = 'Applause-SFXsourc-9027_hifi.mp3'
                    pygame.mixer.music.load(win_snd)
                    check_snd = 'plim-Gato_pre-7790_hifi.mp3'
                    pygame.mixer.music.load(check_snd)
                    print('Sounddateien erfolgreich geladen...')
                    sound_on = 1
                    break
                except IOError:
                    print('''Sounddateien nicht gefunden!
                    Spiel wird ohne Sound fortgesetzt...''')
                    sound_on = 0
                    time.sleep(2.4)
                    break
            elif response_snd == 0:
                sound_on = 0
                break
            else:
                print('''Bestätige [1] um den Sound zu aktivieren
                [0] deaktiviert die Soundausgabe!''')
        except ValueError:
            print('''Bestätige [1] um den Sound zu aktivieren
                [0] deaktiviert die Soundausgabe!''')
    text_render(fast_text, beg=0, end=8)
    print()
    text_render(fast_text, beg=8, end=22)
    if sound_on == 1:
        pygame.mixer.music.load(bkgrnd_people_snd)
        pygame.mixer.music.play()
    print()
    text_render(fast_text, beg=22, end=26)
    text_render(fast_text, beg=26, end=40)
    print()
    mainloop(sound_on, fast_text, player_name)
    close_game(player_name)

if __name__ == '__main__':
    main()
Der DocString für die Main steht noch nicht, da ich den DocString gerne zum Schluß schreibe, da sich meist noch das ein oder andere ändert (hier zB die Soundausgabe...)
Der DocString für die Mainloop() sollte ursprünglich der letzte sein, daher ist er nicht ernst zu nehmen. Der war allerdings nur solange der letzte, bis ich mich entschied, gar keine globalen Variablen mehr benutzen zu wollen, sondern nur noch eine KONSTANTE (nicht im Code abgebildet...)
Daher der evtl. etwas komisch wirkende DocString... :lol:


@ Sirius3: Danke für den Code! Dabei ist mir grade wie Schuppen von den Augen gefallen, dass das ja gar keine variablen sondern KONSTANTEN sind, was die ganze Sache natürlich schon ganz anders aussehen lässt...
Hm, gut, die play()-Funktion kann ich zwar noch immer nicht nutzen, da Unterfunktionen sich sonst wieder selbst aufrufen würden ohne einen konkreten Rückgabewert zu liefern, ABER: Wozu die play()-Funktion? Das bisschen Code für "pygame.mixer.music.load()" und "pygame.mixer.music.play()" habe ich so oder so schon an den entsprechenden Stellen im Code stehen, nur die KONSTANTEN muss ich kurz definieren und dann SOLLTE es ja eigentlich laufen :-D

Ich hab vor lauter Schleifen wohl vergessen, dass KONSTANTEN ruhig global sein dürfen :lol: :oops:

Danke ;-)
Zuletzt geändert von Anonymous am Freitag 10. Januar 2014, 01:50, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
Oftmals beschleicht mich hier im Forum das heimliche Gefühl, an verschiedenen Stellen mal ein einfaches "Bahnhof" zu posten.

Wann du den Fisch auch fängst, er ist frisch. Sprichwort
BlackJack

@BSA: Die Playfunktion verhindert das man immer wieder die zwei Zeilen schreiben muss *und* erlaubt das man den Abspielmechanismus an genau dieser einen Stelle im Programm ändern kann.
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Ja, das ist wohl definitiv richtig, nur was ist, wenn ich mitten in der Funktion7 bspw. den Sound brauche, die play()-Funktion jedoch keinen vernünftigen Wert an Func7 zurückliefern kann/muss. Ist das dann nicht ein rekursiver Funktionsaufruf?
Ich hatte das anderswo im Forum mal so verstanden, dass eine Unterfunktion im Normalfall keine weitere Unterfunktion aufrufen soll ohne ihr einen gewissen Rückgabewert zu liefern, sicher kann man da was einbauen...
Oder habe ich da nun was falsch verstanden..?

Man könnte natürlich auch einen Dummy-Wert zurückliefern, der keine Wirkung hat und auch ständig überschrieben würde, damit es ihn nur einmal gäbe, aber ob das so im Sinne der Sache ist, bei so einem vergleichsweise! doch noch leichten Programm mein ich..?

Momentan steh ich grad noch vor dem Problem:

Code: Alles auswählen

    k = SOUND_FILES.keys()
    print('SOUNDFILES GESAMT : ', len(k))
    time.sleep(1)
    for x in k:
        print(x)
gibt mir folgendes aus:

Code: Alles auswählen

SOUNDFILES GESAMT :  4
bkgrnd_people
check
noticed
win
Jedoch bringt folgender Aufruf wiederum folgenden NameError... :?

Code: Alles auswählen

pygame.mixer.music.load(SOUND_FILES[noticed])

Code: Alles auswählen

Traceback (most recent call last):

  File "/home/gamestation/programmieren/python/scripts/projekte/projekt_guess/testecke.../guess_neuanfang 0/_guess_/__init__.py", line 451, in <module>
    main()
  File "/home/gamestation/programmieren/python/scripts/projekte/projekt_guess/testecke.../guess_neuanfang 0/_guess_/__init__.py", line 415, in main
    pygame.mixer.music.load(SOUND_FILES[noticed])
NameError: global name 'noticed' is not defined


EDIT: Ist ja peinlich...ich hab die Anführungszeichen vergessen :oops:

Problem also gelöst !!
Oftmals beschleicht mich hier im Forum das heimliche Gefühl, an verschiedenen Stellen mal ein einfaches "Bahnhof" zu posten.

Wann du den Fisch auch fängst, er ist frisch. Sprichwort
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@BSA: ich verstehe nicht, warum man "play" einen Rückgabewert braucht? Du benutzt doch ständig Funktionen ohne Rückgabewert. Das ist ja auch ganz normal bei 'print' oder 'sleep', 'text_render', 'main_loop', usw.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Natürlich können Unterfunktionen auch weitere Funktionen aufrufen. Wichtig ist nur, dass die Aufrufkette irgendwann ein Ende findet, sodass die Ebene des ursprünglichen Aufrufs erreicht werden kann. Konkret gesagt sollte daher zum Beispiel vermieden werden, dass innerhalb der ``play()``-Funktion plötzlich das Hauptmenü aufgerufen wird oder sowas.
Antworten