aboutsummaryrefslogtreecommitdiff
path: root/helpers/music_file.py
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2016-07-14 22:18:51 +0200
committerIsmaël Bouya <ismael.bouya@normalesup.org>2016-07-14 22:18:51 +0200
commit1b4b78f5b6df7182ac066fcc26a7b4f0e8586a47 (patch)
treeef2bddec7b9f09c614012ac6ee2588cd732242ee /helpers/music_file.py
parent71715c049145a074b0f2b8d90c8c8c47830323c3 (diff)
downloadMusicSampler-1b4b78f5b6df7182ac066fcc26a7b4f0e8586a47.tar.gz
MusicSampler-1b4b78f5b6df7182ac066fcc26a7b4f0e8586a47.tar.zst
MusicSampler-1b4b78f5b6df7182ac066fcc26a7b4f0e8586a47.zip
Some new features:
- gain function moved to helpers/__init__ - cleanup some unused functions - stop can now wait for fade_out to finish before returning - volume can be incremented - master volume
Diffstat (limited to 'helpers/music_file.py')
-rw-r--r--helpers/music_file.py38
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 @@
1import threading 1import threading
2import pydub 2import pydub
3import math
4import time 3import time
5from transitions.extensions import HierarchicalMachine as Machine 4from transitions.extensions import HierarchicalMachine as Machine
6 5
@@ -9,12 +8,14 @@ import sounddevice as sd
9import os.path 8import os.path
10 9
11from .lock import Lock 10from .lock import Lock
11from . import gain
12
12file_lock = Lock("file") 13file_lock = Lock("file")
13 14
14pyaudio = pa.PyAudio() 15pyaudio = pa.PyAudio()
15 16
16class MusicFile(Machine): 17class 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()