[port] compiles, loads, but crashes on startup
This commit is contained in:
parent
71851154d9
commit
705b5d9404
3 changed files with 202 additions and 33 deletions
116
Amuencha/SpiralDisplay.cpp
Normal file
116
Amuencha/SpiralDisplay.cpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
#include "SpiralDisplay.hpp"
|
||||
|
||||
Amuencha::SpiralDisplay::SpiralDisplay()
|
||||
: min_midi_note{24}
|
||||
, max_midi_note{72}
|
||||
{
|
||||
for (int i{0}; i < 12; i++) note_positions[i] = polar(.80f, half_pi - i * two_pi / 12);
|
||||
}
|
||||
|
||||
void Amuencha::SpiralDisplay::compute_frequencies()
|
||||
{
|
||||
// Now the spiral
|
||||
// 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 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);
|
||||
// one more bound than number of bins
|
||||
display_bins.resize(num_bins + 1);
|
||||
bin_sizes.resize(num_bins);
|
||||
spiral_positions.resize(num_bins + 1);
|
||||
spiral_r_a.resize(num_bins + 1);
|
||||
const float rmin = 0.1;
|
||||
const float rmax = 0.9;
|
||||
// The spiral and bounds are the same independently of how
|
||||
// the log space is divided into notes (e.g. 12ET)
|
||||
// Make it so c is on the y axis. Turn clockwise because people are
|
||||
// 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;
|
||||
|
||||
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.);
|
||||
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] = polar(spiral_r_a[b].r, spiral_r_a[b].a);
|
||||
}
|
||||
|
||||
// repeat one more time to avoid a second for loops
|
||||
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;
|
||||
spiral_r_a[num_bins].a = theta_min + (theta_max - theta_min) * bratio;
|
||||
spiral_positions[num_bins] = polar(spiral_r_a[num_bins].r, spiral_r_a[num_bins].a);
|
||||
|
||||
for (int b{0}; b < num_bins; ++b)
|
||||
bin_sizes[b] = display_bins[b + 1] - display_bins[b];
|
||||
|
||||
for (int id{0}; id < num_ID; ++id)
|
||||
{
|
||||
display_spectrum[id].resize(num_bins);
|
||||
fill(display_spectrum[id].begin(), display_spectrum[id].end(), 0.);
|
||||
}
|
||||
}
|
||||
|
||||
void Amuencha::SpiralDisplay::power_handler(int ID, const std::vector<float> &reassigned_frequencies, const std::vector<float> &power_spectrum)
|
||||
{
|
||||
fill(display_spectrum[ID].begin(), display_spectrum[ID].end(), 0.);
|
||||
|
||||
// simple histogram-like sum, assuming power entries are normalized
|
||||
int nidx = reassigned_frequencies.size();
|
||||
for (int idx{0}; idx < nidx; ++idx)
|
||||
{
|
||||
float rf = reassigned_frequencies[idx];
|
||||
int ri = idx;
|
||||
// reassigned frequencies are never too far off the original
|
||||
//if (rf>display_bins[idx] && rf<display_bins[idx+1]) ri = idx;
|
||||
//else...
|
||||
while (rf<display_bins[ri])
|
||||
{
|
||||
--ri;
|
||||
if (ri==-1) break;
|
||||
}
|
||||
if (ri==-1) continue; // ignore this frequency, it is below display min
|
||||
while (rf>display_bins[ri+1])
|
||||
{
|
||||
++ri;
|
||||
if (ri==nidx) break;
|
||||
}
|
||||
if (ri==nidx) continue; // ignore this frequency, it is above display max
|
||||
|
||||
// Normalization:
|
||||
// - for a given frequency, the sine/window size dependency was already
|
||||
// handled in the frequency analyzer
|
||||
// - but the result should not depend on how many frequencies are provided:
|
||||
// increasing the resolution should not increase the power
|
||||
// => we need a kind of density, not just the histogram-like sum of powers
|
||||
// falling into each bin
|
||||
// - consider the energy is coming from all the original bin size & sum
|
||||
// - This way, using finer bins do not increase the total sum
|
||||
display_spectrum[ID][ri] += power_spectrum[idx] * bin_sizes[idx];
|
||||
}
|
||||
|
||||
// - Then, spread on the destination bin for getting uniform density
|
||||
// measure independently of the target bin size
|
||||
for (int idx{0}; idx < nidx; ++idx) display_spectrum[ID][idx] /= bin_sizes[idx];
|
||||
}
|
||||
|
||||
|
|
@ -14,42 +14,11 @@ using namespace std;
|
|||
|
||||
struct SpiralDisplay
|
||||
{
|
||||
SpiralDisplay();
|
||||
|
||||
static consteval int width() { return 400; }
|
||||
static consteval int height() { return 400; }
|
||||
|
||||
// Cannot intialize note_positions array with consteval, as polar is not consterpx
|
||||
// static consteval const array<complex<float>, 12> init_pos()
|
||||
// {
|
||||
// return [] <size_t... I> (index_sequence<I...>)
|
||||
// -> array<complex<float>, 12>
|
||||
// { return { polar(0.9f, half_pi - I * two_pi/12) ... }; }
|
||||
// (make_index_sequence<12>{});
|
||||
// }
|
||||
|
||||
// static const constexpr array<complex<float>, 12> note_positions{inti_pos()};
|
||||
|
||||
array<complex<float>, 12> note_positions{};
|
||||
|
||||
SpiralDisplay()
|
||||
{
|
||||
for (int i{0}; i < 12; i++) note_positions[i] = polar(.80f, half_pi - i * two_pi / 12);
|
||||
}
|
||||
|
||||
static const constexpr string_view note_names[12]
|
||||
{"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
|
||||
|
||||
float half;
|
||||
|
||||
float x(float x) const
|
||||
{
|
||||
return half + x * half;
|
||||
}
|
||||
|
||||
float y(float y) const
|
||||
{
|
||||
return half - y * half;
|
||||
}
|
||||
|
||||
void paint(avnd::painter auto ctx)
|
||||
{
|
||||
half = height() * .5f;
|
||||
|
@ -65,7 +34,90 @@ struct SpiralDisplay
|
|||
}
|
||||
|
||||
ctx.stroke();
|
||||
|
||||
int num_octaves = (max_midi_note - min_midi_note + 11) / 12;
|
||||
|
||||
for (int id{0}; id < num_ID; ++id)
|
||||
{
|
||||
ctx.move_to(x(spiral_positions[0].real()), y(spiral_positions[0].imag()));
|
||||
|
||||
for (int b{0}; b < display_spectrum[id].size(); ++b)
|
||||
{
|
||||
float amplitude = 0.8 / num_octaves * min(1.f, display_spectrum[id][b] * gain);
|
||||
//if (display_spectrum[id][b]>0) cout << display_spectrum[id][b] << endl;
|
||||
// power normalised between 0 and 1 => 0.1 = spiral branch
|
||||
float r = spiral_r_a[b].r + amplitude;
|
||||
auto p = polar(r, spiral_r_a[b].a);
|
||||
ctx.line_to(x(p.real()), y(p.imag()));
|
||||
r = spiral_r_a[b + 1].r + amplitude;
|
||||
p = polar(r, spiral_r_a[b + 1].a);
|
||||
ctx.line_to(x(p.real()), y(p.imag()));
|
||||
}
|
||||
|
||||
for (int b{static_cast<int>(spiral_positions.size()) - 1}; b >= 0; --b)
|
||||
ctx.line_to(x(spiral_positions[b].real()), y(spiral_positions[b].imag()));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static const constexpr string_view note_names[12]
|
||||
{"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
|
||||
|
||||
static const int num_ID = 2;
|
||||
|
||||
// Cannot intialize note_positions array with consteval, as polar is not consterpx
|
||||
// static consteval const array<complex<float>, 12> init_pos()
|
||||
// {
|
||||
// return [] <size_t... I> (index_sequence<I...>)
|
||||
// -> array<complex<float>, 12>
|
||||
// { return { polar(0.9f, half_pi - I * two_pi/12) ... }; }
|
||||
// (make_index_sequence<12>{});
|
||||
// }
|
||||
|
||||
// static const constexpr array<complex<float>, 12> note_positions{inti_pos()};
|
||||
|
||||
array<complex<float>, 12> note_positions{};
|
||||
|
||||
// central frequencies (log space)
|
||||
std::vector<float> frequencies;
|
||||
|
||||
// local copy for maintaining the display, adapted to the drawing bins
|
||||
std::vector<std::vector<float>> display_spectrum;
|
||||
|
||||
// bin low bounds, each bin consists of [f_b, f_b+1)
|
||||
std::vector<float> display_bins;
|
||||
// duplicate info for faster processing = delta_f in each bin
|
||||
std::vector<float> bin_sizes;
|
||||
|
||||
// xy position of that frequency bin bound on the spiral
|
||||
std::vector<std::complex<float>> spiral_positions;
|
||||
// same info, but r.exp(angle)
|
||||
// avoid all the sqrt, cos and sin at each redraw
|
||||
struct Radius_Angle {float r, a;};
|
||||
std::vector<Radius_Angle> spiral_r_a;
|
||||
|
||||
int min_midi_note, max_midi_note, visual_fading;
|
||||
|
||||
float gain{1.f}, half;
|
||||
|
||||
float x(float x) const
|
||||
{
|
||||
return half + x * half;
|
||||
}
|
||||
|
||||
float y(float y) const
|
||||
{
|
||||
return half - y * half;
|
||||
}
|
||||
|
||||
void compute_frequencies();
|
||||
|
||||
// // 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(int ID,
|
||||
const std::vector<float>& reassigned_frequencies,
|
||||
const std::vector<float>& power_spectrum);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ avnd_score_plugin_add(
|
|||
Amuencha/AmuenchaModel.cpp
|
||||
Amuencha/AmuenchaUi.hpp
|
||||
Amuencha/SpiralDisplay.hpp
|
||||
Amuencha/SpiralDisplay.cpp
|
||||
TARGET amuencha
|
||||
MAIN_CLASS Analyser
|
||||
NAMESPACE Amuencha
|
||||
|
|
Loading…
Add table
Reference in a new issue