source: src/mlx/soundsched.py@ 1094:a2a4b6462f53

python3
Last change on this file since 1094:a2a4b6462f53 was 1094:a2a4b6462f53, checked in by István Váradi <ivaradi@…>, 10 months ago

The taxi sounds can be started on pushback (re #367)

File size: 10.1 KB
Line 
1
2from .sound import startSound
3from . import const
4from . import 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 config = flight.config
152
153 super(TaxiSound, self).__init__(const.SOUND_MALEV,
154 const.STAGE_PUSHANDTAXI,
155 previousSound = boardingSound,
156 extraCondition = lambda _flight, state:
157 config.taxiSoundOnPushback or state.groundSpeed>5)
158
159 self._flight = flight
160
161 def _playbackDone(self, success, extra):
162 """Called when the playback is done.
163
164 It starts playing the aircraft type-specific taxi sound, if any."""
165 super(TaxiSound, self)._playbackDone(success, extra)
166 aircraftType = self._flight.aircraftType
167 sounds = TaxiSound._sounds
168 if aircraftType in sounds:
169 startSound(sounds[aircraftType])
170
171#------------------------------------------------------------------------------
172
173class TouchdownApplause(Sound):
174 """An applause sound that is played after a gentle touchdown."""
175 def __init__(self):
176 super(TouchdownApplause, self).__init__(const.SOUND_APPLAUSE)
177
178 self._touchdownTime = None
179 self._played = False
180
181 def shouldPlay(self, flight, state, pilotHotkeyPressed):
182 """Determine if the sound should be played.
183
184 It should be played if we are 2 seconds after a gentle touchdown."""
185 if self._played or flight.tdRate is None:
186 return False
187
188 if self._touchdownTime is None:
189 self._touchdownTime = state.timestamp
190
191 if state.timestamp>=(self._touchdownTime + 2) and \
192 flight.tdRate>=-150 and flight.tdRate<0:
193 self._played = True
194 return True
195 else:
196 return False
197
198#------------------------------------------------------------------------------
199
200class SoundScheduler(object):
201 """A scheduler for the sounds."""
202 def __init__(self, flight):
203 """Construct the sound scheduler for the given flight."""
204 self._flight = flight
205 self._sounds = []
206
207 self._sounds.append(ScreamSound())
208
209 boardingSound = SimpleSound(const.SOUND_BOARDING,
210 const.STAGE_BOARDING,
211 delay = 10.0)
212 self._sounds.append(boardingSound)
213 self._sounds.append(TaxiSound(flight, boardingSound))
214 self._sounds.append(SimpleSound(const.SOUND_CAPTAIN_TAKEOFF,
215 const.STAGE_TAKEOFF,
216 extraCondition = lambda flight, state:
217 state.onTheGround,
218 considerHotkey = False))
219 self._sounds.append(SimpleSound(const.SOUND_CRUISE, const.STAGE_CRUISE))
220 self._sounds.append(SimpleSound(const.SOUND_DESCENT, const.STAGE_DESCENT,
221 extraCondition = lambda flight, state:
222 state.altitude<15000))
223 self._sounds.append(TouchdownApplause())
224 self._sounds.append(SimpleSound(const.SOUND_TAXIAFTERLAND,
225 const.STAGE_TAXIAFTERLAND,
226 delay = 10.0))
227
228 def schedule(self, state, pilotHotkeyPressed):
229 """Schedule any sound, if needed."""
230 flight = self._flight
231 if flight.config.enableSounds:
232 for sound in self._sounds:
233 sound.check(flight, state, pilotHotkeyPressed)
234
235#------------------------------------------------------------------------------
236
237class ChecklistScheduler(object):
238 """A scheduler for the checklist sounds"""
239 def __init__(self, flight):
240 """Construct the checklist scheduler for the given flight."""
241 self._flight = flight
242 self._checklist = None
243 self._itemIndex = 0
244
245 def hotkeyPressed(self):
246 """Called when the checklist hotkey is pressed."""
247 flight = self._flight
248 config = flight.config
249 if config.enableChecklists and flight.aircraftType is not None:
250 if self._checklist is None:
251 self._checklist = config.getChecklist(flight.aircraftType)
252
253 index = self._itemIndex
254 if index>=len(self._checklist):
255 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
256 "End of checklist")
257 else:
258 startSound(self._checklist[index])
259 self._itemIndex += 1
260
261#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.