diff --git a/app/internal/NoiseGatev2.py b/app/internal/NoiseGatev2.py index ec52ec7..eae3d93 100644 --- a/app/internal/NoiseGatev2.py +++ b/app/internal/NoiseGatev2.py @@ -92,5 +92,102 @@ class AudioStreamManager: if self.stream and self.stream.is_active(): self.stream.stop_stream() self.stream.close() - self.pa.terminate() - LOGGER.info("PyAudio instance terminated.") \ No newline at end of file + LOGGER.debug("[ReopenStream.close_if_open]:\t Stream was open; It was closed.") + + def list_devices(self, _display_input_devices: bool = True, _display_output_devices: bool = True): + LOGGER.info('Getting a list of the devices connected') + info = self.paInstance.get_host_api_info_by_index(0) + numdevices = info.get('deviceCount') + + devices = {'Input': {}, 'Output': {}} + for i in range(0, numdevices): + device_info = self.paInstance.get_device_info_by_host_api_device_index(0, i) + if (device_info.get('maxInputChannels')) > 0: + input_device = device_info.get('name') + devices['Input'][i] = input_device + if _display_input_devices: + LOGGER.debug(f"Input Device id {i} - {input_device}") + + if (device_info.get('maxOutputChannels')) > 0: + output_device = device_info.get('name') + devices['Output'][i] = output_device + if _display_output_devices: + LOGGER.debug(f"Output Device id {i} - {output_device}") + return devices + +# noinspection PyUnresolvedReferences +class NoiseGate(AudioStream): + def __init__(self, _voice_connection, _noise_gate_threshold: int, **kwargs): + super(NoiseGate, self).__init__(_init_on_startup=True, **kwargs) + self.voice_connection = _voice_connection + self.THRESHOLD = _noise_gate_threshold + self.NGStream = NoiseGateStream(self) + + def run(self) -> None: + LOGGER.debug("Starting stream") + self.stream.start_stream() + self.core() + + def core(self): + if self.voice_connection.is_connected() and not self.voice_connection.is_playing(): + LOGGER.debug("Playing stream to discord") + self.voice_connection.play(self.NGStream) + + async def close(self): + LOGGER.debug("Closing NoiseGate resources...") + if self.voice_connection and self.voice_connection.is_connected(): + self.voice_connection.stop() + + self.close_if_open() + + if self.paInstance: + self.paInstance.terminate() + + LOGGER.debug("NoiseGate resources closed.") + +# noinspection PyUnresolvedReferences +class NoiseGateStream(discord.AudioSource): + def __init__(self, noise_gate_instance: NoiseGate): + super(NoiseGateStream, self).__init__() + self.noise_gate = noise_gate_instance + self.NG_fadeout = 12 + self.NG_fadeout_count = 0 + self.process_set_count = 0 + + def read(self): + try: + if not self.noise_gate.voice_connection.is_connected(): + return SILENT_FRAME + + curr_buffer = self.noise_gate.stream.read(960, exception_on_overflow=False) + + if len(curr_buffer) != DISCORD_FRAME_SIZE: + return SILENT_FRAME + + buffer_rms = audioop.rms(curr_buffer, 2) + + if buffer_rms > 0: + buffer_decibel = 20 * math.log10(buffer_rms) + + if self.process_set_count % 10 == 0: + log_msg = f"[{'Open' if buffer_decibel >= self.noise_gate.THRESHOLD else 'Closed'}]" + LOGGER.debug(f"[NoiseGate {log_msg}] {buffer_decibel:.2f} dB") + + if buffer_decibel >= self.noise_gate.THRESHOLD: + self.NG_fadeout_count = self.NG_fadeout + self.process_set_count += 1 + return bytes(curr_buffer) + + elif self.NG_fadeout_count > 0: + self.NG_fadeout_count -= 1 + self.process_set_count += 1 + return bytes(curr_buffer) + + return SILENT_FRAME + + except IOError as e: + LOGGER.error(f"PyAudio IOError in read(): {e}") + return SILENT_FRAME + except Exception as e: + LOGGER.error(f"Unhandled exception in NoiseGateStream.read: {e}", exc_info=True) + return SILENT_FRAME