diff --git a/control/audio.py b/control/audio.py index 3019e0f..d11641a 100644 --- a/control/audio.py +++ b/control/audio.py @@ -7,6 +7,7 @@ import logging import json import os.path +import audioop from pyaudio import PyAudio, Stream logger = logging.getLogger(__name__) @@ -16,9 +17,27 @@ INTERVAL_QUEUE = 0.1 INTERVAL_STREAM = 0.0001 -class PlayEntry(NamedTuple): +class PlayEntry: + def __init__(self, wave_file: wave.Wave_read, stream: Stream, device_framerate): + self.wave_file = wave_file + self.stream = stream + self.cvstate = None + + self.device_framerate = device_framerate + self.sample_width = wave_file.getsampwidth() + self.channels = wave_file.getnchannels() + self.frame_rate = wave_file.getframerate() + + def get_actual_data(self, nframes): + data = self.wave_file.readframes(nframes) + new_data, self.cvstate = audioop.ratecv(data, self.sample_width, self.channels, self.frame_rate, + self.device_framerate, self.cvstate) + return new_data + wave_file: wave.Wave_read stream: Stream + cvstate: Optional['audioop.RatecvState'] + actual_size: int = None class AudioSystem: @@ -29,6 +48,8 @@ class AudioSystem: self.to_play = Queue() self.playing = {} self.audio = PyAudio() + self.default_output_device = self.audio.get_default_output_device_info() + self.default_sample_rate = int(self.default_output_device['defaultSampleRate']) self.scheduler = scheduler self.scheduler.enter(0, 2, self.start_streams) self.scheduler.enter(0, 1, self.fill_streams) @@ -58,13 +79,13 @@ class AudioSystem: stream = self.audio.open( format=self.audio.get_format_from_width(wave_file.getsampwidth()), channels=wave_file.getnchannels(), - rate=wave_file.getframerate(), + rate=self.default_sample_rate, output=True, ) entry_id = uuid.uuid4() while entry_id in self.playing: entry_id = uuid.uuid4() - self.playing[entry_id] = PlayEntry(wave_file, stream) + self.playing[entry_id] = PlayEntry(wave_file, stream, self.default_sample_rate) self.to_play.task_done() except Empty: pass @@ -82,7 +103,7 @@ class AudioSystem: if entry.stream.get_write_available() < CHUNK_SIZE: continue # not enough space in buffer, wait for next iteration try: - data = entry.wave_file.readframes(CHUNK_SIZE) + data = entry.get_actual_data(CHUNK_SIZE) except IOError as e: logger.warning('An IO Error occurred while reading WAV file', exc_info=e) data = b''