Index: src/mlx/acft.py
===================================================================
--- src/mlx/acft.py	(revision 153)
+++ src/mlx/acft.py	(revision 170)
@@ -230,5 +230,6 @@
         """Called when the flare has finished."""
         (flareTimeFromFS, flareTime) = self.flight.flareFinished(flareEnd,
-                                                                 flareEndFS)
+                                                                 flareEndFS,
+                                                                 tdRate)
         self.logger.message(self._aircraftState.timestamp,
                             "Flaretime: %.3f (from %s)" % \
Index: src/mlx/checks.py
===================================================================
--- src/mlx/checks.py	(revision 139)
+++ src/mlx/checks.py	(revision 170)
@@ -7,4 +7,5 @@
 import util
 from acars import ACARS
+from sound import startSound
 
 import time
@@ -178,4 +179,7 @@
                     logger.message(state.timestamp, "Spoilers deployed")
                     self._logged = True
+                    config = flight.config
+                    if config.enableSounds and config.speedbrakeAtTD:
+                        startSound(const.SOUND_SPEEDBRAKE)
             else:
                 self._spoilersExtension = state.spoilersExtension
Index: src/mlx/const.py
===================================================================
--- src/mlx/const.py	(revision 165)
+++ src/mlx/const.py	(revision 170)
@@ -4,5 +4,5 @@
 
 # The version of the program
-VERSION="0.04"
+VERSION="0.05"
 
 #-------------------------------------------------------------------------------
@@ -295,4 +295,47 @@
 SOUND_DING = "ding.wav"
 
+# Sound: notify
+SOUND_NOTIFY = "notify.wav"
+
+# Sound: NOTAM
+SOUND_NOTAM = "notam.mp3"
+
+# Sound: scream
+SOUND_SCREAM = "sikoly.mp3"
+
+# Sound: boarding
+SOUND_BOARDING = "board.mp3"
+
+# Sound: Malev theme
+SOUND_MALEV = "malev.mp3"
+
+# Sound: taxi: Boeing 737 NG
+SOUND_TAXI_BOEING737NG = "737taxi.mp3"
+
+# Sound: taxi: Boeing 767
+SOUND_TAXI_BOEING767 = "767taxi.mp3"
+
+# Sound: taxi: Fokker F70
+SOUND_TAXI_F70 = "F70taxi.mp3"
+
+# Sound: takeoff preparation request from the captain
+SOUND_CAPTAIN_TAKEOFF = "cpt_takeoff.mp3"
+
+# Sound: cruise
+SOUND_CRUISE = "TOC.mp3"
+
+# Sound: descent
+SOUND_DESCENT = "TOD.mp3"
+
+# Sound: applause
+SOUND_APPLAUSE = "taps.mp3"
+
+# Sound: speedbrake
+SOUND_SPEEDBRAKE = "speed.mp3"
+
+# Sound: taxi after landing
+SOUND_TAXIAFTERLAND = "TaxiAfterLand.mp3"
+
+
 #-------------------------------------------------------------------------------
 
Index: src/mlx/flight.py
===================================================================
--- src/mlx/flight.py	(revision 134)
+++ src/mlx/flight.py	(revision 170)
@@ -2,4 +2,6 @@
 
 #---------------------------------------------------------------------------------------
+
+from soundsched import SoundScheduler
 
 import const
@@ -32,4 +34,8 @@
         gui.resetFlightStatus()
 
+        self._soundScheduler = SoundScheduler(self)
+        self._pilotHotkeyPressed = False
+        self._checklistHotkeyPressed = False
+
         self.flareTimeFromFS = False
         self.entranceExam = False
@@ -59,4 +65,6 @@
         self._flareStartFS = None
 
+        self._tdRate = None
+
     @property
     def config(self):
@@ -103,4 +111,9 @@
         """Get the VRef speed of the flight."""
         return self._gui.vref
+
+    @property
+    def tdRate(self):
+        """Get the touchdown rate if known, None otherwise."""
+        return self._tdRate
 
     def handleState(self, oldState, currentState):
@@ -111,4 +124,8 @@
         if self.startFuel is None:
             self.startFuel = self.endFuel
+        
+        self._soundScheduler.schedule(currentState,
+                                      self._pilotHotkeyPressed)
+        self._pilotHotkeyPressed = False
 
     def setStage(self, timestamp, stage):
@@ -155,5 +172,5 @@
         self._flareStartFS = flareStartFS
 
-    def flareFinished(self, flareEnd, flareEndFS):
+    def flareFinished(self, flareEnd, flareEndFS, tdRate):
         """Called when the flare time has ended.
         
@@ -162,4 +179,5 @@
         - the flare time
         """
+        self._tdRate = tdRate
         if self.flareTimeFromFS:
             return (True, flareEndFS - self._flareStartFS)
@@ -176,4 +194,12 @@
         """Get the fleet and call the given callback."""
         self._gui.getFleetAsync(callback = callback, force = force)
+
+    def pilotHotkeyPressed(self):
+        """Called when the pilot hotkey is pressed."""
+        self._pilotHotkeyPressed = True
+
+    def checklistHotkeyPressed(self):
+        """Called when the checklist hotkey is pressed."""
+        self._checklistHotkeyPressed = True
 
     def _updateFlownDistance(self, currentState):
Index: src/mlx/fs.py
===================================================================
--- src/mlx/fs.py	(revision 152)
+++ src/mlx/fs.py	(revision 170)
@@ -109,5 +109,7 @@
         if messageLevel==const.MESSAGELEVEL_SOUND or \
            messageLevel==const.MESSAGELEVEL_BOTH:
-            startSound(const.SOUND_DING)
+            startSound(const.SOUND_NOTIFY
+                       if messageType==const.MESSAGETYPE_VISIBILITY
+                       else const.SOUND_DING)
         if (messageLevel==const.MESSAGELEVEL_FS or \
             messageLevel==const.MESSAGELEVEL_BOTH):
Index: src/mlx/gui/flight.py
===================================================================
--- src/mlx/gui/flight.py	(revision 164)
+++ src/mlx/gui/flight.py	(revision 170)
@@ -10,4 +10,5 @@
 from mlx.pirep import PIREP
 from mlx.i18n import xstr
+from mlx.sound import startSound
 
 import datetime
@@ -1423,4 +1424,5 @@
                                                   bookedFlight.departureICAO,
                                                   bookedFlight.arrivalICAO)
