Update PDAB
- Added standard debugger file for logging - Added fault handler to see what happens if it faults
This commit is contained in:
@@ -6,10 +6,12 @@ import time
|
|||||||
import pyaudio
|
import pyaudio
|
||||||
import discord
|
import discord
|
||||||
import numpy
|
import numpy
|
||||||
|
from debugger import setup_logger
|
||||||
|
|
||||||
voice_connection = None
|
voice_connection = None
|
||||||
|
|
||||||
LOGGER = logging.getLogger("Discord_Radio_Bot.NoiseGateV2")
|
# Configure logging
|
||||||
|
logger = setup_logger('NoiseGatev2')
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
@@ -30,19 +32,19 @@ class AudioStream:
|
|||||||
if _input:
|
if _input:
|
||||||
self.paInstance_kwargs['input_device_index'] = _input_device_index
|
self.paInstance_kwargs['input_device_index'] = _input_device_index
|
||||||
else:
|
else:
|
||||||
LOGGER.warning(f"[AudioStream.__init__]:\tInput was not enabled."
|
logger.warning(f"[AudioStream.__init__]:\tInput was not enabled."
|
||||||
f" Reinitialize with '_input=True'")
|
f" Reinitialize with '_input=True'")
|
||||||
|
|
||||||
if _output_device_index:
|
if _output_device_index:
|
||||||
if _output:
|
if _output:
|
||||||
self.paInstance_kwargs['output_device_index'] = _output_device_index
|
self.paInstance_kwargs['output_device_index'] = _output_device_index
|
||||||
else:
|
else:
|
||||||
LOGGER.warning(f"[AudioStream.__init__]:\tOutput was not enabled."
|
logger.warning(f"[AudioStream.__init__]:\tOutput was not enabled."
|
||||||
f" Reinitialize with '_output=True'")
|
f" Reinitialize with '_output=True'")
|
||||||
|
|
||||||
if _init_on_startup:
|
if _init_on_startup:
|
||||||
# Init PyAudio instance
|
# Init PyAudio instance
|
||||||
LOGGER.info("Creating PyAudio instance")
|
logger.info("Creating PyAudio instance")
|
||||||
self.paInstance = pyaudio.PyAudio()
|
self.paInstance = pyaudio.PyAudio()
|
||||||
|
|
||||||
# Define and initialize stream object if we have been passed a device ID (pyaudio.open)
|
# Define and initialize stream object if we have been passed a device ID (pyaudio.open)
|
||||||
@@ -50,27 +52,30 @@ class AudioStream:
|
|||||||
|
|
||||||
if _output_device_index or _input_device_index:
|
if _output_device_index or _input_device_index:
|
||||||
if _init_on_startup:
|
if _init_on_startup:
|
||||||
LOGGER.info("Init stream")
|
logger.info("Init stream")
|
||||||
self.init_stream()
|
self.init_stream()
|
||||||
|
|
||||||
def init_stream(self, _new_output_device_index: int = None, _new_input_device_index: int = None):
|
def init_stream(self, _new_output_device_index: int = None, _new_input_device_index: int = None):
|
||||||
|
logger.info("Checking what device has been set if any")
|
||||||
# Check what device was asked to be changed (or set)
|
# Check what device was asked to be changed (or set)
|
||||||
if _new_input_device_index:
|
if _new_input_device_index:
|
||||||
if self.paInstance_kwargs['input']:
|
if self.paInstance_kwargs['input']:
|
||||||
self.paInstance_kwargs['input_device_index'] = _new_input_device_index
|
self.paInstance_kwargs['input_device_index'] = _new_input_device_index
|
||||||
else:
|
else:
|
||||||
LOGGER.warning(f"[AudioStream.init_stream]:\tInput was not enabled when initialized."
|
logger.warning(f"[AudioStream.init_stream]:\tInput was not enabled when initialized."
|
||||||
f" Reinitialize with '_input=True'")
|
f" Reinitialize with '_input=True'")
|
||||||
|
|
||||||
if _new_output_device_index:
|
if _new_output_device_index:
|
||||||
if self.paInstance_kwargs['output']:
|
if self.paInstance_kwargs['output']:
|
||||||
self.paInstance_kwargs['output_device_index'] = _new_output_device_index
|
self.paInstance_kwargs['output_device_index'] = _new_output_device_index
|
||||||
else:
|
else:
|
||||||
LOGGER.warning(f"[AudioStream.init_stream]:\tOutput was not enabled when initialized."
|
logger.warning(f"[AudioStream.init_stream]:\tOutput was not enabled when initialized."
|
||||||
f" Reinitialize with '_output=True'")
|
f" Reinitialize with '_output=True'")
|
||||||
|
|
||||||
|
logger.info("Close the stream if it's open")
|
||||||
self.close_if_open()
|
self.close_if_open()
|
||||||
|
|
||||||
|
logger.info("Open the audio stream")
|
||||||
# Open the stream
|
# Open the stream
|
||||||
self.stream = self.paInstance.open(**self.paInstance_kwargs)
|
self.stream = self.paInstance.open(**self.paInstance_kwargs)
|
||||||
|
|
||||||
@@ -80,10 +85,10 @@ class AudioStream:
|
|||||||
if self.stream.is_active():
|
if self.stream.is_active():
|
||||||
self.stream.stop_stream()
|
self.stream.stop_stream()
|
||||||
self.stream.close()
|
self.stream.close()
|
||||||
LOGGER.debug(f"[ReopenStream.close_if_open]:\t Stream was open; It was closed.")
|
logger.debug(f"[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):
|
def list_devices(self, _display_input_devices: bool = True, _display_output_devices: bool = True):
|
||||||
LOGGER.info('Getting a list of the devices connected')
|
logger.info('Getting a list of the devices connected')
|
||||||
info = self.paInstance.get_host_api_info_by_index(0)
|
info = self.paInstance.get_host_api_info_by_index(0)
|
||||||
numdevices = info.get('deviceCount')
|
numdevices = info.get('deviceCount')
|
||||||
|
|
||||||
@@ -96,13 +101,13 @@ class AudioStream:
|
|||||||
input_device = self.paInstance.get_device_info_by_host_api_device_index(0, i).get('name')
|
input_device = self.paInstance.get_device_info_by_host_api_device_index(0, i).get('name')
|
||||||
devices['Input'][i] = input_device
|
devices['Input'][i] = input_device
|
||||||
if _display_input_devices:
|
if _display_input_devices:
|
||||||
LOGGER.debug(f"Input Device id {i} - {input_device}")
|
logger.debug(f"Input Device id {i} - {input_device}")
|
||||||
|
|
||||||
if (self.paInstance.get_device_info_by_host_api_device_index(0, i).get('maxOutputChannels')) > 0:
|
if (self.paInstance.get_device_info_by_host_api_device_index(0, i).get('maxOutputChannels')) > 0:
|
||||||
output_device = self.paInstance.get_device_info_by_host_api_device_index(0, i).get('name')
|
output_device = self.paInstance.get_device_info_by_host_api_device_index(0, i).get('name')
|
||||||
devices['Output'][i] = output_device
|
devices['Output'][i] = output_device
|
||||||
if _display_output_devices:
|
if _display_output_devices:
|
||||||
LOGGER.debug(f"Output Device id {i} - {output_device}")
|
logger.debug(f"Output Device id {i} - {output_device}")
|
||||||
|
|
||||||
return devices
|
return devices
|
||||||
|
|
||||||
@@ -126,28 +131,31 @@ class NoiseGate(AudioStream):
|
|||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
global voice_connection
|
global voice_connection
|
||||||
# Start the audio stream
|
# Start the audio stream
|
||||||
LOGGER.debug(f"Starting stream")
|
logger.debug(f"Starting stream")
|
||||||
self.stream.start_stream()
|
try:
|
||||||
# Start the stream to discord
|
self.stream.start_stream()
|
||||||
self.core()
|
# Start the stream to discord
|
||||||
|
self.core()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(err)
|
||||||
|
|
||||||
def core(self, error=None):
|
def core(self, error=None):
|
||||||
if error:
|
if error:
|
||||||
LOGGER.warning(error)
|
logger.warning(error)
|
||||||
|
|
||||||
while not voice_connection.is_connected():
|
while not voice_connection.is_connected():
|
||||||
time.sleep(.2)
|
time.sleep(.2)
|
||||||
|
|
||||||
if not voice_connection.is_playing():
|
if not voice_connection.is_playing():
|
||||||
LOGGER.debug(f"Playing stream to discord")
|
logger.debug(f"Playing stream to discord")
|
||||||
voice_connection.play(self.NGStream, after=self.core)
|
voice_connection.play(self.NGStream, after=self.core)
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
LOGGER.debug(f"Closing")
|
logger.debug(f"Closing")
|
||||||
await voice_connection.disconnect()
|
await voice_connection.disconnect()
|
||||||
if self.stream.is_active:
|
if self.stream.is_active:
|
||||||
self.stream.stop_stream()
|
self.stream.stop_stream()
|
||||||
LOGGER.debug(f"Stopping stream")
|
logger.debug(f"Stopping stream")
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
@@ -169,9 +177,9 @@ class NoiseGateStream(discord.AudioSource):
|
|||||||
|
|
||||||
if self.process_set_count % 10 == 0:
|
if self.process_set_count % 10 == 0:
|
||||||
if buffer_decibel >= self.stream.THRESHOLD:
|
if buffer_decibel >= self.stream.THRESHOLD:
|
||||||
LOGGER.debug(f"[Noisegate Open] {buffer_decibel} db")
|
logger.debug(f"[Noisegate Open] {buffer_decibel} db")
|
||||||
else:
|
else:
|
||||||
LOGGER.debug(f"[Noisegate Closed] {buffer_decibel} db")
|
logger.debug(f"[Noisegate Closed] {buffer_decibel} db")
|
||||||
|
|
||||||
if buffer_decibel >= self.stream.THRESHOLD:
|
if buffer_decibel >= self.stream.THRESHOLD:
|
||||||
self.NG_fadeout_count = self.NG_fadeout
|
self.NG_fadeout_count = self.NG_fadeout
|
||||||
@@ -182,13 +190,13 @@ class NoiseGateStream(discord.AudioSource):
|
|||||||
else:
|
else:
|
||||||
if self.NG_fadeout_count > 0:
|
if self.NG_fadeout_count > 0:
|
||||||
self.NG_fadeout_count -= 1
|
self.NG_fadeout_count -= 1
|
||||||
LOGGER.debug(f"Frames in fadeout remaining: {self.NG_fadeout_count}")
|
logger.debug(f"Frames in fadeout remaining: {self.NG_fadeout_count}")
|
||||||
self.process_set_count += 1
|
self.process_set_count += 1
|
||||||
if curr_buffer:
|
if curr_buffer:
|
||||||
return bytes(curr_buffer)
|
return bytes(curr_buffer)
|
||||||
|
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
LOGGER.warning(e)
|
logger.warning(e)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def audio_datalist_set_volume(self, datalist, volume):
|
def audio_datalist_set_volume(self, datalist, volume):
|
||||||
|
|||||||
28
debugger.py
Normal file
28
debugger.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
running_dir = os.path.dirname(__file__)
|
||||||
|
# Ensure the directory exists
|
||||||
|
log_dir = f"{running_dir}/logs"
|
||||||
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logger(namespace):
|
||||||
|
# Create the file if it doesn't exist
|
||||||
|
log_file = f"{running_dir}/logs/pdab.log"
|
||||||
|
open(log_file, 'a').close()
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logFormatter = logging.Formatter("%(asctime)s [%(threadName)-16.16s] [%(levelname)-7.7s] - %(message)s", "%Y-%m-%d %H:%M:%S")
|
||||||
|
logger = logging.getLogger(namespace)
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
fileHandler = logging.FileHandler(log_file)
|
||||||
|
fileHandler.setFormatter(logFormatter)
|
||||||
|
logger.addHandler(fileHandler)
|
||||||
|
|
||||||
|
consoleHandler = logging.StreamHandler()
|
||||||
|
consoleHandler.setFormatter(logFormatter)
|
||||||
|
logger.addHandler(consoleHandler)
|
||||||
|
|
||||||
|
return logger
|
||||||
61
main.py
61
main.py
@@ -1,6 +1,5 @@
|
|||||||
# Python client file (client.py)
|
# Python client file (client.py)
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import socketio
|
import socketio
|
||||||
@@ -8,29 +7,11 @@ import asyncio
|
|||||||
from discord import Intents, opus
|
from discord import Intents, opus
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from NoiseGatev2 import NoiseGate
|
from NoiseGatev2 import NoiseGate
|
||||||
|
from debugger import setup_logger, running_dir
|
||||||
|
import faulthandler
|
||||||
|
faulthandler.enable()
|
||||||
|
|
||||||
running_dir = os.path.dirname(__file__)
|
logger = setup_logger('main')
|
||||||
|
|
||||||
# Ensure the directory exists
|
|
||||||
log_dir = f"{running_dir}/logs"
|
|
||||||
os.makedirs(log_dir, exist_ok=True)
|
|
||||||
|
|
||||||
# Create the file if it doesn't exist
|
|
||||||
log_file = f"{running_dir}/logs/pdab.log"
|
|
||||||
open(log_file, 'a').close()
|
|
||||||
|
|
||||||
# Configure logging
|
|
||||||
logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s")
|
|
||||||
logger = logging.getLogger('main')
|
|
||||||
logger.setLevel(logging.INFO)
|
|
||||||
|
|
||||||
fileHandler = logging.FileHandler(log_file)
|
|
||||||
fileHandler.setFormatter(logFormatter)
|
|
||||||
logger.addHandler(fileHandler)
|
|
||||||
|
|
||||||
consoleHandler = logging.StreamHandler()
|
|
||||||
consoleHandler.setFormatter(logFormatter)
|
|
||||||
logger.addHandler(consoleHandler)
|
|
||||||
|
|
||||||
# Example usage
|
# Example usage
|
||||||
logger.info("Logging initialized successfully.")
|
logger.info("Logging initialized successfully.")
|
||||||
@@ -43,14 +24,19 @@ ng_threshold = None
|
|||||||
|
|
||||||
### Core functions
|
### Core functions
|
||||||
def load_opus():
|
def load_opus():
|
||||||
|
logger.info(f"Running dir: '{running_dir}'")
|
||||||
processor = platform.machine()
|
processor = platform.machine()
|
||||||
logger.info(f"Processor: {processor}")
|
logger.info(f"Processor: {processor}")
|
||||||
|
logger.info(f'OS: {os.name}')
|
||||||
|
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
opus_path = f'{running_dir}/opus/libopus_amd64.dll' if processor == "AMD64" else None
|
opus_path = f'{running_dir}/opus/libopus_amd64.dll' if processor == "AMD64" else None
|
||||||
else:
|
else:
|
||||||
opus_path = f'{running_dir}/opus/libopus_aarcch64.so' if processor == "aarch64" else f'{running_dir}/opus/libopus_armv7l.so'
|
opus_path = f'{running_dir}/opus/libopus_aarcch64.so' if processor == "aarch64" else f'{running_dir}/opus/libopus_armv7l.so'
|
||||||
|
|
||||||
|
logger.debug(f"Opus path: '{opus_path}'")
|
||||||
|
logger.info(f"Opus path: '{opus_path}'")
|
||||||
|
|
||||||
if opus_path:
|
if opus_path:
|
||||||
opus.load_opus(opus_path)
|
opus.load_opus(opus_path)
|
||||||
logger.info(f"Loaded OPUS library from {opus_path}")
|
logger.info(f"Loaded OPUS library from {opus_path}")
|
||||||
@@ -85,6 +71,7 @@ async def join_voice_channel(channel_id):
|
|||||||
logging.error(e)
|
logging.error(e)
|
||||||
logging.info('error encountered')
|
logging.info('error encountered')
|
||||||
|
|
||||||
|
|
||||||
async def leave_voice_channel(guild_id):
|
async def leave_voice_channel(guild_id):
|
||||||
guild = await client.fetch_guild(guild_id)
|
guild = await client.fetch_guild(guild_id)
|
||||||
logger.info(f"Leaving voice channel in guild {guild}")
|
logger.info(f"Leaving voice channel in guild {guild}")
|
||||||
@@ -198,19 +185,14 @@ async def on_ready():
|
|||||||
logger.info("Loading OPUS library")
|
logger.info("Loading OPUS library")
|
||||||
load_opus()
|
load_opus()
|
||||||
|
|
||||||
if not opus.is_loaded():
|
logger.info(opus.is_loaded())
|
||||||
return
|
|
||||||
|
|
||||||
# Send update to socket server
|
if opus.is_loaded():
|
||||||
try:
|
|
||||||
logger.info('Emitting to the server')
|
logger.info('Emitting to the server')
|
||||||
await sio.emit('discord_ready', {'message': 'Discord bot is ready'})
|
await sio.emit('discord_ready', {'message': 'Discord bot is ready'})
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error emitting to the server: {e}")
|
|
||||||
logger.info('Server not ready yet')
|
|
||||||
|
|
||||||
|
|
||||||
async def main(args):
|
async def start_bot(args):
|
||||||
global client, device_id, ng_threshold
|
global client, device_id, ng_threshold
|
||||||
|
|
||||||
# Connect to the WebSocket server
|
# Connect to the WebSocket server
|
||||||
@@ -241,6 +223,13 @@ def parse_arguments():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
args = parse_arguments()
|
try:
|
||||||
logger.info("Arguments: %s", args)
|
args = parse_arguments()
|
||||||
asyncio.run(main(args))
|
logger.info("Arguments: %s", args)
|
||||||
|
# Create an event loop
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
# Run the start_bot function within the event loop
|
||||||
|
loop.run_until_complete(start_bot(args))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
logger.warning("Exiting now...")
|
||||||
|
|||||||
Reference in New Issue
Block a user