summaryrefslogtreecommitdiff
path: root/assets/cpp/beat-detector.cpp
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-04 22:40:22 +1000
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-04 22:40:22 +1000
commite7398920468efa212e0c800767b7b2c41baca4f4 (patch)
treef7637f8ba70a9c14905d26ac08f975fe1f5407b3 /assets/cpp/beat-detector.cpp
parentplugin: add BeatTracker (diff)
downloadcaelestia-shell-e7398920468efa212e0c800767b7b2c41baca4f4.tar.gz
caelestia-shell-e7398920468efa212e0c800767b7b2c41baca4f4.tar.bz2
caelestia-shell-e7398920468efa212e0c800767b7b2c41baca4f4.zip
internal: remove beat-detector.cpp
Diffstat (limited to 'assets/cpp/beat-detector.cpp')
-rwxr-xr-xassets/cpp/beat-detector.cpp570
1 files changed, 0 insertions, 570 deletions
diff --git a/assets/cpp/beat-detector.cpp b/assets/cpp/beat-detector.cpp
deleted file mode 100755
index a92ae8a..0000000
--- a/assets/cpp/beat-detector.cpp
+++ /dev/null
@@ -1,570 +0,0 @@
-#include <algorithm>
-#include <atomic>
-#include <aubio/aubio.h>
-#include <chrono>
-#include <csignal>
-#include <cstring>
-#include <fstream>
-#include <iomanip>
-#include <iostream>
-#include <memory>
-#include <pipewire/pipewire.h>
-#include <spa/param/audio/format-utils.h>
-#include <spa/param/props.h>
-#include <sstream>
-#include <vector>
-
-class EnhancedBeatDetector {
-private:
- static constexpr uint32_t SAMPLE_RATE = 44100;
- static constexpr uint32_t CHANNELS = 1;
-
- // PipeWire objects
- pw_main_loop* main_loop_;
- pw_context* context_;
- pw_core* core_;
- pw_stream* stream_;
-
- // Aubio objects
- std::unique_ptr<aubio_tempo_t, decltype(&del_aubio_tempo)> tempo_;
- std::unique_ptr<fvec_t, decltype(&del_fvec)> input_buffer_;
- std::unique_ptr<fvec_t, decltype(&del_fvec)> output_buffer_;
-
- // Additional aubio objects for enhanced features
- std::unique_ptr<aubio_onset_t, decltype(&del_aubio_onset)> onset_;
- std::unique_ptr<aubio_pitch_t, decltype(&del_aubio_pitch)> pitch_;
- std::unique_ptr<fvec_t, decltype(&del_fvec)> pitch_buffer_;
-
- const uint32_t buf_size_;
- const uint32_t fft_size_;
-
- static std::atomic<bool> should_quit_;
- static EnhancedBeatDetector* instance_;
-
- // Enhanced features
- std::ofstream log_file_;
- bool enable_logging_;
- bool enable_performance_stats_;
- bool enable_pitch_detection_;
- bool enable_visual_feedback_;
-
- // Performance tracking
- std::chrono::high_resolution_clock::time_point last_process_time_;
- std::vector<double> process_times_;
- uint64_t total_beats_;
- uint64_t total_onsets_;
- std::chrono::steady_clock::time_point start_time_;
-
- // Beat analysis
- std::vector<float> recent_bpms_;
- static constexpr size_t BPM_HISTORY_SIZE = 10;
- float last_bpm_;
- std::chrono::steady_clock::time_point last_beat_time_;
-
- // Useless Visual feedback
- std::string generate_beat_visual(float bpm, bool is_beat) {
- if (!enable_visual_feedback_)
- return "";
-
- std::stringstream ss;
- if (is_beat) {
- // Useless Animated beat indicator based on BPM intensity
- int intensity = static_cast<int>(std::min(bpm / 20.0f, 10.0f));
- ss << "\r ";
- for (int i = 0; i < intensity; ++i)
- ss << "█";
- for (int i = intensity; i < 10; ++i)
- ss << "░";
- ss << " BPM: " << std::fixed << std::setprecision(1) << bpm;
- ss << " | Avg: " << get_average_bpm();
- }
- return ss.str();
- }
-
-public:
- explicit EnhancedBeatDetector(uint32_t buf_size = 512, bool enable_logging = true,
- bool enable_performance_stats = true, bool enable_pitch_detection = false, bool enable_visual_feedback = true)
- : main_loop_(nullptr)
- , context_(nullptr)
- , core_(nullptr)
- , stream_(nullptr)
- , tempo_(nullptr, &del_aubio_tempo)
- , input_buffer_(nullptr, &del_fvec)
- , output_buffer_(nullptr, &del_fvec)
- , onset_(nullptr, &del_aubio_onset)
- , pitch_(nullptr, &del_aubio_pitch)
- , pitch_buffer_(nullptr, &del_fvec)
- , buf_size_(buf_size)
- , fft_size_(buf_size * 2)
- , enable_logging_(enable_logging)
- , enable_performance_stats_(enable_performance_stats)
- , enable_pitch_detection_(enable_pitch_detection)
- , enable_visual_feedback_(enable_visual_feedback)
- , total_beats_(0)
- , total_onsets_(0)
- , last_bpm_(0.0f) {
- instance_ = this;
- recent_bpms_.reserve(BPM_HISTORY_SIZE);
- if (enable_performance_stats_) {
- process_times_.reserve(1000); // Reserve space for performance data
- }
- initialize();
- }
-
- ~EnhancedBeatDetector() {
- print_final_stats();
- cleanup();
- instance_ = nullptr;
- }
-
- // Delete copy constructor and assignment operator
- EnhancedBeatDetector(const EnhancedBeatDetector&) = delete;
- EnhancedBeatDetector& operator=(const EnhancedBeatDetector&) = delete;
-
- bool initialize() {
- start_time_ = std::chrono::steady_clock::now();
-
- // Useless Initialize logging (actually useful)
- if (enable_logging_) {
- auto now = std::chrono::system_clock::now();
- auto time_t = std::chrono::system_clock::to_time_t(now);
- std::stringstream filename;
- filename << "beat_log_" << std::put_time(std::localtime(&time_t), "%Y%m%d_%H%M%S") << ".txt";
- log_file_.open(filename.str());
- if (log_file_.is_open()) {
- log_file_ << "# Beat Detection Log - " << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S")
- << "\n";
- log_file_ << "# Timestamp,BPM,Onset,Pitch(Hz),ProcessTime(ms)\n";
- std::cout << " Logging to: " << filename.str() << std::endl;
- }
- }
-
- // Initialize PipeWire
- pw_init(nullptr, nullptr);
-
- main_loop_ = pw_main_loop_new(nullptr);
- if (!main_loop_) {
- std::cerr << " Failed to create main loop" << std::endl;
- return false;
- }
-
- context_ = pw_context_new(pw_main_loop_get_loop(main_loop_), nullptr, 0);
- if (!context_) {
- std::cerr << " Failed to create context" << std::endl;
- return false;
- }
-
- core_ = pw_context_connect(context_, nullptr, 0);
- if (!core_) {
- std::cerr << " Failed to connect to PipeWire" << std::endl;
- return false;
- }
-
- // Initialize Aubio objects
- tempo_.reset(new_aubio_tempo("default", fft_size_, buf_size_, SAMPLE_RATE));
- if (!tempo_) {
- std::cerr << " Failed to create aubio tempo detector" << std::endl;
- return false;
- }
-
- input_buffer_.reset(new_fvec(buf_size_));
- output_buffer_.reset(new_fvec(1));
-
- if (!input_buffer_ || !output_buffer_) {
- std::cerr << " Failed to create aubio buffers" << std::endl;
- return false;
- }
-
- // Initialize onset detection
- onset_.reset(new_aubio_onset("default", fft_size_, buf_size_, SAMPLE_RATE));
- if (!onset_) {
- std::cerr << " Failed to create aubio onset detector" << std::endl;
- return false;
- }
-
- // Initialize pitch detection if enabled
- if (enable_pitch_detection_) {
- pitch_.reset(new_aubio_pitch("default", fft_size_, buf_size_, SAMPLE_RATE));
- pitch_buffer_.reset(new_fvec(1));
- if (!pitch_ || !pitch_buffer_) {
- std::cerr << " Failed to create aubio pitch detector" << std::endl;
- return false;
- }
- aubio_pitch_set_unit(pitch_.get(), "Hz");
- }
-
- return setup_stream();
- }
-
- void run() {
- if (!main_loop_)
- return;
-
- print_startup_info();
- pw_main_loop_run(main_loop_);
- }
-
- void stop() {
- should_quit_ = true;
- if (main_loop_) {
- pw_main_loop_quit(main_loop_);
- }
- }
-
- static void signal_handler(int sig) {
- if (instance_) {
- std::cout << "\n Received signal " << sig << ", stopping gracefullllly..." << std::endl;
- instance_->stop();
- }
- }
-
-private:
- void print_startup_info() {
- std::cout << "\n󰝚 Beat Detector Started!" << std::endl;
- std::cout << " Buffer size: " << buf_size_ << " samples" << std::endl;
- std::cout << " Sample rate: " << SAMPLE_RATE << " Hz" << std::endl;
- std::cout << " Features enabled:" << std::endl;
- std::cout << "  Logging: " << (enable_logging_ ? " " : "") << std::endl;
- std::cout << "  Performance stats: " << (enable_performance_stats_ ? " " : "") << std::endl;
- std::cout << " 󰗅 Pitch detection: " << (enable_pitch_detection_ ? " " : "") << std::endl;
- std::cout << "\n Listening for beats... Press Ctrl+C to stop.\n" << std::endl;
- }
-
- void print_final_stats() {
- if (!enable_performance_stats_)
- return;
-
- auto end_time = std::chrono::steady_clock::now();
- auto duration = std::chrono::duration_cast<std::chrono::seconds>(end_time - start_time_);
-
- std::cout << "\n Final Statistics:" << std::endl;
- std::cout << " 󱎫 Total runtime: " << duration.count() << " seconds" << std::endl;
- std::cout << "  Total beats detected: " << total_beats_ << std::endl;
- std::cout << "  Total onsets detected: " << total_onsets_ << std::endl;
-
- if (!process_times_.empty()) {
- double avg_time = 0;
- for (double t : process_times_)
- avg_time += t;
- avg_time /= static_cast<double>(process_times_.size());
-
- auto max_time = *std::max_element(process_times_.begin(), process_times_.end());
- auto min_time = *std::min_element(process_times_.begin(), process_times_.end());
-
- std::cout << " ⚡ Average processing time: " << std::fixed << std::setprecision(3) << avg_time << " ms"
- << std::endl;
- std::cout << " 📈 Max processing time: " << max_time << " ms" << std::endl;
- std::cout << " 📉 Min processing time: " << min_time << " ms" << std::endl;
- }
-
- if (!recent_bpms_.empty()) {
- std::cout << " 󰝚 Final average BPM: " << get_average_bpm() << std::endl;
- }
- }
-
- float get_average_bpm() const {
- if (recent_bpms_.empty())
- return 0.0f;
- float sum = 0;
- for (float bpm : recent_bpms_)
- sum += bpm;
- return sum / static_cast<float>(recent_bpms_.size());
- }
-
- bool setup_stream() {
- // Stream events
- static const pw_stream_events stream_events = {
- .version = PW_VERSION_STREAM_EVENTS,
- .destroy = nullptr,
- .state_changed = on_state_changed,
- .control_info = nullptr,
- .io_changed = nullptr,
- .param_changed = nullptr,
- .add_buffer = nullptr,
- .remove_buffer = nullptr,
- .process = on_process,
- .drained = nullptr,
- .command = nullptr,
- .trigger_done = nullptr,
- };
-
- stream_ = pw_stream_new_simple(pw_main_loop_get_loop(main_loop_), "enhanced-beat-detector",
- pw_properties_new(
- PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Capture", PW_KEY_MEDIA_ROLE, "Music", nullptr),
- &stream_events, this);
-
- if (!stream_) {
- std::cerr << " Failed to create stream" << std::endl;
- return false;
- }
-
- // Audio format parameters
- uint8_t buffer[1024];
- spa_pod_builder pod_builder;
- spa_pod_builder_init(&pod_builder, buffer, sizeof(buffer));
-
- struct spa_audio_info_raw audio_info = {};
- audio_info.format = SPA_AUDIO_FORMAT_F32_LE;
- audio_info.channels = CHANNELS;
- audio_info.rate = SAMPLE_RATE;
- audio_info.flags = 0;
-
- const spa_pod* params[1];
- params[0] = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_EnumFormat, &audio_info);
-
- if (pw_stream_connect(stream_, PW_DIRECTION_INPUT, PW_ID_ANY,
- static_cast<pw_stream_flags>(
- PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS),
- params, 1) < 0) {
- std::cerr << " Failed to connect stream" << std::endl;
- return false;
- }
-
- return true;
- }
-
- static void on_state_changed(
- void* userdata, enum pw_stream_state /* old_state */, enum pw_stream_state state, const char* error) {
- auto* detector = static_cast<EnhancedBeatDetector*>(userdata);
-
- const char* state_emoji = "󰑓 ";
- switch (state) {
- case PW_STREAM_STATE_CONNECTING:
- state_emoji = "󰄙 ";
- break;
- case PW_STREAM_STATE_PAUSED:
- state_emoji = "";
- break;
- case PW_STREAM_STATE_STREAMING:
- state_emoji = "󰝚 ";
- break;
- case PW_STREAM_STATE_ERROR:
- state_emoji = "";
- break;
- case PW_STREAM_STATE_UNCONNECTED:
- state_emoji = " ";
- break;
- }
-
- std::cout << state_emoji << " Stream state: " << pw_stream_state_as_string(state) << std::endl;
-
- if (state == PW_STREAM_STATE_ERROR) {
- std::cerr << " Stream error: " << (error ? error : "unknown") << std::endl;
- detector->stop();
- }
- }
-
- static void on_process(void* userdata) {
- auto* detector = static_cast<EnhancedBeatDetector*>(userdata);
- detector->process_audio();
- }
-
- void process_audio() {
- if (should_quit_)
- return;
-
- auto process_start = std::chrono::high_resolution_clock::now();
-
- pw_buffer* buffer = pw_stream_dequeue_buffer(stream_);
- if (!buffer)
- return;
-
- spa_buffer* spa_buf = buffer->buffer;
- if (!spa_buf->datas[0].data) {
- pw_stream_queue_buffer(stream_, buffer);
- return;
- }
-
- const float* audio_data = static_cast<const float*>(spa_buf->datas[0].data);
- const uint32_t n_samples = spa_buf->datas[0].chunk->size / sizeof(float);
-
- // Process in chunks
- for (uint32_t offset = 0; offset + buf_size_ <= n_samples; offset += buf_size_) {
- std::memcpy(input_buffer_->data, audio_data + offset, buf_size_ * sizeof(float));
-
- // Beat detection
- aubio_tempo_do(tempo_.get(), input_buffer_.get(), output_buffer_.get());
- bool is_beat = output_buffer_->data[0] != 0.0f;
-
- // Onset detection
- aubio_onset_do(onset_.get(), input_buffer_.get(), output_buffer_.get());
- bool is_onset = output_buffer_->data[0] != 0.0f;
-
- float pitch_hz = 0.0f;
- if (enable_pitch_detection_) {
- aubio_pitch_do(pitch_.get(), input_buffer_.get(), pitch_buffer_.get());
- pitch_hz = pitch_buffer_->data[0];
- }
-
- if (is_beat) {
- total_beats_++;
- last_bpm_ = aubio_tempo_get_bpm(tempo_.get());
- last_beat_time_ = std::chrono::steady_clock::now();
-
- // Update BPM history
- recent_bpms_.push_back(last_bpm_);
- if (recent_bpms_.size() > BPM_HISTORY_SIZE) {
- recent_bpms_.erase(recent_bpms_.begin());
- }
-
- // Visual feedback
- if (enable_visual_feedback_) {
- std::cout << generate_beat_visual(last_bpm_, true) << std::flush;
- } else {
- std::cout << " BPM: " << std::fixed << std::setprecision(1) << last_bpm_ << std::endl;
- }
- }
-
- if (is_onset) {
- total_onsets_++;
- }
-
- // Logging
- if (enable_logging_ && log_file_.is_open() && (is_beat || is_onset)) {
- auto now = std::chrono::system_clock::now();
- auto time_t = std::chrono::system_clock::to_time_t(now);
- auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
-
- log_file_ << std::put_time(std::localtime(&time_t), "%H:%M:%S") << "." << std::setfill('0')
- << std::setw(3) << ms.count() << "," << (is_beat ? last_bpm_ : 0) << "," << (is_onset ? 1 : 0)
- << "," << pitch_hz << ",";
- }
- }
-
- pw_stream_queue_buffer(stream_, buffer);
-
- // Performance tracking
- if (enable_performance_stats_) {
- auto process_end = std::chrono::high_resolution_clock::now();
- auto process_time = std::chrono::duration<double, std::milli>(process_end - process_start).count();
-
- if (log_file_.is_open() && (total_beats_ > 0 || total_onsets_ > 0)) {
- log_file_ << std::fixed << std::setprecision(3) << process_time << "\n";
- }
-
- if (process_times_.size() < 1000) {
- process_times_.push_back(process_time);
- }
- }
- }
-
- void cleanup() {
- if (log_file_.is_open()) {
- log_file_.close();
- }
-
- if (stream_) {
- pw_stream_destroy(stream_);
- stream_ = nullptr;
- }
-
- if (core_) {
- pw_core_disconnect(core_);
- core_ = nullptr;
- }
-
- if (context_) {
- pw_context_destroy(context_);
- context_ = nullptr;
- }
-
- if (main_loop_) {
- pw_main_loop_destroy(main_loop_);
- main_loop_ = nullptr;
- }
-
- tempo_.reset();
- input_buffer_.reset();
- output_buffer_.reset();
- onset_.reset();
- pitch_.reset();
- pitch_buffer_.reset();
-
- pw_deinit();
-
- std::cout << "\n Cleanup complete - All resources freed!" << std::endl;
- }
-};
-
-// Static member definitions
-std::atomic<bool> EnhancedBeatDetector::should_quit_{ false };
-EnhancedBeatDetector* EnhancedBeatDetector::instance_{ nullptr };
-
-void print_usage() {
- std::cout << " Beat Detector Usage:" << std::endl;
- std::cout << " ./beat_detector [buffer_size] [options]" << std::endl;
- std::cout << "\nOptions:" << std::endl;
- std::cout << " --no-log Disable logging to file" << std::endl;
- std::cout << " --no-stats Disable performance statistics" << std::endl;
- std::cout << " --pitch Enable pitch detection" << std::endl;
- std::cout << " --no-visual Disable visual feedback" << std::endl;
- std::cout << " --help Show this help" << std::endl;
- std::cout << "\nExamples:" << std::endl;
- std::cout << " ./beat_detector # Default settings" << std::endl;
- std::cout << " ./beat_detector 256 --pitch # Smaller buffer with pitch detection" << std::endl;
- std::cout << " ./beat_detector 1024 --no-visual # Larger buffer, no visual feedback" << std::endl;
-}
-
-int main(int argc, char* argv[]) {
- // Parse command line arguments
- uint32_t buffer_size = 512;
- bool enable_logging = true;
- bool enable_performance_stats = true;
- bool enable_pitch_detection = false;
- bool enable_visual_feedback = true;
-
- for (int i = 1; i < argc; ++i) {
- std::string arg = argv[i];
-
- if (arg == "--help" || arg == "-h") {
- print_usage();
- return 0;
- } else if (arg == "--no-log") {
- enable_logging = false;
- } else if (arg == "--no-stats") {
- enable_performance_stats = false;
- } else if (arg == "--pitch") {
- enable_pitch_detection = true;
- } else if (arg == "--no-visual") {
- enable_visual_feedback = false;
- } else if (arg[0] != '-') {
- // Assume it's a buffer size
- try {
- unsigned long size = std::stoul(arg);
- if (size < 64 || size > 8192) {
- std::cerr << " Buffer size must be between 64 and 8192" << std::endl;
- return 1;
- }
- buffer_size = static_cast<uint32_t>(size);
- } catch (...) {
- std::cerr << " Invalid buffer size: " << arg << std::endl;
- return 1;
- }
- } else {
- std::cerr << " Unknown option: " << arg << std::endl;
- print_usage();
- return 1;
- }
- }
-
- // Set up signal handling
- std::signal(SIGINT, EnhancedBeatDetector::signal_handler);
- std::signal(SIGTERM, EnhancedBeatDetector::signal_handler);
-
- try {
- EnhancedBeatDetector detector(
- buffer_size, enable_logging, enable_performance_stats, enable_pitch_detection, enable_visual_feedback);
- detector.run();
- } catch (const std::exception& e) {
- std::cerr << " Error: " << e.what() << std::endl;
- return 1;
- }
-
- return 0;
-}
-
-/*
- * Compilation command:
- * g++ -std=c++17 -O3 -Wall -Wextra -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -I/usr/include/aubio \
- * -o beat_detector beat_detector.cpp -lpipewire-0.3 -laubio -pthread
- */