+            startSound(const.SOUND_NOTAM)
 
     def _notamsCallback(self, returned, result):
Index: src/mlx/gui/gui.py
===================================================================
--- src/mlx/gui/gui.py	(revision 168)
+++ src/mlx/gui/gui.py	(revision 170)
@@ -1130,3 +1130,7 @@
         """Handle the hotkeys."""
         if id==self._hotkeySetID:
-            print "gui.GUI._handleHotkeys", hotkeys
+            for index in hotkeys:
+                if index==0:
+                    self._flight.pilotHotkeyPressed()
+                else:
+                    self._flight.checklistHotkeyPressed()
Index: src/mlx/sound.py
===================================================================
--- src/mlx/sound.py	(revision 133)
+++ src/mlx/sound.py	(revision 170)
@@ -4,4 +4,5 @@
 
 import os
+import traceback
 
 #------------------------------------------------------------------------------
@@ -50,15 +51,15 @@
 
             self._requestCondition = threading.Condition()
-            self._requestedPaths = []
-            self._pendingAliases = []
+            self._requests = []
+            self._pending = []
             self._count = 0
 
             self.daemon = True
 
-        def requestSound(self, name):
+        def requestSound(self, name, finishCallback = None, extra = None):
             """Request the playback of the sound with the given name."""
             path = os.path.join(self._soundsDirectory, name)
             with self._requestCondition:
-                self._requestedPaths.append(path)
+                self._requests.append((path, (finishCallback, extra)))
                 self._requestCondition.notify()
 
