SOUNDKARTEN, SOUNDSENSOREN

 

Alle Programme können von hier heruntergeladen werden.


 

Sound mit dem Raspberry Pi

 

Der Raspberry Pi ist mit einer eingebauten Soundkarte ausgestattet, die Sound über eine Audio-Buchse ausgibt. Sounddateien in verschiedenen Audioformaten (am häufigsten WAV und MP3) können mit vorinstallierten oder zusätzlichen Soundplayern abgespielt werden. Um beispielsweise die Datei mysong.wav abzuspielen, geben Sie in der Konsole den Befehl aplay <mysong.wav> ein. Dabei wird der Song mit dem ALSA-Player (Advanced Linux Sound Architecture) über die eingebaute Soundkarte abgespielt. Die Qualität ist nicht berauschend, so dass es besser ist, einen USB-Soundadapter verwenden, der Raspberry Pi-kompatibel ist. Dazu müssen Sie den standardmässigen Soundport auf die USB-Karte umstellen. (Anleitungen dazu findet man im Internet, leider sind aber die meisten aufgrund von Änderungen in der Linux-Distribution veraltet und funktionieren nicht.) Gehen Sie ausgehend von der aktuellen NOOBS-Distribution wie folgt vor: Edieren Sie die Datei .asoundrc, die Sie im Verzeichnis /home/pi finden. Ändern Sie den Eintrag card 0 auf card 1.

Eingebaute Soundkarte (default)

pcm.!default {
type hw
card 0
}

ctl.!default {
type hw
card 0
}

  Externer USB Soundadapter

pcm.!default {
type hw
card 1
}

ctl.!default {
type hw
card 1
}

Wir empfehlen, als Soundapplikation SoX (Sound eXchange) zu verwenden, die auch als "Das Schweizer Taschenmesser der Klangverarbeitungsprogramme " bezeichnet wird. SoX ist auf unserer SD-Karten-Distribution vorinstalliert. Um SoX auf einem neuen NOOBS- System zu installieren, schliessen Sie den Raspberry Pi an das Internet an und geben in einem Terminal Folgendes ein:

sudo apt-get install sox
sudo apt-get install libsox-fmt-mp3

Jetzt können mit dem Befehl play <mysong.mp3> einen MP3-Song abspielen. Wenn Sie SoX eintippen, sehen Sie alle unterstützten Audio-Formate. (Weitere Informationen finden Sie auf der Website von SoX .)

Mit SoX können Sie die Soundkarte softwaremässig on-the-fly wechseln, indem Sie die Umgebungsvariable AUDIODEV auf hw:0 oder hw:1 setzen. Dazu geben Sie in der Kommandozeile ein:

AUDIODEV=hw:1 play <mysong.mp3>

wobei mit hw:0 oder hw:1 der Soundport gewählt wird (es ist nicht nötig, die Datei .asoundrc zu editieren.) Informationen über die verfügbaren Abspielgeräte erhalten Sie mit aplay -l. Mit amixer können Sie die Eigenschaften der voreingestellten Soundkarte anzeigen.

 

 

 

Zugriff auf das Sound-System mit Python

 

Für das ALSA-Soundsystem wird üblicherweise das vorinstallierte Python-Modul pygame verwendet. Leider unterstützt es nur die eingebaute Soundkarte. Um den USB-Soundadapter mit Python anzusprechen, haben wir eine kleine Python-Klasse SoundPlayer geschrieben, welche die Linux-Befehle für den SoX-Player aufruft. Damit können Sie die Soundkarte softwaremässig auswählen. Die Python-Dokumentation finden Sie hier.

 

 

Experiment 1: Sounddateien mit Python abspielen

 

Ziel:
Eine Sounddatei 10 s mit der halben Standardlautstärke abspielen, 5 s anhalten, weitere 10 s weiterspielen und dann anhalten.

Downloaden Sie als erstes die Datei soundplayer.py von hier und kopieren Sie sie in das gleiche Verzeichnis, im dem sich Ihr Programm befindet (üblicherweise /home/scripts). Kopieren Sie dann eine Songdatei, die Sie spielen möchten, in irgendein Verzeichnis (zum Beispiel salza1.wav in /home/pi/sound). Verbinden Sie die Audio-Buchse mit dem Eingang eines beliebigen Audioverstärkers.

Programm:[►]

# Sound1a.py

import time
from soundplayer import SoundPlayer

# Use device with ID 1  (mostly USB audio adapter)
p = SoundPlayer("/home/pi/sound/salza1.wav", 1)        
print "play for 10 s with volume 0.5"
p.play(0.5) # non-blocking, volume = 0.5
print "isPlaying:", p.isPlaying()
time.sleep(10)
print "pause for 5 s"
p.pause()
print "isPlaying:", p.isPlaying()
time.sleep(5)
print "resume for 10 s"
p.resume()
time.sleep(10)
print "stop"
p.stop()
print "isPlaying:", p.isPlaying()
print "done"
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
Orientieren Sie sich am Programmcode, um zu verstehen, wie man die Klasse SoundPlayer verwendet. Beachten Sie Folgendes:

