diff options
Diffstat (limited to 'helpers/music_file.py')
-rw-r--r-- | helpers/music_file.py | 38 |
1 files changed, 23 insertions, 15 deletions
diff --git a/helpers/music_file.py b/helpers/music_file.py index e6a340d..6a28d62 100644 --- a/helpers/music_file.py +++ b/helpers/music_file.py | |||
@@ -1,6 +1,5 @@ | |||
1 | import threading | 1 | import threading |
2 | import pydub | 2 | import pydub |
3 | import math | ||
4 | import time | 3 | import time |
5 | from transitions.extensions import HierarchicalMachine as Machine | 4 | from transitions.extensions import HierarchicalMachine as Machine |
6 | 5 | ||
@@ -9,12 +8,14 @@ import sounddevice as sd | |||
9 | import os.path | 8 | import os.path |
10 | 9 | ||
11 | from .lock import Lock | 10 | from .lock import Lock |
11 | from . import gain | ||
12 | |||
12 | file_lock = Lock("file") | 13 | file_lock = Lock("file") |
13 | 14 | ||
14 | pyaudio = pa.PyAudio() | 15 | pyaudio = pa.PyAudio() |
15 | 16 | ||
16 | class MusicFile(Machine): | 17 | class MusicFile(Machine): |
17 | def __init__(self, filename, name = None, gain = 1): | 18 | def __init__(self, filename, mapping, name = None, gain = 1): |
18 | states = [ | 19 | states = [ |
19 | 'initial', | 20 | 'initial', |
20 | 'loading', | 21 | 'loading', |
@@ -34,11 +35,13 @@ class MusicFile(Machine): | |||
34 | 35 | ||
35 | Machine.__init__(self, states=states, transitions=transitions, initial='initial') | 36 | Machine.__init__(self, states=states, transitions=transitions, initial='initial') |
36 | 37 | ||
38 | self.volume = 100 | ||
39 | self.mapping = mapping | ||
37 | self.filename = filename | 40 | self.filename = filename |
38 | self.stream = None | 41 | self.stream = None |
39 | self.name = name or filename | 42 | self.name = name or filename |
40 | self.audio_segment = None | 43 | self.audio_segment = None |
41 | self.gain = gain | 44 | self.volume_factor = gain |
42 | self.music_lock = Lock("music__" + filename) | 45 | self.music_lock = Lock("music__" + filename) |
43 | self.wait_event = threading.Event() | 46 | self.wait_event = threading.Event() |
44 | 47 | ||
@@ -48,8 +51,8 @@ class MusicFile(Machine): | |||
48 | with file_lock: | 51 | with file_lock: |
49 | try: | 52 | try: |
50 | print("Loading « {} »".format(self.name)) | 53 | print("Loading « {} »".format(self.name)) |
51 | volume_factor = 20 * math.log10(self.gain) | 54 | db_gain = gain(self.volume_factor * 100) |
52 | self.audio_segment = pydub.AudioSegment.from_file(self.filename).set_frame_rate(44100).apply_gain(volume_factor) | 55 | self.audio_segment = pydub.AudioSegment.from_file(self.filename).set_frame_rate(44100).apply_gain(db_gain) |
53 | self.sound_duration = self.audio_segment.duration_seconds | 56 | self.sound_duration = self.audio_segment.duration_seconds |
54 | except Exception as e: | 57 | except Exception as e: |
55 | print("failed to load « {} »: {}".format(self.name, e)) | 58 | print("failed to load « {} »: {}".format(self.name, e)) |
@@ -76,11 +79,13 @@ class MusicFile(Machine): | |||
76 | return 0 | 79 | return 0 |
77 | 80 | ||
78 | def play(self, fade_in = 0, volume = 100, start_at = 0): | 81 | def play(self, fade_in = 0, volume = 100, start_at = 0): |
79 | self.db_gain = self.volume_to_gain(volume) | 82 | db_gain = gain(volume) + self.mapping.master_gain |
83 | self.volume = volume | ||
84 | |||
80 | ms = int(start_at * 1000) | 85 | ms = int(start_at * 1000) |
81 | ms_fi = max(1, int(fade_in * 1000)) | 86 | ms_fi = max(1, int(fade_in * 1000)) |
82 | with self.music_lock: | 87 | with self.music_lock: |
83 | self.current_audio_segment = (self.audio_segment + self.db_gain).fade(from_gain=-120, duration=ms_fi, start=ms) | 88 | self.current_audio_segment = (self.audio_segment + db_gain).fade(from_gain=-120, duration=ms_fi, start=ms) |
84 | self.before_loaded_playing(initial_frame = int(start_at * self.audio_segment.frame_rate)) | 89 | self.before_loaded_playing(initial_frame = int(start_at * self.audio_segment.frame_rate)) |
85 | self.start_playing() | 90 | self.start_playing() |
86 | 91 | ||
@@ -124,7 +129,7 @@ class MusicFile(Machine): | |||
124 | 129 | ||
125 | out_data[:] = audio_segment.ljust(len(out_data), b'\0') | 130 | out_data[:] = audio_segment.ljust(len(out_data), b'\0') |
126 | 131 | ||
127 | def stop(self, fade_out = 0): | 132 | def stop(self, fade_out = 0, wait = False): |
128 | if self.is_loaded_playing(): | 133 | if self.is_loaded_playing(): |
129 | ms = int(self.sound_position * 1000) | 134 | ms = int(self.sound_position * 1000) |
130 | ms_fo = max(1, int(fade_out * 1000)) | 135 | ms_fo = max(1, int(fade_out * 1000)) |
@@ -132,22 +137,25 @@ class MusicFile(Machine): | |||
132 | with self.music_lock: | 137 | with self.music_lock: |
133 | self.current_audio_segment = self.current_audio_segment[:ms + ms_fo].fade_out(ms_fo) | 138 | self.current_audio_segment = self.current_audio_segment[:ms + ms_fo].fade_out(ms_fo) |
134 | self.stop_playing() | 139 | self.stop_playing() |
140 | if wait: | ||
141 | self.wait_end() | ||
135 | else: | 142 | else: |
136 | self.stop_playing() | 143 | self.stop_playing() |
137 | self.stopped() | 144 | self.stopped() |
138 | 145 | ||
139 | def set_volume(self, value): | 146 | def set_gain(self, db_gain): |
140 | if self.is_loaded_stopped(): | 147 | if not self.is_not_stopped(): |
141 | return | 148 | return |
142 | 149 | ||
143 | db_gain = self.volume_to_gain(value) | 150 | new_audio_segment = self.current_audio_segment + db_gain |
144 | new_audio_segment = self.current_audio_segment + (db_gain - self.db_gain) | 151 | |
145 | self.db_gain = db_gain | ||
146 | with self.music_lock: | 152 | with self.music_lock: |
147 | self.current_audio_segment = new_audio_segment | 153 | self.current_audio_segment = new_audio_segment |
148 | 154 | ||
149 | def volume_to_gain(self, volume): | 155 | def set_volume(self, value, add = False): |
150 | return 20 * math.log10(max(volume, 0.0001) / 100) | 156 | [db_gain, self.volume] = gain(value + int(add) * self.volume, self.volume) |
157 | |||
158 | self.set_gain(db_gain) | ||
151 | 159 | ||
152 | def wait_end(self): | 160 | def wait_end(self): |
153 | self.wait_event.clear() | 161 | self.wait_event.clear() |