]> git.immae.eu Git - perso/Immae/Projets/Python/MusicSampler.git/blob - music_sampler/mixer.py
Improve error message and handling
[perso/Immae/Projets/Python/MusicSampler.git] / music_sampler / mixer.py
1 import sounddevice as sd
2 import audioop
3 import time
4
5 from .helpers import Config, error_print
6
7 sample_width = Config.sample_width
8
9 def sample_width_to_dtype():
10 if sample_width == 1 or sample_width == 2 or sample_width == 4:
11 return 'int' + str(8*sample_width)
12 else:
13 error_print("Unknown sample width, setting default value 2.")
14 Config.sample_width = 2
15 return 'int16'
16
17 def _latency(latency):
18 if latency == "high" or latency == "low":
19 return latency
20 else:
21 return float(latency)
22
23 class Mixer:
24 def __init__(self):
25 self.stream = sd.RawOutputStream(
26 samplerate=Config.frame_rate,
27 channels=Config.channels,
28 dtype=sample_width_to_dtype(),
29 latency=_latency(Config.latency),
30 blocksize=Config.blocksize,
31 callback=self.play_callback)
32 self.open_files = []
33
34 def add_file(self, music_file):
35 if music_file not in self.open_files:
36 self.open_files.append(music_file)
37 self.start()
38
39 def remove_file(self, music_file):
40 if music_file in self.open_files:
41 self.open_files.remove(music_file)
42 if len(self.open_files) == 0:
43 self.stop()
44
45 def stop(self):
46 self.stream.stop()
47
48 def start(self):
49 self.stream.start()
50
51 def play_callback(self, out_data, frame_count, time_info, status_flags):
52 out_data_length = len(out_data)
53 empty_data = b"\0" * out_data_length
54 data = b"\0" * out_data_length
55
56 for open_file in self.open_files:
57 file_data = open_file.play_callback(out_data_length, frame_count)
58
59 if data == empty_data:
60 data = file_data
61 elif file_data != empty_data:
62 data = audioop.add(data, file_data, sample_width)
63
64 out_data[:] = data
65