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 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
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}")
    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}")
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}")
    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
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))
 def create_queues(self):
     self.__device_notification_queue_in = QueueWrapper(Queue(2))
     self.__device_notification_queue_out = QueueWrapper(Queue(2))
     self.__effect_queue = QueueWrapper(Queue(2))
     self.__audio_queue = QueueWrapper(Queue(2))
     self.__output_queue = QueueWrapper(Queue(2))