From c8e051a4a336e542f368e57613d491660c0dc514 Mon Sep 17 00:00:00 2001 From: thibaudk Date: Fri, 1 Nov 2024 15:35:00 +0000 Subject: [PATCH] [frequencies] seperate compute_frequencies betwin analyzer and display --- Amuencha/AmuenchaModel.cpp | 12 +++++++++++- Amuencha/AmuenchaModel.hpp | 17 ----------------- Amuencha/AmuenchaUi.hpp | 20 ++------------------ Amuencha/FrequencyAnalyzer.cpp | 23 +++++++++++++++++++++-- Amuencha/FrequencyAnalyzer.hpp | 7 ++++++- Amuencha/SpiralDisplay.cpp | 31 ++++++++----------------------- Amuencha/SpiralDisplay.hpp | 9 --------- 7 files changed, 48 insertions(+), 71 deletions(-) diff --git a/Amuencha/AmuenchaModel.cpp b/Amuencha/AmuenchaModel.cpp index 85ff0e6..51e4e9e 100644 --- a/Amuencha/AmuenchaModel.cpp +++ b/Amuencha/AmuenchaModel.cpp @@ -4,7 +4,17 @@ namespace Amuencha { void Model::prepare(setup info) { - sampling_rate = info.rate; + if (analyzer.isRunning()) return; + + analyzer.setup(info.rate, + [&] (const std::vector& r_f, + const std::vector& p_s) + { + this->send_message({.reassigned_frequencies = r_f, + .power_spectrum = p_s}); + }); + + analyzer.start(QThread::NormalPriority); } void Model::operator()(tick t) diff --git a/Amuencha/AmuenchaModel.hpp b/Amuencha/AmuenchaModel.hpp index 624a9e8..b9ac84d 100644 --- a/Amuencha/AmuenchaModel.hpp +++ b/Amuencha/AmuenchaModel.hpp @@ -35,22 +35,6 @@ public: halp::spinbox_i32<"Periods", halp::range{.min = 0, .max = 99, .init = 30}> periods; } inputs; - void process_message(const std::vector& frequencies) - { - analyzer.setup(sampling_rate, - frequencies, - [&] (const std::vector& r_f, - const std::vector& p_s) - { - this->send_message({.reassigned_frequencies = r_f, - .power_spectrum = p_s}); - }, - inputs.periods); - - if (!analyzer.isRunning()) - analyzer.start(QThread::NormalPriority); - } - struct outs { halp::midi_bus<"Output"> midi; @@ -68,7 +52,6 @@ public: struct ui; private: - float sampling_rate; FrequencyAnalyzer analyzer; }; diff --git a/Amuencha/AmuenchaUi.hpp b/Amuencha/AmuenchaUi.hpp index 355f1f8..51cbe0b 100644 --- a/Amuencha/AmuenchaUi.hpp +++ b/Amuencha/AmuenchaUi.hpp @@ -30,34 +30,22 @@ struct Model::ui // Define the communication between UI and processor. struct bus { - std::function&&)> send_message; - // Set up connections void init(ui& self) { - self.spiral.set_frequencies_callback( - [&] { this->send_message(self.spiral.get_frequencies()); } - ); - self.controls.min.on_changed = [&] (int min) { - // self.spiral.set_min_max_notes(min, self.controls.max.value); + self.spiral.set_min_max_notes(min, self.controls.max.value); }; self.controls.max.on_changed = [&] (int max) { - // self.spiral.set_min_max_notes(self.controls.min.value, max); + self.spiral.set_min_max_notes(self.controls.min.value, max); }; } // Receive a message on the UI thread from the processing thread static void process_message(ui& self, const processor_to_ui& msg) { - if (!first_msg) - { - self.spiral.compute_frequencies(); - first_msg = true; - } - if (msg.power_spectrum.empty() || msg.reassigned_frequencies.empty()) return; @@ -65,10 +53,6 @@ struct Model::ui self.spiral.power_handler(msg.reassigned_frequencies, msg.power_spectrum); } - - static bool first_msg; }; }; } - -inline bool Amuencha::Model::ui::bus::first_msg = false; diff --git a/Amuencha/FrequencyAnalyzer.cpp b/Amuencha/FrequencyAnalyzer.cpp index 904d838..870c7ac 100644 --- a/Amuencha/FrequencyAnalyzer.cpp +++ b/Amuencha/FrequencyAnalyzer.cpp @@ -174,13 +174,32 @@ void Amuencha::FrequencyAnalyzer::run() mutex.unlock(); } -void Amuencha::FrequencyAnalyzer::setup(float sampling_rate, const std::vector& frequencies, PowerHandler&& handler, float periods, float max_buffer_duration) +void Amuencha::FrequencyAnalyzer::setup(float sampling_rate, + PowerHandler&& handler, + int min_midi_note, + int max_midi_note, + float periods, + float max_buffer_duration) { // Block data processing while changing the data structures data_mutex.lock(); this->samplerate_div_2pi = sampling_rate/two_pi; - this->frequencies = frequencies; + + // Start with A440, but this could be parametrizable as well + const float fref = 440; + const float log2_fref = log2(fref); + const int aref = 69; // use the midi numbering scheme, because why not + float log2_fmin = (min_midi_note - aref) / 12. + log2_fref; + float log2_fmax = (max_midi_note - aref) / 12. + log2_fref; + int num_bins = max_midi_note - min_midi_note; + + frequencies.resize(num_bins); + for (int b{0}; b < num_bins; ++b) + { + float bratio = (float)b / (num_bins - 1.); + frequencies[b] = exp2(log2_fmin + (log2_fmax - log2_fmin) * bratio); + } this->reassigned_frequencies = frequencies; this->power_spectrum.resize(frequencies.size()); diff --git a/Amuencha/FrequencyAnalyzer.hpp b/Amuencha/FrequencyAnalyzer.hpp index 3055a30..2d06f7e 100644 --- a/Amuencha/FrequencyAnalyzer.hpp +++ b/Amuencha/FrequencyAnalyzer.hpp @@ -56,7 +56,12 @@ public: // At lower frequencies, long buffers are needed for accurate frequency separation. // When that max buffer duration is reached, then it is capped and the frequency resolution decreases // Too low buffers also limit the min_freq, duration must be >= period - void setup(float sampling_rate, const std::vector& frequencies, PowerHandler&& handler, float periods = 20, float max_buffer_duration = 500); + void setup(float sampling_rate, + PowerHandler&& handler, + int min_midi_note = 24, + int max_midi_note = 72, + float periods = 20, + float max_buffer_duration = 500); // call to remove all existing chunk references // this may cause signal loss, but this is usually called precisely when the signal is lost... diff --git a/Amuencha/SpiralDisplay.cpp b/Amuencha/SpiralDisplay.cpp index 08da440..678b94d 100644 --- a/Amuencha/SpiralDisplay.cpp +++ b/Amuencha/SpiralDisplay.cpp @@ -5,7 +5,6 @@ Amuencha::SpiralDisplay::SpiralDisplay() , max_midi_note{72} , gain{1.f} , visual_fading{1} - , on_new_frequencies{[]{}} { for (int i{0}; i < 12; i++) note_positions[i] = std::polar(.9f, half_pi - i * two_pi / 12); @@ -24,16 +23,6 @@ void Amuencha::SpiralDisplay::set_min_max_notes(int min_midi_note, int max_midi_ // update(); } -std::vector Amuencha::SpiralDisplay::get_frequencies() const noexcept -{ - return frequencies; -} - -void Amuencha::SpiralDisplay::set_frequencies_callback(std::function &&callback) -{ - on_new_frequencies = callback; -} - void Amuencha::SpiralDisplay::compute_frequencies() { // Now the spiral @@ -43,16 +32,17 @@ void Amuencha::SpiralDisplay::compute_frequencies() const int aref = 69; // use the midi numbering scheme, because why not float log2_fmin = (min_midi_note - aref) / 12. + log2_fref; float log2_fmax = (max_midi_note - aref) / 12. + log2_fref; - int approx_pix_bin_width = 3; + // int approx_pix_bin_width = 3; // number of frequency bins is the number of pixels // along the spiral path / approx_pix_bin_width // According to mathworld, the correct formula for the path length // from the origin involves sqrt and log computations. // Here, we just want some approximate pixel count // => use all circles for the approx - int num_octaves = (max_midi_note - min_midi_note + 11) / 12; - float approx_num_pix = 0.5 * half * pi * num_octaves; - int num_bins = (int)(approx_num_pix / approx_pix_bin_width); + // int num_octaves = (max_midi_note - min_midi_note + 11) / 12; + // float approx_num_pix = 0.5 * half * pi * num_octaves; + // int num_bins = (int)(approx_num_pix / approx_pix_bin_width); + int num_bins = max_midi_note - min_midi_note; // one more bound than number of bins display_bins.resize(num_bins + 1); bin_sizes.resize(num_bins); @@ -66,21 +56,18 @@ void Amuencha::SpiralDisplay::compute_frequencies() // used to it (e.g. wikipedia note circle) const float theta_min = half_pi - two_pi * (min_midi_note % 12) / 12; // wrap in anti-trigonometric direction - const float theta_max = theta_min - two_pi * (max_midi_note - min_midi_note) / 12; + const float theta_max = theta_min - two_pi * (max_midi_note - min_midi_note - 1) / 12; - frequencies.resize(num_bins); for (int b{0}; b < num_bins; ++b) { - float bratio = (float)b / (num_bins - 1.); - frequencies[b] = exp2(log2_fmin + (log2_fmax - log2_fmin) * bratio); - bratio = (float)(b - 0.5) / (float)(num_bins - 1.); + float bratio = (float)(b - 0.5) / (float)(num_bins - 1.); display_bins[b] = exp2(log2_fmin + (log2_fmax - log2_fmin) * bratio); spiral_r_a[b].r = rmin + (rmax - rmin) * bratio; spiral_r_a[b].a = theta_min + (theta_max - theta_min) * bratio; spiral_positions[b] = std::polar(spiral_r_a[b].r, spiral_r_a[b].a); } - // repeat one more time to avoid a second for loops + // repeat one more time to avoid a second for loop float bratio = (float)(num_bins - 0.5) / (float)(num_bins - 1.); display_bins[num_bins] = exp2(log2_fmin + (log2_fmax - log2_fmin) * bratio); spiral_r_a[num_bins].r = rmin + (rmax - rmin) * bratio; @@ -92,8 +79,6 @@ void Amuencha::SpiralDisplay::compute_frequencies() display_spectrum.resize(num_bins); fill(display_spectrum.begin(), display_spectrum.end(), 0.); - - on_new_frequencies(); } void Amuencha::SpiralDisplay::power_handler(const std::vector& reassigned_frequencies, diff --git a/Amuencha/SpiralDisplay.hpp b/Amuencha/SpiralDisplay.hpp index 5403ace..0d76244 100644 --- a/Amuencha/SpiralDisplay.hpp +++ b/Amuencha/SpiralDisplay.hpp @@ -78,10 +78,6 @@ struct SpiralDisplay ctx.update(); } - [[nodiscard]] std::vector get_frequencies() const noexcept; - - void set_frequencies_callback(std::function&& callback); - // // Callback when the power spectrum is available at the prescribed frequencies // // The ID is that of the caller, setting the color of the display void power_handler(const std::vector& reassigned_frequencies, @@ -107,9 +103,6 @@ private: int min_midi_note, max_midi_note, visual_fading; - // central frequencies (log space) - std::vector frequencies; - // local copy for maintaining the display, adapted to the drawing bins std::vector display_spectrum; @@ -136,8 +129,6 @@ private: { return half - y * half; } - - std::function on_new_frequencies; }; }