@@ -72,8 +73,8 @@
             while True:
                 with self._requestCondition:
-                    if not self._requestedPaths:
-                        if self._pendingAliases:
-                            timeout = max(time.time() -
-                                          self._pendingAliases[0][0], 0.0)
+                    if not self._requests:
+                        if self._pending:
+                            timeout = max(time.time() - self._pending[0][0],
+                                          0.0)
                         else:
                             timeout = 10.0
@@ -81,18 +82,19 @@
                         self._requestCondition.wait(timeout)
 
-                    requestedPaths = []
-                    for path in self._requestedPaths:
-                        requestedPaths.append((path, self._count))
+                    requests = []
+                    for (path, finishData) in self._requests:
+                        requests.append((path, finishData, self._count))
                         self._count += 1
-                    self._requestedPaths = []
+                    self._requests = []
 
                     now = time.time()
-                    aliasesToClose = []
-                    while self._pendingAliases and \
-                          self._pendingAliases[0][0]<=now:
-                        aliasesToClose.append(self._pendingAliases[0][1])
-                        del self._pendingAliases[0]
+                    toClose = []
+                    while self._pending and \
+                          self._pending[0][0]<=now:
+                        toClose.append(self._pending[0][1])
+                        del self._pending[0]
 
-                for alias in aliasesToClose:
+                for (alias, (finishCallback, extra)) in toClose:
+                    success = True
                     try:
                         print "Closing", alias
@@ -101,6 +103,13 @@
                     except Exception, e:
                         print "Failed closing " + alias + ":", str(e)
+                        success = False
 
-                for (path, counter) in requestedPaths:
+                    if finishCallback is not None:
+                        try:
+                            finishCallback(success, extra)
+                        except:
+                            traceback.print_exc()
+
+                for (path, finishData, counter) in requests:
                     try:
                         alias = "mlxsound%d" % (counter,)
@@ -117,9 +126,15 @@
                         timeout = time.time() + length / 1000.0
                         with self._requestCondition:
-                            self._pendingAliases.append((timeout, alias))
-                            self._pendingAliases.sort()
+                            self._pending.append((timeout, (alias, finishData)))
+                            self._pending.sort()
                         print "Started to play", path
                     except Exception, e:
-                        print "Failed to start playing " + path + ":", str(e)
+                        print "Failed to start playing " + path + ":", str(e)                        
+                        (finishCallback, extra) = finishData
+                        if finishCallback is not None:
+                            try:
+                                finishCallback(None, extra)
+                            except:
+                                traceback.print_exc()
 
     _thread = None
@@ -132,10 +147,11 @@
         _thread.start()
 
-    def startSound(name):
+    def startSound(name, finishCallback = None, extra = None):
         """Start playing back the given sound.
         
         name should be the name of a sound file relative to the sound directory
         given in initializeSound."""
-        _thread.requestSound(name)
+        _thread.requestSound(name, finishCallback = finishCallback,
+                             extra = extra)
         
 #------------------------------------------------------------------------------
@@ -147,5 +163,5 @@
         pass
 