Da das Abspielen eines Sounds in einem eigenen Linux-Prozess erfolgt, spielt die Musik weiter, auch wenn das Python-Programm beendet wird. Um den Linux-Prozess zu stoppen, müssen Sie am Ende des Programms die Funktion stop() aufrufen. Die Funktion isPlaying() gibt True zurück, so lange der Prozess läuft, sie liefert also auch während einer Pause den Wert True.

 

 

Experiment 2: Raspberry Pi als ein 4-Button Music-Player

 

Ziel:
Sie möchten den Raspberry Pi als Multimedia-Gerät verwenden, um Songs aus einer Sammlung Ihrer Lieblingssongs (einer Playlist) abzuspielen.

Zur Steuerung des Players verwenden wir 4 Tastenschalter (Run, Pause, Stop, Select) und implementieren folgende Spezifikationen:

  • Beim Programmstart wird der erste Song automatisch abgespielt
  • Die Songs werden ohne Betätigung der Buttons nacheinander abgespielt. Nach dem Ende der Playlist wird wieder der erste Song gespielt
  • Der Run-Button startet das Abspielen oder führt es nach einem Stop weiter
  • Der Select-Button ist nur dann aktiviert, wenn das Abspielen gestoppt ist. Der gewählte Song wird gestartet

Es wird dringend empfohlen, das Programm als eine Zustandsmaschine zu schreiben. Folgende Zustände werden verwendet:

  • STOPPED: Abspielprozess beendet
  • PAUSED: Abspielprozess angehalten, kann aber weitergeführt werden
  • PLAYING: Abspielprozess wird ausgeführt

Programm:[►]

# Sound1b.py

from soundplayer import SoundPlayer
import RPi.GPIO as GPIO
import time

'''
states:
STOPPED : play process terminated
PAUSED: play process stopped (playing still underway)
PLAYING: play process executing
'''

# Button pins, adapt to your configuration
P_PLAY = 24 
P_PAUSE = 16
P_STOP = 22
P_SELECT = 12

def setup():
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(P_STOP, GPIO.IN, GPIO.PUD_UP)
    GPIO.setup(P_PAUSE, GPIO.IN, GPIO.PUD_UP)
    GPIO.setup(P_PLAY, GPIO.IN, GPIO.PUD_UP)
    GPIO.setup(P_SELECT, GPIO.IN, GPIO.PUD_UP)
 
setup()
nbSongs = 4
songID = 0
p = SoundPlayer("/home/pi/songs/song" + str(songID) + ".mp3", 1)        
p.play()
print "playing, song ID", songID
state = "PLAYING"

while True:
    if GPIO.input(P_PAUSE) == GPIO.LOW and state == "PLAYING":
        state = "PAUSED"
        print "playing->paused"
        p.pause()
    elif GPIO.input(P_PLAY) == GPIO.LOW and state == "STOPPED":
        state = "PLAYING"
        print "stopped->playing, song ID", songID
        p.play()
    elif GPIO.input(P_PLAY) == GPIO.LOW and state == "PAUSED":
        state = "PLAYING"
        print "paused->playing"
        p.resume()
    elif GPIO.input(P_STOP) == GPIO.LOW and (state == "PAUSED" or 
           state == "PLAYING"):
        state = "STOPPED"
        print "paused/playing->stopped"
        p.stop()
    elif (GPIO.input(P_SELECT) == GPIO.LOW and state == "STOPPED") \
          or (state == "PLAYING" and not p.isPlaying()):
        songID += 1
        if songID == nbSongs:
            songID = 0
        p = SoundPlayer("/home/pi/songs/song" + str(songID) + ".mp3", 1)
        print "stopped->playing, song ID", songID
        p.play()
        state = "PLAYING"
    time.sleep(0.1) # Do not waste processor time   
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
Wie Sie sehen, haben Tastenevents zwei Auswirkungen: Zum einen modifizieren sie den Zustand und zum anderen rufen Sie eine Funktion von SoundPlayer auf. Da beim Drücken der Taste der Zustand geändert wird, verursacht das Schalterprellen (entsprechend dem mehrmaligen Drücken und Loslassen des gleichen Buttons) keine Probleme.

 

 

Experiment 3: Sinuston-Generator

 

Ziel:
Signaltöne abspielen.

Für einfache Robotik-Anwendungen werden lediglich bestimmte Signaltöne benötigt. Dazu ist Qualität der internen Soundkarte ausreichend. Die Klasse SoundPlayer enthält die (statische) Funktion playTone(), die einen Ton mit der vorgegebener Frequenz und Dauer abspielt. Im folgenden Beispiel verwenden wir die blockierende Version, um einen Ton nach dem anderen abzuspielen. Dann spielen wir alle drei Töne zusammen und verwenden für die Frequenzen eine Listenotation.

