aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2016-07-16 16:12:06 +0200
committerIsmaël Bouya <ismael.bouya@normalesup.org>2016-07-16 16:12:06 +0200
commitccc8b4f206e254f70def3fe42c437bc58d474167 (patch)
tree2219de78998023cacc6ff5820a6cae4c48cb84b5
parent6f4944c18398a7482297bd1d80fcd4ee926270ae (diff)
downloadMusicSampler-ccc8b4f206e254f70def3fe42c437bc58d474167.tar.gz
MusicSampler-ccc8b4f206e254f70def3fe42c437bc58d474167.tar.zst
MusicSampler-ccc8b4f206e254f70def3fe42c437bc58d474167.zip
Put music effect in separate file
-rw-r--r--helpers/music_file.py76
1 files changed, 63 insertions, 13 deletions
diff --git a/helpers/music_file.py b/helpers/music_file.py
index 0bfb1ec..8b1c480 100644
--- a/helpers/music_file.py
+++ b/helpers/music_file.py
@@ -41,6 +41,7 @@ class MusicFile(Machine):
41 self.stream = None 41 self.stream = None
42 self.name = name or filename 42 self.name = name or filename
43 self.audio_segment = None 43 self.audio_segment = None
44 self.audio_segment_frame_width = 0
44 self.volume_factor = gain 45 self.volume_factor = gain
45 self.music_lock = Lock("music__" + filename) 46 self.music_lock = Lock("music__" + filename)
46 self.wait_event = threading.Event() 47 self.wait_event = threading.Event()
@@ -53,6 +54,7 @@ class MusicFile(Machine):
53 print("Loading « {} »".format(self.name)) 54 print("Loading « {} »".format(self.name))
54 db_gain = gain(self.volume_factor * 100) 55 db_gain = gain(self.volume_factor * 100)
55 self.audio_segment = pydub.AudioSegment.from_file(self.filename).set_frame_rate(44100).apply_gain(db_gain) 56 self.audio_segment = pydub.AudioSegment.from_file(self.filename).set_frame_rate(44100).apply_gain(db_gain)
57 self.audio_segment_frame_width = self.audio_segment.frame_width
56 self.sound_duration = self.audio_segment.duration_seconds 58 self.sound_duration = self.audio_segment.duration_seconds
57 except Exception as e: 59 except Exception as e:
58 print("failed to load « {} »: {}".format(self.name, e)) 60 print("failed to load « {} »: {}".format(self.name, e))
@@ -84,15 +86,21 @@ class MusicFile(Machine):
84 self.loop = loop 86 self.loop = loop
85 87
86 ms = int(start_at * 1000) 88 ms = int(start_at * 1000)
87 ms_fi = max(1, int(fade_in * 1000)) 89 ms_fi = int(fade_in * 1000)
88 with self.music_lock: 90 with self.music_lock:
89 self.current_audio_segment = (self.audio_segment + db_gain).fade(from_gain=-120, duration=ms_fi, start=ms) 91 self.current_audio_segment = (self.audio_segment + db_gain)
90 self.initial_frame = int(start_at * self.audio_segment.frame_rate) 92 self.current_frame = int(start_at * self.audio_segment.frame_rate)
93 if ms_fi > 0:
94 # 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)
96 self.current_frame_with_effect = 0
97 else:
98 self.a_s_with_effect = None
99
91 self.before_loaded_playing() 100 self.before_loaded_playing()
92 self.start_playing() 101 self.start_playing()
93 102
94 def before_loaded_playing(self): 103 def before_loaded_playing(self):
95 self.current_frame = self.initial_frame
96 with self.music_lock: 104 with self.music_lock:
97 segment = self.current_audio_segment 105 segment = self.current_audio_segment
98 106
@@ -121,32 +129,69 @@ class MusicFile(Machine):
121 129
122 def play_callback(self, out_data, frame_count, time_info, status_flags): 130 def play_callback(self, out_data, frame_count, time_info, status_flags):
123 with self.music_lock: 131 with self.music_lock:
124 audio_segment = self.current_audio_segment.get_sample_slice_data( 132 [data, nb_frames] = self.get_next_sample(frame_count)
125 start_sample=self.current_frame, 133 if nb_frames < frame_count:
126 end_sample=self.current_frame + frame_count
127 )
128 self.current_frame += frame_count
129 if len(audio_segment) == 0:
130 if self.is_loaded_playing() and self.loop != 0: 134 if self.is_loaded_playing() and self.loop != 0:
131 self.loop -= 1 135 self.loop -= 1
132 self.current_frame = self.initial_frame 136 self.current_frame = 0
133 else: 137 [new_data, new_nb_frames] = self.get_next_sample(frame_count - nb_frames)
138 data += new_data
139 nb_frames += new_nb_frames
140 elif nb_frames == 0:
134 raise sd.CallbackStop 141 raise sd.CallbackStop
135 142
136 out_data[:] = audio_segment.ljust(len(out_data), b'\0') 143 out_data[:] = data.ljust(len(out_data), b'\0')
144
145 def get_next_sample(self, frame_count):
146 fw = self.audio_segment_frame_width
147
148 data = b""
149 nb_frames = 0
150 if self.a_s_with_effect is not None:
151 segment = self.a_s_with_effect
152 max_val = int(segment.frame_count())
153
154 start_i = max(self.current_frame_with_effect, 0)
155 end_i = min(self.current_frame_with_effect + frame_count, max_val)
156
157 data += segment._data[(start_i * fw):(end_i * fw)]
158
159 frame_count = max(0, self.current_frame_with_effect + frame_count - max_val)
160
161 self.current_frame_with_effect += end_i - start_i
162 self.current_frame += end_i - start_i
163 nb_frames += end_i - start_i
164
165 if frame_count > 0:
166 self.a_s_with_effect = None
167
168 segment = self.current_audio_segment
169 max_val = int(segment.frame_count())
170
171 start_i = max(self.current_frame, 0)
172 end_i = min(self.current_frame + frame_count, max_val)
173 data += segment._data[(start_i * fw):(end_i * fw)]
174 nb_frames += end_i - start_i
175 self.current_frame += end_i - start_i
176
177 return [data, nb_frames]
137 178
138 def seek(self, value = 0, delta = False): 179 def seek(self, value = 0, delta = False):
139 # We don't want to do that while stopping 180 # We don't want to do that while stopping
140 if not (self.is_loaded_playing() or self.is_loaded_paused()): 181 if not (self.is_loaded_playing() or self.is_loaded_paused()):
141 return 182 return
142 with self.music_lock: 183 with self.music_lock:
184 self.a_s_with_effect = None
143 self.current_frame = max(0, int(delta) * self.current_frame + int(value * self.audio_segment.frame_rate)) 185 self.current_frame = max(0, int(delta) * self.current_frame + int(value * self.audio_segment.frame_rate))
186 # FIXME: si on fait un seek + delta, adapter le "loop"
144 187
145 def stop(self, fade_out = 0, wait = False): 188 def stop(self, fade_out = 0, wait = False):
146 if self.is_loaded_playing(): 189 if self.is_loaded_playing():
147 ms = int(self.sound_position * 1000) 190 ms = int(self.sound_position * 1000)
148 ms_fo = max(1, int(fade_out * 1000)) 191 ms_fo = max(1, int(fade_out * 1000))
149 192
193 # FIXME: stop fade_out puis seek -5 -> on abandonne le fade ? (cf
194 # commentaire dans fonction seek
150 with self.music_lock: 195 with self.music_lock:
151 self.current_audio_segment = self.current_audio_segment[:ms + ms_fo].fade_out(ms_fo) 196 self.current_audio_segment = self.current_audio_segment[:ms + ms_fo].fade_out(ms_fo)
152 self.stop_playing() 197 self.stop_playing()
@@ -162,8 +207,13 @@ class MusicFile(Machine):
162 207
163 new_audio_segment = self.current_audio_segment + db_gain 208 new_audio_segment = self.current_audio_segment + db_gain
164 209
210 new_a_s_with_effect = None
211 if self.a_s_with_effect is not None:
212 new_a_s_with_effect = self.a_s_with_effect + db_gain
213
165 with self.music_lock: 214 with self.music_lock:
166 self.current_audio_segment = new_audio_segment 215 self.current_audio_segment = new_audio_segment
216 self.a_s_with_effect = new_a_s_with_effect
167 217
168 def set_volume(self, value, delta = False): 218 def set_volume(self, value, delta = False):
169 [db_gain, self.volume] = gain(value + int(delta) * self.volume, self.volume) 219 [db_gain, self.volume] = gain(value + int(delta) * self.volume, self.volume)