From 0cac9c8f8150a5299bbd685a303a85dce34d67e1 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Fri, 4 Feb 2022 12:35:00 -0500 Subject: [PATCH] Implement Noisegate function to start and stop playing the audio stream --- NoiseGate.py | 43 ++++++++++++++++ bot.py | 143 +++++++++++++++++++++++++++------------------------ main.py | 3 +- sound.py | 10 +++- 4 files changed, 129 insertions(+), 70 deletions(-) create mode 100644 NoiseGate.py diff --git a/NoiseGate.py b/NoiseGate.py new file mode 100644 index 0000000..4a82358 --- /dev/null +++ b/NoiseGate.py @@ -0,0 +1,43 @@ +import time + +import numpy as np +import discord +import sounddevice as sd +from threading import Thread, Event + +noise_gate_trigger = 0 # Set this value for the trigger on the noise-gate +voice_connection = None +audio_stream = None + +class NoiseGate(Thread): + def __init__(self, trigger_value: int = 1000): + global noise_gate_trigger + super(NoiseGate, self).__init__() + self.stream = None + + noise_gate_trigger = trigger_value + + def init_stream(self, num, _voice_connection, _audio_stream): + global voice_connection, audio_stream + self.stream = sd.InputStream(device=num, callback=stream_callback) + voice_connection = _voice_connection + audio_stream = _audio_stream + + def run(self) -> None: + self.stream.start() + + +def stream_callback(indata, *args): + volume_normalization = np.linalg.norm(indata) * 10 + if int(volume_normalization) >= noise_gate_trigger: + # Triggered noise-gate + if not voice_connection.is_playing(): + voice_connection.play(discord.PCMAudio(audio_stream)) + #print("|" * int(volume_normalization / 4)) + print("Noise Gate was Triggered") + time.sleep(10) + else: + if voice_connection.is_playing(): + print("Noise Gate stopped") + voice_connection.stop() + # try disconnecting and reconnecting diff --git a/bot.py b/bot.py index c2c765b..d09bac1 100644 --- a/bot.py +++ b/bot.py @@ -21,6 +21,7 @@ class Bot(commands.Bot): self.BOT_TOKEN = kwargs['Token'] self.Default_Channel_ID = kwargs['Channel_ID'] self.Default_Mention_Group = kwargs['Mention_Group'] + self.Handler = kwargs['Handler'] # Init the audio devices list self.Devices_List = sound.query_devices().items() @@ -34,7 +35,10 @@ class Bot(commands.Bot): # Init SDR Variables self.system_os_type = None self.sdr_started = False - self.GQRXHandler = GQRXHandler() + + if self.Handler == "gqrx": + print("Starting gqrx handler") + self.GQRXHandler = GQRXHandler() # Set linux or windows self.check_os_type() @@ -69,17 +73,18 @@ class Bot(commands.Bot): self.load_opus() if discord.opus.is_loaded(): - # Create an audio stream from selected device - stream = sound.PCMStream() channel = ctx.author.voice.channel await ctx.send(f"Ok {str(member).capitalize()}, I'm joining {channel}") - # Ensure the selected device is available and start the audio stream - stream.change_device(self.DEVICE_ID) - # Join the voice channel with the audio stream voice_connection = await channel.connect() - voice_connection.play(discord.PCMAudio(stream)) + + + + # Create an audio stream from selected device + stream = sound.PCMStream(voice_connection) + # Ensure the selected device is available and start the audio stream + stream.change_device(self.DEVICE_ID) # Start the SDR and begin playing to the audio stream self.start_sdr() @@ -101,66 +106,68 @@ class Bot(commands.Bot): # Stop the SDR so it can cool off self.stop_sdr() - @self.command(name='chfreq', help="Use this command to change the frequency the bot is listening to. " - "\nExample command: '@ chfreq wfm 104700000\n" - "Example command: '@ chfreq fm 154785000", - brief="Changes radio frequency") - async def chfreq(ctx, mode: str, freq: str, member: discord.Member = None): - # Possible band-types that can be used - possible_modes = ['wfm', 'fm'] - member = member or ctx.author.display_name + if self.Handler == 'gqrx': + @self.command(name='chfreq', help="Use this command to change the frequency the bot is listening to. " + "\nExample command: '@ chfreq wfm 104700000\n" + "Example command: '@ chfreq fm 154785000", + brief="Changes radio frequency") + async def chfreq(ctx, mode: str, freq: str, member: discord.Member = None): + # Possible band-types that can be used + possible_modes = ['wfm', 'fm'] + member = member or ctx.author.display_name - # Check to make sure the frequency input matches the syntax needed - if len(freq) >= 6: - self.freq = freq - # Check to make sure the selected mode is valid - if mode in possible_modes: - self.mode = mode + # Check to make sure the frequency input matches the syntax needed + if len(freq) >= 6: + self.freq = freq + # Check to make sure the selected mode is valid + if mode in possible_modes: + self.mode = mode - await ctx.send(f"Ok {str(member).capitalize()}, I'm changing the mode to {str(self.mode).upper()} and frequency to" - f" {self.freq}") + await ctx.send(f"Ok {str(member).capitalize()}, I'm changing the mode to {str(self.mode).upper()} and frequency to" + f" {self.freq}") - # Reset the profile name since we have made a change to the freq - self.profile_name = None + # Reset the profile name since we have made a change to the freq + self.profile_name = None - # If the SDR is started, restart it with the updates - if self.sdr_started: - self.start_sdr() - await self.set_activity() + # If the SDR is started, restart it with the updates + if self.sdr_started: + self.start_sdr() + await self.set_activity() + else: + await ctx.send(f"{str(member).capitalize()}, {mode} is not valid. You may only enter 'fm' or 'wbfm'") else: - await ctx.send(f"{str(member).capitalize()}, {mode} is not valid. You may only enter 'fm' or 'wbfm'") - else: - await ctx.send(f"{str(member).capitalize()}, {freq} is not valid. please refer to the help page '@ help chfreq'") + await ctx.send(f"{str(member).capitalize()}, {freq} is not valid. please refer to the help page '@ help chfreq'") - @self.command(name='chsquelch', help="Use this command to change the squelch for the frequency" - "the bot is listening to", - brief="Changes radio squelch") - async def chsquelch(ctx, squelch: float, member: discord.Member = None): - member = member or ctx.author.display_name + @self.command(name='chsquelch', help="Use this command to change the squelch for the frequency" + "the bot is listening to", + brief="Changes radio squelch") + async def chsquelch(ctx, squelch: float, member: discord.Member = None): + member = member or ctx.author.display_name - self.squelch = squelch - await ctx.send(f"Ok {str(member).capitalize()}, I'm changing the squelch to {self.squelch}") + self.squelch = squelch + await ctx.send(f"Ok {str(member).capitalize()}, I'm changing the squelch to {self.squelch}") - # If the SDR is started, restart it with the updates - if self.sdr_started: - self.start_sdr() + # If the SDR is started, restart it with the updates + if self.sdr_started: + self.start_sdr() + + # Hidden admin commands + @self.command(name='saveprofile', hidden=True) + async def _saveprofile(ctx, profile_name: str, member: discord.Member = None): + member = member or ctx.author.display_name + await self.save_radio_config(profile_name) + await ctx.send(f"Ok {str(member).capitalize()}, I saved the current settings as {profile_name}") + + @self.command(name='loadprofile', hidden=True) + async def _loadprofile(ctx, profile_name: str, member: discord.Member = None): + member = member or ctx.author.display_name + config_loaded = await self.load_radio_config(profile_name) + if config_loaded: + await ctx.send(f"Ok {str(member).capitalize()}, I loaded the settings saved as {profile_name}") + else: + await ctx.send(f"{str(member).capitalize()}, there is no profile with the name '{profile_name}'") # Hidden admin commands - @self.command(name='saveprofile', hidden=True) - async def _saveprofile(ctx, profile_name: str, member: discord.Member = None): - member = member or ctx.author.display_name - await self.save_radio_config(profile_name) - await ctx.send(f"Ok {str(member).capitalize()}, I saved the current settings as {profile_name}") - - @self.command(name='loadprofile', hidden=True) - async def _loadprofile(ctx, profile_name: str, member: discord.Member = None): - member = member or ctx.author.display_name - config_loaded = await self.load_radio_config(profile_name) - if config_loaded: - await ctx.send(f"Ok {str(member).capitalize()}, I loaded the settings saved as {profile_name}") - else: - await ctx.send(f"{str(member).capitalize()}, there is no profile with the name '{profile_name}'") - @self.command(name='reload', hidden=True) async def _reload(ctx, module: str, member: discord.Member = None): """Reloads a module.""" @@ -230,26 +237,26 @@ class Bot(commands.Bot): # Check to see if there is only one frequency def start_sdr(self): - if type(self.freq) == str: - # Single freq sent - # Stop the SDR if it is running - self.stop_sdr() + if self.Handler == 'gqrx': + if type(self.freq) == str: + # Single freq sent + # Stop the SDR if it is running + self.stop_sdr() - # Start the radio - print(f"Starting freq: {self.freq}") + # Start the radio + print(f"Starting freq: {self.freq}") - # Set the settings in GQRX - self.GQRXHandler.set_all_settings(self.mode, self.squelch, self.freq) + # Set the settings in GQRX + self.GQRXHandler.set_all_settings(self.mode, self.squelch, self.freq) - # Set the started variable for later checks - self.sdr_started = True + # Set the started variable for later checks + self.sdr_started = True # Check to see if the SDR is running def stop_sdr(self): if self.sdr_started: # Wait for the running processes to close # Need a way to 'close' GQRX - self.sdr_started = False # Set the activity of the bot diff --git a/main.py b/main.py index b619547..e1a67de 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,7 @@ import os import time import bot +import argparse from BotResources import check_if_config_exists, write_config_file, read_config_file # Jorn @@ -33,7 +34,7 @@ def main(): print('Starting Bot...') discord_bot_client = bot.Bot(Token=config['Bot Token'], Device_ID=config['Device ID'], Device_Name=config['Device Name'], - Mention_Group=config['Mention Group'], Channel_ID=config['Channel ID']) + Mention_Group=config['Mention Group'], Channel_ID=config['Channel ID'], Handler=config['Handler']) print(f"Verifying audio device:\t{config['Device Name']}") diff --git a/sound.py b/sound.py index ec46691..39e2d46 100644 --- a/sound.py +++ b/sound.py @@ -1,5 +1,6 @@ import sounddevice as sd from pprint import pformat +from NoiseGate import NoiseGate DEFAULT = 0 sd.default.channels = 2 @@ -7,10 +8,14 @@ sd.default.dtype = "int16" sd.default.latency = "low" sd.default.samplerate = 48000 +noisegate_obj = NoiseGate() class PCMStream: - def __init__(self): + globals() + + def __init__(self, voice_connection): self.stream = None + self.voice_connection = voice_connection def read(self, num_bytes): # frame is 4 bytes @@ -26,7 +31,10 @@ class PCMStream: self.stream.close() self.stream = sd.RawInputStream(device=num) + noisegate_obj.init_stream(num, self.voice_connection, self) + self.stream.start() + noisegate_obj.start() class DeviceNotFoundError(Exception):