Programm:[►]

# Sound2a.py

from soundplayer import SoundPlayer
import time

# Sine tone during 0.1 s, blocking, device 0
dev = 0
SoundPlayer.playTone(900, 0.1, True, dev) # 900 Hz
SoundPlayer.playTone(800, 0.1, True, dev) # 600 Hz
SoundPlayer.playTone(600, 0.1, True, dev) # 600 Hz
time.sleep(1)
SoundPlayer.playTone([900, 800, 600], 5, True, dev) # 3 tones together
print "done" 
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
Da es sich bei playTone() um eine statische Funktion handelt, kann diese durch Vorstellen des Klassennamens aufgerufen werden.

Im nächsten Beispiel verwenden wir die nicht-blockierende Version der Funktion playTone(), damit das Programm während dem Abspielen eine LED blinken lassen kann.

Programm:[►]

# Sound2b.py

import RPi.GPIO as GPIO
from soundplayer import SoundPlayer
import time

P_LED = 18 # adapt to your wiring

def setup():
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(P_LED, GPIO.OUT)
    
setup()
# Sine of 1000 Hz during 5 s, non-blocking, device 0
SoundPlayer.playTone(1000, 5, False, 0)
while SoundPlayer.isPlaying():
    GPIO.output(P_LED, GPIO.HIGH)
    print "on"
    time.sleep(0.5)
    GPIO.output(P_LED, GPIO.LOW)
    print "off"
    time.sleep(0.5)

print "done" 
Programmmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

 

Experiment 4: Mit einem Mikrofonmodul akustische Ereignis erfassen

 

Ziel:
Eine Aktion durch Klatschen, Schreien oder Pfeifen auslösen. Hier wird beim Klatschen eine LED eingeschaltet und beim nächsten Klatschen wieder ausgeschaltet.

Wir verwenden ein Mikrofon als Schallempfänger. Dieses gibt ein analoges Schallsignal (als Spannung) ab. Hier ist aber nicht notwendig ist, das Signal zu digitalisieren und mit dem Raspberry Pi zu analysieren. (Dies ist sowieso wegen des kleinen Spannungspegels und der schnellen Änderung des Signals mit grösseren Schwierigkeiten verbunden.) Es genügt, aus dem analogen Signal ein digitales Signal mit zwei Spannungspegeln LOW/HIGH zu erzeugen, das an einen GPIO-Port angelegt wird. Dabei soll der Wert HIGH sein, wenn das analoge Signal einen bestimmten Pegel (Threshold) überschritten hat. Dies kann elektronisch leicht mit einem Operationsverstärker erreicht werden. Hier wird ein billiges Soundmodul verwendet (z.B. KY-038 gekauft über Ebay oder als Teil eines Arduino Sensorkits).

 

hand1

Achtung: Alle Spannungen am GPIO-Eingang müssen im Bereich 0..3.3V liegen. Versorgen Sie das Modul mit 3.3V oder verwenden Sie einen Pegelwandler.

Das KY-038-Modul ist kein sehr empfindliches Gerät und kann nur sehr laute Geräusche erkennen. Stellen Sie das Potentiometer so ein, dass die eingebaute Status-LED gerade abschaltet. Wenn Sie einen Ton in der Nähe des Mikrofons erzeugen (zum Beispiel indem Sie auf dem Mikrofon tippen), sollte die Status-LED für eine kurze Zeit aufleuchten.

Programm:[►]

# Sound3a.py
# Sound Sensor Module KY-038
# Using GPIO events

import RPi.GPIO as GPIO
import time

P_SOUND = 22 # adapt to your wiring
P_LED = 24 # adapt to your wiring

def setup():
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(P_LED, GPIO.OUT)
    GPIO.output(P_LED, GPIO.LOW)
    GPIO.setup(P_SOUND, GPIO.IN)
    GPIO.add_event_detect(P_SOUND, GPIO.BOTH, onButtonEvent)

def onButtonEvent(channel):
    global isOn
    if GPIO.input(P_SOUND) == GPIO.HIGH:
        isOn = not isOn
        if isOn:
            GPIO.output(P_LED, GPIO.HIGH)
        else:
            GPIO.output(P_LED, GPIO.LOW)
        # Have to wait a while
        # because event is triggered several times (like button bouncing)    
        time.sleep(0.5) 
       
setup()
isOn = False
while True:
    time.sleep(0.1)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
Die Änderung des Zustandes am GPIO-Eingang fassen wir als einen GPIO-Event auf und registrieren mit add_event_detect() die Callbackfunktion onSoundEvent(). Im Callback wechseln wir den Zustand der LED, falls das Signal von LOW auf HIGH geht. Um zu verhindern, dass der Callback mehrmals kurz hintereinander aufgerufen wird, deaktivieren wir ihn mit time.sleep(0.5) für eine halbe Sekunde.