mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-06-05 15:15:58 +02:00
Clean spectral analysis
This commit is contained in:
@@ -1,70 +0,0 @@
|
|||||||
import numpy as np
|
|
||||||
import scipy.io.wavfile as wavfile
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
import sys # Import the sys module
|
|
||||||
|
|
||||||
from utils.helpers import get_settings
|
|
||||||
|
|
||||||
# Dependencies /usr/bin/pip install numpy scipy matplotlib
|
|
||||||
|
|
||||||
# Define the directory containing the WAV files
|
|
||||||
conf = get_settings()
|
|
||||||
input_directory = os.path.join(conf['RECS_DIR'], 'StreamData')
|
|
||||||
output_directory = os.path.join(conf['RECS_DIR'], 'Extracted/Charts')
|
|
||||||
|
|
||||||
# Ensure the output directory exists
|
|
||||||
if not os.path.exists(output_directory):
|
|
||||||
os.makedirs(output_directory)
|
|
||||||
|
|
||||||
# Check if a command-line argument is provided
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
# If an argument is provided, use it as the file to analyze
|
|
||||||
wav_files = [sys.argv[1]]
|
|
||||||
else:
|
|
||||||
# If no argument is provided, analyze all WAV files in the directory
|
|
||||||
wav_files = glob.glob(os.path.join(input_directory, '*.wav'))
|
|
||||||
|
|
||||||
# Process each file
|
|
||||||
for file_path in wav_files:
|
|
||||||
# Load the WAV file
|
|
||||||
sample_rate, audio_data = wavfile.read(file_path)
|
|
||||||
|
|
||||||
# If stereo, select only one channel
|
|
||||||
if len(audio_data.shape) > 1:
|
|
||||||
audio_data = audio_data[:, 0]
|
|
||||||
|
|
||||||
# Apply the Hamming window to the audio data
|
|
||||||
hamming_window = np.hamming(len(audio_data))
|
|
||||||
windowed_data = audio_data * hamming_window
|
|
||||||
|
|
||||||
# Compute the FFT of the windowed audio data
|
|
||||||
audio_fft = np.fft.fft(windowed_data)
|
|
||||||
audio_fft = np.abs(audio_fft)
|
|
||||||
|
|
||||||
# Compute the frequencies associated with the FFT values
|
|
||||||
frequencies = np.fft.fftfreq(len(windowed_data), d=1/sample_rate)
|
|
||||||
|
|
||||||
# Select the range of interest
|
|
||||||
idx = np.where((frequencies >= 150) & (frequencies <= 15000))
|
|
||||||
|
|
||||||
# Calculate the saturation threshold based on the bit depth
|
|
||||||
bit_depth = audio_data.dtype.itemsize * 8
|
|
||||||
max_amplitude = 2**(bit_depth - 1) - 1
|
|
||||||
saturation_threshold = 0.8 * max_amplitude
|
|
||||||
|
|
||||||
# Plot the spectrum with a logarithmic Y-axis
|
|
||||||
plt.figure(figsize=(10, 6))
|
|
||||||
plt.semilogy(frequencies[idx], audio_fft[idx], label='Spectrum')
|
|
||||||
plt.axhline(y=saturation_threshold, color='r', linestyle='--', label='Saturation Threshold')
|
|
||||||
plt.xlabel("Frequency (Hz)")
|
|
||||||
plt.ylabel("Amplitude (Logarithmic)")
|
|
||||||
plt.title(f"Frequency Spectrum (150 - 15000 Hz) - {os.path.basename(file_path)}")
|
|
||||||
plt.legend()
|
|
||||||
plt.grid(True)
|
|
||||||
|
|
||||||
# Save the plot as a PNG file
|
|
||||||
output_filename = os.path.basename(file_path).replace('.wav', '_spectrum.png')
|
|
||||||
plt.savefig(os.path.join(output_directory, output_filename))
|
|
||||||
plt.close() # Close the figure to free memory
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Performs the recording from the specified RTSP stream or soundcard
|
|
||||||
set +u
|
|
||||||
# shellcheck disable=SC1091
|
|
||||||
source /etc/birdnet/birdnet.conf
|
|
||||||
|
|
||||||
# Read the logging level from the configuration option
|
|
||||||
# shellcheck disable=SC2154
|
|
||||||
LOGGING_LEVEL="${LogLevel_BirdnetRecordingService:-error}"
|
|
||||||
# If empty for some reason default to log level of error
|
|
||||||
[ -z "$LOGGING_LEVEL" ] && LOGGING_LEVEL='error'
|
|
||||||
# Additionally if we're at debug or info level then allow printing of script commands and variables
|
|
||||||
if [ "$LOGGING_LEVEL" == "info" ] || [ "$LOGGING_LEVEL" == "debug" ];then
|
|
||||||
# Enable printing of commands/variables etc to terminal for debugging
|
|
||||||
set -x
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ -z "$RECORDING_LENGTH" ] && RECORDING_LENGTH=15
|
|
||||||
[ -d "$RECS_DIR"/StreamData ] || mkdir -p "$RECS_DIR"/StreamData
|
|
||||||
|
|
||||||
filename="Spectrum_$(date "+%Y-%m-%d_%H:%M").wav"
|
|
||||||
|
|
||||||
if [ -n "${RTSP_STREAM:-}" ];then
|
|
||||||
# Explode the RSPT steam setting into an array so we can count the number we have
|
|
||||||
RTSP_STREAMS_EXPLODED_ARRAY=("${RTSP_STREAM//,/ }")
|
|
||||||
|
|
||||||
while true;do
|
|
||||||
|
|
||||||
# Initially start the count off at 1 - our very first stream
|
|
||||||
RTSP_STREAMS_STARTED_COUNT=1
|
|
||||||
FFMPEG_PARAMS=""
|
|
||||||
|
|
||||||
# Loop over the streams
|
|
||||||
for i in "${RTSP_STREAMS_EXPLODED_ARRAY[@]}"
|
|
||||||
do
|
|
||||||
# Map id used to map input to output (first stream being 0), this is 0 based in ffmpeg so decrement our counter (which is more human readable) by 1
|
|
||||||
MAP_ID="$((RTSP_STREAMS_STARTED_COUNT-1))"
|
|
||||||
# Build up the parameters to process the RSTP stream, including mapping for the output
|
|
||||||
FFMPEG_PARAMS+="-vn -thread_queue_size 512 -i ${i} -map ${MAP_ID}:a:0 -t ${RECORDING_LENGTH} -acodec pcm_s16le -ac 2 -ar 48000 file:${RECS_DIR}/StreamData/$filename "
|
|
||||||
# Increment counter
|
|
||||||
((RTSP_STREAMS_STARTED_COUNT += 1))
|
|
||||||
done
|
|
||||||
|
|
||||||
# Make sure were passing something valid to ffmpeg, ffmpeg will run interactive and control our loop by waiting ${RECORDING_LENGTH} between loops because it will stop once that much has been recorded
|
|
||||||
if [ -n "$FFMPEG_PARAMS" ];then
|
|
||||||
ffmpeg -hide_banner -loglevel "$LOGGING_LEVEL" -nostdin "$FFMPEG_PARAMS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
done
|
|
||||||
else
|
|
||||||
if pgrep arecord &> /dev/null ;then
|
|
||||||
echo "Recording"
|
|
||||||
else
|
|
||||||
if [ -z "${REC_CARD}" ];then
|
|
||||||
arecord -f S16_LE -c"${CHANNELS}" -r48000 -t wav --max-file-time "${RECORDING_LENGTH}"\
|
|
||||||
--use-strftime "${RECS_DIR}"/StreamData/"$filename"
|
|
||||||
else
|
|
||||||
arecord -f S16_LE -c"${CHANNELS}" -r48000 -t wav --max-file-time "${RECORDING_LENGTH}"\
|
|
||||||
-D "${REC_CARD}" --use-strftime "${RECS_DIR}"/StreamData/"$filename"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create the spectral analysis
|
|
||||||
"$PYTHON_VIRTUAL_ENV" "$HOME"/BirdNET-Pi/scripts/spectral_analysis.py
|
|
||||||
Reference in New Issue
Block a user