diff options
Diffstat (limited to 'helpers/music_file.py')
-rw-r--r-- | helpers/music_file.py | 109 |
1 files changed, 82 insertions, 27 deletions
diff --git a/helpers/music_file.py b/helpers/music_file.py index 56060bd..54a3fdc 100644 --- a/helpers/music_file.py +++ b/helpers/music_file.py | |||
@@ -14,25 +14,62 @@ from .mixer import Mixer | |||
14 | file_lock = Lock("file") | 14 | file_lock = Lock("file") |
15 | 15 | ||
16 | class MusicFile(Machine): | 16 | class MusicFile(Machine): |
17 | def __init__(self, filename, mapping, name = None, gain = 1): | 17 | def __init__(self, filename, mapping, name=None, gain=1): |
18 | states = [ | 18 | states = [ |
19 | 'initial', | 19 | 'initial', |
20 | 'loading', | 20 | 'loading', |
21 | 'failed', | 21 | 'failed', |
22 | { 'name': 'loaded', 'children': ['stopped', 'playing', 'paused', 'stopping'] } | 22 | { |
23 | 'name': 'loaded', | ||
24 | 'children': ['stopped', 'playing', 'paused', 'stopping'] | ||
25 | } | ||
23 | ] | 26 | ] |
24 | transitions = [ | 27 | transitions = [ |
25 | { 'trigger': 'load', 'source': 'initial', 'dest': 'loading'}, | 28 | { |
26 | { 'trigger': 'fail', 'source': 'loading', 'dest': 'failed'}, | 29 | 'trigger': 'load', |
27 | { 'trigger': 'success', 'source': 'loading', 'dest': 'loaded_stopped'}, | 30 | 'source': 'initial', |
28 | { 'trigger': 'start_playing', 'source': 'loaded_stopped', 'dest': 'loaded_playing'}, | 31 | 'dest': 'loading' |
29 | { 'trigger': 'pause', 'source': 'loaded_playing', 'dest': 'loaded_paused'}, | 32 | }, |
30 | { 'trigger': 'unpause', 'source': 'loaded_paused', 'dest': 'loaded_playing'}, | 33 | { |
31 | { 'trigger': 'stop_playing', 'source': ['loaded_playing','loaded_paused'], 'dest': 'loaded_stopping'}, | 34 | 'trigger': 'fail', |
32 | { 'trigger': 'stopped', 'source': 'loaded_stopping', 'dest': 'loaded_stopped', 'after': 'trigger_stopped_events'} | 35 | 'source': 'loading', |
36 | 'dest': 'failed' | ||
37 | }, | ||
38 | { | ||
39 | 'trigger': 'success', | ||
40 | 'source': 'loading', | ||
41 | 'dest': 'loaded_stopped' | ||
42 | }, | ||
43 | { | ||
44 | 'trigger': 'start_playing', | ||
45 | 'source': 'loaded_stopped', | ||
46 | 'dest': 'loaded_playing' | ||
47 | }, | ||
48 | { | ||
49 | 'trigger': 'pause', | ||
50 | 'source': 'loaded_playing', | ||
51 | 'dest': 'loaded_paused' | ||
52 | }, | ||
53 | { | ||
54 | 'trigger': 'unpause', | ||
55 | 'source': 'loaded_paused', | ||
56 | 'dest': 'loaded_playing' | ||
57 | }, | ||
58 | { | ||
59 | 'trigger': 'stop_playing', | ||
60 | 'source': ['loaded_playing','loaded_paused'], | ||
61 | 'dest': 'loaded_stopping' | ||
62 | }, | ||
63 | { | ||
64 | 'trigger': 'stopped', | ||
65 | 'source': 'loaded_stopping', | ||
66 | 'dest': 'loaded_stopped', | ||
67 | 'after': 'trigger_stopped_events' | ||
68 | } | ||
33 | ] | 69 | ] |
34 | 70 | ||
35 | Machine.__init__(self, states=states, transitions=transitions, initial='initial') | 71 | Machine.__init__(self, states=states, |
72 | transitions=transitions, initial='initial') | ||
36 | 73 | ||
37 | self.volume = 100 | 74 | self.volume = 100 |
38 | self.mapping = mapping | 75 | self.mapping = mapping |
@@ -45,7 +82,7 @@ class MusicFile(Machine): | |||
45 | self.wait_event = threading.Event() | 82 | self.wait_event = threading.Event() |
46 | self.db_gain = 0 | 83 | self.db_gain = 0 |
47 | 84 | ||
48 | threading.Thread(name = "MSMusicLoad", target = self.load).start() | 85 | threading.Thread(name="MSMusicLoad", target=self.load).start() |
49 | 86 | ||
50 | def on_enter_loading(self): | 87 | def on_enter_loading(self): |
51 | with file_lock: | 88 | with file_lock: |
@@ -53,7 +90,12 @@ class MusicFile(Machine): | |||
53 | debug_print("Loading « {} »".format(self.name)) | 90 | debug_print("Loading « {} »".format(self.name)) |
54 | self.mixer = self.mapping.mixer or Mixer() | 91 | self.mixer = self.mapping.mixer or Mixer() |
55 | initial_db_gain = gain(self.initial_volume_factor * 100) | 92 | initial_db_gain = gain(self.initial_volume_factor * 100) |
56 | self.audio_segment = pydub.AudioSegment.from_file(self.filename).set_frame_rate(Config.frame_rate).set_channels(Config.channels).set_sample_width(Config.sample_width).apply_gain(initial_db_gain) | 93 | self.audio_segment = pydub.AudioSegment \ |
94 | .from_file(self.filename) \ | ||
95 | .set_frame_rate(Config.frame_rate) \ | ||
96 | .set_channels(Config.channels) \ | ||
97 | .set_sample_width(Config.sample_width) \ | ||
98 | .apply_gain(initial_db_gain) | ||
57 | self.audio_segment_frame_width = self.audio_segment.frame_width | 99 | self.audio_segment_frame_width = self.audio_segment.frame_width |
58 | self.sound_duration = self.audio_segment.duration_seconds | 100 | self.sound_duration = self.audio_segment.duration_seconds |
59 | except Exception as e: | 101 | except Exception as e: |
@@ -80,7 +122,7 @@ class MusicFile(Machine): | |||
80 | else: | 122 | else: |
81 | return 0 | 123 | return 0 |
82 | 124 | ||
83 | def play(self, fade_in = 0, volume = 100, loop = 0, start_at = 0): | 125 | def play(self, fade_in=0, volume=100, loop=0, start_at=0): |
84 | self.db_gain = gain(volume) + self.mapping.master_gain | 126 | self.db_gain = gain(volume) + self.mapping.master_gain |
85 | self.volume = volume | 127 | self.volume = volume |
86 | self.loop = loop | 128 | self.loop = loop |
@@ -92,7 +134,9 @@ class MusicFile(Machine): | |||
92 | self.current_frame = int(start_at * self.audio_segment.frame_rate) | 134 | self.current_frame = int(start_at * self.audio_segment.frame_rate) |
93 | if ms_fi > 0: | 135 | if ms_fi > 0: |
94 | # FIXME: apply it to repeated when looping? | 136 | # FIXME: apply it to repeated when looping? |
95 | self.a_s_with_effect = self.current_audio_segment[ms:ms+ms_fi].fade_in(ms_fi) | 137 | self.a_s_with_effect = self \ |
138 | .current_audio_segment[ms : ms+ms_fi] \ | ||
139 | .fade_in(ms_fi) | ||
96 | self.current_frame_with_effect = 0 | 140 | self.current_frame_with_effect = 0 |
97 | else: | 141 | else: |
98 | self.a_s_with_effect = None | 142 | self.a_s_with_effect = None |
@@ -122,12 +166,15 @@ class MusicFile(Machine): | |||
122 | if self.is_loaded_playing() and self.loop != 0: | 166 | if self.is_loaded_playing() and self.loop != 0: |
123 | self.loop -= 1 | 167 | self.loop -= 1 |
124 | self.current_frame = 0 | 168 | self.current_frame = 0 |
125 | [new_data, new_nb_frames] = self.get_next_sample(frame_count - nb_frames) | 169 | [new_data, new_nb_frames] = self.get_next_sample( |
170 | frame_count - nb_frames) | ||
126 | data += new_data | 171 | data += new_data |
127 | nb_frames += new_nb_frames | 172 | nb_frames += new_nb_frames |
128 | elif nb_frames == 0: | 173 | elif nb_frames == 0: |
129 | # FIXME: too slow | 174 | # FIXME: too slow |
130 | threading.Thread(name = "MSFinishedCallback", target=self.finished_callback).start() | 175 | threading.Thread( |
176 | name="MSFinishedCallback", | ||
177 | target=self.finished_callback).start() | ||
131 | 178 | ||
132 | return data.ljust(out_data_length, b'\0') | 179 | return data.ljust(out_data_length, b'\0') |
133 | 180 | ||
@@ -141,11 +188,13 @@ class MusicFile(Machine): | |||
141 | max_val = int(segment.frame_count()) | 188 | max_val = int(segment.frame_count()) |
142 | 189 | ||
143 | start_i = max(self.current_frame_with_effect, 0) | 190 | start_i = max(self.current_frame_with_effect, 0) |
144 | end_i = min(self.current_frame_with_effect + frame_count, max_val) | 191 | end_i = min(self.current_frame_with_effect + frame_count, max_val) |
145 | 192 | ||
146 | data += segment._data[(start_i * fw):(end_i * fw)] | 193 | data += segment._data[start_i*fw : end_i*fw] |
147 | 194 | ||
148 | frame_count = max(0, self.current_frame_with_effect + frame_count - max_val) | 195 | frame_count = max( |
196 | 0, | ||
197 | self.current_frame_with_effect + frame_count - max_val) | ||
149 | 198 | ||
150 | self.current_frame_with_effect += end_i - start_i | 199 | self.current_frame_with_effect += end_i - start_i |
151 | self.current_frame += end_i - start_i | 200 | self.current_frame += end_i - start_i |
@@ -159,7 +208,7 @@ class MusicFile(Machine): | |||
159 | 208 | ||
160 | start_i = max(self.current_frame, 0) | 209 | start_i = max(self.current_frame, 0) |
161 | end_i = min(self.current_frame + frame_count, max_val) | 210 | end_i = min(self.current_frame + frame_count, max_val) |
162 | data += segment._data[(start_i * fw):(end_i * fw)] | 211 | data += segment._data[start_i*fw : end_i*fw] |
163 | nb_frames += end_i - start_i | 212 | nb_frames += end_i - start_i |
164 | self.current_frame += end_i - start_i | 213 | self.current_frame += end_i - start_i |
165 | 214 | ||
@@ -167,21 +216,25 @@ class MusicFile(Machine): | |||
167 | 216 | ||
168 | return [data, nb_frames] | 217 | return [data, nb_frames] |
169 | 218 | ||
170 | def seek(self, value = 0, delta = False): | 219 | def seek(self, value=0, delta=False): |
171 | # We don't want to do that while stopping | 220 | # We don't want to do that while stopping |
172 | if not (self.is_loaded_playing() or self.is_loaded_paused()): | 221 | if not (self.is_loaded_playing() or self.is_loaded_paused()): |
173 | return | 222 | return |
174 | with self.music_lock: | 223 | with self.music_lock: |
175 | self.a_s_with_effect = None | 224 | self.a_s_with_effect = None |
176 | self.current_frame = max(0, int(delta) * self.current_frame + int(value * self.audio_segment.frame_rate)) | 225 | self.current_frame = max( |
226 | 0, | ||
227 | int(delta) * self.current_frame | ||
228 | + int(value * self.audio_segment.frame_rate)) | ||
177 | # FIXME: si on fait un seek + delta, adapter le "loop" | 229 | # FIXME: si on fait un seek + delta, adapter le "loop" |
178 | 230 | ||
179 | def stop(self, fade_out = 0, wait = False): | 231 | def stop(self, fade_out=0, wait=False): |
180 | if self.is_loaded_playing(): | 232 | if self.is_loaded_playing(): |
181 | ms = int(self.sound_position * 1000) | 233 | ms = int(self.sound_position * 1000) |
182 | ms_fo = max(1, int(fade_out * 1000)) | 234 | ms_fo = max(1, int(fade_out * 1000)) |
183 | 235 | ||
184 | new_audio_segment = self.current_audio_segment[:ms + ms_fo].fade_out(ms_fo) | 236 | new_audio_segment = self.current_audio_segment[: ms+ms_fo] \ |
237 | .fade_out(ms_fo) | ||
185 | with self.music_lock: | 238 | with self.music_lock: |
186 | self.current_audio_segment = new_audio_segment | 239 | self.current_audio_segment = new_audio_segment |
187 | self.stop_playing() | 240 | self.stop_playing() |
@@ -195,8 +248,10 @@ class MusicFile(Machine): | |||
195 | self.db_gain += db_gain | 248 | self.db_gain += db_gain |
196 | self.volume_factor = 10 ** (self.db_gain / 20) | 249 | self.volume_factor = 10 ** (self.db_gain / 20) |
197 | 250 | ||
198 | def set_volume(self, value, delta = False): | 251 | def set_volume(self, value, delta=False): |
199 | [db_gain, self.volume] = gain(value + int(delta) * self.volume, self.volume) | 252 | [db_gain, self.volume] = gain( |
253 | value + int(delta) * self.volume, | ||
254 | self.volume) | ||
200 | 255 | ||
201 | self.set_gain(db_gain) | 256 | self.set_gain(db_gain) |
202 | 257 | ||