Compare commits
6 Commits
bc0fc23fb0
...
#21-op25-r
| Author | SHA1 | Date | |
|---|---|---|---|
| f369e09a0c | |||
|
|
2fbca72f63 | ||
| 0ff11589e9 | |||
| a2abe7e71d | |||
| b23a0768e3 | |||
| 27516a0a25 |
@@ -12,6 +12,7 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@discordjs/voice": "^0.17.0",
|
||||
"axios": "^1.7.7",
|
||||
"convert-units": "^2.3.4",
|
||||
"discord.js": "^14.15.3",
|
||||
"dotenv": "^16.4.5",
|
||||
|
||||
4
setup.sh
4
setup.sh
@@ -94,8 +94,8 @@ rm -rf /usr/lib/python3.11/EXTERNALLY-MANAGED
|
||||
|
||||
# Getting the Python DAB
|
||||
echo "Installing PDAB and Dependencies"
|
||||
git clone -b DRBv3 https://git.vpn.cusano.net/logan/Python-Discord-Audio-Bot.git ./discordAudioBot/pdab
|
||||
pip3 install -r ./discordAudioBot/pdab/requirements.txt
|
||||
git clone -b DRBv3 https://git.vpn.cusano.net/logan/Python-Discord-Audio-Bot.git ./pdab
|
||||
pip3 install -r ./pdab/requirements.txt
|
||||
|
||||
# Create a systemd service file for the DRB Client
|
||||
echo "Adding DRB Node service..."
|
||||
|
||||
97
src/discordAudioBot/pdab-recorder.py
Normal file
97
src/discordAudioBot/pdab-recorder.py
Normal file
@@ -0,0 +1,97 @@
|
||||
import pyaudio
|
||||
import wave
|
||||
import argparse
|
||||
import os
|
||||
import datetime
|
||||
from pydub import AudioSegment
|
||||
from pydub.silence import detect_nonsilent
|
||||
|
||||
# Parameters for the audio stream
|
||||
FORMAT = pyaudio.paInt16
|
||||
CHANNELS = 2
|
||||
RATE = 48000
|
||||
CHUNK = 1024
|
||||
SILENCE_THRESHOLD = -50 # Silence threshold (adjust for noise level)
|
||||
SILENCE_DURATION = 1250 # n miliseconds of silence to stop recording
|
||||
|
||||
audio = pyaudio.PyAudio()
|
||||
|
||||
# Create the recordings directory if it doesn't exist
|
||||
RECORDINGS_DIR = './recordings'
|
||||
if not os.path.exists(RECORDINGS_DIR):
|
||||
os.makedirs(RECORDINGS_DIR)
|
||||
|
||||
def get_filename():
|
||||
"""Generate a filename with the current date and time, stored in the recordings directory."""
|
||||
return os.path.join(RECORDINGS_DIR, datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + ".wav")
|
||||
|
||||
|
||||
def save_wave_file(filename, audio_segment):
|
||||
"""Save the given audio segment to a WAV file in the recordings directory."""
|
||||
audio_segment.export(filename, format="wav")
|
||||
print(f"Saved recording: {filename}")
|
||||
|
||||
|
||||
def detect_nonsilent_chunk(sound, silence_thresh=SILENCE_THRESHOLD, silence_len=SILENCE_DURATION):
|
||||
"""Detect if there's a nonsilent chunk in the sound."""
|
||||
return detect_nonsilent(sound, min_silence_len=silence_len, silence_thresh=silence_thresh)
|
||||
|
||||
|
||||
def record_transmissions(device_id: int):
|
||||
stream = audio.open(format=FORMAT, channels=CHANNELS,
|
||||
rate=RATE, input=True,
|
||||
frames_per_buffer=CHUNK, input_device_index=device_id )
|
||||
|
||||
frames = []
|
||||
recording = False
|
||||
|
||||
print("Listening for transmissions...")
|
||||
|
||||
while True:
|
||||
data = stream.read(CHUNK)
|
||||
frames.append(data)
|
||||
|
||||
# Convert current audio buffer to AudioSegment for processing
|
||||
sound = AudioSegment(b''.join(frames), sample_width=audio.get_sample_size(FORMAT), frame_rate=RATE, channels=CHANNELS)
|
||||
|
||||
# Detect if there's sound (nonsilent chunk)
|
||||
nonsilent_chunks = detect_nonsilent_chunk(sound)
|
||||
|
||||
if nonsilent_chunks and not recording:
|
||||
print("Transmission detected, starting recording...")
|
||||
recording = True
|
||||
frames = [] # Reset frames to start fresh for this transmission
|
||||
|
||||
elif recording and not nonsilent_chunks:
|
||||
# Check if there were valid nonsilent chunks before trimming
|
||||
if len(nonsilent_chunks) > 0:
|
||||
# Transmission has ended (silence detected for the required duration)
|
||||
if len(frames) > 0:
|
||||
# Save recording without leading/trailing silence
|
||||
trimmed_audio = sound[nonsilent_chunks[0][0]:nonsilent_chunks[-1][1]]
|
||||
filename = get_filename()
|
||||
save_wave_file(filename, trimmed_audio)
|
||||
|
||||
recording = False
|
||||
frames.clear() # Clear frames to prepare for the next transmission
|
||||
print("Recording stopped, waiting for the next transmission...")
|
||||
|
||||
# Optionally: adjust silence threshold if needed
|
||||
# E.g., try increasing silence_thresh to detect lower sounds
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("device_id", type=int, help="The ID of the audio device to use")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
args = parse_arguments()
|
||||
print("Arguments: %s", args)
|
||||
record_transmissions(args.device_id)
|
||||
except KeyboardInterrupt:
|
||||
print("Stopping...")
|
||||
finally:
|
||||
audio.terminate()
|
||||
@@ -25,8 +25,11 @@ let botCallback;
|
||||
export const initDiscordBotClient = (clientId, callback, runPDAB = true) => {
|
||||
botCallback = callback;
|
||||
|
||||
if (runPDAB) launchProcess("python", [join(__dirname, "./pdab/main.py"), process.env.AUDIO_DEVICE_ID, clientId, port], false, false, join(__dirname, "./pdab"));
|
||||
if (runPDAB) launchProcess("python", [join(__dirname, "../../pdab/main.py"), process.env.AUDIO_DEVICE_ID, clientId, port], false, false, join(__dirname, "../../pdab"));
|
||||
pdabProcess = true; // TODO - Make this more dynamic
|
||||
|
||||
// Start the PDAB Recorder
|
||||
if (runPDAB) launchProcess("python", [join(__dirname, "./pdab-recorder.py"), process.env.AUDIO_DEVICE_ID], false, false, __dirname);
|
||||
}
|
||||
|
||||
export const startPdabSocketServer = () => {
|
||||
|
||||
@@ -11,6 +11,9 @@ dotenv.config();
|
||||
let currentSystem = undefined;
|
||||
let crashDetectionInterval; // Variable to store the crash detection interval ID
|
||||
|
||||
// Sleep utility to add delays between retries
|
||||
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* Checks the health of the OP25 web portal by making an HTTP HEAD request.
|
||||
* If the portal does not respond or there is an issue, retries a specified number of times.
|
||||
@@ -25,7 +28,7 @@ const checkServiceHealth = async () => {
|
||||
try {
|
||||
log.INFO("Checking OP25 web portal health...");
|
||||
// Perform an HTTP HEAD request to the web portal with a 5-second timeout
|
||||
await axios.head('http://localhost:8081', { timeout: 5000 });
|
||||
await axios({ method: "get", url: 'http://localhost:8081', timeout: 5000 });
|
||||
log.INFO("Web portal is healthy.");
|
||||
} catch (error) {
|
||||
if (error.code === 'ECONNABORTED') {
|
||||
@@ -40,10 +43,13 @@ const checkServiceHealth = async () => {
|
||||
|
||||
// Retry mechanism
|
||||
const retryAttempts = 3;
|
||||
const delayBetweenRetries = 3000; // 3 seconds delay
|
||||
|
||||
for (let i = 1; i <= retryAttempts; i++) {
|
||||
log.INFO(`Retrying to check web portal health... Attempt ${i}/${retryAttempts}`);
|
||||
try {
|
||||
await axios.head('http://localhost:8081', { timeout: 5000 });
|
||||
await sleep(delayBetweenRetries); // Add delay before retrying
|
||||
await axios({ method: "get", url: 'http://localhost:8081', timeout: 5000 });
|
||||
log.INFO("Web portal is healthy on retry.");
|
||||
return;
|
||||
} catch (retryError) {
|
||||
|
||||
Reference in New Issue
Block a user