summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2016-07-02 23:35:00 +0200
committerRobin Gareus <robin@gareus.org>2016-07-02 23:36:34 +0200
commitf169ff3db3943b9992042e71048cade2ca1fe39d (patch)
tree47d8f30c2c88668b48215a115ca03260d9d8c1c7 /libs
parent8b142a2fd6214c51ca1d1ccdcf043c6445141e24 (diff)
extend lua API:
* add a basic FFT spectrum analyzer * prepare Cairo::ImageSurface * HSL colorspace conversion
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/dsp_filter.h51
-rw-r--r--libs/ardour/ardour/lua_api.h12
-rw-r--r--libs/ardour/dsp_filter.cc105
-rw-r--r--libs/ardour/lua_api.cc41
-rw-r--r--libs/ardour/luabindings.cc10
5 files changed, 219 insertions, 0 deletions
diff --git a/libs/ardour/ardour/dsp_filter.h b/libs/ardour/ardour/dsp_filter.h
index 994d8725ea..6220dddf5a 100644
--- a/libs/ardour/ardour/dsp_filter.h
+++ b/libs/ardour/ardour/dsp_filter.h
@@ -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
diff --git a/libs/ardour/ardour/lua_api.h b/libs/ardour/ardour/lua_api.h
index e3cf7bb30f..9ae6bf2ff6 100644
--- a/libs/ardour/ardour/lua_api.h
+++ b/libs/ardour/ardour/lua_api.h
@@ -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 {
diff --git a/libs/ardour/dsp_filter.cc b/libs/ardour/dsp_filter.cc
index 9d6e2cf710..3b93c9c6f3 100644
--- a/libs/ardour/dsp_filter.cc
+++ b/libs/ardour/dsp_filter.cc
@@ -155,6 +155,16 @@ Biquad::run (float *data, const uint32_t n_samples)
}
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)
{
if (Q <= .001) { Q = 0.001; }
@@ -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;
+}
diff --git a/libs/ardour/lua_api.cc b/libs/ardour/lua_api.cc
index 911280305a..94b3f816c3 100644
--- a/libs/ardour/lua_api.cc
+++ b/libs/ardour/lua_api.cc
@@ -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;
+}
diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc
index f9a9b7bc11..aebe60c9ce 100644
--- a/libs/ardour/luabindings.cc
+++ b/libs/ardour/luabindings.cc
@@ -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")