extend lua API:
* add a basic FFT spectrum analyzer * prepare Cairo::ImageSurface * HSL colorspace conversion
This commit is contained in:
parent
8b142a2fd6
commit
f169ff3db3
|
@ -23,6 +23,8 @@
|
|||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <glib.h>
|
||||
#include <glibmm.h>
|
||||
#include <fftw3.h>
|
||||
#include "ardour/libardour_visibility.h"
|
||||
|
||||
namespace ARDOUR { namespace DSP {
|
||||
|
@ -229,6 +231,9 @@ namespace ARDOUR { namespace DSP {
|
|||
*/
|
||||
void compute (Type t, double freq, double Q, double gain);
|
||||
|
||||
/** setup filter, set coefficients directly */
|
||||
void configure (double a1, double a2, double b0, double b1, double b2);
|
||||
|
||||
/** filter transfer function (filter response for spectrum visualization)
|
||||
* @param freq frequency
|
||||
* @return gain at given frequency in dB (clamped to -120..+120)
|
||||
|
@ -244,5 +249,51 @@ namespace ARDOUR { namespace DSP {
|
|||
double _b0, _b1, _b2;
|
||||
};
|
||||
|
||||
class LIBARDOUR_API FFTSpectrum {
|
||||
public:
|
||||
FFTSpectrum (uint32_t window_size, double rate);
|
||||
~FFTSpectrum ();
|
||||
|
||||
/** set data to be analyzed and pre-process with hanning window
|
||||
* n_samples + offset must not be larger than the configured window_size
|
||||
*
|
||||
* @param data raw audio data
|
||||
* @param n_samples number of samples to write to analysis buffer
|
||||
* @param offset destination offset
|
||||
*/
|
||||
void set_data_hann (float const * const data, const uint32_t n_samples, const uint32_t offset = 0);
|
||||
|
||||
/** process current data in buffer */
|
||||
void execute ();
|
||||
|
||||
/** query
|
||||
* @param bin the frequency bin 0 .. window_size / 2
|
||||
* @param norm gain factor (set equal to @bin for 1/f normalization)
|
||||
* @return signal power at given bin (in dBFS)
|
||||
*/
|
||||
float power_at_bin (const uint32_t bin, const float norm = 1.f) const;
|
||||
|
||||
float freq_at_bin (const uint32_t bin) const {
|
||||
return bin * _fft_freq_per_bin;
|
||||
}
|
||||
|
||||
private:
|
||||
static Glib::Threads::Mutex fft_planner_lock;
|
||||
float* hann_window;
|
||||
|
||||
void init (uint32_t window_size, double rate);
|
||||
void reset ();
|
||||
|
||||
uint32_t _fft_window_size;
|
||||
uint32_t _fft_data_size;
|
||||
double _fft_freq_per_bin;
|
||||
|
||||
float* _fft_data_in;
|
||||
float* _fft_data_out;
|
||||
float* _fft_power;
|
||||
|
||||
fftwf_plan _fftplan;
|
||||
};
|
||||
|
||||
} } /* namespace */
|
||||
#endif
|
||||
|
|
|
@ -118,6 +118,18 @@ namespace ARDOUR { namespace LuaAPI {
|
|||
* @returns 3 parameters: AutomationList, ControlList, ParamaterDescriptor
|
||||
*/
|
||||
int plugin_automation (lua_State *lua);
|
||||
|
||||
/**
|
||||
* A convenience function for colorspace HSL to RGB conversion.
|
||||
* All ranges are 0..1
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* local r, g, b, a = ARDOUR.LuaAPI.hsla_to_rgba (hue, saturation, luminosity, alpha)
|
||||
* @endcode
|
||||
* @returns 4 parameters: red, green, blue, alpha (in range 0..1)
|
||||
*/
|
||||
int hsla_to_rgba (lua_State *lua);
|
||||
} } /* namespace */
|
||||
|
||||
namespace ARDOUR { namespace LuaOSC {
|
||||
|
|
|
@ -154,6 +154,16 @@ Biquad::run (float *data, const uint32_t n_samples)
|
|||
if (!isfinite_local (_z2)) { _z2 = 0; }
|
||||
}
|
||||
|
||||
void
|
||||
Biquad::configure (double a1, double a2, double b0, double b1, double b2)
|
||||
{
|
||||
_a1 = a1;
|
||||
_a2 = a2;
|
||||
_b0 = b0;
|
||||
_b1 = b1;
|
||||
_b2 = b2;
|
||||
}
|
||||
|
||||
void
|
||||
Biquad::compute (Type type, double freq, double Q, double gain)
|
||||
{
|
||||
|
@ -289,3 +299,98 @@ Biquad::dB_at_freq (float freq) const
|
|||
if (!isfinite_local (rv)) { rv = 0; }
|
||||
return std::min (120.f, std::max(-120.f, rv));
|
||||
}
|
||||
|
||||
|
||||
Glib::Threads::Mutex FFTSpectrum::fft_planner_lock;
|
||||
|
||||
FFTSpectrum::FFTSpectrum (uint32_t window_size, double rate)
|
||||
: hann_window (0)
|
||||
{
|
||||
init (window_size, rate);
|
||||
}
|
||||
|
||||
FFTSpectrum::~FFTSpectrum ()
|
||||
{
|
||||
{
|
||||
Glib::Threads::Mutex::Lock lk (fft_planner_lock);
|
||||
fftwf_destroy_plan (_fftplan);
|
||||
}
|
||||
fftwf_free (_fft_data_in);
|
||||
fftwf_free (_fft_data_out);
|
||||
free (_fft_power);
|
||||
free (hann_window);
|
||||
}
|
||||
|
||||
void
|
||||
FFTSpectrum::init (uint32_t window_size, double rate)
|
||||
{
|
||||
Glib::Threads::Mutex::Lock lk (fft_planner_lock);
|
||||
|
||||
_fft_window_size = window_size;
|
||||
_fft_data_size = window_size / 2;
|
||||
_fft_freq_per_bin = rate / _fft_data_size / 2.f;
|
||||
|
||||
_fft_data_in = (float *) fftwf_malloc (sizeof(float) * _fft_window_size);
|
||||
_fft_data_out = (float *) fftwf_malloc (sizeof(float) * _fft_window_size);
|
||||
_fft_power = (float *) malloc (sizeof(float) * _fft_data_size);
|
||||
|
||||
reset ();
|
||||
|
||||
_fftplan = fftwf_plan_r2r_1d (_fft_window_size, _fft_data_in, _fft_data_out, FFTW_R2HC, FFTW_MEASURE);
|
||||
|
||||
hann_window = (float *) malloc(sizeof(float) * window_size);
|
||||
double sum = 0.0;
|
||||
|
||||
for (uint32_t i = 0; i < window_size; ++i) {
|
||||
hann_window[i] = 0.5f - (0.5f * (float) cos (2.0f * M_PI * (float)i / (float)(window_size)));
|
||||
sum += hann_window[i];
|
||||
}
|
||||
const double isum = 2.0 / sum;
|
||||
for (uint32_t i = 0; i < window_size; ++i) {
|
||||
hann_window[i] *= isum;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FFTSpectrum::reset ()
|
||||
{
|
||||
for (uint32_t i = 0; i < _fft_data_size; ++i) {
|
||||
_fft_power[i] = 0;
|
||||
}
|
||||
for (uint32_t i = 0; i < _fft_window_size; ++i) {
|
||||
_fft_data_out[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FFTSpectrum::set_data_hann (float const * const data, uint32_t n_samples, uint32_t offset)
|
||||
{
|
||||
assert(n_samples + offset <= _fft_window_size);
|
||||
for (uint32_t i = 0; i < n_samples; ++i) {
|
||||
_fft_data_in[i + offset] = data[i] * hann_window[i + offset];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FFTSpectrum::execute ()
|
||||
{
|
||||
fftwf_execute (_fftplan);
|
||||
|
||||
_fft_power[0] = _fft_data_out[0] * _fft_data_out[0];
|
||||
|
||||
#define FRe (_fft_data_out[i])
|
||||
#define FIm (_fft_data_out[_fft_window_size - i])
|
||||
for (uint32_t i = 1; i < _fft_data_size - 1; ++i) {
|
||||
_fft_power[i] = (FRe * FRe) + (FIm * FIm);
|
||||
//_fft_phase[i] = atan2f (FIm, FRe);
|
||||
}
|
||||
#undef FRe
|
||||
#undef FIm
|
||||
}
|
||||
|
||||
float
|
||||
FFTSpectrum::power_at_bin (const uint32_t b, const float norm) const {
|
||||
assert (b >= 0 && b < _fft_data_size);
|
||||
const float a = _fft_power[b] * norm;
|
||||
return a > 1e-12 ? 10.0 * fast_log10 (a) : -INFINITY;
|
||||
}
|
||||
|
|
|
@ -304,3 +304,44 @@ ARDOUR::LuaOSC::Address::send (lua_State *L)
|
|||
luabridge::Stack<bool>::push (L, (rv == 0));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static double hue2rgb (const double p, const double q, double t) {
|
||||
if (t < 0.0) t += 1.0;
|
||||
if (t > 1.0) t -= 1.0;
|
||||
if (t < 1.0 / 6.0) return p + (q - p) * 6.0 * t;
|
||||
if (t < 1.0 / 2.0) return q;
|
||||
if (t < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
|
||||
return p;
|
||||
}
|
||||
|
||||
int
|
||||
ARDOUR::LuaAPI::hsla_to_rgba (lua_State *L)
|
||||
{
|
||||
int top = lua_gettop(L);
|
||||
if (top < 3) {
|
||||
return luaL_argerror (L, 1, "invalid number of arguments, :hsla_to_rgba (h, s, l [,a])");
|
||||
}
|
||||
double h = luabridge::Stack<double>::get (L, 1);
|
||||
double s = luabridge::Stack<double>::get (L, 2);
|
||||
double l = luabridge::Stack<double>::get (L, 3);
|
||||
double a = 1.0;
|
||||
if (top > 3) {
|
||||
a = luabridge::Stack<double>::get (L, 4);
|
||||
}
|
||||
|
||||
// we can't use ArdourCanvas::hsva_to_color here
|
||||
// besides we want HSL not HSV and without intermediate
|
||||
// color_to_rgba (rgba_to_color ())
|
||||
double r,g,b;
|
||||
const double cq = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const double cp = 2.f * l - cq;
|
||||
r = hue2rgb (cp, cq, h + 1.0 / 3.0);
|
||||
g = hue2rgb (cp, cq, h);
|
||||
b = hue2rgb (cp, cq, h - 1.0 / 3.0);
|
||||
|
||||
luabridge::Stack<double>::push (L, r);
|
||||
luabridge::Stack<double>::push (L, g);
|
||||
luabridge::Stack<double>::push (L, b);
|
||||
luabridge::Stack<double>::push (L, a);
|
||||
return 4;
|
||||
}
|
||||
|
|
|
@ -140,6 +140,7 @@ CLASSINFO(RegionSelection);
|
|||
CLASSINFO(PublicEditor);
|
||||
CLASSINFO(Selection);
|
||||
CLASSINFO(ArdourMarker);
|
||||
CLASSINFO(LuaCairoImageSurface);
|
||||
|
||||
namespace Cairo {
|
||||
class Context;
|
||||
|
@ -1204,6 +1205,7 @@ LuaBindings::common (lua_State* L)
|
|||
.addFunction ("set_processor_param", ARDOUR::LuaAPI::set_processor_param)
|
||||
.addFunction ("set_plugin_insert_param", ARDOUR::LuaAPI::set_plugin_insert_param)
|
||||
.addCFunction ("plugin_automation", ARDOUR::LuaAPI::plugin_automation)
|
||||
.addCFunction ("hsla_to_rgba", ARDOUR::LuaAPI::hsla_to_rgba)
|
||||
.addFunction ("usleep", Glib::usleep)
|
||||
.endNamespace () // end LuaAPI
|
||||
.endNamespace ();// end ARDOUR
|
||||
|
@ -1300,9 +1302,17 @@ LuaBindings::dsp (lua_State* L)
|
|||
.addConstructor <void (*) (double)> ()
|
||||
.addFunction ("run", &DSP::Biquad::run)
|
||||
.addFunction ("compute", &DSP::Biquad::compute)
|
||||
.addFunction ("configure", &DSP::Biquad::configure)
|
||||
.addFunction ("reset", &DSP::Biquad::reset)
|
||||
.addFunction ("dB_at_freq", &DSP::Biquad::dB_at_freq)
|
||||
.endClass ()
|
||||
.beginClass <DSP::FFTSpectrum> ("FFTSpectrum")
|
||||
.addConstructor <void (*) (uint32_t, double)> ()
|
||||
.addFunction ("set_data_hann", &DSP::FFTSpectrum::set_data_hann)
|
||||
.addFunction ("execute", &DSP::FFTSpectrum::execute)
|
||||
.addFunction ("power_at_bin", &DSP::FFTSpectrum::power_at_bin)
|
||||
.addFunction ("freq_at_bin", &DSP::FFTSpectrum::freq_at_bin)
|
||||
.endClass ()
|
||||
|
||||
/* DSP enums */
|
||||
.beginNamespace ("BiquadType")
|
||||
|
|
Loading…
Reference in New Issue
Block a user