class AudioProcessService: def start(self, config_lock, notification_queue_in, notification_queue_out, audio_queue, py_audio): self.logger = logging.getLogger(__name__) self._config_lock = config_lock self._notification_queue_in = QueueWrapper(notification_queue_in) self._notification_queue_out = QueueWrapper(notification_queue_out) self._audio_queue = QueueWrapper(audio_queue) self.audio_buffer_queue = QueueWrapper(Queue(2)) self._py_audio = py_audio self.stream = None self.init_audio_service(show_output=True) while True: try: self.audio_service_routine() self._fps_limiter.fps_limiter() except KeyboardInterrupt: break def init_audio_service(self, show_output=False): try: # Initial config load. ConfigService.instance(self._config_lock).load_config() self._config = ConfigService.instance(self._config_lock).config # Init FPS Limiter. self._fps_limiter = FPSLimiter(120) self._skip_routine = False self._devices = AudioInfo.get_audio_devices(self._py_audio) self.log_output(show_output, logging.INFO, "Found the following audio sources:") # Select the audio device you want to use. selected_device_list_index = 0 try: mic_id = self._config["general_settings"]["device_id"] if mic_id != "no_mic": selected_device_list_index = int(mic_id) except Exception as e: self.logger.exception(f"Could not parse audio id: {e}") # Check if the index is inside the list. self.selected_device = None # For each audio device, add to list of devices. for current_audio_device in self._devices: if current_audio_device.id == selected_device_list_index: self.selected_device = current_audio_device self.logger.debug(f"Selected Device: {self.selected_device}") # Could not find a mic with the selected mic id, so I will use the first device I found. if self.selected_device is None: self.log_output( show_output, logging.ERROR, "********************************************************") self.log_output( show_output, logging.ERROR, "* Error *") self.log_output( show_output, logging.ERROR, "********************************************************") self.log_output( show_output, logging.ERROR, f"Could not find the mic with the id: {selected_device_list_index}" ) self.log_output(show_output, logging.ERROR, "Using the first mic as fallback.") self.log_output( show_output, logging.ERROR, "Please change the id of the mic inside the config.") self.selected_device = self._devices[0] self._device_rate = self._config["general_settings"][ "default_sample_rate"] self._frames_per_buffer = self._config["general_settings"][ "frames_per_buffer"] self.n_fft_bins = self._config["general_settings"]["n_fft_bins"] self.log_output( show_output, logging.INFO, f"Selected Device: {self.selected_device.to_string()}") # Init Timer self.start_time_1 = time() self.ten_seconds_counter_1 = time() self.start_time_2 = time() self.ten_seconds_counter_2 = time() self._dsp = DSP(self._config) self.audio = np.empty((self._frames_per_buffer), dtype="int16") # Reinit buffer queue self.audio_buffer_queue = QueueWrapper(Queue(2)) # callback function to stream audio, another thread. def callback(in_data, frame_count, time_info, status): if self._skip_routine: return (self.audio, pyaudio.paContinue) self.audio_buffer_queue.put_none_blocking(in_data) self.end_time_1 = time() if time() - self.ten_seconds_counter_1 > 10: self.ten_seconds_counter_1 = time() time_dif = self.end_time_1 - self.start_time_1 fps = 1 / time_dif self.logger.info(f"Callback | FPS: {fps:.2f}") self.start_time_1 = time() return (self.audio, pyaudio.paContinue) self.log_output(show_output, logging.DEBUG, "Starting Open Audio Stream...") self.stream = self._py_audio.open( format=pyaudio.paInt16, channels=1, rate=self._device_rate, input=True, input_device_index=self.selected_device.id, frames_per_buffer=self._frames_per_buffer, stream_callback=callback) except Exception as e: self.logger.error("Could not init AudioService.") self.logger.exception( f"Unexpected error in init_audio_service: {e}") def log_output(self, show_output, log_level, message): if show_output: if log_level == logging.INFO: self.logger.info(message) elif log_level == logging.DEBUG: self.logger.debug(message) elif log_level == logging.ERROR: self.logger.error(message) else: self.logger.debug(message) else: self.logger.debug(message) def audio_service_routine(self): try: if not self._notification_queue_in.empty(): current_notification_item = self._notification_queue_in.get_blocking( ) if current_notification_item.notification_enum is NotificationEnum.config_refresh: if self.stream is not None: self.stream.stop_stream() self.stream.close() self.init_audio_service() self._notification_queue_out.put_blocking( NotificationItem( NotificationEnum.config_refresh_finished, current_notification_item.device_id)) elif current_notification_item.notification_enum is NotificationEnum.process_continue: self._skip_routine = False elif current_notification_item.notification_enum is NotificationEnum.process_pause: self._skip_routine = True if self._skip_routine: return in_data = None in_data = self.audio_buffer_queue.get_blocking_with_timeout() if in_data is None: self.logger.debug("Audio in timeout. Queue is Empty") return # Convert the raw string audio stream to an array. y = np.fromstring(in_data, dtype=np.int16) # Use the type float32. y = y.astype(np.float32) # Process the audio stream. audio_datas = self._dsp.update(y) # Check if value is higher than min value. if audio_datas["vol"] < self._config["general_settings"][ "min_volume_threshold"]: # Fill the array with zeros, to fade out the effect. audio_datas["mel"] = np.zeros(self.n_fft_bins) self._audio_queue.put_none_blocking(audio_datas) self.end_time_2 = time() if time() - self.ten_seconds_counter_2 > 10: self.ten_seconds_counter_2 = time() time_dif = self.end_time_2 - self.start_time_2 fps = 1 / time_dif self.logger.info(f"Routine | FPS: {fps:.2f}") self.start_time_2 = time() except IOError: self.logger.exception( "IOError while reading the Microphone Stream.") pass except Exception as e: self.logger.error("Could not run AudioService routine.") self.logger.exception(f"Unexpected error in routine: {e}")
class DeviceManager(): def start(self, config_lock, notification_queue_in, notification_queue_out, effect_queue, audio_queue): self.logger = logging.getLogger(__name__) self._config_lock = config_lock self._config = ConfigService.instance(self._config_lock).config self._notification_queue_in = QueueWrapper(notification_queue_in) self._notification_queue_out = QueueWrapper(notification_queue_out) self._effect_queue = QueueWrapper(effect_queue) self._audio_queue = QueueWrapper(audio_queue) # Init FPS Limiter. self._fps_limiter = FPSLimiter(120) self._skip_routine = False self._devices = {} self.init_devices() self.start_devices() self.start_time = time() self.ten_seconds_counter = time() while True: try: self.routine() except KeyboardInterrupt: break def routine(self): # Check the effect queue. if not self._effect_queue.empty(): current_effect_item = self._effect_queue.get_blocking() self.logger.debug( f"Device Manager received new effect: {current_effect_item.effect_enum} {current_effect_item.device_id}" ) current_device = self._devices[current_effect_item.device_id] current_device.effect_queue.put_blocking(current_effect_item) if not self._notification_queue_in.empty(): current_notification_item = self._notification_queue_in.get_blocking( ) self.logger.debug( f"Device Manager received new notification: {current_notification_item.notification_enum} - {current_notification_item.device_id}" ) if current_notification_item.notification_enum is NotificationEnum.config_refresh: devices_count_before_reload = len( self._config["device_configs"].keys()) self.logger.debug( f"Device count before: {devices_count_before_reload}") self.reload_config() devices_count_after_reload = len( self._config["device_configs"].keys()) self.logger.debug( f"Device count after: {devices_count_after_reload}") if (devices_count_before_reload != devices_count_after_reload): self.reinit_devices() if (current_notification_item.device_id == "all_devices"): for key, value in self._devices.items(): self.restart_device(key) else: self.restart_device(current_notification_item.device_id) self._notification_queue_out.put_blocking( NotificationItem(NotificationEnum.config_refresh_finished, current_notification_item.device_id)) elif current_notification_item.notification_enum is NotificationEnum.process_continue: self._skip_routine = False elif current_notification_item.notification_enum is NotificationEnum.process_pause: self._skip_routine = True # Limit the fps to decrease lags caused by 100 percent CPU. self._fps_limiter.fps_limiter() if self._skip_routine: return audio_data = self.get_audio_data() self.refresh_audio_queues(audio_data) self.end_time = time() if time() - self.ten_seconds_counter > 10: self.ten_seconds_counter = time() self.time_dif = self.end_time - self.start_time self.fps = 1 / self.time_dif self.logger.info(f"FPS: {self.fps:.2f}") self.start_time = time() def get_audio_data(self): audio_data = None if not self._audio_queue.empty(): audio_data = self._audio_queue.get_blocking() return audio_data def refresh_audio_queues(self, audio_data): if audio_data is None: return for key, value in self._devices.items(): audio_copy = copy.deepcopy(audio_data) value.audio_queue.put_none_blocking(audio_data) def init_devices(self): self.logger.debug("Entering init_devices()") self._color_service_global = ColorServiceGlobal(self._config) for key in self._config["device_configs"].keys(): device_id = key self.logger.debug(f"Init device with device id: {device_id}") self._devices[device_id] = Device( self._config, self._config["device_configs"][device_id], self._color_service_global) self.logger.debug("Leaving init_devices()") def reinit_devices(self): self.logger.debug("Entering reinit_devices()") for key, value in self._devices.items(): self.stop_device(key) self._devices = {} self.init_devices() self.start_devices() self.logger.debug("Leaving reinit_devices()") def start_devices(self): for key, value in self._devices.items(): self.logger.debug(f"Starting device: {key}") value.start_device() def reload_config(self): self.logger.debug("Entering reload_config()") ConfigService.instance(self._config_lock).load_config() self._config = ConfigService.instance(self._config_lock).config self.logger.debug("Leaving reload_config()") def restart_device(self, device_id): self.logger.debug(f"Restarting {device_id}") self._devices[device_id].refresh_config( self._config, self._config["device_configs"][device_id]) self.logger.debug(f"Restarted {device_id}") def stop_device(self, device_id): self.logger.debug(f"Stopping {device_id}") self._devices[device_id].stop_device() self.logger.debug(f"Stopped {device_id}")
class NotificationService(): def start(self, config_lock, notification_queue_device_manager_in, notification_queue_device_manager_out, notification_queue_audio_in, notification_queue_audio_out, notification_queue_webserver_in, notification_queue_webserver_out): self.logger = logging.getLogger(__name__) self._config_lock = config_lock self._notification_queue_device_manager_in = QueueWrapper( notification_queue_device_manager_in) self._notification_queue_device_manager_out = QueueWrapper( notification_queue_device_manager_out) self._notification_queue_audio_in = QueueWrapper( notification_queue_audio_in) self._notification_queue_audio_out = QueueWrapper( notification_queue_audio_out) self._notification_queue_webserver_in = QueueWrapper( notification_queue_webserver_in) self._notification_queue_webserver_out = QueueWrapper( notification_queue_webserver_out) self._current_notification_item = NotificationItem( NotificationEnum.config_refresh, "all_devices") self._cancel_token = False self.logger.debug("NotificationService component started.") while not self._cancel_token: # 1. Check Webserver # 2. Check Output # 3. Check Effects try: sleep(0.5) if not self._notification_queue_webserver_out.empty(): self.logger.debug( "NotificationService: New Notification detected.") self._current_notification_item = self._notification_queue_webserver_out.get_blocking( ) self.logger.debug("Item get") if self._current_notification_item.notification_enum is NotificationEnum.config_refresh: self.logger.debug("Reloading config...") self.config_refresh(self._current_notification_item) self.logger.debug("Config reloaded.") except KeyboardInterrupt: break def stop(self): self._cancel_token = True def config_refresh(self, original_notification_item): device_id = original_notification_item.device_id # Summary # 1. Pause every process that has to refresh the config. # 2. Send the refresh command. # 3. Wait for all to finish the process. # 4. Continue the processes. self.logger.debug("1. Pause") # 1. Pause every process that has to refresh the config. self._notification_queue_device_manager_in.put_blocking( NotificationItem(NotificationEnum.process_pause, device_id)) self._notification_queue_audio_in.put_blocking( NotificationItem(NotificationEnum.process_pause, device_id)) self.logger.debug("2. Refresh") # 2. Send the refresh command. self._notification_queue_device_manager_in.put_blocking( NotificationItem(NotificationEnum.config_refresh, device_id)) self._notification_queue_audio_in.put_blocking( NotificationItem(NotificationEnum.config_refresh, device_id)) # 3. Wait for all to finish the process. processes_not_ready = True self.logger.debug("3. Wait") device_ready = False effect_ready = False while processes_not_ready: # Check the notification queue of device_manager, if it is ready to continue. if (not self._notification_queue_device_manager_out.empty()): current_output_out = self._notification_queue_device_manager_out.get_blocking( ) if current_output_out.notification_enum is NotificationEnum.config_refresh_finished: device_ready = True self.logger.debug("Device refreshed the config.") # Check the notification queue of audio, if it is ready to continue. if (not self._notification_queue_audio_out.empty()): current_effects_out = self._notification_queue_audio_out.get_blocking( ) if current_effects_out.notification_enum is NotificationEnum.config_refresh_finished: effect_ready = True self.logger.debug("Audio refreshed the config.") if device_ready and effect_ready: processes_not_ready = False # 4. Continue the processes. self._notification_queue_device_manager_in.put_blocking( NotificationItem(NotificationEnum.process_continue, device_id)) self._notification_queue_audio_in.put_blocking( NotificationItem(NotificationEnum.process_continue, device_id))