From cf5b1396b1e789ffcdc8d247ef8fc25697003765 Mon Sep 17 00:00:00 2001 From: CroneKorkN Date: Sun, 1 Jun 2025 19:23:40 +0200 Subject: [PATCH] wip --- process | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/process b/process index 484df53..8e7a578 100755 --- a/process +++ b/process @@ -1,13 +1,13 @@ #!/usr/bin/env python3 import os -import concurrent.futures import datetime import numpy as np import matplotlib.pyplot as plt import soundfile -import scipy.signal from scipy.fft import rfft, rfftfreq import shutil +import traceback + RECORDINGS_DIR = "recordings" PROCESSED_RECORDINGS_DIR = "recordings/processed" @@ -23,6 +23,7 @@ DETECTION_DISTANCE_SECONDS = 30 # seconds (minimum time between detections) DETECTION_DISTANCE_BLOCKS = DETECTION_DISTANCE_SECONDS // BLOCK_SECONDS # number of blocks to skip after a detection BLOCK_OVERLAP_FACTOR = 0.9 # overlap between blocks (0.2 means 20% overlap) MIN_SIGNAL_QUALITY = 1000.0 # maximum noise level (relative DB) to consider a detection valid +PLOT_PADDING_SECONDS = 2 # seconds (padding before and after the event in the plot) def process_recording(filename): print('processing', filename) @@ -78,6 +79,8 @@ def process_recording(filename): current_event = { 'start_at': block_date, 'end_at': block_date, + 'start_sample': sample_num, + 'end_sample': sample_num + samples_per_block, 'start_freq': max_freq, 'end_freq': max_freq, 'max_amplitude': max_amplitude, @@ -86,6 +89,7 @@ def process_recording(filename): current_event.update({ 'end_at': block_date, 'end_freq': max_freq, + 'end_sample': sample_num + samples_per_block, 'max_amplitude': max(max_amplitude, current_event['max_amplitude']), }) print(f'- {block_date.strftime('%Y-%m-%d %H:%M:%S')}: {max_amplitude:.1f}rDB @ {max_freq:.1f}Hz (signal {signal_quality:.3f}x)') @@ -95,8 +99,7 @@ def process_recording(filename): duration = (current_event['end_at'] - current_event['start_at']).total_seconds() print(f'🔊 {current_event['start_at'].strftime('%Y-%m-%d %H:%M:%S')} ({duration:.1f}s): {current_event['start_freq']:.1f}Hz->{current_event['end_freq']:.1f}Hz @{current_event['max_amplitude']:.0f}rDB') - write_clip() - write_plot() + write_event(current_event=current_event, sound=sound, samplerate=samplerate) current_event = None sample_num += DETECTION_DISTANCE_BLOCKS * samples_per_block @@ -105,12 +108,25 @@ def process_recording(filename): -def write_clip(): - pass - - -def write_plot(): - pass +def write_event(current_event, sound, samplerate): + # write a spectrogram using the sound from start to end of the event + event_start_sample = current_event['start_sample'] - samplerate * PLOT_PADDING_SECONDS + event_end_sample = current_event['end_sample'] + samplerate * PLOT_PADDING_SECONDS + event_clip = sound[event_start_sample:event_end_sample] + plot_start_date = current_event['start_at'] - datetime.timedelta(seconds=PLOT_PADDING_SECONDS) + plt.figure(figsize=(8, 6)) + plt.specgram(event_clip, Fs=samplerate, NFFT=samplerate, noverlap=samplerate//2, cmap='inferno', vmin=-100, vmax=-10) + plt.title(f"Bootshorn @{plot_start_date.strftime('%Y-%m-%d %H:%M:%S')}") + plt.xlabel("Time (s)") + plt.ylabel("Frequency (Hz)") + plt.colorbar(label="Intensity (dB)") + plt.ylim(50, 1000) + spectrogram_path = os.path.join(DETECTIONS_DIR, f"{current_event['start_at'].strftime('%Y-%m-%d_%H-%M-%S.%f%z')}.png") + plt.savefig(spectrogram_path) + plt.close() + # write flac + flac_path = os.path.join(DETECTIONS_DIR, f"{current_event['start_at'].strftime('%Y-%m-%d_%H-%M-%S.%f%z')}.flac") + soundfile.write(flac_path, event_clip, samplerate, format='FLAC') def main(): @@ -124,7 +140,6 @@ def main(): except Exception as e: print(f"Error processing {filename}: {e}") # print stacktrace - import traceback traceback.print_exc()