-    def startSound(name):
+    def startSound(name, finishCallback = None, extra = None):
         """Start playing back the given sound.
 
@@ -157,12 +173,15 @@
 
 if __name__ == "__main__":
+    def callback(result, extra):
+        print "callback", result, extra
+    
     initializeSound("e:\\home\\vi\\tmp")
-    startSound("malev.mp3")
+    startSound("malev.mp3", finishCallback = callback, extra="malev.mp3")
     time.sleep(5)
-    startSound("ding.wav")
+    startSound("ding.wav", finishCallback = callback, extra="ding1.wav")
     time.sleep(5)
-    startSound("ding.wav")
+    startSound("ding.wav", finishCallback = callback, extra="ding2.wav")
     time.sleep(5)
-    startSound("ding.wav")
+    startSound("ding.wav", finishCallback = callback, extra="ding3.wav")
     time.sleep(50)
 
Index: src/mlx/soundsched.py
===================================================================
--- src/mlx/soundsched.py	(revision 170)
+++ src/mlx/soundsched.py	(revision 170)
@@ -0,0 +1,212 @@
+# Module to schedule the sounds to be played
+
+#------------------------------------------------------------------------------
+
+from sound import startSound
+import const
+
+import threading
+
+#------------------------------------------------------------------------------
+
+class Sound(object):
+    """A sound (file) that should be played in certain circumstances."""
+
+    # Common lock for sounds
+    _lock = threading.Lock()
+
+    def __init__(self, name):
+        """Construct the sound object for the sound file with the given
+        name."""
+        self._name = name
+        
+        self._playing = 0
+
+    @property
+    def playing(self):
+        """Determine if sound is playing or not."""
+        return self._playing>0
+
+    def check(self, flight, state, pilotHotkeyPressed):
+        """Check if the file should be played, and if it should be, play it."""
+        if self.shouldPlay(flight, state, pilotHotkeyPressed):
+            with Sound._lock:
+                self._playing += 1
+            startSound(self._name, finishCallback = self._playbackDone)
+
+    def shouldPlay(self, flight, state, pilotHotkeyPressed):
+        """Determine if the sound should be played.
+
+        This default implementation returns False."""
+        return False
+
+    def _playbackDone(self, success, extra):
+        """Called when the playback of thee sound has finished (or failed)."""
+        if success is None:
+            print "Failed to start sound", self._name
+        elif not success:
+            print "Failed to finish sound", self._name
+        with Sound._lock:            
+            self._playing -= 1
+
+#------------------------------------------------------------------------------
+
+class ScreamSound(Sound):
+    """A screaming sound that is played under certain circumstance."""
+    def __init__(self):
+        """Construct the screaming sound."""
+        super(ScreamSound, self).__init__(const.SOUND_SCREAM)
+
+    def shouldPlay(self, flight, state, pilotHotkeyPressed):
+        """Determine if the sound should be played.
+
+        It should be played if it is not being played and the absolute value of
+        the vertical speed is greater than 6000 fpm."""
+        return not self.playing and abs(state.vs)>6000        
+
+#------------------------------------------------------------------------------
+
+class SimpleSound(Sound):
+    """A simple sound that should be played only once, in a certain flight
+    stage when the hotkey has been pressed or it is not the pilot who controls
+    the sounds.
+
+    If it is not the pilot who controls the sounds, it may be delayed relative
+    to the first detection of a certain flight stage."""
+    def __init__(self, name, stage, delay = 0.0, extraCondition = None,
+                 considerHotkey = True, previousSound = None):
+        """Construct the simple sound."""
+        super(SimpleSound, self).__init__(name)
+        
+        self._stage = stage
+        self._delay = delay
+        self._extraCondition = extraCondition
+        self._considerHotkey = considerHotkey
+        self._previousSound = previousSound
+
+        self._played = False
+        self._stageDetectionTime = None
+
+    def shouldPlay(self, flight, state, pilotHotkeyPressed):
+        """Determine if the sound should be played."""
+        if flight.stage!=self._stage or self._played or \
+           (self._previousSound is not None and
+            self._previousSound.playing):
+            return False
+
+        toPlay = False
+        if flight.config.pilotControlsSounds and self._considerHotkey:
+            toPlay = pilotHotkeyPressed
+        else:
+            if self._stageDetectionTime is None:
+                self._stageDetectionTime = state.timestamp
+            toPlay = state.timestamp>=(self._stageDetectionTime + self._delay)
+            if toPlay and self._extraCondition is not None:
+                toPlay = self._extraCondition(flight, state)
+
+        if toPlay:
+            self._played = True
+
+        return toPlay
+
+#------------------------------------------------------------------------------
+
+class TaxiSound(SimpleSound):
+    """The taxi sound.
+
+    It first plays the Malev theme song, then an aircraft-specific taxi
+    sound. The playback is started only, if the boarding sound is not being
+    played."""
+
+    _sounds = { const.AIRCRAFT_B736 : const.SOUND_TAXI_BOEING737NG,
+                const.AIRCRAFT_B737 : const.SOUND_TAXI_BOEING737NG,
+                const.AIRCRAFT_B738 : const.SOUND_TAXI_BOEING737NG,
+                const.AIRCRAFT_B762 : const.SOUND_TAXI_BOEING767,
+                const.AIRCRAFT_B763 : const.SOUND_TAXI_BOEING767,
+                const.AIRCRAFT_F70  : const.SOUND_TAXI_F70 }
+                    
+    def __init__(self, flight, boardingSound = None):
+        """Construct the taxi sound."""
+        super(TaxiSound, self).__init__(const.SOUND_MALEV,
+                                        const.STAGE_PUSHANDTAXI,
+                                        previousSound = boardingSound,
+                                        extraCondition = lambda _flight, state:
+                                        state.groundSpeed>5)
+
+        self._flight = flight
+
+    def _playbackDone(self, success, extra):
+        """Called when the playback is done.
+
+        It starts playing the aircraft type-specific taxi sound, if any."""
+        super(TaxiSound, self)._playbackDone(success, extra)
+        aircraftType = self._flight.aircraftType
+        sounds = TaxiSound._sounds
+        if aircraftType in sounds:
+            startSound(sounds[aircraftType])
+
+#------------------------------------------------------------------------------
+
+class TouchdownApplause(Sound):
+    """An applause sound that is played after a gentle touchdown."""
+    def __init__(self):
+        super(TouchdownApplause, self).__init__(const.SOUND_APPLAUSE)
+
+        self._touchdownTime = None
+        self._played = False
+        
+    def shouldPlay(self, flight, state, pilotHotkeyPressed):
+        """Determine if the sound should be played.
+
+        It should be played if we are 2 seconds after a gentle touchdown."""
+        if self._played or flight.tdRate is None:
+            return False
+
+        if self._touchdownTime is None:
+            self._touchdownTime = state.timestamp
+
+        if state.timestamp>=(self._touchdownTime + 2) and \
+           flight.tdRate<150:
+            self._played = True
+            return True
+        else:
+            return False
+        
+#------------------------------------------------------------------------------
+
+class SoundScheduler(object):
+    """A scheduler for the sounds."""
+    def __init__(self, flight):
+        """Construct the sound scheduler for the given flight."""
+        self._flight = flight
+        self._sounds = []
+
+        self._sounds.append(ScreamSound())
+
+        boardingSound = SimpleSound(const.SOUND_BOARDING,
+                                    const.STAGE_BOARDING,
+                                    delay = 10.0)
+        self._sounds.append(boardingSound)
+        self._sounds.append(TaxiSound(flight, boardingSound))
+        self._sounds.append(SimpleSound(const.SOUND_CAPTAIN_TAKEOFF,
+                                        const.STAGE_TAKEOFF,
+                                        extraCondition = lambda flight, state:
+                                        state.landingLightsOn or state.gs>80,
+                                        considerHotkey = False))
+        self._sounds.append(SimpleSound(const.SOUND_CRUISE, const.STAGE_CRUISE))
+        self._sounds.append(SimpleSound(const.SOUND_DESCENT, const.STAGE_DESCENT,
+                                        extraCondition = lambda flight, state:
+                                        state.altitude<15000))
+        self._sounds.append(TouchdownApplause())
+        self._sounds.append(SimpleSound(const.SOUND_TAXIAFTERLAND,
+                                        const.STAGE_TAXIAFTERLAND,
+                                        delay = 10.0))
+
+    def schedule(self, state, pilotHotkeyPressed):
+        """Schedule any sound, if needed."""
+        flight = self._flight
+        if flight.config.enableSounds:
+            for sound in self._sounds:
+                sound.check(flight, state, pilotHotkeyPressed)
+
+#------------------------------------------------------------------------------
