diff --git a/birbnetes_iot_platform_raspberry/__init__.py b/birbnetes_iot_platform_raspberry/__init__.py index d4bfa81..db5043c 100644 --- a/birbnetes_iot_platform_raspberry/__init__.py +++ b/birbnetes_iot_platform_raspberry/__init__.py @@ -1 +1,3 @@ -from .led_stuff import BirbnetesIoTPlatformStatusDriver \ No newline at end of file +from .led_stuff import BirbnetesIoTPlatformStatusDriver +from .playback_stuff import BirbnetesIoTPlatformPlaybackDriver +from .record_stuff import BirbnetesIoTPlatformRecordDriver diff --git a/birbnetes_iot_platform_raspberry/record_stuff.py b/birbnetes_iot_platform_raspberry/record_stuff.py index e69de29..19c280e 100644 --- a/birbnetes_iot_platform_raspberry/record_stuff.py +++ b/birbnetes_iot_platform_raspberry/record_stuff.py @@ -0,0 +1,88 @@ +from typing import Optional, Tuple +import alsaaudio +import tempfile +from queue import Queue, Empty +from threading import Thread +import wave + +SAMPLE_RATE = 44100 +MICROPHONE = 'default' +DEST_FOLDER = "/dev/shm/" + + +class SlicedRecorder(Thread): + + BYTES_PER_SAMPLE = 2 # 16bit + + def __init__(self, slice_size: float, sample_rate=SAMPLE_RATE, microphone=MICROPHONE, dest_folder=DEST_FOLDER): + super().__init__() + self._output_queue = Queue() + self._samples_per_slice = slice_size * sample_rate + self._dest_folder = dest_folder + self._sample_rate = sample_rate + self._inp = alsaaudio.PCM( + alsaaudio.PCM_CAPTURE, + alsaaudio.PCM_NORMAL, + microphone, + channels=1, + rate=sample_rate, + format=alsaaudio.PCM_FORMAT_S16_LE, # = 2bytes + periodsize=512 + ) + self._active = True + + def _request_new_file(self) -> Tuple[str, wave.Wave_write]: + _, record_path = tempfile.mkstemp(prefix="rec", suffix=".wav", dir=self._dest_folder) + wavefile = wave.open(record_path, 'wb') + wavefile.setnchannels(1) # mono + wavefile.setframerate(self._sample_rate) + wavefile.setsampwidth(self.BYTES_PER_SAMPLE) # 16bit + return record_path, wavefile + + def run(self): + + current_working_file_name, current_working_file = self._request_new_file() + current_samples_saved = 0 + + while self._active: + length, data = self._inp.read() + + if length > 0: # will be always larger than zero (except on error) + file_size_after_append = current_samples_saved + length + + if file_size_after_append > self._samples_per_slice: # Appending this would cause a too big slice + + samples_append_to_this_slice = self._samples_per_slice - current_samples_saved + + current_working_file.writeframes(data[:(samples_append_to_this_slice*self.BYTES_PER_SAMPLE)]) + + current_working_file.close() + self._output_queue.put(current_working_file_name) + current_working_file_name, current_working_file = self._request_new_file() + + current_working_file.writeframes(data[(samples_append_to_this_slice*self.BYTES_PER_SAMPLE):]) + current_samples_saved = length - samples_append_to_this_slice + + else: # it is safe to append, the resulting file will be smaller than the slice + current_working_file.writeframes(data) + current_samples_saved = file_size_after_append + + def get_recording(self, blocking: bool) -> Optional[str]: + try: + return self._output_queue.get(block=blocking) + except Empty: + return None + + def stop(self): + self._active = False + + +class BirbnetesIoTPlatformRecordDriver: + + @classmethod + def init(cls, sample_length_sec: float): + pass + + @classmethod + def get_recording(cls, blocking: bool = False): + pass