Implemented recorder
This commit is contained in:
		@@ -1 +1,3 @@
 | 
				
			|||||||
from .led_stuff import BirbnetesIoTPlatformStatusDriver
 | 
					from .led_stuff import BirbnetesIoTPlatformStatusDriver
 | 
				
			||||||
 | 
					from .playback_stuff import BirbnetesIoTPlatformPlaybackDriver
 | 
				
			||||||
 | 
					from .record_stuff import BirbnetesIoTPlatformRecordDriver
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user