source: src/mlx/soundsched.py@ 305:ddc2dfec2080

Last change on this file since 305:ddc2dfec2080 was 298:24c67ec5cdca, checked in by István Váradi <ivaradi@…>, 12 years ago

Documented the non-GUI modules

File size: 10.0 KB
Line 
1
2from sound import startSound
3import const
4import fs
5
6import threading
7
8#------------------------------------------------------------------------------
9
10## @package mlx.soundsched
11#
12# Sound playback scheduling.
13#
14# The logger may play certain sound files when certain conditions hold. For
15# example, if the landing or the strobe lights are switched on during taxi, and
16# we go to the takeoff state, a voice says "Cabin crew, please take your seats
17# for takeoff". Some sounds are played only when the pilot presses a hotkey, if
18# the program is set up that way.
19#
20# Each sound is represented by an instance of a subclass of \ref Sound. If a
21# new \ref mlx.fs.AircraftState "aircraft state" comes in from the simulator,
22# the \ref Sound.shouldPlay "shouldPlay" functions of the registered sound
23# objects are called with that state, and if the function returns \c True, the
24# corresponding sound file is played.
25#
26# The \ref ChecklistScheduler class handles the playback of the checklists,
27# which are basically sequences of sound files played back one-by-one when a
28# certain hotkey is pressed.
29
30#------------------------------------------------------------------------------
31
32class Sound(object):
33 """A sound (file) that should be played in certain circumstances."""
34
35 # Common lock for sounds
36 _lock = threading.Lock()
37
38 def __init__(self, name):
39 """Construct the sound object for the sound file with the given
40 name."""
41 self._name = name
42
43 self._playing = 0
44
45 @property
46 def playing(self):
47 """Determine if sound is playing or not."""
48 return self._playing>0
49
50 def check(self, flight, state, pilotHotkeyPressed):
51 """Check if the file should be played, and if it should be, play it."""
52 if self.shouldPlay(flight, state, pilotHotkeyPressed):
53 with Sound._lock:
54 self._playing += 1
55 startSound(self._name, finishCallback = self._playbackDone)
56
57 def shouldPlay(self, flight, state, pilotHotkeyPressed):
58 """Determine if the sound should be played.
59
60 This default implementation returns False."""
61 return False
62
63 def _playbackDone(self, success, extra):
64 """Called when the playback of thee sound has finished (or failed)."""
65 if success is None:
66 print "Failed to start sound", self._name
67 elif not success:
68 print "Failed to finish sound", self._name
69 with Sound._lock:
70 self._playing -= 1
71
72#------------------------------------------------------------------------------
73
74class ScreamSound(Sound):
75 """A screaming sound that is played under certain circumstance."""
76 def __init__(self):
77 """Construct the screaming sound."""
78 super(ScreamSound, self).__init__(const.SOUND_SCREAM)
79
80 def shouldPlay(self, flight, state, pilotHotkeyPressed):
81 """Determine if the sound should be played.
82
83 It should be played if it is not being played and the absolute value of
84 the vertical speed is greater than 6000 fpm."""
85 return not self.playing and abs(state.vs)>6000
86
87#------------------------------------------------------------------------------
88
89class SimpleSound(Sound):
90 """A simple sound that should be played only once, in a certain flight
91 stage when the hotkey has been pressed or it is not the pilot who controls
92 the sounds.
93
94 If it is not the pilot who controls the sounds, it may be delayed relative
95 to the first detection of a certain flight stage."""
96 def __init__(self, name, stage, delay = 0.0, extraCondition = None,
97 considerHotkey = True, previousSound = None):
98 """Construct the simple sound."""
99 super(SimpleSound, self).__init__(name)
100
101 self._stage = stage
102 self._delay = delay
103 self._extraCondition = extraCondition
104 self._considerHotkey = considerHotkey
105 self._previousSound = previousSound
106
107 self._played = False
108 self._stageDetectionTime = None
109
110 def shouldPlay(self, flight, state, pilotHotkeyPressed):
111 """Determine if the sound should be played."""
112 if flight.stage!=self._stage or self._played or \
113 (self._previousSound is not None and
114 self._previousSound.playing):
115 return False
116
117 toPlay = False
118 if flight.config.pilotControlsSounds and self._considerHotkey:
119 toPlay = pilotHotkeyPressed
120 else:
121 if self._stageDetectionTime is None:
122 self._stageDetectionTime = state.timestamp
123 toPlay = state.timestamp>=(self._stageDetectionTime + self._delay)
124 if toPlay and self._extraCondition is not None:
125 toPlay = self._extraCondition(flight, state)
126
127 if toPlay:
128 self._played = True
129
130 return toPlay
131
132#------------------------------------------------------------------------------
133
134class TaxiSound(SimpleSound):
135 """The taxi sound.
136
137 It first plays the Malev theme song, then an aircraft-specific taxi
138 sound. The playback is started only, if the boarding sound is not being
139 played."""
140
141 _sounds = { const.AIRCRAFT_B736 : const.SOUND_TAXI_BOEING737NG,
142 const.AIRCRAFT_B737 : const.SOUND_TAXI_BOEING737NG,
143 const.AIRCRAFT_B738 : const.SOUND_TAXI_BOEING737NG,
144 const.AIRCRAFT_B738C : const.SOUND_TAXI_BOEING737NG,
145 const.AIRCRAFT_B762 : const.SOUND_TAXI_BOEING767,
146 const.AIRCRAFT_B763 : const.SOUND_TAXI_BOEING767,
147 const.AIRCRAFT_F70 : const.SOUND_TAXI_F70 }
148
149 def __init__(self, flight, boardingSound = None):
150 """Construct the taxi sound."""
151 super(TaxiSound, self).__init__(const.SOUND_MALEV,
152 const.STAGE_PUSHANDTAXI,
153 previousSound = boardingSound,
154 extraCondition = lambda _flight, state:
155 state.groundSpeed>5)
156
157 self._flight = flight
158
159 def _playbackDone(self, success, extra):
160 """Called when the playback is done.
161
162 It starts playing the aircraft type-specific taxi sound, if any."""
163 super(TaxiSound, self)._playbackDone(success, extra)
164 aircraftType = self._flight.aircraftType
165 sounds = TaxiSound._sounds
166 if aircraftType in sounds:
167 startSound(sounds[aircraftType])
168
169#------------------------------------------------------------------------------
170
171class TouchdownApplause(Sound):
172 """An applause sound that is played after a gentle touchdown."""
173 def __init__(self):
174 super(TouchdownApplause, self).__init__(const.SOUND_APPLAUSE)
175
176 self._touchdownTime = None
177 self._played = False
178
179 def shouldPlay(self, flight, state, pilotHotkeyPressed):
180 """Determine if the sound should be played.
181
182 It should be played if we are 2 seconds after a gentle touchdown."""
183 if self._played or flight.tdRate is None:
184 return False
185
186 if self._touchdownTime is None:
187 self._touchdownTime = state.timestamp
188
189 if state.timestamp>=(self._touchdownTime + 2) and \
190 flight.tdRate>=-150 and flight.tdRate<0:
191 self._played = True
192 return True
193 else:
194 return False
195
196#------------------------------------------------------------------------------
197
198class SoundScheduler(object):
199 """A scheduler for the sounds."""
200 def __init__(self, flight):
201 """Construct the sound scheduler for the given flight."""
202 self._flight = flight
203 self._sounds = []
204
205 self._sounds.append(ScreamSound())
206
207 boardingSound = SimpleSound(const.SOUND_BOARDING,
208 const.STAGE_BOARDING,
209 delay = 10.0)
210 self._sounds.append(boardingSound)
211 self._sounds.append(TaxiSound(flight, boardingSound))
212 self._sounds.append(SimpleSound(const.SOUND_CAPTAIN_TAKEOFF,
213 const.STAGE_TAKEOFF,
214 extraCondition = lambda flight, state:
215 state.landingLightsOn or state.groundSpeed>80,
216 considerHotkey = False))
217 self._sounds.append(SimpleSound(const.SOUND_CRUISE, const.STAGE_CRUISE))
218 self._sounds.append(SimpleSound(const.SOUND_DESCENT, const.STAGE_DESCENT,
219 extraCondition = lambda flight, state:
220 state.altitude<15000))
221 self._sounds.append(TouchdownApplause())
222 self._sounds.append(SimpleSound(const.SOUND_TAXIAFTERLAND,
223 const.STAGE_TAXIAFTERLAND,
224 delay = 10.0))
225
226 def schedule(self, state, pilotHotkeyPressed):
227 """Schedule any sound, if needed."""
228 flight = self._flight
229 if flight.config.enableSounds:
230 for sound in self._sounds:
231 sound.check(flight, state, pilotHotkeyPressed)
232
233#------------------------------------------------------------------------------
234
235class ChecklistScheduler(object):
236 """A scheduler for the checklist sounds"""
237 def __init__(self, flight):
238 """Construct the checklist scheduler for the given flight."""
239 self._flight = flight
240 self._checklist = None
241 self._itemIndex = 0
242
243 def hotkeyPressed(self):
244 """Called when the checklist hotkey is pressed."""
245 flight = self._flight
246 config = flight.config
247 if config.enableChecklists and flight.aircraftType is not None:
248 if self._checklist is None:
249 self._checklist = config.getChecklist(flight.aircraftType)
250
251 index = self._itemIndex
252 if index>=len(self._checklist):
253 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
254 "End of checklist")
255 else:
256 startSound(self._checklist[index])
257 self._itemIndex += 1
258
259#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.