Changeset 800:709f86ab4573


Ignore:
Timestamp:
07/29/16 08:39:59 (8 years ago)
Author:
István Váradi <ivaradi@…>
Branch:
default
Phase:
public
Message:

Fixed sound playback on Linux

Location:
src/mlx
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/mlx/common.py

    r740 r800  
    2727    pygobject = False
    2828    import gobject
    29     try:
    30         import pygst
    31         pygst.require('0.10')
    32         import gst
    33 
    34         def gst_init():
    35             pass
    36 
    37         def gst_element_factory_make(what):
    38             return gst.element_factory_make(what)
    39 
    40         GST_STATE_PLAYING=gst.STATE_PLAYING
    41         GST_MESSAGE_EOS=gst.MESSAGE_EOS
    42     except:
    43         pass
    4429else:
    4530    print "Using PyGObject"
     
    4732
    4833    from gi.repository import GObject as gobject
    49 
    50     try:
    51         from gi.repository import Gst as gst
    52 
    53         def gst_init():
    54             gst.init()
    55 
    56         def gst_element_factory_make(what):
    57             return gst.ElementFactory.make(what)
    58 
    59         GST_STATE_PLAYING=gst.State.PLAYING
    60         GST_MESSAGE_EOS=gst.MessageType.EOS
    61     except:
    62         import traceback
    63         traceback.print_exc()
    64         pass
  • src/mlx/mlx.py

    r573 r800  
    22from config import Config
    33from i18n import setLanguage
    4 from sound import initializeSound, finalizeSound
     4from sound import preInitializeSound, initializeSound, finalizeSound
    55from util import secondaryInstallation
    66from const import VERSION
     
    7878    setLanguage(programDirectory, config.getLanguage())
    7979
     80    preInitializeSound()
     81
    8082    from .gui.gui import GUI
    8183    gui = GUI(programDirectory, config)
  • src/mlx/sound.py

    r585 r800  
    157157    _thread = None
    158158
     159    def preInitializeSound():
     160        """Perform any-pre initialization.
     161
     162        This does nothing on Windows."""
     163
    159164    def initializeSound(soundsDirectory):
    160165        """Initialize the sound handling with the given directory containing
     
    179184
    180185else: # os.name!="nt"
    181     import threading
     186    from multiprocessing import Process, Queue
     187    from threading import Thread, Lock
     188
     189    COMMAND_STARTSOUND = 1
     190    COMMAND_QUIT = 2
     191
     192    REPLY_FINISHED = 101
     193    REPLY_FAILED = 102
     194    REPLY_QUIT = 103
     195
     196    _initialized = False
     197    _process = None
     198    _thread = None
     199    _inQueue = None
     200    _outQueue = None
     201    _nextReference = 1
     202    _ref2Data = {}
     203    _lock = Lock()
     204
     205    def _processFn(inQueue, outQueue):
     206        """The function running in the helper process created.
     207
     208        It tries to load the Gst module. If successful, True is sent back,
     209        otherwise False followed by the exception caught and the function
     210        quits.
     211
     212        In case of successful initialization, the directory of the sound files
     213        is read, the command reader thread is created and the gobject main loop
     214        is executed."""
     215        try:
     216            import gi.repository
     217            gi.require_version("Gst", "1.0")
     218            from gi.repository import Gst
     219            from gi.repository import GObject as gobject
     220
     221            Gst.init(None)
     222        except Exception, e:
     223            outQueue.put(False)
     224            outQueue.put(e)
     225            return
     226
     227        outQueue.put(True)
     228
     229        soundsDirectory = inQueue.get()
     230
     231        _bins = set()
     232
     233        mainLoop = None
     234
     235        def _handlePlayBinMessage(bus, message, bin, reference):
     236            """Handle messages related to a playback."""
     237            if bin in _bins:
     238                if message.type==Gst.MessageType.EOS:
     239                    _bins.remove(bin)
     240                    if reference is not None:
     241                        outQueue.put((REPLY_FINISHED, (reference,)))
     242                elif message.type==Gst.MessageType.ERROR:
     243                    _bins.remove(bin)
     244                    if reference is not None:
     245                        outQueue.put((REPLY_FAILED, (reference,)))
     246
     247        def _handleCommand(command, args):
     248            """Handle commands sent to the server."""
     249            if command==COMMAND_STARTSOUND:
     250                (name, reference) = args
     251                try:
     252                    playBin = Gst.ElementFactory.make("playbin", "player")
     253
     254                    bus = playBin.get_bus()
     255                    bus.add_signal_watch()
     256                    bus.connect("message", _handlePlayBinMessage,
     257                                playBin, reference)
     258
     259                    path = os.path.join(soundsDirectory, name)
     260                    playBin.set_property( "uri", "file://%s" % (path,))
     261
     262                    playBin.set_state(Gst.State.PLAYING)
     263                    _bins.add(playBin)
     264                except Exception as e:
     265                    if reference is not None:
     266                        outQueue.put((REPLY_FAILED, (reference,)))
     267            elif command==COMMAND_QUIT:
     268                outQueue.put((REPLY_QUIT, None))
     269                mainLoop.quit()
     270
     271        def _processCommands():
     272            """Process incoming commands.
     273
     274            It is to be executed in a separate thread and it reads the incoming
     275            queue for commands. The commands with their arguments are added to the
     276            idle queue of gobject so that _handleCommand will be called by them.
     277
     278            If COMMAND_QUIT is received, the thread exits."""
     279
     280            while True:
     281                (command, args) = inQueue.get()
     282
     283                gobject.idle_add(_handleCommand, command, args)
     284                if command==COMMAND_QUIT:
     285                    break
     286
     287        commandThread = Thread(target = _processCommands)
     288        commandThread.daemon = True
     289        commandThread.start()
     290
     291
     292        mainLoop = gobject.MainLoop()
     293        mainLoop.run()
     294
     295        commandThread.join()
     296
     297    def _handleInQueue():
     298        """Handle the incoming queue in the main program.
     299
     300        It reads the replies sent by the helper process. In case of
     301        REPLY_FINISHED and REPLY_FAILED the appropriate callback is called. In
     302        case of REPLY_QUIT, the thread quits as well."""
     303        while True:
     304            (reply, args) = _inQueue.get()
     305            if reply==REPLY_FINISHED or reply==REPLY_FAILED:
     306                (reference,) = args
     307                callback = None
     308                extra = None
     309                with _lock:
     310                    (callback, extra) = _ref2Data.get(reference, (None, None))
     311                    if callback is not None:
     312                        del _ref2Data[reference]
     313                if callback is not None:
     314                    callback(reply==REPLY_FINISHED, extra)
     315            elif reply==REPLY_QUIT:
     316                break
     317
     318    def preInitializeSound():
     319        """Start the sound handling process and create the thread handling the
     320        incoming queue."""
     321        global _thread
     322        global _process
     323        global _inQueue
     324        global _outQueue
     325
     326        _inQueue = Queue()
     327        _outQueue = Queue()
     328
     329        _process = Process(target = _processFn, args = (_outQueue, _inQueue))
     330        _process.start()
     331
     332        _thread = Thread(target = _handleInQueue)
     333        _thread.daemon = True
     334
     335    def initializeSound(soundsDirectory):
     336        """Initialize the sound handling. It reads a boolean from the incoming
     337        queue indicating if the libraries could be loaded by the process.
     338
     339        If the boolean is True, the thread handling the incoming replies is
     340        started and the directory containing the sounds file is written to the
     341        output queue.
     342
     343        Otherwise the exception is read from the queue, and printed with an
     344        error message."""
     345        global _initialized
     346        _initialized = _inQueue.get()
     347
     348        if _initialized:
     349            _thread.start()
     350            _outQueue.put(soundsDirectory)
     351        else:
     352            exception = _inQueue.get()
     353            print "The Gst library is missing from your system. It is needed for sound playback on Linux:"
     354            print exception
     355
     356    def startSound(name, finishCallback = None, extra = None):
     357        """Start playing back the given sound.
     358
     359        If a callback is given, a new reference is acquired and the callback is
     360        registered with it. Then a COMMAND_STARTSOUND command is written to the
     361        output queue"""
     362        if _initialized:
     363            reference = None
     364            if finishCallback is not None:
     365                with _lock:
     366                    global _nextReference
     367                    reference = _nextReference
     368                    _nextReference += 1
     369                    _ref2Data[reference] = (finishCallback, extra)
     370
     371            _outQueue.put((COMMAND_STARTSOUND, (name, reference)))
     372
     373    def finalizeSound():
     374        """Finalize the sound handling.
     375
     376        COMMAND_QUIT is sent to the helper process, and then it is joined."""
     377        if _initialized:
     378            _outQueue.put((COMMAND_QUIT, None))
     379            _process.join()
     380            _thread.join()
     381
     382#------------------------------------------------------------------------------
     383#------------------------------------------------------------------------------
     384
     385if __name__ == "__main__":
    182386    import time
    183     try:
    184         from common import gst_init, gobject, gst_element_factory_make
    185         from common import GST_STATE_PLAYING, GST_MESSAGE_EOS
    186 
    187         _soundsDirectory = None
    188         _bins = set()
    189 
    190         def initializeSound(soundsDirectory):
    191             """Initialize the sound handling with the given directory containing
    192             the sound files."""
    193             gst_init()
    194             global _soundsDirectory
    195             _soundsDirectory = soundsDirectory
    196 
    197         def startSound(name, finishCallback = None, extra = None):
    198             """Start playing back the given sound."""
    199             try:
    200                 playBin = gst_element_factory_make("playbin")
    201 
    202                 bus = playBin.get_bus()
    203                 bus.enable_sync_message_emission()
    204                 bus.add_signal_watch()
    205                 bus.connect("message", _handlePlayBinMessage,
    206                             playBin, finishCallback, extra)
    207 
    208                 path = os.path.join(_soundsDirectory, name)
    209                 playBin.set_property( "uri", "file://%s" % (path,))
    210 
    211                 playBin.set_state(GST_STATE_PLAYING)
    212                 _bins.add(playBin)
    213             except Exception, e:
    214                 print "mlx.sound.startSound: " + str(e)
    215                 if finishCallback is not None:
    216                     finishCallback(False, extra)
    217 
    218         def finalizeSound():
    219             """Finalize the sound handling."""
    220             pass
    221 
    222         def _handlePlayBinMessage(bus, message, bin, finishCallback, extra):
    223             """Handle the end of the playback of a sound file."""
    224 
    225             # if message.type==GST_MESSAGE_EOS:
    226             #     _bins.remove(bin)
    227             #     if finishCallback is not None:
    228             #         finishCallback(True, extra)
    229 
    230     except:
    231         print "The Gst library is missing from your system. It is needed for sound playback on Linux"
    232         traceback.print_exc()
    233         def initializeSound(soundsDirectory):
    234             """Initialize the sound handling with the given directory containing
    235             the sound files."""
    236             pass
    237 
    238         def startSound(name, finishCallback = None, extra = None):
    239             """Start playing back the given sound.
    240 
    241             FIXME: it does not do anything currently, but it should."""
    242             print "sound.startSound:", name
    243 
    244         def finalizeSound():
    245             """Finalize the sound handling."""
    246             pass
    247 
    248 #------------------------------------------------------------------------------
    249 #------------------------------------------------------------------------------
    250 
    251 if __name__ == "__main__":
     387
    252388    def callback(result, extra):
    253389        print "callback", result, extra
    254390
    255     initializeSound("e:\\home\\vi\\tmp")
     391    preInitializeSound()
     392
     393    soundsPath = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)),
     394                                              "..", "..", "sounds"))
     395    print "soundsPath:", soundsPath
     396    initializeSound(soundsPath)
     397    startSound("notam.mp3", finishCallback = callback, extra= "notam.mp3")
     398    time.sleep(5)
    256399    startSound("malev.mp3", finishCallback = callback, extra="malev.mp3")
    257400    time.sleep(5)
     
    261404    time.sleep(5)
    262405    startSound("ding.wav", finishCallback = callback, extra="ding3.wav")
     406    time.sleep(5)
     407    startSound("dong.wav", finishCallback = callback, extra="dong3.wav")
    263408    time.sleep(50)
    264409
    265 #------------------------------------------------------------------------------
     410    finalizeSound()
     411
     412#------------------------------------------------------------------------------
Note: See TracChangeset for help on using the changeset viewer.