线程上的pygame.mixer失败并显示“(pygame parachute)Segmentation Fault”

时间:2014-03-27 00:25:49

标签: python audio pygame raspberry-pi

在Raspberry Pi上,我尝试使用pygame.mixer播放一些声音,并且在主线程以外的任何线程上播放它时遇到问题。例如,我连接了一个键盘,当按下一个键(检测线程为螺纹)时,它会触发一个回调,并在该回调中尝试播放声音。但是当它尝试时,大部分时间都失败了:

Fatal Python error: (pygame parachute) Segmentation Fault

如果我在主线程上运行它,它可以正常工作。有没有理由为什么单独的线程不起作用或者这样做?请参阅下面的代码,在线程上调用的方法是audio_handler.playTone()

import pygame.mixer as mixer
import glob

class audio_handler:
    def __init__(self):
        mixer.init(channels = 2)
        mixer.set_num_channels(3)
        self._chanTones = mixer.Channel(0)
        self._chanMusic = mixer.Channel(1)
        self._chanRinger = mixer.Channel(2)
        self._dtmfTones = {}
        self._songs = []
        self._musicDir = "/home/pi/music/"

        self.loadDTMF()
        #self.loadSongs()

    def cleanup(self):
        mixer.quit()

    def loadDTMF(self):
        self._dtmfTones["0"] = mixer.Sound("./DTMF/0.ogg")
        self._dtmfTones["1"] = mixer.Sound("./DTMF/1.ogg")
        self._dtmfTones["2"] = mixer.Sound("./DTMF/2.ogg")
        self._dtmfTones["3"] = mixer.Sound("./DTMF/3.ogg")
        self._dtmfTones["4"] = mixer.Sound("./DTMF/4.ogg")
        self._dtmfTones["5"] = mixer.Sound("./DTMF/5.ogg")
        self._dtmfTones["6"] = mixer.Sound("./DTMF/6.ogg")
        self._dtmfTones["7"] = mixer.Sound("./DTMF/7.ogg")
        self._dtmfTones["8"] = mixer.Sound("./DTMF/8.ogg")
        self._dtmfTones["9"] = mixer.Sound("./DTMF/9.ogg")
        self._dtmfTones["*"] = mixer.Sound("./DTMF/star.ogg")
        self._dtmfTones["#"] = mixer.Sound("./DTMF/pound.ogg")

    def loadSongs(self):
        for file in glob.glob(self._musicDir + "*.ogg"):
            self._songs.append(file)

        print str(len(self._songs)) + " songs loaded."

    def playTone(self, tone):
        if self._dtmfTones.has_key(str(tone)):
            self._dtmfTones[str(tone)].play()
            self._chanTones.set_volume(1.0, 0.0)
            self._chanTones.play(self._dtmfTones[str(tone)])

旁注:这似乎只发生在ogg文件中,而不是wav。

Matrix键盘处理程序:

#!/usr/bin/python

import RPi.GPIO as GPIO
import time

class keypad():
    def __init__(self, callback):
        GPIO.setmode(GPIO.BCM)
        self._count = 0
        self._inInterrupt = False
        self._callback = callback

        # CONSTANTS 
        self.KEYPAD = [
            [1,2,3],
            [4,5,6],
            [7,8,9],
            ["*",0,"#"]
        ]

        self.ROW         = [18,23,24,25]
        self.COLUMN      = [4,17,22]

        self.__setInterruptMode()

    def __colInt(self, channel):
        time.sleep(0.05) #give it a moment to settle
        if GPIO.input(channel) > 0:
            return

        #remove interrupts temporarily
        for c in range(len(self.COLUMN)):
            GPIO.remove_event_detect(self.COLUMN[c])

        #get column number
        colVal = -1
        for c in range(len(self.COLUMN)):
            if channel == self.COLUMN[c]:
                colVal = c

        #continue if valid column (it should always be)
        if colVal >=0 and colVal < len(self.COLUMN):

            #set rows as intputs
            for r in range(len(self.ROW)):
                GPIO.setup(self.ROW[r], GPIO.IN, pull_up_down=GPIO.PUD_UP)

            #set triggered column as low output
            GPIO.setup(channel, GPIO.OUT, initial=GPIO.LOW)

            # Scan rows for pushed key/button
            rowVal = -1
            for r in range(len(self.ROW)):
                tmpRead = GPIO.input(self.ROW[r])
                if tmpRead == 0:
                    rowVal = r
                    break

            #continue if row is valid (possible that it might not be if the key was very quickly released)
            if rowVal >= 0 and rowVal < len(self.ROW):
                #send key info right away
                self._callback(self.KEYPAD[rowVal][colVal])
                #This avoids nasty boucning errors when the key is released
                #By waiting for the rising edge before re-enabling interrupts it 
                #avoids interrupts fired due to bouncing on key release and 
                #any repeated interrupts that would otherwise fire.
                try:
                    GPIO.wait_for_edge(self.ROW[rowVal], GPIO.RISING)
                    self.__setInterruptMode()
                except RuntimeError:
                    pass

                return

            else:
                print "Invalid Row!"
        else:
            print "Invalid Col!"

        #re-enable interrupts
        self.__setInterruptMode()

    def __changeWrapper(self, channel):
        #if there is already another interrupt going on (multiple key press or something)
        #return right away to avoid collisions
        if self._inInterrupt:
            return;

        self._inInterrupt = True
        self.__colInt(channel) #handle the actual interrupt
        self._inInterrupt = False

    def __setInterruptMode(self):
        #set the first row as output low
        #only first one needed as it will ground to all columns
        for r in range(len(self.ROW)):
            GPIO.setup(self.ROW[r], GPIO.OUT, initial=GPIO.LOW)

        #set columns as inputs and attach interrupt handlers on rising edge
        for c in range(len(self.COLUMN)):
            GPIO.setup(self.COLUMN[c], GPIO.IN, pull_up_down=GPIO.PUD_UP)
            GPIO.add_event_detect(self.COLUMN[c], GPIO.FALLING, bouncetime=250, callback=self.__changeWrapper)


    def cleanup(self):
        GPIO.cleanup()
        print "Cleanup done!"

import time     
if __name__ == '__main__':
    audio = audio_handler()
    def keypadCallback(value):
        audio.playTone(value)
        print "Keypad: " + value

    key = keypad(keypadCallback)

    try:
        while True:
            time.sleep(1)

    except KeyboardInterrupt:
        key.cleanup()

虽然我从不直接生成新线程,但是在它自己的内部GPIO中断回调的上下文中,通过键盘()类调用keypadCallback()。所以,我认为这意味着keypadCallback()实际上不在主线程上。

0 个答案:

没有答案