From b4252d2149f223972d35dbced77de2bf1779f168 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Sun, 27 Oct 2024 17:55:37 +0100 Subject: [PATCH 01/34] try to identify invalid library path on some homebrew cases --- libtascar/src/tascar_os.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libtascar/src/tascar_os.cc b/libtascar/src/tascar_os.cc index b2425d5b..07a8aa52 100644 --- a/libtascar/src/tascar_os.cc +++ b/libtascar/src/tascar_os.cc @@ -210,8 +210,11 @@ namespace TASCAR { if(!lib) { auto homebrewprefix = localgetenv("HOMEBREW_PREFIX"); if(homebrewprefix.size()) { + DEBUG(homebrewprefix); homebrewprefix += "/lib/"; + DEBUG(homebrewprefix); homebrewprefix += filename; + DEBUG(homebrewprefix); lib = ::dlopen(homebrewprefix.c_str(), flags); } } From 76f051841061774d2ef30b6a57c23805ad40b2ee Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Sun, 27 Oct 2024 22:04:12 +0100 Subject: [PATCH 02/34] remove unused debug info --- libtascar/src/tascar_os.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/libtascar/src/tascar_os.cc b/libtascar/src/tascar_os.cc index 07a8aa52..b2425d5b 100644 --- a/libtascar/src/tascar_os.cc +++ b/libtascar/src/tascar_os.cc @@ -210,11 +210,8 @@ namespace TASCAR { if(!lib) { auto homebrewprefix = localgetenv("HOMEBREW_PREFIX"); if(homebrewprefix.size()) { - DEBUG(homebrewprefix); homebrewprefix += "/lib/"; - DEBUG(homebrewprefix); homebrewprefix += filename; - DEBUG(homebrewprefix); lib = ::dlopen(homebrewprefix.c_str(), flags); } } From a5e4b43451c7481f59887d81c1675e023ba3c2ee Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Mon, 28 Oct 2024 11:43:20 +0100 Subject: [PATCH 03/34] correct installation via homebrew --- packaging/homebrew/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/homebrew/Makefile b/packaging/homebrew/Makefile index 87e69940..fb362c9e 100644 --- a/packaging/homebrew/Makefile +++ b/packaging/homebrew/Makefile @@ -25,6 +25,7 @@ usr-local-bin: build usr-local-lib: build mkdir -p "$(DESTDIR)$(PREFIX)/lib" + mkdir -p "$(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/" cp -r "homebrew-build-dir/$(PREFIX)/lib/"* "$(DESTDIR)$(PREFIX)/lib" cp "$(REPO)/artwork/tascarpro.png" "$(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/" cp "$(REPO)/artwork/tascarspkcalib.png" "$(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/" From c58f77910a45a400174b8f72a207d3f582af5e35 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Thu, 31 Oct 2024 23:52:52 +0100 Subject: [PATCH 04/34] improve documentation --- manual/modmididispatch.tex | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/manual/modmididispatch.tex b/manual/modmididispatch.tex index 28456617..8024146a 100644 --- a/manual/modmididispatch.tex +++ b/manual/modmididispatch.tex @@ -1,4 +1,4 @@ -This plugins can dispatch OSC messages upon MIDI events (CC or note events). Event handlers can be registered via OSC or in the XML configuration, using the \elem{ccmsg} or \elem{notemsg} elements. Multiple event handlers for the same event can be registered. In that case all event handlers will be called. Event handler can be removed via OSC. The communication is bi-directional; MIDI events can be emitted by sending an OSC message to \verb!/mididispatch/send/cc! or \verb!/mididispatch/send/note!. +This plugins can dispatch OSC messages upon MIDI events (CC or note events). Event handlers can be registered via OSC or in the XML configuration, using the \elem{ccmsg} or \elem{notemsg} elements (see below). Parameters to the message can be added using the \elem{f v="1.234"}, \elem{i v="1"} or \elem{s v="string"} sub-elements. Multiple event handlers for the same event can be registered. In that case all event handlers will be called. Event handler can be removed via OSC. The communication is bi-directional; MIDI events can be emitted by sending an OSC message to \verb!/mididispatch/send/cc! or \verb!/mididispatch/send/note!. \begin{snugshade} {\footnotesize @@ -81,4 +81,14 @@ } \end{snugshade} +An example configuration can look like this: + +\begin{lstlisting} + + + + + +\end{lstlisting} + \input{oscdoc_tascarmod_mididispatch.tex} From 6aee8854fccaf31a595509c2a24c67adb61034f4 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Mon, 4 Nov 2024 17:12:54 +0100 Subject: [PATCH 05/34] set TV standard, add warning for non-standard FPS --- plugins/src/tascarmod_ltcgen.cc | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/plugins/src/tascarmod_ltcgen.cc b/plugins/src/tascarmod_ltcgen.cc index c8d43cff..71710d9c 100644 --- a/plugins/src/tascarmod_ltcgen.cc +++ b/plugins/src/tascarmod_ltcgen.cc @@ -62,7 +62,24 @@ ltcgen_t::ltcgen_t(const TASCAR::module_cfg_t& cfg) GET_ATTRIBUTE(addtime, "s", "Add time, e.g., for time zone compensation"); GET_ATTRIBUTE_BOOL(usewallclock, "Use wallclock time instead of session time"); - encoder = ltc_encoder_create(get_srate(), fpsnum / fpsden, LTC_TV_625_50, 0); + LTC_TV_STANDARD ltc_tv_standard = LTC_TV_625_50; + switch((int32_t)(fpsnum / fpsden + 0.5)) { + case 30: + ltc_tv_standard = LTC_TV_525_60; + break; + case 25: + ltc_tv_standard = LTC_TV_625_50; + break; + case 24: + ltc_tv_standard = LTC_TV_FILM_24; + break; + default: + TASCAR::add_warning("Non-standard LTC frame rate: " + + TASCAR::to_string(fpsnum) + "/" + + TASCAR::to_string(fpsden) + " fps"); + } + encoder = + ltc_encoder_create(get_srate(), fpsnum / fpsden, ltc_tv_standard, 0); enc_buf = new ltcsnd_sample_t[ltc_encoder_get_buffersize(encoder)]; memset(enc_buf, 0, ltc_encoder_get_buffersize(encoder) * sizeof(ltcsnd_sample_t)); From cc33e211cc8d7ebf58c71e8fbe1a813e0564ed33 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Mon, 4 Nov 2024 17:13:07 +0100 Subject: [PATCH 06/34] change default value of jack start --- gui/src/tascar_mainwindow.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/src/tascar_mainwindow.cc b/gui/src/tascar_mainwindow.cc index 1f58fef9..bbddd2e0 100644 --- a/gui/src/tascar_mainwindow.cc +++ b/gui/src/tascar_mainwindow.cc @@ -275,7 +275,7 @@ tascar_window_t::tascar_window_t(BaseObjectType* cobject, TASCAR::add_warning("css error: " + e.what()); } // optionally test for running jack server and start qjackctl: - bool checkforjack = TASCAR::config("tascar.gui.checkforjack", 1); + bool checkforjack = TASCAR::config("tascar.gui.checkforjack", 0); if(checkforjack) { bool jack_is_running = test_for_jack_server(); if(!jack_is_running) { From 41d6b4fc42aea13f0056a26c889ea54d974f8f9b Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Mon, 11 Nov 2024 14:38:18 +0100 Subject: [PATCH 07/34] create copy of fdn class with biquad allpass filters --- libtascar/include/fdn.h | 156 +++++++++++++++++++++++++++ libtascar/src/fdn.cc | 231 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 387 insertions(+) diff --git a/libtascar/include/fdn.h b/libtascar/include/fdn.h index c42b4ca5..f01fb4c9 100644 --- a/libtascar/include/fdn.h +++ b/libtascar/include/fdn.h @@ -22,6 +22,7 @@ #include "coordinates.h" #include "fft.h" +#include "filterclass.h" namespace TASCAR { @@ -118,6 +119,34 @@ namespace TASCAR { foa_sample_t sapy; ///< output state variable of allpass filter }; + // y[n] = -g x[n] + x[n-1] + g y[n-1] + class reflectionfilter_biquadallpass_t { + public: + reflectionfilter_biquadallpass_t(); + inline void filter(foa_sample_t& x) + { + x *= B1; + x -= A2 * sy; + sy = x; + // all pass section: + x.w = ap_w.filter(x.w); + x.y = ap_y.filter(x.y); + x.z = ap_z.filter(x.z); + x.x = ap_x.filter(x.x); + }; + void set_lp(float g, float c); + void set_allpass(float rw, float ry, float rz, float rx, float phase); + + protected: + float B1 = 0.0f; ///< non-recursive filter coefficient for all channels + float A2 = 0.0f; ///< recursive filter coefficient for all channels + TASCAR::biquadf_t ap_w; + TASCAR::biquadf_t ap_y; + TASCAR::biquadf_t ap_z; + TASCAR::biquadf_t ap_x; + foa_sample_t sy; ///< output state buffer + }; + class fdnpath_t { public: fdnpath_t(); @@ -141,6 +170,29 @@ namespace TASCAR { uint32_t pos = 0u; }; + class fdnpath_biquadallpass_t { + public: + fdnpath_biquadallpass_t(); + void init(uint32_t maxdelay); + void set_zero() + { + for(auto& dl : delayline) + dl.set_zero(); + dlout.set_zero(); + } + // delay line: + std::vector delayline; + // reflection filter: + reflectionfilter_biquadallpass_t reflection; + TASCAR::quaternion_t rotation; + // delayline output for reflection filters: + foa_sample_t dlout; + // delays: + uint32_t delay = 0u; + // delayline pointer: + uint32_t pos = 0u; + }; + class fdn_t { public: enum gainmethod_t { original, mean, schroeder }; @@ -245,6 +297,110 @@ namespace TASCAR { foa_sample_t outval; }; + class fdn_biquadallpass_t { + public: + enum gainmethod_t { original, mean, schroeder }; + fdn_biquadallpass_t(uint32_t fdnorder, uint32_t maxdelay, bool logdelays, + gainmethod_t gm, bool feedback_); + ~fdn_biquadallpass_t(){}; + inline void process(std::vector& src) + { + outval.set_zero(); + if(feedback) { + // get output values from delayline, apply reflection filters and + // rotation: + for(auto& path : fdnpath) { + foa_sample_t tmp(path.delayline[path.pos]); + path.reflection.filter(tmp); + path.rotation.rotate(tmp); + path.dlout = tmp; + outval += tmp; + } + // put rotated+attenuated value to delayline, add input: + uint32_t tap = 0; + for(auto& path : fdnpath) { + // first put input into delayline: + path.delayline[path.pos].set_zero(); + // now add feedback signal: + uint32_t otap = 0; + for(auto& opath : fdnpath) { + foa_sample_t tmp = opath.dlout; + tmp += src[otap].dlout; + path.delayline[path.pos] += + tmp * feedbackmat[fdnorder_ * tap + otap]; + ++otap; + } + // iterate delayline: + if(!path.pos) + path.pos = path.delay; + if(path.pos) + --path.pos; + ++tap; + } + } else { + // put rotated+attenuated value to delayline, add input: + { + uint32_t tap = 0; + for(auto& path : fdnpath) { + foa_sample_t tmp; + uint32_t otap = 0; + for(auto& opath : src) { + tmp += opath.dlout * feedbackmat[fdnorder_ * tap + otap]; + ++otap; + } + // first put input into delayline: + path.delayline[path.pos] = tmp; + // iterate delayline: + if(!path.pos) + path.pos = path.delay; + if(path.pos) + --path.pos; + ++tap; + } + } + // get output values from delayline, apply reflection filters and + // rotation: + for(auto& path : fdnpath) { + foa_sample_t tmp(path.delayline[path.pos]); + path.reflection.filter(tmp); + path.rotation.rotate(tmp); + path.dlout = tmp; + outval += tmp; + } + } + }; + void setpar_t60(float az, float daz, float t, float dt, float t60, + float damping, bool fixcirculantmat, bool truncate_forward); + void set_scatterpar(float daz, float t, float dt, float t60, float damping); + void set_logdelays(bool ld) { logdelays_ = ld; }; + void set_zero() + { + for(auto& path : fdnpath) + path.set_zero(); + }; + + // private: + bool logdelays_ = true; + uint32_t fdnorder_ = 5u; + uint32_t maxdelay_ = 8u; + // feedback matrix: + std::vector feedbackmat; + // reflection filter: + reflectionfilter_biquadallpass_t prefilt0; + reflectionfilter_biquadallpass_t prefilt1; + // FDN path: + std::vector fdnpath; + // gain calculation method: + gainmethod_t gainmethod = original; + // use feedback matrix: + bool feedback = true; + // + + public: + // output FOA sample: + foa_sample_t outval; + }; + } // namespace TASCAR #endif diff --git a/libtascar/src/fdn.cc b/libtascar/src/fdn.cc index 63b7a96c..e0c5d54a 100644 --- a/libtascar/src/fdn.cc +++ b/libtascar/src/fdn.cc @@ -238,6 +238,215 @@ void fdn_t::set_scatterpar(float daz, float t_min, float t_max, float t60, } } +fdn_biquadallpass_t::fdn_biquadallpass_t(uint32_t fdnorder, uint32_t maxdelay, + bool logdelays, gainmethod_t gm, + bool feedback_) + : logdelays_(logdelays), fdnorder_(fdnorder), maxdelay_(maxdelay), + feedbackmat(fdnorder_ * fdnorder_), gainmethod(gm), feedback(feedback_) +{ + for(auto& v : feedbackmat) + v = 0.0f; + prefilt0.set_allpass(0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + prefilt1.set_allpass(0.87f, 0.87f, 0.87f, 0.87f, 0.25f * TASCAR_PIf); + fdnpath.resize(fdnorder); + for(size_t k = 0; k < fdnpath.size(); ++k) { + fdnpath[k].init(maxdelay); + } + // inval.set_zero(); + outval.set_zero(); +} + +/** + \brief Set parameters of FDN + \param az Average rotation in radians per reflection + \param daz Spread of rotation in radians per reflection + \param t Average/maximum delay in samples + \param dt Spread of delay in samples + \param g Gain + \param damping Damping +*/ +void fdn_biquadallpass_t::setpar_t60(float az, float daz, float t_min, + float t_max, float t60, float damping, + bool fixcirculantmat, + bool truncate_forward) +{ + // set delays: + set_zero(); + float t_mean(0); + for(uint32_t tap = 0; tap < fdnorder_; ++tap) { + float t_(t_min); + if(logdelays_) { + // logarithmic distribution: + if(fdnorder_ > 1) + t_ = + t_min * powf(t_max / t_min, (float)tap / ((float)fdnorder_ - 1.0f)); + ; + } else { + // squareroot distribution: + if(fdnorder_ > 1) + t_ = t_min + (t_max - t_min) * + powf((float)tap / ((float)fdnorder_ - 1.0f), 0.5f); + } + uint32_t d((uint32_t)std::max(0.0f, t_)); + fdnpath[tap].delay = std::max(2u, std::min(maxdelay_ - 1u, d)); + fdnpath[tap].reflection.set_allpass( + 0.87f, 0.88f, 0.89f, 0.9f, + TASCAR_PIf * (0.001f + 0.25f * (float)tap / ((float)fdnorder_ - 1.0f))); + // eta[k] = 0.87f * (float)k / ((float)d1 - 1.0f); + t_mean += (float)(fdnpath[tap].delay); + } + // if feed forward model, then truncate delays: + if(!feedback) { + if(truncate_forward) { + auto d_min = maxdelay_; + for(auto& path : fdnpath) + d_min = std::min(d_min, path.delay); + if(d_min > 2u) + d_min -= 2u; + for(auto& path : fdnpath) + path.delay -= d_min; + } else { + for(auto& path : fdnpath) + path.delay++; + } + } + t_mean /= (float)std::max(1u, fdnorder_); + float g(0.0f); + switch(gainmethod) { + case fdn_t::original: + g = expf(-4.2f * t_min / t60); + break; + case fdn_t::mean: + g = expf(-4.2f * t_mean / t60); + break; + case fdn_t::schroeder: + g = powf(10.0f, -3.0f * t_mean / t60); + break; + } + prefilt0.set_lp(g, damping); + prefilt1.set_lp(g, damping); + // set rotation: + for(uint32_t tap = 0; tap < fdnorder_; ++tap) { + // set reflection filters: + fdnpath[tap].reflection.set_lp(g, damping); + float laz(az); + if(fdnorder_ > 1) + laz = az - daz + 2.0f * daz * (float)tap / (float)fdnorder_; + fdnpath[tap].rotation.set_rotation(laz, TASCAR::posf_t(0, 0, 1)); + TASCAR::quaternion_t q; + q.set_rotation(0.5f * daz * (float)(tap & 1) - 0.5f * daz, + TASCAR::posf_t(0, 1, 0)); + fdnpath[tap].rotation.rmul(q); + q.set_rotation(0.125f * daz * (float)(tap % 3) - 0.25f * daz, + TASCAR::posf_t(1, 0, 0)); + fdnpath[tap].rotation.rmul(q); + } + // set feedback matrix: + if(fdnorder_ > 1) { + TASCAR::fft_t fft(fdnorder_); + TASCAR::spec_t eigenv(fdnorder_ / 2 + 1); + for(uint32_t k = 0; k < eigenv.n_; ++k) + eigenv[k] = std::exp(i_f * TASCAR_2PIf * + powf((float)k / (0.5f * (float)fdnorder_), 2.0f)); + ; + fft.execute(eigenv); + for(uint32_t itap = 0; itap < fdnorder_; ++itap) + for(uint32_t otap = 0; otap < fdnorder_; ++otap) + if(fixcirculantmat) + feedbackmat[fdnorder_ * itap + otap] = + fft.w[(otap + fdnorder_ - itap) % fdnorder_]; + else + feedbackmat[fdnorder_ * itap + otap] = + fft.w[(otap + itap) % fdnorder_]; + } else { + feedbackmat[0] = 1.0; + } +} + +void fdn_biquadallpass_t::set_scatterpar(float daz, float t_min, float t_max, + float t60, float damping) +{ + // set delays: + set_zero(); + float t_mean(0); + for(uint32_t tap = 0; tap < fdnorder_; ++tap) { + float t_(t_min); + if(logdelays_) { + // logarithmic distribution: + if(fdnorder_ > 1) + t_ = + t_min * powf(t_max / t_min, (float)tap / ((float)fdnorder_ - 1.0f)); + ; + } else { + // squareroot distribution: + if(fdnorder_ > 1) + t_ = t_min + (t_max - t_min) * + powf((float)tap / ((float)fdnorder_ - 1.0f), 0.5f); + } + uint32_t d((uint32_t)std::max(0.0f, t_)); + fdnpath[tap].delay = std::max(2u, std::min(maxdelay_ - 1u, d)); + fdnpath[tap].reflection.set_allpass( + 0.87f, 0.88f, 0.89f, 0.9f, + TASCAR_PIf * (0.001f + 0.25f * (float)tap / ((float)fdnorder_ - 1.0f))); + // fdnpath[tap].reflection.set_eta(0.87f * (float)tap / + // ((float)fdnorder_ - 1.0f)); + // eta[k] = 0.87f * (float)k / ((float)d1 - 1.0f); + t_mean += (float)(fdnpath[tap].delay); + } + // if feed forward model, then truncate delays: + if(!feedback) { + for(auto& path : fdnpath) + path.delay++; + } + t_mean /= (float)std::max(1u, fdnorder_); + float g(0.0f); + switch(gainmethod) { + case fdn_t::original: + g = expf(-4.2f * t_min / t60); + break; + case fdn_t::mean: + g = expf(-4.2f * t_mean / t60); + break; + case fdn_t::schroeder: + g = powf(10.0f, -3.0f * t_mean / t60); + break; + } + prefilt0.set_lp(g, damping); + prefilt1.set_lp(g, damping); + // set rotation: + for(uint32_t tap = 0; tap < fdnorder_; ++tap) { + // set reflection filters: + fdnpath[tap].reflection.set_lp(g, damping); + float laz = 0.0f; + if(fdnorder_ > 1) + laz = -daz + 2.0f * daz * (float)tap / (float)(fdnorder_ - 1); + fdnpath[tap].rotation.set_rotation(laz, TASCAR::posf_t(0, 0, 1)); + TASCAR::quaternion_t q; + q.set_rotation(0.5f * daz * (float)(tap & 1) - 0.5f * daz, + TASCAR::posf_t(0, 1, 0)); + fdnpath[tap].rotation.rmul(q); + q.set_rotation(0.125f * daz * (float)(tap % 3) - 0.25f * daz, + TASCAR::posf_t(1, 0, 0)); + fdnpath[tap].rotation.rmul(q); + } + // set feedback matrix: + if(fdnorder_ > 1) { + TASCAR::fft_t fft(fdnorder_); + TASCAR::spec_t eigenv(fdnorder_ / 2 + 1); + for(uint32_t k = 0; k < eigenv.n_; ++k) + eigenv[k] = std::exp(i_f * TASCAR_2PIf * + powf((float)k / (0.5f * (float)fdnorder_), 2.0f)); + ; + fft.execute(eigenv); + for(uint32_t itap = 0; itap < fdnorder_; ++itap) + for(uint32_t otap = 0; otap < fdnorder_; ++otap) + feedbackmat[fdnorder_ * itap + otap] = + fft.w[(otap + fdnorder_ - itap) % fdnorder_]; + } else { + feedbackmat[0] = 1.0; + } +} + reflectionfilter_t::reflectionfilter_t() { sy.set_zero(); @@ -262,6 +471,28 @@ void reflectionfilter_t::set_lp(float g, float c) A2 = -c; } +reflectionfilter_biquadallpass_t::reflectionfilter_biquadallpass_t() +{ + sy.set_zero(); +} + +void reflectionfilter_biquadallpass_t::set_lp(float g, float c) +{ + sy.set_zero(); + float c2(1.0f - c); + B1 = g * c2; + A2 = -c; +} + +void reflectionfilter_biquadallpass_t::set_allpass(float rw, float ry, float rz, + float rx, float phase) +{ + ap_w.set_allpass(rw, phase); + ap_y.set_allpass(ry, phase); + ap_z.set_allpass(rz, phase); + ap_x.set_allpass(rx, phase); +} + /* * Local Variables: * mode: c++ From 64e45c42a836debd97b5b8ef22dcdbe50a4d82be Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Mon, 11 Nov 2024 15:43:40 +0100 Subject: [PATCH 08/34] add option to use biquad allpass filters to simplefdn --- libtascar/include/fdn.h | 215 +++++---------------- libtascar/src/acousticmodel.cc | 2 +- libtascar/src/fdn.cc | 239 ++---------------------- manual/oscdoc_receivermod_simplefdn.tex | 1 + plugins/src/receivermod_simplefdn.cc | 14 +- 5 files changed, 70 insertions(+), 401 deletions(-) diff --git a/libtascar/include/fdn.h b/libtascar/include/fdn.h index 3aa91256..56ea9925 100644 --- a/libtascar/include/fdn.h +++ b/libtascar/include/fdn.h @@ -21,7 +21,6 @@ #define FDN_H #include "coordinates.h" -#include "filterclass.h" #include "fft.h" #include "filterclass.h" @@ -97,19 +96,27 @@ namespace TASCAR { class reflectionfilter_t { public: reflectionfilter_t(); - inline void filter(foa_sample_t& x) + inline void filter(foa_sample_t& x, bool use_biquad) { x *= B1; x -= A2 * sy; sy = x; // all pass section: - foa_sample_t tmp(eta * x + sapx); - sapx = x; - x = tmp - eta * sapy; - sapy = x; + if(use_biquad) { + x.w = ap_w.filter(x.w); + x.y = ap_y.filter(x.y); + x.z = ap_z.filter(x.z); + x.x = ap_x.filter(x.x); + } else { + foa_sample_t tmp(eta * x + sapx); + sapx = x; + x = tmp - eta * sapy; + sapy = x; + } }; void set_lp(float g, float c); - void set_eta(float e) { eta = e; }; + void set_allpass(float eta, float rw, float ry, float rz, float rx, + float phase); protected: float B1 = 0.0f; ///< non-recursive filter coefficient for all channels @@ -118,34 +125,10 @@ namespace TASCAR { foa_sample_t sy; ///< output state buffer foa_sample_t sapx; ///< input state variable of allpass filter foa_sample_t sapy; ///< output state variable of allpass filter - }; - - // y[n] = -g x[n] + x[n-1] + g y[n-1] - class reflectionfilter_biquadallpass_t { - public: - reflectionfilter_biquadallpass_t(); - inline void filter(foa_sample_t& x) - { - x *= B1; - x -= A2 * sy; - sy = x; - // all pass section: - x.w = ap_w.filter(x.w); - x.y = ap_y.filter(x.y); - x.z = ap_z.filter(x.z); - x.x = ap_x.filter(x.x); - }; - void set_lp(float g, float c); - void set_allpass(float rw, float ry, float rz, float rx, float phase); - - protected: - float B1 = 0.0f; ///< non-recursive filter coefficient for all channels - float A2 = 0.0f; ///< recursive filter coefficient for all channels TASCAR::biquadf_t ap_w; TASCAR::biquadf_t ap_y; TASCAR::biquadf_t ap_z; TASCAR::biquadf_t ap_x; - foa_sample_t sy; ///< output state buffer }; class fdnpath_t { @@ -171,28 +154,32 @@ namespace TASCAR { uint32_t pos = 0u; }; - class fdnpath_biquadallpass_t { - public: - fdnpath_biquadallpass_t(); - void init(uint32_t maxdelay); - void set_zero() - { - for(auto& dl : delayline) - dl.set_zero(); - dlout.set_zero(); - } - // delay line: - std::vector delayline; - // reflection filter: - reflectionfilter_biquadallpass_t reflection; - TASCAR::quaternion_t rotation; - // delayline output for reflection filters: - foa_sample_t dlout; - // delays: - uint32_t delay = 0u; - // delayline pointer: - uint32_t pos = 0u; - }; + //class fdn_base_t { + //public: + // enum gainmethod_t { original, mean, schroeder }; + // fdn_base_t(uint32_t fdnorder, uint32_t maxdelay, bool logdelays, + // gainmethod_t gm, bool feedback_); + // virtual ~fdn_base_t(){}; + // virtual void setpar_t60(float az, float daz, float t, float dt, float t60, + // float damping, bool fixcirculantmat, + // bool truncate_forward) = 0; + // virtual void set_scatterpar(float daz, float t, float dt, float t60, + // float damping) = 0; + // virtual void set_logdelays(bool ld) { logdelays_ = ld; }; + // virtual void set_zero() = 0; + // virtual void prefilt() = 0; + // bool logdelays_ = true; + // uint32_t fdnorder_ = 5u; + // uint32_t maxdelay_ = 8u; + // // feedback matrix: + // std::vector feedbackmat; + // // gain calculation method: + // gainmethod_t gainmethod = original; + // // use feedback matrix: + // bool feedback = true; + // // output FOA sample: + // foa_sample_t outval; + //}; class fdn_t { public: @@ -200,7 +187,8 @@ namespace TASCAR { fdn_t(uint32_t fdnorder, uint32_t maxdelay, bool logdelays, gainmethod_t gm, bool feedback_); ~fdn_t(){}; - inline void process(std::vector& src) + void set_logdelays(bool ld) { logdelays_ = ld; }; + inline void process(std::vector& src, bool use_biquad) { outval.set_zero(); if(feedback) { @@ -208,7 +196,7 @@ namespace TASCAR { // rotation: for(auto& path : fdnpath) { foa_sample_t tmp(path.delayline[path.pos]); - path.reflection.filter(tmp); + path.reflection.filter(tmp, use_biquad); path.rotation.rotate(tmp); path.dlout = tmp; outval += tmp; @@ -259,7 +247,7 @@ namespace TASCAR { // rotation: for(auto& path : fdnpath) { foa_sample_t tmp(path.delayline[path.pos]); - path.reflection.filter(tmp); + path.reflection.filter(tmp, use_biquad); path.rotation.rotate(tmp); path.dlout = tmp; outval += tmp; @@ -269,137 +257,28 @@ namespace TASCAR { void setpar_t60(float az, float daz, float t, float dt, float t60, float damping, bool fixcirculantmat, bool truncate_forward); void set_scatterpar(float daz, float t, float dt, float t60, float damping); - void set_logdelays(bool ld) { logdelays_ = ld; }; void set_zero() { for(auto& path : fdnpath) path.set_zero(); }; - // private: bool logdelays_ = true; uint32_t fdnorder_ = 5u; uint32_t maxdelay_ = 8u; // feedback matrix: std::vector feedbackmat; - // reflection filter: - reflectionfilter_t prefilt0; - reflectionfilter_t prefilt1; - // FDN path: - std::vector fdnpath; // gain calculation method: gainmethod_t gainmethod = original; // use feedback matrix: bool feedback = true; - // - - public: // output FOA sample: foa_sample_t outval; - }; - - class fdn_biquadallpass_t { - public: - enum gainmethod_t { original, mean, schroeder }; - fdn_biquadallpass_t(uint32_t fdnorder, uint32_t maxdelay, bool logdelays, - gainmethod_t gm, bool feedback_); - ~fdn_biquadallpass_t(){}; - inline void process(std::vector& src) - { - outval.set_zero(); - if(feedback) { - // get output values from delayline, apply reflection filters and - // rotation: - for(auto& path : fdnpath) { - foa_sample_t tmp(path.delayline[path.pos]); - path.reflection.filter(tmp); - path.rotation.rotate(tmp); - path.dlout = tmp; - outval += tmp; - } - // put rotated+attenuated value to delayline, add input: - uint32_t tap = 0; - for(auto& path : fdnpath) { - // first put input into delayline: - path.delayline[path.pos].set_zero(); - // now add feedback signal: - uint32_t otap = 0; - for(auto& opath : fdnpath) { - foa_sample_t tmp = opath.dlout; - tmp += src[otap].dlout; - path.delayline[path.pos] += - tmp * feedbackmat[fdnorder_ * tap + otap]; - ++otap; - } - // iterate delayline: - if(!path.pos) - path.pos = path.delay; - if(path.pos) - --path.pos; - ++tap; - } - } else { - // put rotated+attenuated value to delayline, add input: - { - uint32_t tap = 0; - for(auto& path : fdnpath) { - foa_sample_t tmp; - uint32_t otap = 0; - for(auto& opath : src) { - tmp += opath.dlout * feedbackmat[fdnorder_ * tap + otap]; - ++otap; - } - // first put input into delayline: - path.delayline[path.pos] = tmp; - // iterate delayline: - if(!path.pos) - path.pos = path.delay; - if(path.pos) - --path.pos; - ++tap; - } - } - // get output values from delayline, apply reflection filters and - // rotation: - for(auto& path : fdnpath) { - foa_sample_t tmp(path.delayline[path.pos]); - path.reflection.filter(tmp); - path.rotation.rotate(tmp); - path.dlout = tmp; - outval += tmp; - } - } - }; - void setpar_t60(float az, float daz, float t, float dt, float t60, - float damping, bool fixcirculantmat, bool truncate_forward); - void set_scatterpar(float daz, float t, float dt, float t60, float damping); - void set_logdelays(bool ld) { logdelays_ = ld; }; - void set_zero() - { - for(auto& path : fdnpath) - path.set_zero(); - }; - - // private: - bool logdelays_ = true; - uint32_t fdnorder_ = 5u; - uint32_t maxdelay_ = 8u; - // feedback matrix: - std::vector feedbackmat; // reflection filter: - reflectionfilter_biquadallpass_t prefilt0; - reflectionfilter_biquadallpass_t prefilt1; + reflectionfilter_t prefilt0; + reflectionfilter_t prefilt1; // FDN path: - std::vector fdnpath; - // gain calculation method: - gainmethod_t gainmethod = original; - // use feedback matrix: - bool feedback = true; - // - - public: - // output FOA sample: - foa_sample_t outval; + std::vector fdnpath; }; } // namespace TASCAR diff --git a/libtascar/src/acousticmodel.cc b/libtascar/src/acousticmodel.cc index aa8f097e..4400d0e4 100644 --- a/libtascar/src/acousticmodel.cc +++ b/libtascar/src/acousticmodel.cc @@ -750,7 +750,7 @@ void receiver_t::postproc(std::vector& output) // path.dlout = x; ++kflt; } - scatterfilter->process(scatterfilterpath); + scatterfilter->process(scatterfilterpath,false); scatterbuffer->w()[k] = scatterfilter->outval.w; scatterbuffer->x()[k] = scatterfilter->outval.x; scatterbuffer->y()[k] = scatterfilter->outval.y; diff --git a/libtascar/src/fdn.cc b/libtascar/src/fdn.cc index e0c5d54a..2bd1e07c 100644 --- a/libtascar/src/fdn.cc +++ b/libtascar/src/fdn.cc @@ -42,8 +42,8 @@ fdn_t::fdn_t(uint32_t fdnorder, uint32_t maxdelay, bool logdelays, { for(auto& v : feedbackmat) v = 0.0f; - prefilt0.set_eta(0.0f); - prefilt1.set_eta(0.87f); + prefilt0.set_allpass(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + prefilt1.set_allpass(0.87f, 0.87f, 0.87f, 0.87f, 0.87f, 0.25f * TASCAR_PIf); fdnpath.resize(fdnorder); for(size_t k = 0; k < fdnpath.size(); ++k) { fdnpath[k].init(maxdelay); @@ -64,211 +64,6 @@ fdn_t::fdn_t(uint32_t fdnorder, uint32_t maxdelay, bool logdelays, void fdn_t::setpar_t60(float az, float daz, float t_min, float t_max, float t60, float damping, bool fixcirculantmat, bool truncate_forward) -{ - // set delays: - set_zero(); - float t_mean(0); - for(uint32_t tap = 0; tap < fdnorder_; ++tap) { - float t_(t_min); - if(logdelays_) { - // logarithmic distribution: - if(fdnorder_ > 1) - t_ = - t_min * powf(t_max / t_min, (float)tap / ((float)fdnorder_ - 1.0f)); - ; - } else { - // squareroot distribution: - if(fdnorder_ > 1) - t_ = t_min + (t_max - t_min) * - powf((float)tap / ((float)fdnorder_ - 1.0f), 0.5f); - } - uint32_t d((uint32_t)std::max(0.0f, t_)); - fdnpath[tap].delay = std::max(2u, std::min(maxdelay_ - 1u, d)); - fdnpath[tap].reflection.set_eta(0.87f * (float)tap / - ((float)fdnorder_ - 1.0f)); - // eta[k] = 0.87f * (float)k / ((float)d1 - 1.0f); - t_mean += (float)(fdnpath[tap].delay); - } - // if feed forward model, then truncate delays: - if(!feedback) { - if(truncate_forward) { - auto d_min = maxdelay_; - for(auto& path : fdnpath) - d_min = std::min(d_min, path.delay); - if(d_min > 2u) - d_min -= 2u; - for(auto& path : fdnpath) - path.delay -= d_min; - } else { - for(auto& path : fdnpath) - path.delay++; - } - } - t_mean /= (float)std::max(1u, fdnorder_); - float g(0.0f); - switch(gainmethod) { - case fdn_t::original: - g = expf(-4.2f * t_min / t60); - break; - case fdn_t::mean: - g = expf(-4.2f * t_mean / t60); - break; - case fdn_t::schroeder: - g = powf(10.0f, -3.0f * t_mean / t60); - break; - } - prefilt0.set_lp(g, damping); - prefilt1.set_lp(g, damping); - // set rotation: - for(uint32_t tap = 0; tap < fdnorder_; ++tap) { - // set reflection filters: - fdnpath[tap].reflection.set_lp(g, damping); - float laz(az); - if(fdnorder_ > 1) - laz = az - daz + 2.0f * daz * (float)tap / (float)fdnorder_; - fdnpath[tap].rotation.set_rotation(laz, TASCAR::posf_t(0, 0, 1)); - TASCAR::quaternion_t q; - q.set_rotation(0.5f * daz * (float)(tap & 1) - 0.5f * daz, - TASCAR::posf_t(0, 1, 0)); - fdnpath[tap].rotation.rmul(q); - q.set_rotation(0.125f * daz * (float)(tap % 3) - 0.25f * daz, - TASCAR::posf_t(1, 0, 0)); - fdnpath[tap].rotation.rmul(q); - } - // set feedback matrix: - if(fdnorder_ > 1) { - TASCAR::fft_t fft(fdnorder_); - TASCAR::spec_t eigenv(fdnorder_ / 2 + 1); - for(uint32_t k = 0; k < eigenv.n_; ++k) - eigenv[k] = std::exp(i_f * TASCAR_2PIf * - powf((float)k / (0.5f * (float)fdnorder_), 2.0f)); - ; - fft.execute(eigenv); - for(uint32_t itap = 0; itap < fdnorder_; ++itap) - for(uint32_t otap = 0; otap < fdnorder_; ++otap) - if(fixcirculantmat) - feedbackmat[fdnorder_ * itap + otap] = - fft.w[(otap + fdnorder_ - itap) % fdnorder_]; - else - feedbackmat[fdnorder_ * itap + otap] = - fft.w[(otap + itap) % fdnorder_]; - } else { - feedbackmat[0] = 1.0; - } -} - -void fdn_t::set_scatterpar(float daz, float t_min, float t_max, float t60, - float damping) -{ - // set delays: - set_zero(); - float t_mean(0); - for(uint32_t tap = 0; tap < fdnorder_; ++tap) { - float t_(t_min); - if(logdelays_) { - // logarithmic distribution: - if(fdnorder_ > 1) - t_ = - t_min * powf(t_max / t_min, (float)tap / ((float)fdnorder_ - 1.0f)); - ; - } else { - // squareroot distribution: - if(fdnorder_ > 1) - t_ = t_min + (t_max - t_min) * - powf((float)tap / ((float)fdnorder_ - 1.0f), 0.5f); - } - uint32_t d((uint32_t)std::max(0.0f, t_)); - fdnpath[tap].delay = std::max(2u, std::min(maxdelay_ - 1u, d)); - fdnpath[tap].reflection.set_eta(0.87f * (float)tap / - ((float)fdnorder_ - 1.0f)); - // eta[k] = 0.87f * (float)k / ((float)d1 - 1.0f); - t_mean += (float)(fdnpath[tap].delay); - } - // if feed forward model, then truncate delays: - if(!feedback) { - for(auto& path : fdnpath) - path.delay++; - } - t_mean /= (float)std::max(1u, fdnorder_); - float g(0.0f); - switch(gainmethod) { - case fdn_t::original: - g = expf(-4.2f * t_min / t60); - break; - case fdn_t::mean: - g = expf(-4.2f * t_mean / t60); - break; - case fdn_t::schroeder: - g = powf(10.0f, -3.0f * t_mean / t60); - break; - } - prefilt0.set_lp(g, damping); - prefilt1.set_lp(g, damping); - // set rotation: - for(uint32_t tap = 0; tap < fdnorder_; ++tap) { - // set reflection filters: - fdnpath[tap].reflection.set_lp(g, damping); - float laz = 0.0f; - if(fdnorder_ > 1) - laz = -daz + 2.0f * daz * (float)tap / (float)(fdnorder_ - 1); - fdnpath[tap].rotation.set_rotation(laz, TASCAR::posf_t(0, 0, 1)); - TASCAR::quaternion_t q; - q.set_rotation(0.5f * daz * (float)(tap & 1) - 0.5f * daz, - TASCAR::posf_t(0, 1, 0)); - fdnpath[tap].rotation.rmul(q); - q.set_rotation(0.125f * daz * (float)(tap % 3) - 0.25f * daz, - TASCAR::posf_t(1, 0, 0)); - fdnpath[tap].rotation.rmul(q); - } - // set feedback matrix: - if(fdnorder_ > 1) { - TASCAR::fft_t fft(fdnorder_); - TASCAR::spec_t eigenv(fdnorder_ / 2 + 1); - for(uint32_t k = 0; k < eigenv.n_; ++k) - eigenv[k] = std::exp(i_f * TASCAR_2PIf * - powf((float)k / (0.5f * (float)fdnorder_), 2.0f)); - ; - fft.execute(eigenv); - for(uint32_t itap = 0; itap < fdnorder_; ++itap) - for(uint32_t otap = 0; otap < fdnorder_; ++otap) - feedbackmat[fdnorder_ * itap + otap] = - fft.w[(otap + fdnorder_ - itap) % fdnorder_]; - } else { - feedbackmat[0] = 1.0; - } -} - -fdn_biquadallpass_t::fdn_biquadallpass_t(uint32_t fdnorder, uint32_t maxdelay, - bool logdelays, gainmethod_t gm, - bool feedback_) - : logdelays_(logdelays), fdnorder_(fdnorder), maxdelay_(maxdelay), - feedbackmat(fdnorder_ * fdnorder_), gainmethod(gm), feedback(feedback_) -{ - for(auto& v : feedbackmat) - v = 0.0f; - prefilt0.set_allpass(0.0f, 0.0f, 0.0f, 0.0f, 0.0f); - prefilt1.set_allpass(0.87f, 0.87f, 0.87f, 0.87f, 0.25f * TASCAR_PIf); - fdnpath.resize(fdnorder); - for(size_t k = 0; k < fdnpath.size(); ++k) { - fdnpath[k].init(maxdelay); - } - // inval.set_zero(); - outval.set_zero(); -} - -/** - \brief Set parameters of FDN - \param az Average rotation in radians per reflection - \param daz Spread of rotation in radians per reflection - \param t Average/maximum delay in samples - \param dt Spread of delay in samples - \param g Gain - \param damping Damping -*/ -void fdn_biquadallpass_t::setpar_t60(float az, float daz, float t_min, - float t_max, float t60, float damping, - bool fixcirculantmat, - bool truncate_forward) { // set delays: set_zero(); @@ -290,7 +85,8 @@ void fdn_biquadallpass_t::setpar_t60(float az, float daz, float t_min, uint32_t d((uint32_t)std::max(0.0f, t_)); fdnpath[tap].delay = std::max(2u, std::min(maxdelay_ - 1u, d)); fdnpath[tap].reflection.set_allpass( - 0.87f, 0.88f, 0.89f, 0.9f, + 0.87f * (float)tap / ((float)fdnorder_ - 1.0f), 0.87f, 0.88f, 0.89f, + 0.9f, TASCAR_PIf * (0.001f + 0.25f * (float)tap / ((float)fdnorder_ - 1.0f))); // eta[k] = 0.87f * (float)k / ((float)d1 - 1.0f); t_mean += (float)(fdnpath[tap].delay); @@ -363,8 +159,8 @@ void fdn_biquadallpass_t::setpar_t60(float az, float daz, float t_min, } } -void fdn_biquadallpass_t::set_scatterpar(float daz, float t_min, float t_max, - float t60, float damping) +void fdn_t::set_scatterpar(float daz, float t_min, float t_max, float t60, + float damping) { // set delays: set_zero(); @@ -386,10 +182,9 @@ void fdn_biquadallpass_t::set_scatterpar(float daz, float t_min, float t_max, uint32_t d((uint32_t)std::max(0.0f, t_)); fdnpath[tap].delay = std::max(2u, std::min(maxdelay_ - 1u, d)); fdnpath[tap].reflection.set_allpass( - 0.87f, 0.88f, 0.89f, 0.9f, + 0.87f * (float)tap / ((float)fdnorder_ - 1.0f), 0.87f, 0.88f, 0.89f, + 0.9f, TASCAR_PIf * (0.001f + 0.25f * (float)tap / ((float)fdnorder_ - 1.0f))); - // fdnpath[tap].reflection.set_eta(0.87f * (float)tap / - // ((float)fdnorder_ - 1.0f)); // eta[k] = 0.87f * (float)k / ((float)d1 - 1.0f); t_mean += (float)(fdnpath[tap].delay); } @@ -471,22 +266,10 @@ void reflectionfilter_t::set_lp(float g, float c) A2 = -c; } -reflectionfilter_biquadallpass_t::reflectionfilter_biquadallpass_t() -{ - sy.set_zero(); -} - -void reflectionfilter_biquadallpass_t::set_lp(float g, float c) -{ - sy.set_zero(); - float c2(1.0f - c); - B1 = g * c2; - A2 = -c; -} - -void reflectionfilter_biquadallpass_t::set_allpass(float rw, float ry, float rz, - float rx, float phase) +void reflectionfilter_t::set_allpass(float eta_, float rw, float ry, float rz, + float rx, float phase) { + eta = eta_; ap_w.set_allpass(rw, phase); ap_y.set_allpass(ry, phase); ap_z.set_allpass(rz, phase); diff --git a/manual/oscdoc_receivermod_simplefdn.tex b/manual/oscdoc_receivermod_simplefdn.tex index 2bae6365..d863a283 100644 --- a/manual/oscdoc_receivermod_simplefdn.tex +++ b/manual/oscdoc_receivermod_simplefdn.tex @@ -10,6 +10,7 @@ \hline \attr{/.../dim\_damp\_absorption} & fffff & & no & Set dimension (x,y,z in m), damping and absorption coefficient\\ \attr{/.../fixcirculantmat} & i & bool & no & Fix a neglegible bug in the feedback matrix design\\ +\attr{/.../usebiquad} & i & bool & yes & Use biquad allpass filters instead of first order\\ \hline \end{tabularx} } diff --git a/plugins/src/receivermod_simplefdn.cc b/plugins/src/receivermod_simplefdn.cc index e0475927..b3b0071a 100644 --- a/plugins/src/receivermod_simplefdn.cc +++ b/plugins/src/receivermod_simplefdn.cc @@ -55,6 +55,7 @@ class simplefdn_vars_t : public TASCAR::receivermod_base_t { TASCAR::biquadf_t lowcut_z; bool use_lowcut = false; bool truncate_forward = false; + bool use_biquad_allpass = false; }; simplefdn_vars_t::simplefdn_vars_t(tsccfg::node_t xmlsrc) @@ -97,6 +98,9 @@ simplefdn_vars_t::simplefdn_vars_t(tsccfg::node_t xmlsrc) "\". Possible values are original, mean or schroeder."); GET_ATTRIBUTE(lowcut, "Hz", "low cut off frequency, or zero for no low cut"); GET_ATTRIBUTE_BOOL(truncate_forward, "Truncate delays of feed forward path"); + GET_ATTRIBUTE_BOOL( + use_biquad_allpass, + "Use biquad allpass filters instead of first order filters"); } simplefdn_vars_t::~simplefdn_vars_t() {} @@ -350,6 +354,8 @@ void simplefdn_t::add_variables(TASCAR::osc_server_t* srv) "/dim_damp_absorption", "fffff", &osc_set_dim_damp_absorption, this, true, false, "", "Set dimension (x,y,z in m), damping and absorption coefficient"); + srv->add_bool("/usebiquad", &use_biquad_allpass, + "Use biquad allpass filters instead of first order"); srv->unset_variable_owner(); } @@ -459,17 +465,17 @@ void simplefdn_t::postproc(std::vector& output) void simplefdn_t::fdnfilter(TASCAR::foa_sample_t& x) { if(prefilt) { - feedback_delay_network->prefilt0.filter(x); - feedback_delay_network->prefilt1.filter(x); + feedback_delay_network->prefilt0.filter(x, use_biquad_allpass); + feedback_delay_network->prefilt1.filter(x, use_biquad_allpass); } auto psrc = &srcpath; for(auto& path : srcpath) path.dlout = x; for(auto& pff : feedforward_delay_network) { - pff->process(*psrc); + pff->process(*psrc, use_biquad_allpass); psrc = &(pff->fdnpath); } - feedback_delay_network->process(*psrc); + feedback_delay_network->process(*psrc, use_biquad_allpass); x = feedback_delay_network->outval; } From 2f1e2a9b9de5ddd05604e737b4cb4164336f96df Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Mon, 11 Nov 2024 18:28:47 +0100 Subject: [PATCH 09/34] make allpass filter radius configurable --- libtascar/include/fdn.h | 8 +- libtascar/src/acousticmodel.cc | 11 +- libtascar/src/fdn.cc | 21 ++-- plugins/src/receivermod_simplefdn.cc | 15 ++- test/Makefile | 100 ++++++++++--------- test/expected_ir_simplefdn_biquadallpass.wav | Bin 0 -> 64104 bytes test/test_ir_simplefdn_biquadallpass.tsc | 9 ++ 7 files changed, 97 insertions(+), 67 deletions(-) create mode 100644 test/expected_ir_simplefdn_biquadallpass.wav create mode 100644 test/test_ir_simplefdn_biquadallpass.tsc diff --git a/libtascar/include/fdn.h b/libtascar/include/fdn.h index 56ea9925..0a3b7a26 100644 --- a/libtascar/include/fdn.h +++ b/libtascar/include/fdn.h @@ -185,7 +185,7 @@ namespace TASCAR { public: enum gainmethod_t { original, mean, schroeder }; fdn_t(uint32_t fdnorder, uint32_t maxdelay, bool logdelays, gainmethod_t gm, - bool feedback_); + bool feedback_, std::vector rallpass); ~fdn_t(){}; void set_logdelays(bool ld) { logdelays_ = ld; }; inline void process(std::vector& src, bool use_biquad) @@ -196,8 +196,8 @@ namespace TASCAR { // rotation: for(auto& path : fdnpath) { foa_sample_t tmp(path.delayline[path.pos]); - path.reflection.filter(tmp, use_biquad); path.rotation.rotate(tmp); + path.reflection.filter(tmp, use_biquad); path.dlout = tmp; outval += tmp; } @@ -247,8 +247,8 @@ namespace TASCAR { // rotation: for(auto& path : fdnpath) { foa_sample_t tmp(path.delayline[path.pos]); - path.reflection.filter(tmp, use_biquad); path.rotation.rotate(tmp); + path.reflection.filter(tmp, use_biquad); path.dlout = tmp; outval += tmp; } @@ -279,6 +279,8 @@ namespace TASCAR { reflectionfilter_t prefilt1; // FDN path: std::vector fdnpath; + // allpass filter radius, requires four entries: + std::vector rallpass; }; } // namespace TASCAR diff --git a/libtascar/src/acousticmodel.cc b/libtascar/src/acousticmodel.cc index 4400d0e4..f40a18c2 100644 --- a/libtascar/src/acousticmodel.cc +++ b/libtascar/src/acousticmodel.cc @@ -260,8 +260,8 @@ uint32_t acoustic_model_t::process(const TASCAR::transport_t& tp) return 1; } } // of visible - } // of layers check - } // of ISM order check + } // of layers check + } // of ISM order check } else { delayline.add_chunk(audio); } @@ -631,8 +631,9 @@ void receiver_t::configure() scatter_handle = create_diffuse_state_data(f_sample, n_fragment); scatterfilterpath.resize(scatterreflections); if(scatterreflections > 0) { - scatterfilter = new TASCAR::fdn_t(scatterreflections, (uint32_t)f_sample, - true, TASCAR::fdn_t::mean, false); + scatterfilter = + new TASCAR::fdn_t(scatterreflections, (uint32_t)f_sample, true, + TASCAR::fdn_t::mean, false, {0.0f, 0.0f, 0.0f, 0.0f}); scatterfilter->set_scatterpar( scatterspread, f_sample * (0.1f * scatterstructuresize / 340.0f), f_sample * (scatterstructuresize / 340.0f), f_sample, @@ -750,7 +751,7 @@ void receiver_t::postproc(std::vector& output) // path.dlout = x; ++kflt; } - scatterfilter->process(scatterfilterpath,false); + scatterfilter->process(scatterfilterpath, false); scatterbuffer->w()[k] = scatterfilter->outval.w; scatterbuffer->x()[k] = scatterfilter->outval.x; scatterbuffer->y()[k] = scatterfilter->outval.y; diff --git a/libtascar/src/fdn.cc b/libtascar/src/fdn.cc index 2bd1e07c..1d266f24 100644 --- a/libtascar/src/fdn.cc +++ b/libtascar/src/fdn.cc @@ -36,14 +36,20 @@ void fdnpath_t::init(uint32_t maxdelay) }; fdn_t::fdn_t(uint32_t fdnorder, uint32_t maxdelay, bool logdelays, - gainmethod_t gm, bool feedback_) + gainmethod_t gm, bool feedback_, std::vector rallpass_) : logdelays_(logdelays), fdnorder_(fdnorder), maxdelay_(maxdelay), - feedbackmat(fdnorder_ * fdnorder_), gainmethod(gm), feedback(feedback_) + feedbackmat(fdnorder_ * fdnorder_), gainmethod(gm), feedback(feedback_), + rallpass(rallpass_) { + if(rallpass.size() != 4u) + throw TASCAR::ErrMsg( + "Allpass filter radius vector requires four entries, received " + + std::to_string(rallpass.size())); for(auto& v : feedbackmat) v = 0.0f; prefilt0.set_allpass(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); - prefilt1.set_allpass(0.87f, 0.87f, 0.87f, 0.87f, 0.87f, 0.25f * TASCAR_PIf); + prefilt1.set_allpass(0.87f, rallpass[0], rallpass[1], rallpass[2], + rallpass[3], 0.25f * TASCAR_PIf); fdnpath.resize(fdnorder); for(size_t k = 0; k < fdnpath.size(); ++k) { fdnpath[k].init(maxdelay); @@ -85,8 +91,8 @@ void fdn_t::setpar_t60(float az, float daz, float t_min, float t_max, float t60, uint32_t d((uint32_t)std::max(0.0f, t_)); fdnpath[tap].delay = std::max(2u, std::min(maxdelay_ - 1u, d)); fdnpath[tap].reflection.set_allpass( - 0.87f * (float)tap / ((float)fdnorder_ - 1.0f), 0.87f, 0.88f, 0.89f, - 0.9f, + 0.87f * (float)tap / ((float)fdnorder_ - 1.0f), rallpass[0], + rallpass[1], rallpass[2], rallpass[3], TASCAR_PIf * (0.001f + 0.25f * (float)tap / ((float)fdnorder_ - 1.0f))); // eta[k] = 0.87f * (float)k / ((float)d1 - 1.0f); t_mean += (float)(fdnpath[tap].delay); @@ -182,10 +188,9 @@ void fdn_t::set_scatterpar(float daz, float t_min, float t_max, float t60, uint32_t d((uint32_t)std::max(0.0f, t_)); fdnpath[tap].delay = std::max(2u, std::min(maxdelay_ - 1u, d)); fdnpath[tap].reflection.set_allpass( - 0.87f * (float)tap / ((float)fdnorder_ - 1.0f), 0.87f, 0.88f, 0.89f, - 0.9f, + 0.87f * (float)tap / ((float)fdnorder_ - 1.0f), rallpass[0], + rallpass[1], rallpass[2], rallpass[3], TASCAR_PIf * (0.001f + 0.25f * (float)tap / ((float)fdnorder_ - 1.0f))); - // eta[k] = 0.87f * (float)k / ((float)d1 - 1.0f); t_mean += (float)(fdnpath[tap].delay); } // if feed forward model, then truncate delays: diff --git a/plugins/src/receivermod_simplefdn.cc b/plugins/src/receivermod_simplefdn.cc index b3b0071a..92426c0a 100644 --- a/plugins/src/receivermod_simplefdn.cc +++ b/plugins/src/receivermod_simplefdn.cc @@ -56,6 +56,7 @@ class simplefdn_vars_t : public TASCAR::receivermod_base_t { bool use_lowcut = false; bool truncate_forward = false; bool use_biquad_allpass = false; + std::vector rallpass = {0.96f, 0.95f, 0.951f, 0.93f}; }; simplefdn_vars_t::simplefdn_vars_t(tsccfg::node_t xmlsrc) @@ -101,6 +102,12 @@ simplefdn_vars_t::simplefdn_vars_t(tsccfg::node_t xmlsrc) GET_ATTRIBUTE_BOOL( use_biquad_allpass, "Use biquad allpass filters instead of first order filters"); + GET_ATTRIBUTE(rallpass, "[0,1]", + "Allpass filter radius vector (requires four entries)"); + if(rallpass.size() != 4u) + throw TASCAR::ErrMsg( + "Allpass filter radius vector requires four entries, received " + + std::to_string(rallpass.size())); } simplefdn_vars_t::~simplefdn_vars_t() {} @@ -264,14 +271,14 @@ void simplefdn_t::configure() if(feedback_delay_network) delete feedback_delay_network; srcpath.resize(fdnorder); - feedback_delay_network = - new TASCAR::fdn_t(fdnorder, (uint32_t)f_sample, logdelays, gm, true); + feedback_delay_network = new TASCAR::fdn_t(fdnorder, (uint32_t)f_sample, + logdelays, gm, true, rallpass); for(auto& pff : feedforward_delay_network) delete pff; feedforward_delay_network.clear(); for(uint32_t k = 0; k < forwardstages; ++k) - feedforward_delay_network.push_back( - new TASCAR::fdn_t(fdnorder, (uint32_t)f_sample, logdelays, gm, false)); + feedforward_delay_network.push_back(new TASCAR::fdn_t( + fdnorder, (uint32_t)f_sample, logdelays, gm, false, rallpass)); if(foa_out) delete foa_out; foa_out = new TASCAR::amb1wave_t(n_fragment); diff --git a/test/Makefile b/test/Makefile index be5baf43..dee9002a 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,50 +1,56 @@ -TESTS = test_snd_pink_diffuse test_ir1 test_ir2 test_ir3 test_ir4 \ - test_ir_proxy_air test_ir_proxy_gain test_ir_proxy_delay \ - test_ir_proxy_direction test_ir6_ismminmax test_snd1 \ - test_snd_include_sound test_snd2 test_snd4 test_snd3 test_snd5 \ - test_pos1 test_pos2 test_pos_trackinterp test_snd_door1 \ - test_snd_door2 test_snd_door3 test_pos_door test_snd_layers \ - test_snd_rec_amb1h1v test_wav_diffuse test_wav_diffuse_hoa2d \ - test_ir_hoa2d_offset test_wav_diffuse_nsp4 test_wav_diffuse_hoa2d4 \ - test_wav_diffuse_layerbug test_wav_diffirs test_sndgainramp \ - test_sndgainramp2 test_ir_spkcorr test_snd_calib1 test_snd_calib2 \ - test_snd_vbap51 test_snd_pink test_snd_sndfile test_snd_sndfile2 \ - test_snd_door4 test_snd_roommic test_level_vbap3d test_ir_ortf_0deg \ - test_ir_ortf_55deg test_ir_scatter test_ir_noscatter \ - test_ir_scatter_ortf test_snd_sourcedir_generic \ - test_snd_sourcedir_generic1 test_snd_sourcedir_generic_cardioid \ - test_ir_wfs test_ir_wfs_noplanewave test_level_hoa2d_basic \ - test_level_hoa2d_maxre test_ir_reverb test_ir_simplefdn \ - test_ir_simplefdn_gain test_ir_simplefdn_feedforward \ - test_snd_vbap3d_speakermatch test_ir_volumetric test_pos_obstacle \ - test_pos_obstacle_inner test_level_absorption test_ir_hrtf_0deg \ - test_ir_hrtf_45deg test_ir_hrtf_75deg test_ir_hrtf_90deg \ - test_ir_hrtf_105deg test_ir_hrtf_135deg test_ir_hrtf_180deg \ - test_ir_hrtf_225deg test_ir_hrtf_270deg test_ir_hrtf_315deg \ - test_ir_hrtf_el90deg test_ir_hrtf_el-90deg test_ir_hrtf_el45deg \ - test_ir_hrtf_el-45deg test_ir_hrtf_diffuse1 test_ir_hrtf_diffuse2 \ - test_ir_delay test_ir_delay2 test_ir_delay3 test_snd_reverb \ - test_wav_multichannel test_snd_zeros test_ir_sub test_ir_sub_hoa2d \ - test_ir_sub_nsp test_ir_partconvrev test_ir_partconvrev_offset \ - test_ir_micarray_az0deg test_ir_micarray_az45deg \ - test_ir_micarray_az75deg test_ir_micarray_az90deg \ - test_ir_micarray_az105deg test_ir_micarray_az135deg \ - test_ir_micarray_az180deg test_ir_micarray_az225deg \ - test_ir_micarray_az270deg test_ir_micarray_az315deg \ - test_ir_micarray_el45deg test_ir_micarray_el90deg \ - test_ir_micarray_el-45deg test_ir_micarray_el-90deg \ - test_snd_diffuse_micarray test_snd_micarray_az0deg \ - test_ir_micarraysinc test_ir_amb1 test_ir_amb1_y test_ir_amb1_z \ - test_snd_diffuse_0deg test_snd_diffuse_17deg \ - test_snd_diffuse_rec17deg test_snd_maskplugin_fig8 \ - test_snd_maskplugin_multibeam test_snd_maskplugin_multibeam1 \ - test_snd_maskplugin_sampledgain test_snd_diffuse_inphase \ - test_snd_diffuse_basic test_snd_diffuse_maxre \ - test_snd_maskplugin_multibeam_diff test_ir_apfilter_highpass \ - test_ir_apfilter_lowpass test_snd_hoa2d_fuma_hos \ - test_ir_material_carpet test_ir_material_window test_ir1_spkcalib \ - test_snd_spkcorr0 test_snd_spkcorr70 test_snd_nospkcorr \ - test_snd_spkcorr_long test_ir_nodelaylineimagesource +TESTS = test_snd_pink_diffuse test_ir1 test_ir2 test_ir3 test_ir4 \ + test_ir_proxy_air test_ir_proxy_gain test_ir_proxy_delay \ + test_ir_proxy_direction test_ir6_ismminmax test_snd1 \ + test_snd_include_sound test_snd2 test_snd4 test_snd3 \ + test_snd5 test_pos1 test_pos2 test_pos_trackinterp \ + test_snd_door1 test_snd_door2 test_snd_door3 test_pos_door \ + test_snd_layers test_snd_rec_amb1h1v test_wav_diffuse \ + test_wav_diffuse_hoa2d test_ir_hoa2d_offset \ + test_wav_diffuse_nsp4 test_wav_diffuse_hoa2d4 \ + test_wav_diffuse_layerbug test_wav_diffirs test_sndgainramp \ + test_sndgainramp2 test_ir_spkcorr test_snd_calib1 \ + test_snd_calib2 test_snd_vbap51 test_snd_pink \ + test_snd_sndfile test_snd_sndfile2 test_snd_door4 \ + test_snd_roommic test_level_vbap3d test_ir_ortf_0deg \ + test_ir_ortf_55deg test_ir_scatter test_ir_noscatter \ + test_ir_scatter_ortf test_snd_sourcedir_generic \ + test_snd_sourcedir_generic1 \ + test_snd_sourcedir_generic_cardioid test_ir_wfs \ + test_ir_wfs_noplanewave test_level_hoa2d_basic \ + test_level_hoa2d_maxre test_ir_reverb test_ir_simplefdn \ + test_ir_simplefdn_biquadallpass test_ir_simplefdn_gain \ + test_ir_simplefdn_feedforward test_snd_vbap3d_speakermatch \ + test_ir_volumetric test_pos_obstacle test_pos_obstacle_inner \ + test_level_absorption test_ir_hrtf_0deg test_ir_hrtf_45deg \ + test_ir_hrtf_75deg test_ir_hrtf_90deg test_ir_hrtf_105deg \ + test_ir_hrtf_135deg test_ir_hrtf_180deg test_ir_hrtf_225deg \ + test_ir_hrtf_270deg test_ir_hrtf_315deg test_ir_hrtf_el90deg \ + test_ir_hrtf_el-90deg test_ir_hrtf_el45deg \ + test_ir_hrtf_el-45deg test_ir_hrtf_diffuse1 \ + test_ir_hrtf_diffuse2 test_ir_delay test_ir_delay2 \ + test_ir_delay3 test_snd_reverb test_wav_multichannel \ + test_snd_zeros test_ir_sub test_ir_sub_hoa2d test_ir_sub_nsp \ + test_ir_partconvrev test_ir_partconvrev_offset \ + test_ir_micarray_az0deg test_ir_micarray_az45deg \ + test_ir_micarray_az75deg test_ir_micarray_az90deg \ + test_ir_micarray_az105deg test_ir_micarray_az135deg \ + test_ir_micarray_az180deg test_ir_micarray_az225deg \ + test_ir_micarray_az270deg test_ir_micarray_az315deg \ + test_ir_micarray_el45deg test_ir_micarray_el90deg \ + test_ir_micarray_el-45deg test_ir_micarray_el-90deg \ + test_snd_diffuse_micarray test_snd_micarray_az0deg \ + test_ir_micarraysinc test_ir_amb1 test_ir_amb1_y \ + test_ir_amb1_z test_snd_diffuse_0deg test_snd_diffuse_17deg \ + test_snd_diffuse_rec17deg test_snd_maskplugin_fig8 \ + test_snd_maskplugin_multibeam test_snd_maskplugin_multibeam1 \ + test_snd_maskplugin_sampledgain test_snd_diffuse_inphase \ + test_snd_diffuse_basic test_snd_diffuse_maxre \ + test_snd_maskplugin_multibeam_diff test_ir_apfilter_highpass \ + test_ir_apfilter_lowpass test_snd_hoa2d_fuma_hos \ + test_ir_material_carpet test_ir_material_window \ + test_ir1_spkcalib test_snd_spkcorr0 test_snd_spkcorr70 \ + test_snd_nospkcorr test_snd_spkcorr_long \ + test_ir_nodelaylineimagesource #test_ir_simplefdn_optimt60 #test_ir_scatter_reflections diff --git a/test/expected_ir_simplefdn_biquadallpass.wav b/test/expected_ir_simplefdn_biquadallpass.wav new file mode 100644 index 0000000000000000000000000000000000000000..65c8f571c24bdc1881546faa7f1c02914d3d63fd GIT binary patch literal 64104 zcmeFY`8QVI|Hp00m=Gy4Q;I}|>+ILQ&>#|pN`nScG>;mTlzE;Bl}eHl%6y%@_f=>v zO;Tu7RGO4ZiKg4&si_YY^C^;qZZ^V<9QJab%}oP7HwBo<7WKRsZ> zHUm`&2?=Ql841S{35m&<6eUz83?u@Uuh=FtFrKF(A>lE7itCtxzU091!fxWKJo$+Z zVFUf}X|i^9QW6q&$2IJB4V)z}2LIpq%H`XZOAK89U;9VkAAx@a{t@^`;2(j11pZey z{72z`wZioILg9Zp*eptzDs)EwY5%rFwBXVl+y8X1?0(zN-J)^-``GCqdh5vN#bPGk3qmpv}>0z@3atZnh|0~J< zuU&dGMP`_a+W$(j|7%AKXZ?Tu|Hl5E{}K2{;2(j11pX2DN8lfUe+2#!_($L$fqw-4 z5%@>oAAx@a{t@^`;2(j11pX2D|096|UhTq^9DQ1Oaj0ETq8kqLN);a4c8uuM>=*Vc zl|b#LBRJ>7Q2vRUCZ1fhl{{oGEdPr z7IOUN-O4z1_D&*ef0jK}mO$GQ+nLMg7`m>1qIm4AQ)t||9C6=lDfW5zc5zwld(`UF zA{O-DPwT@wSj*wJr|o$E!e(<(x4v(f*%dzXlE$ZmCp|&P~$df95)=ki#nbjqSr0cMVluD zK*Nt%^wXz?&L5g0QZ@Dk)&3ZCbVVa?-DJeCeWrxhwOv4y1pT09k;+ex+=thN$-$BL z{y6rdnEyPv2Jp&`;^2? zK3pvZ`z;ouX5-KBc8i9I;|Q7%%R@>@8N z`8nr-gYk(QE5r$o37^|2@i2IXkDXewT&d;zM1m6$}m zs<5}i6_>0ygbRPBv1-0fmObQ(flb11bhMIq(=;?Dzm55HV(wtb=*n>4#{d60A<$MB79Q7TJ$*x5B{Z^W~at`*% z{sj4ANxWd8EsJc*$1643;cdPN9{y}J^H`pOV`c8ao)#(GaUg|y>e}FO)tA82H4Rz} z$Dk724mKX=z|GbJ}C!s4%a#B}2r+#>F|;EsTqSo ztR#IAEx>CMF3_oK^+2F~m&h4N;Sb0B=&!TdV5)0RT)G#-`XvVBMTjP}A@dYZTO*tfV$;mY~l(D?eWaJE+zogaMzcS{}x;nP!iylX9Y)N3YI zZMh5p&vBXXrbyQ944#h%Ve9I(!W;`ddP=GX-#x2@yX%sKUKch{mE2eOjo)KPnYlnX zbdEEPnsFF&JNAJ?VjW)j>NVLvFrTmky{J3>GVJv`O&H#Rzvewe4fRX#@^xC&X6b!= zE!+;KKN=;Bk4U0TPql<=W+cL+o_Jx`%sQ(1!$io2r@`{!2ZfhMi|GaDcld~$73A() zEd26nE%h!w3P)Z^lJ3ifc-GU9t$WmNm1pLqJ#H85+AJ3Cmvm|ot0i`7||qhj5waAe0- z0>KhcP|?Njy%dK77RiuBT`{;t|1`+w>@%6D#gzhSCeUbkKiV= zE9m5{c6|NpTXJwlKBiYbA=`7$@QWjl$*-^TaAm+nlxd!fm%rXc=HAieHVe(sVf$rB zP;S6|+3C+pOfz^_-$?ds=Mi+b@dA_JSCbo6ZR~w$7t;TImz9|8Al~|)*zJyYNXPC7 zt237%N+EO`32Ws!#0h(#>&+{aS3`tKKA$^%9P;>C$ggV(Aa4E-Z0G4V(&n|!Gn<*r93NzCvI-Mip^W((yhmv-m8HzpO1)Qk1huDOT+op zYw-{|o2zkm2!klQJEG5O_RvbF3R?Z*Av-QzG-};MxW**7rAk>KqgO5p`xOXL)SFxV zA`kwy%JNEc@*t-xk!v)}1&wWHe6!{;IMSHN$;AdjbD$0%_sALc6u5JrrnMoJ6URk| zwJ!00Oo{~6NkP~Q`qL_HV7XuZWB!5v#!T6bPv&|aZU-M7vVlsxJXDl}CKQx{v2 zf=f?@7dIXf4%4146wS9nXXkkcCrGLaD@*p{PnB0u=)BFiNlHdI_0m_I_(KBv^i+h4 zFV7GrmM#;@j8%aTT4}=6vTeeVLE*xY{lg&hVX070f4)$V<|Z_^*MM+H6zVSgj;l4# zii`=&XKv@>G8E3S~RnbP$6sXJ6SOO~lsJ)*5@a&)AAGMm3&n|X~5 zWaau#NTNq4%Q&mU(iF1Up4-u6)yPlm^7<6Ic8N24^R$r9FW%lWW2Ts!3>Ngre|blO%rb zXpDnjry#0tjB}OjnOwXji>HTHo2En+hmq z!btI_wtU8aFJ#Z*hroIPW7|!2iNQV#x_`%UeocJ?_u*z1zCLm@9_4FEux1e!c6Q>` z;!D_N#!B*NMK}JNbrWxDs=+R20?5kXtN2y86<)D?GJaKzNs_$@RvQ0`nkD=tL3}5_ zGV~nPyMCOly)ugpN>kxwZiTbrSB!m$iDI=|26JIQlGxEnVQf>V7aN~;mQ$aC*m`Xj zih62jbyhbw>B4a8cu58CupA8Ef6Jo0O~+yU&_LX{~sJT2jd8;u6?5+nrl8s-6p-kxJ{kTeyC`eYRb(>15&C zb9C$FR?@3u!Iy2!Bt1IWH0cu1m-@%}N)QmKu>yK$%L=NHbB*ua=0^0dcabN5A=n{2`)8eav5z&@&%Md%Yuni8G)@8bipB#!7De2!Bzz=>c-< ziX5Den*h$s69khIV^D$L3HalZ0*CI7<{sO-LDigxP-K${%~w}&=GVeNZcio1opFF? zks;iQE+?2D--zVzOL7yEc5=1mLbTgtIjs+{rbp&_aXvjih|<(9Ds>`*qIhlYg5o+# zl_Xivr&RhN<)GlV|9Tp!c#OtxH>3NWNY_Nl%TbM|S)66O6gPQIndtM{^&A&j0HK~*9H*?xI0 zh`G2NWwivuiKM$|TN4w=%K^I5CkQVIAFhPh+1?Q@NL}YP$H_~6zZu@+Ua+UPp zda^JnksD#6TT}eYl5e&CL>7nD^V57SMV|f>ctu|k*>-0h(RYg#otW7!x}D`r=HhVj z^U7h7!TcIgZJwB4dt?LIYM3PoGakanU3nqY+~&??s)yQ5Q8)nuv%iqYy`*bYP6{tw zFo3_~#t8G4%%LULmcqTGiqY2s5JsOoMSHF!3yrGm!A>?@m>4X_hRuE|yu0BLH2sMb z4$n|xr%$#CBRg`Tf{hkh+LhAbH)Diu#2&ii%wVt4BqF)`JhXG=+@2F3*bRVbl%F_!S<%{GpW z3kA8;uesbRBR0n6IM?=QD)HFd#C^40%YG>K)ub#4Ctuu8a_*1JnB)0_e6w8?@whNs zu*1xT>iihYYrPDo?L{~E?&)iwT#a&uRE2$fK7&-t)WL;&c4$Vs6?-#z9?^Mu02-+} zlG$*J#-=v%O_y}wguXH_8xR5QE(bWJZf}&cZ!2l>T#FN?u0oqMW4R>dqa?pP1t0qn zfhuK6`0=*3WRaa2wrW&IlH*?zkJy`>iee~j)AQyBzMM38Lls(iJAs)c`4O*s@iZ@> z5k-`pW@Spzq-*+U+STNZ6hdY)DSI8VGUYVNVS~u#*zJ(6KZ-jfRRWL9Tgb?y4E*Gs zJIY9Z1bLBP$rr;){2?g_x!A=*`JXV-l`|JR|5inDJq2Wl{|`><@)~Nat;TO$bf3y? zdWz!v)7aWGKuV-#*+ck?KB|^64eh05{jGeueN!qLdwe!4-~Wpr^8Ol%X|3U#xh0^z zSD8~Z{|-v7w@7MrJRXrc8tn`lj@>WzkUds~ct-XV)R$5LA5c2cIzI*X3tG6xw2Od4 z05{>2D_tO^$WQi@V}+l~(HPlK=C)j&44!4omef5$E7s&NGItEQBN9{3x@csv(u!?Q z7V)@M5PK8VZ|o5n8c7 z`KhLhOBWvIVmqbi%~&~Z$N@8IQr0Z$3{_>?(?U^`)ok`D{~_NbGmeozxoD^MR(3_< zJFj`_3cYd|p^t?MjJZbhy}3Qe{DcvI>by1}+hPHWRmN^*e&oPG8$1)LIKNqAaY#iJ zX{eZsgL7|ky(6!H=7eFyJn18ZZCcKK?|eYQl&5mtFBGYAd8DY^T$cSgG7ELi(_@M& z)A_R}3|UuVhifaN_e4rM@<@_0PWu}0OANPdJ ztu_+{MR!sUkqLSi@Sg58vE#LeE3vMT0u+g5nS|kdK3+MW?izNHTO&@VHFa-9I&(`= zWt$ogiWkw4AT`0O_!da88ce)=u0w3*2=0oC1a|AtCBj!7p!Lv<m{JXmQpvpNogYkk8!w1H=K`Vg)%eff-=d}%Rl&F;k>uOXaKV`v zCo;QZLCvVJB7beI!kfA1LUgW~;PE6QSle1B z((5~n3Vh0J-95ZW>hC22H!BUYI$O48@kBpb``4Iz-}#js>+h+_>IkEalH<6c;Zn4r zN=D?CGlcf477AQZ7|D){sadY&jF#6-5`BpJ%T<4WWV80cOb~yO=WFgZAk&$3wv|5v zpt(+!-|y6lAnLZQ#o&H)_gk_kLuW2pkne7rC%{DhtfFAyw#PhftgN2oXiL+EvOqv9+M`?O@emyapZWee$8ApOJwTXTvOD~xRi_O zHp8zO!w{27(TwOM6kN5@_WWD{t~o+}}R5xq7B(T3zi zb(!tY7kPYm#JcJQ%|og2{#3z%)$54XK3qdr8Pl5gIRd}Efn<;J^qR$siiwY}y1?yS zJKrd%tL|7a6ro{9YHlXSapFzLrdmlF#u{uC%_^9NoCAj1HY`gN46n`ETOgRL31p3!Uh;f|`+yUpGZ$&)h#8-0w(8@rC`8L1L9 zF~LQkq%?{+KJKc%U>{BLzm?kVYRKf*yb7q!?taBR*c4N9VxKdovGlq1z#M}vDw>OI zvSpEa^;w%V<#u%Z#2As`Avq-HEwY(-$OMO+FkmSO(nxB~RoLbG7;1Od(_-%`(dWgp z!Qsgh+xz#wazi|ZlaECyWV?nRwVpo?Dr-zgXOs?AezKn0UQmLX(KUSLtY0L!+kq+s z=)vTK*)@U6QdB=}A&t;H0>=iA=90E9rfQk_RPC7z4y}A4@c43)mgPOAABGv@ZMQQ- z^lz*m^ZaaUp6W2CE5jD6SCChdf3tGFRZD($kKptbpke=;?XpW2%d0wUH zZCfgOC=Q?=7d&cKez5@mp)PRgNdb*{|6VYnZ5e3v#et{kX?lzWaKBaNf%KvX2-b_D z2TXT!P5l~B8#Mu@CXb|0sn4x8j75(X_9MOFab!kIk;IqshEOfGDY1f<1Z^o z@Gb+B`q!#MWYKnKo|-S?CYMwr8+!$6vA~QUe03Vi4IKxG3KQtUcPB-qw_;JK)gnmo z8B6bmIaM#zS&nWrIDwYa8`3@Mux*-QBxmCw0XK}Th`8rdjZw@W{!{rxwB5#wGhW{- zs+El$6cd*f4bn&MT>1uI9>jM`d z&){5<2<4HEKhyXwNF%Va~|Qj+CWjbcrv@#+i8 z$Zy>`zCkks&7L-v59&xG?tdb<>k>QA_)DFlIfX%FrrSs)EC@m0-S&uXR|gXF&gWcc z;b9b#GDM_f8A~pEjpcv*zJgl$(ISQDLuC7IKO))l3OQ~{5ba)5NIoc*k_U^sklL4e zk+T0Wa;xDC3HtpMmCd~^8n!f<6rBhml^&PS65HdVfZX+@vr2+|)-FSP?~E2@b~=&9 zn32Lx^KN)E0^!1O7x9uS-FTDsz&@P&Uy#ug`WYPnfQUW-3eprZ>rdRD?fVduntRi$zZFGA7l-F7WC=(A*^Y&C$j`iwl{wh zKgKGDHVtbcGF?8vZ5|J^{@S8vqsQT%{>3=&Mh-+D&Vy;y8MwSN77yB!4?P99A^KVd zo)qDZD;{ow*q|fuF3AqpmnFg7Cl64tZa2EW)ErE5)=-T*aUvN>KQcQYncBI0q^_+q z$ku^1*7U@ul&!o>-vvsLmn+YZn9(Pw-XcYs(<|hr`#cb69r#36e4mUq{gOqGUdN$r z&X&C7gBrdp`-ot|Qgf7}+(rrqi78c0B!9-Iko<~zYE!LhD{ee<9deZ2 zja*K11ts+HgQYCDpq#lc*+zfds;9Hl7co!wJeF$bO}pyO&{@7R;_17R7WS)|8tN~;+1@S%WA<|hFAG5sz1;f0N@1I@Z z=dFuh?vce~B;(PPVeybtGz#0B+Tw3XMQEyh4k#WQk6qV#Vn&<_M;TjWH@_o2&Pgxc)IjrwBzGeIMMMA)R(`4&^}eP>yZm= z*-{IFn>;LFwOr#AGnnz@IG7m__&Y6?yLEpUDC%d!fuZ+c#Ho$k+E6(t{E`aCa@xWB zsv);2_!IIF#lVey4J_&JOSa?O0(Rm^3pN>YjNLMeU^g2i*}|W@vD5Q8^pDhgv}~ar zy6v6Ki*k>`R)?$L@EyTR%M>a*^#mkMk%i8^zUW|W4p|_P0&Dy8&>_N7;yx}%vglURD8)f4$<>RrY9M69XJ4xLiiXmi8 z9@u^uLG3z5fy{PA_RL&{iWf-XZ-z!pH|!)E{AmzNTOr2XOvE;Be8r~PyRc=3gM@Vx zE7+IX&&GjAx?ohuY|PtO)3Ki8xa7VUkTX^vRMiz|x1k^IyPT|@J^?@T z9E-%8blDBTE;`}tXOP&CgMa0@vsH0b)P0yUF(@1_jDI_i?H$EvK(09(lzI7Z3dBelR%o%T0oNb6~NvP%cwH5qhCL+A}d2K zf_{oF>vWt$8#OXW&8a>ZHEspVIh#N&pS~dtCl#?^+GKXK^EBPKN0(~jFJL-#G%Zgr zrcaieQuTYuFzMcCyd*S~KH4r#*WIY#s@03I*N1athwf7H+{=qbzrTd_4&FiDI>zX! zOc{00&&7j%Gr)iSAh=S0j*8wWV|9<`u%*Bqd@4`U{HURHS)USKcrqMpPbbl!lS5g? zcmq8BOBVQ|SSsDA%RV2n!Z&acl-T9ch>~;EA=w`9H(_vAj?rsFx08+K&bVsC4bYx< ziyl7Pgrbw3@#?m_a4VypzLYgZ%Xb0}xpf2P;r-O%rzy3NH^6HS)PT&wv6Lsp)Z%n6 zR61mX<$@99x?v&Rqj(BB-TYxqK^RKwdq84`Vc6EG3!_V<;E=^4s8tR{OC#&h&V%Ei zUit+HazF8QBREuDB5fTtE-Tz~#Y8(6>dO+GNU+e*aRq)NzUi$j_ml zl@o~%dH`#ZU75`OJ=C}17D?>r0f%Yw!Z|`|acP=A&c9Ry^&8LPx|0=5>HSdr+VvKQ zlB#IjP!XN?`T&>R^a*sWB3Q>nJ^J(VSrV-O2u2>8##SuprQZ9yP*VL#7%F{*MozL~ zKBuE$-lzc&Vm70bX8obYNs}Rb!zxHg8o}~zUM0fdX#Uml6;Qr^EW714801!{(C_cv z;M?PU^pRsNZxlR&9Bo#GCyz#8!7oYn<8(Z%42nS8%Fp9L`e7`_TMs+9NpMbeKk)IY z9JaJ|HvZLrMzDQd3y!m2!)%=}KBcz{MHPACk4I8zw&zn=v1l0V+c8BL6C8xayPSoo zcWRi?Ag`x7%nAJr`^?fc3lzU%Cumn z{Yx0vs7TTJlk8Pw7ISq^z|e4pJ+4b*PHR3hZBmM7biHS#x@*}U=N_gnorjN^G&8$x zHmo4x7L$nHgRevtv77SpY_Vkx3wCwK`=8~r`hjO^W5HFHTog2u~d-d?1Gq>2@mRy=CU(c@H+=@?qd%)WJDrx6l#;Da=T%4c9 z_OA@138IbNfqA5>u0D!c=uJjKxz>SPmRJ>^geo66^3K6;z0bB$z2y#VQ)*-u}0XCm!CHZ1M*9(2xN5vf9<2<|)}`Z3^n?cC{kZ_*}4~LJZvJ;Y3 z!t>uk#B09RV5jTTsr$uj;k5;QEPvBN++eD~=J}WiYmRQmMMc(V-i8|1Ufze#>E&b9 zVIHK_w3|8C7z)F9b=vnw499|7n1u=_tWhsvLl(@$Zbsoud4ZykxmK`^#jEk}slK$< zSRFfF7|JYoA+|kp5jf8%rl*<->m9pc<~nV>{ahED^iX5qJ;{QaU!Bml@+xa>41syI zsRKUgRg~k{PCdVtQ`b>f!Kz~GfH&!lN3^-KvAVL@Na8&8&w7d1CP%TSTE_U&z8&=F zXC>k0qI|YY%K`h1xquv1h6+~{yk#TKtioT8{)PD#Izlf)8}WqPG<Uq?X(;!$k;HDyVxLXeuczEqjnPCicPTe!zkgfzvZmE zc=3QAEX|JA4Hs(Ld|;&(Wl*yyk@_^K2(yHv#b%EZp>{|CDry69q_GDGC5mb9Om(LFu?m+h(Sf6(iY(#7NtWf% zh;!cGLpNWJW=B6%FnYWek1xtWoP{34w|&_)mW73-d8lu75B<1ZlkV>D#hFLDk#F^Y zx3{+z4*bx?t`{Ak!Bt3{7E0jV3LC+@A_?Aa34^y2E8xu5RvOkN24i_$e5Kk3rs@|k zN5c*nJTC|lr_O(}8EmhbRTd;pW~I>?9{DlXeOkhl7ogU?+XhRb3)L+m_?AdW!dm%*+FXm!I`LbjerRf#`s}XJUh|?>suh=dGb^;x&$M;NOGZ zifFtmb_v;|m!HkUB95@7U4J^|c?yOK%zDh1Lj1PM%0sni)W>@oN}+ z(i=)1Y(Q%cN7E{+CUjFJ5#qOoz^3P?xa)>u>MAt~%m3^KV|#VXk6+6AE-c4Bca8Dq z%{%e>?P+Ykt_SvN9)tb9h2q%f!OZ!s3SM9I2PE5OVoTv0dhtpP6rWoGMh@zD_gif+ zT2ll=+F$XFi5j?1^cK|byoAzR8G0~oF%DfGIgsTUg43q}DdqF<{kpSsns^ZI`Y;kE z@fWb;^f1<;cN|9B7s0Uxij4=nkmPYXC^w`Yu8xesXIk=E^>%BjJ0$@^jQe5ry}4|4 z+B$k=GeOzTZQOT-dfIkDi53jgBO8>X@$nPo^hLmBIQ@6dKn67eyPX}yCVwn~&gbTM zk@XVn-*cGV-;{!O>nr1j3nK8d>u(t9|3)PKEWnqyAI8u3NQp-%c+*zrR9q04h&xoC zuza6wG^uSj*1o5MGfRV6Z-hM!y>SDU&VP&$8&BoC-|~+{>GbdFHvY>?9?VpiLjFlh z=01Z#|EEWgJ+RjB`EAJlm?h%KGYsWKXyZQjx;lC?D!yxp|2lu6H$R2(nV&C_mVr9az)gaUS|&};$OThF_e#h-e4AeBcBXn6 zh19)Un+vE6r6ap$(5LNLRMB}W-SkPF>dhQZ{WB)e^#y5kX|y&qQK%&wV&uu?wOZ79 zXB?eA_7K?{0?2*zQdE)um^SFNlWT$Jxla$A;j^J4`~7wz6~YnXdUP%f8LPm;XRM$( zm+Wb{dOzaA?o)lyd|G*MJ=JvK(3w}8X#E&Tns-n@CF>8Mb)g5z=H?pP-zKX`@Sq(~ zXRyB}&b=4C9G;F`-+qP6Z?||lP&zKlP=Vi@%&_mzS0wJvIEX#`2mSW7!S8H5so#Z* zs9=^WDpeeYpN|ZuzURLPdhR#yxBRPtzqgl$ICzqgdN$;Mxf+ZayMQY6uO-h0G6eqE z8vS@WkyarD4D z;rL_{9v)SLXK5GY1cUo;1G)~M<%iL zPKM}&R2p-+BEoxjpJY|}^H9W%5~eOViQ9Z4*mLJpu6ohd%Z=eB+DKyP`BX(4t1eM=j)4Q3LO#N;JE(w}R z)3eUgKhCji`k^a0sX>-iTyUaybkf-MltWmmNpHa08&tFVvnN|$+z+SKUee!3B;m-- zINCJ-6uqbRjtq;^hmoqu5O!iC6Kl?bJ-5FipGyOITEApw^!_TGsd|C>a$WGwjiKy$ zG=-AGuhAG^Yg{XD$1X*?LgElD7%^B5@4xtjhAuq7Rdl$(pE+U}b6lM+G$-Wu$EhGM zu>tmK{zUEE6Oyz2Ec%~EA(%5MgDg(HLXN9AL-2YD`a;GJ1?F}Vhk$$V#&;Zjl^qTIQ(rpej1;D(Ux?iE zPWYsMmQH-!4yy4{yzXQvERs4;8xod-&h9)E{a6eSssrhIaS>_1-G{>K(ooAxWu$VU znhN)}prF-}bit}pa9sPFj(+HdHYa|jjb9Y;?bUDT>LDilD}^5FIbIFRK8T@ek3ERx z_zYUD@EAf9ZTRMpB$C>;hF>c1gV#shV4mkeqOr>jUaa4Y==e+sI8{jWwIkr~_NHpz zd)r~$l?oCf(1y9*@mxphMU)8BTtq;zfF_h4x-6AYLF71j&`p7Mm-J>CP}k3;KrA!kQKnw z`|D<+*`F$rU5+cv`?{8tH1bGu)dXH&?J6?T&I9#xrD*uv4pR4Z4(azAhItt`G{eh+ zj&L1K8)h3|336ZXY@`Xz9mqx%dbEPUL@BaZSVPXr+#?O#LG(8yl-zBa#z~Cy;1p_i z({jria-CgAk+GN2YlCa_;G8zHXr&TpTRlKk=onq5_?8S&e2=;Z_Fk?lR1s|rO7Kq) z6lH{oVZ|^gOHm4vrcrv<+D=UK3W!D&}N~n@0$Q zwck9%?rNEAX5~)t%sIn^)>r#kroAFN9i<|+wL60cspv88*gDQ2V>6pEQ41Sy7y`4O zC&9C)?8| zFUQ6WF-+*0N$=gOX5T(q;`Lju)6hS|sDBVkbc@xXVU7LlcUmZuSwv^L@u!4zwrs34onJ_D10@e8t zMHN~qA#mq#thx6nl6pNHZ}U%}z28=16D}QUcb&k6&)wOJ(cAGm?-B^~I)|H;_p%4h z6LE;~G}xXSf$#a-vaGQa;QmfADXLP2aJOgVXUV|*=hx9G4pZ5>57Icv3A1NKgP257 zF7r%TgdM|7n3eb){dFdal_l8Y3R!hJLuWZ1oL)xvd8U9}kUUoSl7$py`eDkTbUMd9 z8h>Av3SRBu_{Q0>Y~9N=Jb1)$Fr6QU?-^UMwPjQAow8}5pd*FHKfg|!hF?Mk=7xM{ zlPwjWyw0sUFU3+q&FEH-@k~)%3>Qv0v5RM2X?yT=Hr@3GD2&r#trwbz{kPBbX8%&K znPfmGCoV(dJRc;-P|bv4o8W+}Sv+DK?TYJ=+j9Dt#|d#LC2YEbrhg3kC{g_~+I)H0XBg%R_R z)XKw94nlfzeJd>dl}20lk6_7xZ|SHZJxHirNZ0Nj#?I;}(3OU{bopLI`e`_(;}uI` z?mRj6dR+s`i}&I44JgUb=ETaEBZCbH=6*f1~<~UlW}xwUIMim9m0=}8%*y7Po|m=htjCaW;7;p3XFLo zM+EmH;qpfpy2P;wM*BQO57Z{(N~7zt;B(`=`?H(rr?H3?)y!_YmJOFeo|5e>;W=y3Xm?9SDYmr`xK*41M$+E0vflI3Au zVHQ%qyBzWc_md}@$#7(p8yF1aJ9w`)I&0}c@X^hI5adJ(rW&#M#j9XTXCB;+{Xn`N zDl%20_o!_0X4o+LCn*dTQ0)u_#Mg<@DEl;Gptu`4HhXeTahR+tS4V1NKEU;;k?6O< z2lB_x1~l&$!KE>YsQPma`Ly;R)SL<@>Ap!wbG1EJ&y|72KppVznF8{2RD$fRDEL%1 z@K0620{+OPjga;(8YRf|(jR|SNWuFg*u&B8|%rSa&nlTh$sD(tfyjN<|?!!M&#AR6T|u+DLY6Q5Q> zW!FT&wmV>Tp@4=&U*s(3rIQOskHE6)FKJ-gFgnCNn(95TfgbH&G-=xu+NChC_MK1y zaWB&8=VbyiN2iwb9@_$r4=a#lVHl7CCx|WJk^asvQ2PE5cI}TL{1Q9*hWEx`SdQZx zr$N#|M;dZ(E^f)&jA!{st_j3^d_XQYg-I)un+loiH-`80>J3#V#M#)09WD_;Rj1ZnloZDk{e*mDI=HReLc$^dUp$ zQ85&2`7gxILtc}b#~N(D=sd*Vt%cz&iga>GH;taF2~DGoFD&1E* ziJoyXU^~}bB}QNK>Bd`g=#kBX*ozV~(v(y~TW<)dLCPmeEi(i*9oOiSonOh=^{=Vb zqo15s#v{6F+cJ{(<_R6OaRmCksf%U{Mi2?bt2E+?1hSKqWDs6BP?Hu>BMF{6tfj;< zs|~5|&}@3gF_gRWb10k3me4BcJQ^=)$2Aa5CL24I?f7IwXLXIIav9rLkJya;JaU@$ zCJv##<*ux2n?IE>Q)YWjkMUd2eV|{(bJ5L(3QTcYBd2!nG;I+);UZmf>9S{!MS^oz zsfMu@{d}OA?{t|&qyk^kV83))I@kabwMUcSX*UMyrG4zj7AdCOD8eV6UZPe}i&$(W zql=oi;2jN-v^gr4O8q&7H12p&tMaK-|5q@w(eZ^VZ!}o#=tR2jO)1ZP%s|5ia%Z0! zTc~R1e(I66p7?1Sz>rye)V%8_6>IqOyF%^YM)pnm;eZrdq9g^@=d{V?zPa?ltA5;% zgZ>|i&NQyZu8YGJD$=M)11Tj5mFVoXk|arrD54BW6hh`6L((h_QYuqOQi%qsbFZDs z6d6(>goFn3Oi6gp`}KUf&-tCb@4eUhU)REcZfdxE`F$K6_!GnX55dQlvN+o-2D@}` zf$a0a#GQ_Tmv$+A4m9L$D;ub6`$_1T>BaNQMBGHPNO+tb3=IJaynb>E+5Kq-*W@C4 zdoPQ|-6^O2g-+-nS;EJz(&5DoMqK6BT$HiUudt2G;OmY@@t`PkRQzjKk$vVCzh88m zzp#1?y3a>ftaH7{t=g0L)>Ts>^V@6glNi9=+>py#eH7sY2OfNGGCdz8$=M!RDokI0 zw_JBh0Yr_N#11T7Ehrva0E-(G@Zr3T5HC|Ds*Q`X5mzaq>Zi+K)nNsJ8OLa-{UCIb z9fY?pltTOHM7LAt8#02Lq_>)izZXMo>TXo565(|3BT#=% z8{a-M#f#%kvK5=uh0)RDu>ywDzPMF%Xyjst@fG{V z=kw@6@%-G1k6cGVw<1mVIRAChirZey=8s38=aY2&`SRz!!nV8-lyG?lbsujg7mXn} zn}y@=vEwmh#w?-mY%-4g^9G$Iysdta0xa$=Lt_m&+b7Ed(9uT+2Dn_tAkkkuDD=Yt zo<&T&=^`2pX-3V=wXh&hkLBtVV?YYW1DAXu)o(d7-*X%fMdhQ;`j2qVMFDo*kHcjR z$8o>HY<#vl47}biLg_haSkXPyHs|9hSU74fXn7Iqx+r8G4D;9*B^fx$ff| zl_B_bfCRS2SVIyA+hUb{^HnTH)}Ym)YUa zJ(%NJh1$yhz^=*&+F~6s_ULhJToDWtl9M3-Pr~6H2jG`VG9_4Lqu$5uxa;0!QPErhZ{53y=JT4_b+q{S1 zR=f-AA0MalHp&?4=!R9Qld)k?JXwBFk|3{&csNUeuT0O9aQm;>uqQgKe-7}k>mRe5 z_ZL%M$v~d_Mv9)g*@{!`^U1V#H20qQ6O`>=h`VZ&=~lHXh3YQEO@8y3;X7&i)UJZs z!xK?awStxXEoGAQD8Aly8Z(b-i{&Kmw$PljINjqsj!fD~p^38CcgjAt+>N8#C3ls6oAJiI?U?(*6bGC8@u2&uc>4YUyyo)+l%D(Wv=!Otx3w5A zU2B2=f+zBl9Bd=6VJM_}F-O|ICr9euy8!7*Qh@PbJvjasLI9kX@dRZk3Ntt=(Y zX)Ww%%{c!1`6j$}c0WmHETk7n*?er0F}`}enJjlDQva{#_|4DR`S zNgQ0Dz`HIVCOfY)P#t=Y8JJDrms~Yz&e;(l7d{GVj=S@|g`3+*)Ug;PP)i-|rdCHZ%_A-AC&LwTl-259h1U+ z-;T4fI@V0>yDQlCYvJ5}W=q+DwX!^V+c_9CK3~|}oCoKxSlbEQ%BhVIPO%+NyB0J8Xq}?N>nSBSo>CPCC}Mt_S%(ry$nrxzOAv7N?y0 z38Np0V7lWaX#Z`AMH)-cuj+^R_tPEN{W1=U{;omUS!2m`+G%vJ`Anns5we|IntuDp_0>90XPuCu7ox4q=ykA&gBp10R+N zs2g}d(l;%GH*LufEG=#8UDYVI@acvYrHjm0Qf*u@`he)0ran5=nNZrXc06va$P7-K zW52;y>1sw_+Y?<@aPVvz+HKC~|EY`d(TX%?J|-O1u19lsIYaz1@v7iA*A|~OyryYS zyrKS@u3#%~js0s~q{c_H6mc@?YoZG#cD z{GLEzX)+dgXp4x7neQe1|~HJ z*?s>Z6!u%1KT25-+Ye0wpK}qc@W)~LjT522=T5kl76JjY+=QBYDs1K8G?+Xv6t<{c z2mf8esc20BY;oGhDo=;umRF~#MDm_+G@T^L*UV?T zgCkL@3(G$xuBFy_ieUIt3ir*P0ZNsTv_CqXm2A@nsn8~HI2lfn;3-ThsI*zRbTIz0 z*h*K8htc&;c{aMp9AAg8rjTEZd>l2vTTvfNESA#e%ueCRzzE2WcmZC)u4JL5A-X&3 z1Y}vRgr^$%l%o8FmH6dB$ftE|$(p@FW{ozeE0x3Ng`r|uhpkLrJDGj(dI!EWza|X% z?FUspkA+$nGi=ETWkxMEz?yRDaMfB2Dr#mzSzk0Xm*wO3rJ&iwM@%711>@ZGdH?-& z*giD{-uABsg9aBKtX+trB*~q0%`7M_?x6>Y!|`1|HCXsxtXSpVE%CmjOksjo1k9gx zlJ0!p3(6$RryI)PcpY8-QIdW5#*Ml8CK0Ae`eU9FZ$@6mH_}}`6>AgBcw`rX*O{xr zrx7FZt3o3gJh2kXT>5~n>T@WeuM}qe>;{(;18x1qe}weqL(yJN$p)gXh=QyTdsy0!7Jz$Y$?2}*2VtYHE@bwI6jCoVPS7y z!uP-|(3~!hrQ_d;e_6o7j0)HI41ds5LOr}xc#z+u%p*O?Y@MCS6@nhCFi%_lT`WdjGsd90A z7dnR~vEiy|6ny=y5H-Mr!siu0MTajvU1&xF8ovvx)#dO~>rcVaUxQjk*NMljR>A1! z!9sCYyzupPDofth1{z#iP!i8zF7-p9d}=62b)Oa8_%Ri@&jv`wqwHy?A$SGmLr?r^ zm^5v*NOS5XSmOK$LLN0jpKT}DUqMTzpfnKPH`jUKc|Iy%Qvt#KMa=2=8$<(9<|u}!IA$u*cFmw)%M+_ ze!~=Let|Iz+7d&4F3~jd^ii^Jm8_?6%gUubM~uIyemz`8ex`uxm^l40!sFa`ax)c;27-iq+6! zpt#&~#zZ>Z?o2})X5ga(e?`GHwc^tYgJHp$+4x$J)N+oc*%&og;hrIeShxKfRgW24 zu2?)5`|uJ-U-y8*0vc^D>JG*Fsom_<%s`1ZWSMB%#$Z0-6@gqJtv zVNZ%0@4c*yYtuP6jb2aj!;JaVHPX02KOI(tydlLa!@2nK9gt&-;f!RDx0+_f!(Fey z;$hl=3q$$RHh*s9&87Mbr^+;4e3@!JcLdjBb-qYsJCIKApNM1?dR(2aL4{HOC0kN z$4~FZzT@74``IkkWOEI#j}ODck=Nkk89U*2@+|ywdjeLm7C86x3d@&G!nNaKaH4%b z^yrfTHCtOTT{#nz`@DmPIT5h%E%F!^YjQNi96Y)0+#q>_`i4 z799jh7iaO^vEKaqt-k#AwP5%-Y$@*piu`DH zvqNRMOG-Z&Gd`C7cAn%@?)2eH0{mdrHcwjUHG;2pe@r#zmas4}NO0ZyQuM+048_iO z5{+nyU^GmCWe(X=KE!iD6sCQ;F~{&jS~7|PS8Z?0dIN|7XZcqGchv$ zGMs*1KvJupLHa3Q+>!ADOmq*?mS#g7*C;}Z0Ro&sZJJs!1zR^a(b_0u&>5i(3XkSs z#%L%0apXG|l6H+bj~|06jkEZiV^hJm@{QQeWHf4gn8u@;RpFiYYi4jr87qH_xc|UQ z;)H!az*F@D^yyUNU)M=$w6^oH(oYee#~me|{(EVDL?kBKx!^IiY2wSH_R)1oW=VTU zI9|vygP1j|sLy^^tos{}3X?S8U$GRGZT<(Y<96fky_d!2zV4#6Rt#o^PDhMzpj=%8 zD7aH8cw5Wjg_G{oH8Te4h9}TJN$tFmiaayY+3|23pDW6;YDy`XAjdk zn8=*&x#O1|11V#A277RDx9}=wCN49qr5k3W*|+Ae!nfM~=;+l(o;S{lT6D^U@5TdR zxm^-j?6(w!bv2cT-f^M3E0?qPx>ch5Z|B*K_Twbu`2(shWw4jyO`+i6WGaqYjth;= zpzoB~;4YuQiYFY#NXK}%bY(gWOH6}_$!G9;_XY4;tPEQ%IZQS>in6yWLFMTb7Bf8_ z-ke^B?sk&jKyiX#67i9he0vBV6c)qxK@~#k&Ud0??uFuDmA|Z0X15Sla-FRUIzWkk z^4NV3T{foe2n2jOK}zOLOy}e%aDA?TwVwUy^C}}~oErl(yvE>~E4^&>f78JuWFL$< zW`He^55qr+Lu;e{66o_=!k_hILBhB~R^}(kPZ@Qwz!nBRV+RS5ckhdu8w!Q0uexRL zmMM_bf!PzjzL265yBY=DDPgVlXfuoY;h`pAo5 zv*mT+O3gvRSyK1$np7{gJUM{eZ`~HFn+8*1bvR5j_M@mB5lq(L0PQ|n1TVCJ)b3e= zdqk@6?Z_ms^G_G+&j^L}r#8dgv}BP-xG5BicEiGM0QEfCx9E3-i_SQqxYIzCNmhOe$-^9@HzJj)2kwUx5 zPY!=Ib0o-kD0E_U{KctRD&;S7K<>f;943A&u8t1B5v9Xwuwm$+Nxm zv3;Y28ynU@Yn>zcg!iTx@LUPbt=U1DTU`07R3$w9N}mS3n!|2CK16R-_QMkMV!>QcUit%5GeJQ()|I37+0MKQln3Z`s-xS zhayvOnJbH9woahDr`FVQ{suXYABM^8aTNXCM9_Wb#6Nr-hW~}TQfx~&lRIzChtB!| zH{!>_vwQ8V-&tusTB#H~Pd$S<<^y5Xgj`w}xEXG&>5tZ?7BJClCdoIQV{sGuW7m+i zu-93i_7A8M-v3t%i{mq4N!=tGGJg#nNk7VzbgN->+E&_rD23w1c{F_4P55BDjE?_{ zrwH2-eDtqk$g2+%;?{1Xu4{}(9$f=@Ju9Ge?M^a^lIHrM32f3`73>;$n8b>ggs17R zga^?+_~Swi4J^)uwM9w9-bG;U_Se*=sf+VYHPPkCNx1jhZCVKXP&~t&FFO%~tK5re z>eT>TFFlg?UYB@n-zU>4hrOV=|jOJ2k2Tu9BUbP9Zoz*X5X{2 zsCn#o^4xd>=4$?d@bRJaR*>iR=?-ALe-YMw_$ze1lH=W1j6}=WdJH>v!lve+54pC@ z6r7$ayN~yutNC73T7-5jhS4*rWu(5c$TxT?uq38+!L0nuAs(} z5%joApHDDQ$M0jmOZr4=WWR0%-*8F+<>#sK2TO~X_xxO{3s?`6D^KdFqfp z2Q@#V==~)P`e}PpD3;V?cU*r#y>DjIM449h>}`LP4{E2bbQ|I`L&0@H3)Cj;qm{Gk zB_4}Z=nf2rPgX+(Z$VBxGX6g(nj6V3WEjKn^M}}g2NU3ZVX?5=bQdfLRsqM8wlELu zXnXZ0@OU;A>L(t9FL%R9&wW0qp4tMH{&ILd{tR{R4TFB!`@vsfB2IeMPNlyjxui4+ zH(TL>Dm@z9eX=GxndZTP8;C7o^970rdB?*`RC8+zT)k`0<2Q!z?+s_j|8*Wnw+-emmIU%e zFI9PViw^c=J)`RVJNU}~*7LLHCSkjwyHbfQaYOL_9}wfIqW8Q$@*0u`5C+{7jh{fhp> z85yCl`t3YE=VJ_BR(HaKp%!e9l03gRZY3r+c0m97RNC6di;|MYVStnc%u-U}3#LqF zYip$Nc-c7{^G0Kyr@0#X|0#x5=BEVhC-M>&;R4j%SP5qqX_HOz0}4Jf21{Pa!rzDU z$@Rb-(k{}&Z%WA2X9m+Qrdzh~Koaar@)EnZ>Qi#!e{A1}Hc`l-(c-o?Cl((yLfDn; zAn0i&vCqejzyX6mYK*!gu3Iu2blZBtcJysBsahdYNh*NbzhrRRiUIudEq@xI)hrl3 z4}!8rEiM(dO_<&JLC7taW5&f@)bl|NL?;izj#77_BxfH@Xo_Rk<)tw!;g)!4;Ss_0 z`Yx)zp9img^@V#yH(7M2F5hZ;mZj=d!N%`fA)?;`zWv7-w(YJK_B51&?BhU=``zGO z?kIGtA4%Ki$MFu0K{%l4E`%?dOjeo;czeP(_&Ll22GytZ@lRV}O+WHBNB7~81YEqFg)N*7B@z)-vxZf{HhN9l!BxML**ral2CHxv6Q z#0$#xn(3MuTC2 zI@0HzXlEM%j@iFh=+_%?K-wHL_UOUC8+~k6h;pIN_xEtQ&q@}xd7$9o)&cW>zW|Z% z&GOWrR-&6lyY4QVFHjdz~Go9(gSS4QB z(hmF0q_}xP6#Y0B#Y4B8gQ87ydHVQfAxI&SUv-%Y>fuJbx+ALGr^KDFO(|g(k~~qE ztF&m}St;IY5x^Ed)f1dIT8ot*M^MwoL9qJgM5@33N4#;LCmViA;<;4JCQm(Mq4Z!s zbX4g96^|{{DmpAq*58B8UIWnHthT(hWCq&@l^9|!fyIW|LJjDHjZ!V@{TvS6>XYE< z#GMdbbP^5B6=;4Y!I+C2kul zkoNZe{EsB7{O|QdJk+?GF1(w|n>w7as_cm9{?IyMr}7Sd(|<7To@Bu1HE2`%ge2}% zdk!8*@+}ops>vzg5I^N*E#YKEyy=lTw_lpfgV(h)ec!KCnXboYbS~unf1TK#3yJiq z;XOru|3Wp*Tu2|$WF!CCU%0qFO0a%YPXkXhv-i_Kid?-0fwIvrTEF}T%bBN3J`XLy z!|4=#(p3Yq{8O~1M<0%DDHK#kZGy;SRrKeb4HU|@u|*-Vu-U4BHnv5<#2*Wx{}F9a zX^Ej&yCP^aoef#Z*T~*kU=NmPV&bR?EPS~pAEx<`&1k*>Ayb=$Pt(QJvZO+YeSJ{K zF53@l!F19rlJGL!DYQoQ478a$()a0#e3kQ2N>P%<(Vf0D<@itP9U4cgE7j4Y z?mQ)=ACx%4=h1={{ZN2D{Pn$&f>DhnMQ^);u|*Tb(o7CJXOx2d=t^wevrc%nb{cm4 zyv=ro>te^1BKn;*6?+bUA^GUX3)rS1H<~r!K{VOQy%tFtQ1{yw710M~F z5uOZG#X~k;-&AunYLmNl(fL#Z zyg0VVv4P^}ts^JBub?rj4o;j2Bg??caN98y`__%YSrerNZS{3{ZeAX`zjs6FTZ7=M zgaHpo$iW{!E%BPMIdolGi#dnZqn4oi zw+HfeFDZ#{e<(LC45zKvym-WA9lrJZMDDa|6CK_W%>5=;llF}|n%}lg{GfU|f4E{b zIlTy_y4v?_cYz&Gui8UR0VipQUM)M8@509}d`kDVUQ_RvTjJL=ljqu)^2_%6{D$&k z$r_l%3ljagC}R%KIrfa+=<4z-%cJ?Hph!NgP=}x0PP9cQiu-Qg&qv-F$+a)GiWjxd z<7uvYxRI6ykJ@?$YNpEZ;1hHC=7j6i8>EfTTq8(*y&Ug4FqkYy8{vqqp>%RyEah%U zXCY@=ApKFdICcGZLGi*P=vk8l!*2Odd`YFKW>sJGoOxOJ86)w}E?5ro>fNCHwwBt= zWVs|LihtCVaNvnUG~GghZkhUENbp4LDN}@N)MTVVsAM#I~+SccVG@y;RH^cinZ_nyD>VA@tnZIkD_ zjNemH)C&G@e;g^_Nu?XrSICKW^Je!4!nMNPOg3%=c^_KD@BW+td+ups&qc|eGTVet z&~Jf3d-vel*4s>eYdf_`c(It6EPN-vE#YSNl5?aC{<*Rrr}{dxSD#)9ZW*m`cKcEc z{`sQ(>F@2#?J|dYKT8Zb5-m1t+yigk=fIhE2BI0Vd`m|VI92`z!{;e*>S8+SU-|`} z0q@)JhEZaCRJSGFdj%7C1Tek>W*Dizh`@vLtw4S7X zJueShG6)A}6jSMyAv|tmGt8I1YV28?WE@p)f0daoM_`ta-U62B%pF?-iemLktAb-r*5Y(D7CD_RdDK zzcXK)9VSCrjeUg5vxE2<#Sfy?!#&izeFAB}TFLu1e_$&gOyoJ257N8xME>OSWZ3(V z3IF-Y^IDeznwBrAFIIR!qPUEHEDxq=>jP}^_b`dy>9! zR5I9PVUy%M2Ib5c%(znyD=xGOWB1R+H7bQHVCD5XCp-4nX;&5g0c1hG6~gEWc_RhUY9sq27W1RO4UD&;2`%zdj7YYmvjr^=St8 z{Co!MU7O&qVn5P6=*}(W_TUkt9bok3m{@OnE9r#kVZQPcw*U4dG#+?Nuo_y%JU8gF z2Q$Jku)9&n(&-d}a~Ft@rO(IAw?w&BhAeve4#8{Able!A#IM+J*i=$4%-ypK`(+Q{ zoAS-jTjF@}=ywt4ToF^v93Nb}pp;^tU&Dik2a-<5WPIBgPbO*^`1HVRQ5m*F#{F<% zMqUc~1*|2{z1D2R(k#~cWh;8yrqQTOHwxZ7UbH678WU^}kef_81wGXiR8AYla9UHLI2$dg|g2J&+LS~GS#E~`}f3u~K z_}~CFHk<{S+$aq9wGzZ>m+5T8ADC8Ff=5j^63zZZ#tMpls5jzT`f}O z26EA`?|cG-kfcAtD{CdDw(tO~{k4T;`J|vcJ z?j1p6!UFk+U$Jo1xX(4$bwZ4MTlwYQ^e?YXp~UkbTTSjrr7Brb;Dvx2%-9rY#<)2Vgj z8&t-VgP#U}x3`2<{awmOhCif7I+i@G)s$V?xsd<&tPlULs?GgMo`?gK=W>m>JZik3 zMmc@Bu%vb_S80A)9{)0&sekH6Cx&|P48N72{ox4o=1G1hK9Psp?|?Z*=OEmqPKZ!b z=5mQ=S=kmB2K3epTKbl$i7qo$ zDLD6q(BWo5KHs!3_g4d(xAKWFz5h6{KK}?7+7-gKPbI?J>oriXWDhEeJy1DVQ}`tt z5BDqnh~n-3LcXOIyKuo3qJuM8=%v@tk{J&FS(MWgtuT;vN(IC4NWh7v+@bzCJgr#? zUIVv4T+0mpB}z@gSoy&~3p==1sKB+#CH0n%hr#-?E_@15h0T7C!FuaU*sx6z?)_PT z*#Q$lsZ8Mr0wRHpdj*o(1=BG^8j1wdt4m)o7Vj^Y!X%VVxI0RkMCz%%tyzSClXp)-;qf902 zNzQn_W%Fqm7#;<$&7yEk!Vo@Q&J`5@eubzPN$48VOUKg}vEXUS*!^=S8s@$y`;k-F zfHTdoxz9XwE&E4r=4pd=(<&%g?2LnC=@7 zal~`lR;30q51vTQMM*fb@jaCte8CJd2H*^zz1Zp5MYa38nbM5DXcOd*VSXL7#Yq)x zH(Z0Sebn%pshHH8zOow`!LW1pN;0@zCJvi>X@Yp&NMXw)4Su(OI!iomO7TS+LPGCd zvR@or?igD@n3%&JyIv$IH<_|>kE`UYa#TpVAjd_&}dV*fJvrt;GniR@PXuppG$=5CyisGGFagzdB z-}5BLoto64v=6G5hy=|Urc^0AmTfamW}8o_!Cr$Z!N)X1h}$p&zNOul%qq$fR$1<( zfSPugm1v9_uMDYWl`6fx+yPYuPN?=VmHZzL1L>8~@I&7dJ31Tb#+qx;og@eUjarN| zC;TH@)klz4st=0B8_?mMDj)t%fQ)xuaAd_Ed>UlJy%j59-gPxF@Hvd{4+DREtOt5C zCG&{fbFgvL6nb3oc&G;Efe&L8No(1UaXhU~BpcwAGTOR>$L{^=LS{9dtsej8~$` z<4#lOD;?}VxQ}E#jD?O%Y4m(WH}p%K4=-^Y^e-PyPp9pKRZY6AqI?OcXkKLVMBjxg z|JI7LMpZJ$bLC*@^@{EeKFz!rB?$SFenoYoAz!d*3S4+=MCKV<*l=kKU-@l2WF1ta zr>FYjlxce0s4W1@ZNdaCN#E_Y#YeK-uL!{pe%WkkR|LP(BUJe+RZz_dW4Ujm#2J@1 z(zEwQw5ljtRGCsMDBYYyvx1qBF7fK#w|p#YvNxjBJw~u2C07`DX{q38<49YBPeSIB z%c6=2XG96lqsempW%lGzAbeb~8*uY0n)TC1+~Ictrbsx+mrl26$?H4iUlSzxo}zwW ze}9*F*FimD;G%`l+B^~B&)SME9BvbuudRS@MUCLNCYciZNYkY*0JB5d*wyooQfyQN zgT#x>@0B5DE&NTbcVsld{wyJkte7w@FwR)w(zmo7y2!mOw*GFfJ258n7+-1 zK-0UlQZo0bQ!5Et`s6^BZC|d`+aHfs7J{r{4%ltd=2J@^LR~AumirOlHR2W>cohKe z5BUlP?eZXHQ8{6xkO%RboT+Y|23xc3wB-I{hWlQ6Q=q*HyFaHII)^Nha1ju-cOa|qVCurSDHx}u5NHDor2RHSQUs_WpD3dpQzW%kQ&lsj1+BaIX5NY=QFMw~lJ-g}W}kE)}l z^;I;GKlC#sy2$u_9kP+EUmh}Fv{OC5pb=c?1PLwniX@21?EXA4X1 zp3F zls0)Nci6muq}t@fpEDXsxkHIxHk6~wzng_=U%%2LN$t+`p^H$HkuR+E7{u4sJQ4hd zzY(()H-E6ym$-NnjKuz5&FG*C`On|)A;TG zC^8`Qhc`x?ST6bdaf5g+2OOv?ort5Uz*Elln3>Sts zt>FPdEi_Fs|FYiChG~DDz@LviNDJ8(sy_IfJzZT**8VDjMzkYEnMZ=w`y;|g$(~~T za2|;>u0ZMW3byp}00>_vnfE0+1>aO zWEW{5OQ&0*l5BX76$QEv;k|V~X=CjbFu!iff@?i^(t&TZ$S4i8yc5CcSRn7nPNcDI z$>L(AM_?nF(G?h!D>`l4KpzsDK)%_O&%2lbQ&%&-?M?=4cJ85>ac^Oy_9C8mQyDe} z+@ZD4KSS&lFJ3oQ8rSnbaj9l>PgtxI7z1H4SPr9YCprH@r{Cw<_f6&Pmk-= zo1>fWNt(Sg1wtZ3oCWQ|JTo2cF|`}~YUlH3*8&kvPv%B3zhU4ZM@gOZA>6p+%r^~n zfQ&KkN!`Jm%CznH?w@N(`=dNzuo{9O6k$b;U-1B#&Fyt2S-0?2L_`%Y=O=qcaVYWRlIvyg)P)hOl2fRgc20=X7yhiN% z#*k+lJY{A9vAn`OlEw}l!B_lqhaBBtZmRv8#A;8d^<=cfL!r#iL=WauHy#tpBX+@S zuL-1aOOc10Fc=(V0`3NvY{VCDQ@_PlkWi4w$~wH6XMQQIdn7IP&)OjLJ+nd>J$^b3 z9yFiAf~}}~qBL2=l#AnfPf~rJJH6J95zW6FA)b0OgTBukNaM~Ng*R0uv~0T*i7Jna zd$Z*6(9y%>BlSug81h*Zb9NBEGkZy6BbSM{%@=T)u$*c%286s+|{@aPAU^Y5@XEO2y|=ta0E9~W_xmaVLVL(84%VW=~2 zp>~G^yR>?)18+TIf$=?Wg{oWaHgAW8aEMVrXYWz0k5|(FT`PRCTsF*) zNMTCW&p{<&Js){+64WnUE0knQ?m!YAaackOIP~nNdog2hUejX!UtWKFI=T-}-(-hr zn)7+;l!bU#bvQ>0bG-F>0{^@q8gCU2;S-j>g24PvnkXNJ8}4?JUj8>0dH$c^&}4_J zo2Qe9mMsm5)qq25o1u8}a(24kdGZ?-4afFMX2tQv5UG_p23NyBZLqBU1`gT1JvbTGZ+Sl{!YJ;_qamx)d zO1IFNMUq(-XF`}+y$4(Fx`ApXPMqDbA6e8^Z=qF1n)02mP;_+*({0rugF`>X;x$cF z8KB1k+-xcM?paYpeKCcNEf*F~JuPHZEn|288dF(H1Z~{Z4|1&w*z|Mfn1OmTt(klt zD5;g1Up)?^FDmfkySm_wB)i%DR|a>NORAD*3xF1RL5#s0xNp`I)p7I~L{y*x>?;lVqJV7`?BLfT`*G@VI#yHFh3t@*SVoM z;+_=``1BoKl@ze_)y9(PwWE35`@8VEQ4E3FDtL9i#69q2A2eV;Jig%x+@Fyl3LAUg zMpa)KXZiI&OJ0zK;~6i~P5uizlb?YPcVj+A(a=_K6)J}xhUfMt>F2x;a4&otI88YK z1KRbu-Oz4G$?akb&OL#qGD%M;=`P$%lx0C3O87)+GT$|-6oS7Ml=t5!sj;^^@w69> zaKv|yc;oa7V9V^dHFm?-$s3u$c?-oLYhAJVeH6s!6C;2ymsEA`&%V*>>k+Ai{atm(p5tGqO;8JLpJ%8mcxv-qlo_r zpoYC|G;mZ8oG;I2RdWaM(gkfa!@~gAStWqa-$}exJ%gSo*y2o|99Vffh->%XN-{EY z@l;MS^!w?^zuP|)GJhaGzO@$QXG`jz=G(7Ed{M$-9op91$uK<;k_Jb@ zR`-__acl-XYrF(w7GH%ea%Q}_rGtivmqLEw5UhJNf-8?z<_CKxOSm0lTov4hUtd3z zkNe&P`UaQa^T7fNOF2Wy+hd{GFb{G{=TXm{kz`|$Ofr5i;YO%6?c1SD2WB|(P=^Ol zsxpn1$)^Y_XD#6hJ6D6fz8PJszbH~ZYQklyl-V2o5LfRwKY=|cqJ?W!;j%lxJ2x8& zOB+V_RSO~2{vsHhzD083xhyqU6`@8BlWacGqJJvPsX+xP`v=^1xqMb4N&|96tZ%{u%c-pQ866y1Zp7G$-i9^zXK@_! zOq_IBo|&N456ja>u_5mnesvpw0$~Yefub5(gb!fVk`h?ALYcWOEe!(^m$Bu!Hw3Cr zWCEtXhLq_QwJJj&cuiG#aK+*$On&i(+}P%A^}e`>YsB05)4GGwv@=;+f;q7=EKQJnrXw8i@+DbX1vh zT5lv(S9PIxAK_THGK~AbME=dY;>eMiL7!D)-sy zwe*V(m;IV_8*N?IX3283tAVG;%RlhkfuM3T^GxnV_CYP&!4OP2Rtir}R&kNgtdD z|K^V1l_SmYOMVoVeUO7((OUfbS(#a`9R=#&xmlt{89qLs!|;~BBOmQTVQ*6f?wWWX z*6e z=aey8j-}us3oc)C@&-yg)5bMxJn_tryg`u!P~mv<){O* z;`M86-&=#S!}VbKR)G2SFCWjCRN%_9XCQ}ng*4u@!+PIxJS*e_7NsSakoJ(48;0QL zaZU1*%j_$M#iDH4HX6KS2KpW~WL-oKqv!)GSjyE~S9b=Y-hB;j-Zuh6&*z}$29|8L znMaLg4uhZCCmOr>8#FlN;|AlIaAQn~PJA`Qu znr>sS%T(f{!|H5Oi#Xf4a25O7&K}<_GiGOVwa}Ea&v0AFOEPKZH1_O=H`Kb5WBu4S z!a)BgOwJ4-MV+#!7%s_liW}j9(J-iRJ&6t+d*VrqFkI*zhUbExP_I{ji7#crdygtJ zy-$j@Nx27$-cDpbax*-|JTbN=CJE{s^%%|G&9F`7Dyqe(fwL~>wp=klk2$2{@%gqS zdS5lH6gi922hL&Vidw#8^%?R~z6GVknsAm4x3@ZDEnYCm$4Q1y@aV7xU8i&%U835k ziQQe)`dmaa>+;bdkK=WvT|s-!J9jrCj$TbW0qee>#LW>hShjc%WQZ+bzVhgmMb+keC$y#VUc^U1qUBJll1>9LK%3LwIg0Z2`AUo$f=V7P^7tKpJaPJ-L zeIw3@N{B-6U;u9Qt%9wc(#(}0K{C>Nk#DDY5LVq3V$4%H*4KSoD83a1$C6{RUD(wb=$iqHAE6!4Af*Eeb?K#X#CC5AJh&8aWY7E2Zm{}W> zInPT1-74^pq?$xQoBbRz>+d`IRAvH%n|kn%%Xje@%bJNb|kI~k4HM-UW+DNDYl7i$Q#F7@x_?5s}l7dWv~K=Wm$`x{ixb+ifw8a z*m(g8tnyPK_QcO5BGXXH&PklWwug?O#NS}h4@_n4&$ghCdp>UNI|gx=jb;Y#<;uJ%l^unJ~Iuf{{xBgzz-5S}D!I z_DAscuoVrv*-CPX!ET`~t<4iEO;XFcbc)kOXu0qvvLC(Ic*(x$#n)ulRBp0wW4>q18R+ zci}dA<*W>IY{zjF`*Mbv-gK7+>vDW5r%O2TTP}0JB#s7KOEQl-no(dhnmIHzk$kC> zWZY^-aOEd6CQUa5=Ix%$+}-{gUt5)d-oD?!o1o6DS%KKR_y#%ieG>ENyBPCr=m*`| zf1kp&FVL-e0S-8SBTfq<@hq2znsZhfzM36^_uGr`oQnq;+MUF^H?kc*^b6o7r$`hx zh^GQZzpWo#lY`ia4Y=dLBfjzWGWu5T3Any3!}+?GiAhg8|J4#xM&`eC42oX|^P;^; zgw-bQ`AWg12BmOr6(yeX2RUxeK~zj{hZoy!l0?frjF9wS`uE~@SjXj@T6V-RkM@U= zoQ_G1g>w%Te0YS>3Gjsw8y#lXc?C?_>dBZl#KQAjT_)8nmFnl%F!l>JgRjw4W@SJ( zT~%kzq}>*U6~`19wSvuPr((vq-;yTn|9yuWk{Jo}pq+((_+#}Vc+oqKF)mlalWi|(Zs~HO5WbXAb=rlI`!vy!%>+9l$oR@f zW7^v5^qd$AZ-km5yKpU<-8)Kteb@mRo&(??_Jt~OJNwW({hSq~~QKDIq5m@`uvgmmU>}sEkQ&ob%n2zyV$2k9S?-lwY`x2KG zqjbctgtR>;{HceIVB`37Y*=VcUaQ|E%KF79pt={$^uJnrbZrOkDUImE?Mhr3N}{1k zjZk&@Jr<0dLn}cGtXLz=>~j2w`4wkTFW?BO=gBdunFaXdSSBu*mw;cIe!>EqY+koR zIDXl+3C#wMLDQ+P;B;~|-rXmSIasHj%#TOISnm2u5J&O!eX6D89tFHLIt_WeC!&JNtnCvKrul^n$LQ>^ zDk$&7m@Yf!MeqxfU;4B5s6rl|vPxzIRwmYbuRlgYcLd-*^;+iFXMg(kurcVok-!IA z?l1y3p76cyhQW>NYCJuSR;I)~nY>m!34y9gaFlXx5$|R)lyV$sy)#H%&R_(_7V>{T z&jr1fPh{itbWH-u(!u8M*2 zYa~a}6!n^?P$P#v2zBLiJ0W@4))!aXt}M*NNC`7v58OakOy>PzKSJ@@iA>^dJ8e{KI}uO=nfHdZezuwFOvtR#cXpWR5JS6-socOUb+$BN;t<~;4QUrl22-gABTat%0i@Hlb4E5umLi$sN`axf}a3?Hf{Fjssy zKKy<@S<@xO99pi-RO~IrkYzyh_8K#}`c_PQt~A@b$dfO{o5oCPHfN%m7I3xJR&FLB z&Aid%-Wg{@*evB`bXQ3?+?C6RlAw4ty!(9Zn(Q(V;CfbWd&1bE-Rh*lQ9V=Z$e#Ra^G1GiHrf)&y3T=2Rb5l{o z%^g#Uhsm$xcl>$zQD`}R1C7~{K-!lkQPI;zSQ~yBQs!--IwyyzOUpUhbK8N*s8GjQ z*(NAz+(oy4jb<#%%rU4ZjN7q@!tpMS8!RD+t}gMIv~LI(AKb>=7>=im`vz=ZIGOdK z*39#qd2}FUlrHs{$i}J4G1b~@srbCwTKHarr!?MyRHgwvLk5XEcQ^jHC>K&zXj{LK z^@7#LNOC`Sk)mLhXkU&6=fk?(j&%vu@SDvsbR!|(ua#_{>xbQoR>0nx>5z5PlGk-Q z63+x0g8O_GGCQ!7-!X&BHd>w`Vp-?ukjhNnKkWb_cXt9`bg>}@8nYxl=NQO`pQ1Ha z%y8c5Y*3DBfRbc=%)Gt~?@n_D%Z^)6;+2j|+7t1WpE9_wv}abV08R!Tb8Lfe>!sL&K*WKbZl2S0E>n+B@ti?4 z`f+{l2h=qw6WT}SCa#%5am>L&hD zkYYUN=Ml5KO}IQ;nXO2cV9XX+fv7?R=2uy<)fpWe@5vZ$RLJ54A60ht)?BdrSWoOC z8*39)2XIEEDF|#_z{}Q^BoEyF;=f(XA@bsWzO~6ya?i+|okUIp-_(yc_3IH3y|t3{ zyL<>V9?vF0%U?j$MIIY&AI>l7s^lrAPhw!#nZwyNeCJaSaLLLQ zD79Ljd0b`!w`XKy-M}sUW+cyasnx2Qa?Q~H~JL5$SfguT}9#Uu`XJ?Hxe6`8N-CyV9vE>!LP`6z&L4nIAnVk zvnM9-_2TV2jugD4m+57+ zax;c}_7M(wMo>d-hP~1IiUhegqHjV3?P^%SY8ttaIRCpCUZhN9V_x9V3ku{{RwtHN zt%1iokCFB~CHS|l9u0;#Z%UN}gHrYI^uu2KFv8^s*KK6{Zck;}vkU0v@W~*%JefIa zHjin4TFNt97t6nVJ&*Y}kH?gxsF9kXDQJBwk6DqZz`*bOyta?2D4lkU+2QvJjBI^K z-IX`E>Z%DNW#0-xw>048Z9Ue1u_iQd&Y3E)G*Efq!meCdkKKl1pEV>NG%=*9DYB$*M5Wbp4>0PGDVwpDC0+#7rS932(L=p83u%`7s-qdZytv*k2Hj#^VKVbrhdX5t2Nhn;eF=dCGbBX^{KcZal zkq9qyW4)}siLZz)UrpdBVJ9tNn?`&|P5v%wx?LCUC1|o)&GU)yI?kPKkOP`B4=_yj z7V!%aBp$0@K&{~k4EsC4%Ws~^a~rx1p`y9eUg{9dT{wlh+a3c01tIj`Wro<%J=vuP1? z^Q<>~D~>=f@g-p5xrGs1BnJtCg7`-4G6)|IVtz*E^0Zc3)fV==0u*;=?yJU8MHhRr z_wWnw@a1McFV9n>N;~rAdJ!x%Er54dfAhn;EqFIVXTsq46(Y9TmxN3UrCGbVZf}kd zznk`#{1EO7r%KtmlQLc)+0+KKT=xMK+9>VD_(X5a>|Aw;1Up zCz(i;dBE-K?BQmI<_n3g3zx;q?+5F}@fi4R0@*q7Gq32eCkSip#koz_h^E8>kg@6G z?L1_Qi}WPn*_#WHZxBU)ZuvnIi&9C>zAte4TmyBDs^yzX*3ko7CNa}%gwSYPr4g2hdlSimw+Fxc}=h?rJK6O&srH)0UM~aPD(l zwOkTP4LZ=FW(&@)>cV9fr}*oRJ;Kd@T6eSv+7HnV=kZzzm$iEpigUt*8E(rMH4@#hNhunyuF+z&V+jh)bi~%6J*a3V z%gkN#jV`U+gG(B=b6n{wFh72fYOOB7#D#Hq;Z+tnXF+hQb`zdoSBT4|N1?eZH*+rN zMd#E!%yfN)p=y6oP3{{OWe20&`*&#OBg;BFPh{JppU~<#7jg9#MRtCtG+U$YNVeMQ zVB2q5Hf_oTHfThPb0(kXt-d&p?eYJx{+bOOwo-<^OKtdPtt9(#zYMsaTMvqN3h?b_ zW!6M1hwRGkBF%|Mv8>35^>@#Y39#Go}-bb(R1xwV3zK?kCB7 zc?xH7`&3WWbE)3T5VFN?3SKmy4gxhTw9Vk8b@H0^yprrWB;xgAx?sV6dapGQE@#MN z`+{3MDKQni@_PbvXwoSRsgEG{uScM|y$$m+tsb@Zh475e7UM-VuD`*}9m9)zsm}UJ z44%Ma9BrNP_~CQYw;?Yuk?5LMW3}`?oVj2Z%$eUu z&d3*H>(u!uTPOi}hDC&T@gREDh~oX6K-ycoo&535Lifpnw13q;j(2;Cl;+&V8!uV% z>d;x#I{bhPt$K`Ri#CG+#|~|X`#~baeqh?nJ_z&Z#`;bDya1DjxZsf#^Y*|#?Afvy zA1E!sLt27N&l(L3Q9;aIyOEwzd@oasB)W?93@@aA8w4 zELIOf&jDHXpGqF&PAnvs?``AxaG#|ma(6+-t(krm6M~sF1NblWGU(ot#!&5Wa{Af{ zOy+ET?S2w;rCc!$E}Vt}woW`bzZx=T0H{?diAFu=>6g$4M6BT#w}#B1oxdW_*Y|zx z_`m8J*MnL3W2BtO>lov>JCd)0t#~v#ogCaAk41@wuyo}^6s|EQVV@)M>WMut|70$z zO6icS#j<#6%^8>;Fcn3`1jr59-?hva7Rpt>^HZ8{^BcTV$@aKQFgT=7%FTY#;HDs= zqq`q!n@^FHe=A9R+)Bg(`$^H7D0I7jpZFA4@gfdiBLO$ppiHS62tJ%je-sPB z_Np#g^hp_Bt&XEj=eXUIK6BpDx2H(SkI7{6uPIk>qAO_#$Vpi?SA)oGAgO`n|%a5yxhlX)?-=feY26a9P}sWJ%i!=XCurrjiM;1 z4vV-R!o->TxW3@`nlC?A11Wq9D@-GCWK#~$PJ1cXzJ3hd`@B$N@F_X!vIV@8E`e?z zgVKYiU{3Zico7x_$7ifTzeSC3b4es{I#KZ79f86hkAV)ghdk9`(%HymWLK&Xx6fll z#-^Oie?LsUKkUYU^mw9`I!shwtC5D~l5Du%dfvxbD?nXS8x(X_u>Y33(hRM97@Ow| z@1;Un{)snqm*zNkCV7FM(Jt0t&;-wo2r@4}m~;Q`mh9Vw@_5}m4>|*cfxom3TWp-@ zWBw{K`b+{OCG2r{#blywg!G|~I;1+Opqf`6(a;M)zg#J}qkjb5u6`y{eTMMrpDf~a zvl-hut>6qdb1hRi%XbXyMvvDXF!scjt#h;B|H;Y5d3B57eu4?xCZb0C<@i*(C=60{ zC$eQXW#Po%HWIFo21|Ro@y4rg@Oqm&nBMHbl;V0K-hYa(O>4yLcGOkJ>@j!rNF8*+uWqAHf`j47e2d7)|q6 zl7Vb>cI&;%pqY6cO}i#R_3Fv2%GxFf(}==VOJ0)8Ki*=I(>3`1DvKtQ628uv6m$XoHq>1v{Cay?taWyh1W~Dd3w}t>fLz+GQKP%%P#L^ z1RhMQEqTa?(4263!Z?ZPT$4cTzCVD8Pd?L^f1;WCRnj1^ihHhJ6w}=$7R;>T7?53C z0`|TEWam&mEctsH=AYUH7ZvQ_FOx%V$;X3xnHpRdkAR(H@A=9ikq~o20WNw*!&LEc z{_NaT81G4e;_#zz;e<6=s+S`Jy06th%UCdh?gHg&c}@~ z^eh$E#BRd4Rn^d3tOvPriCD8u6bH4+z;oFR67!ucwN3Tv}h<&BCaBv8o6ET2kQ<`zQ(Iws`oAVgH#}c+n)MMZ7o4oDTh#JdAh;2<4 z`YQQaU7ufvj|P&+w#Rz7T91&*)qL!Bh#=hw=jo^T*W_zt6^1vTwbuGkPS?(|g6p-X z@nW3{`Zh}7b~i4o`p6YuZQ$-hbIdU1-8nc?7D(T7cNZj=sYhNT4BDI^eirI%{*JS? z!sYec?nMF-|D?_y63HNgy5XQR-F<%7y!k~5K zKbW!W01Tc!4blFCVDQq2xtU)I!1+PdXG~*8&1W-(A(wzZat(_2I5HCsO=1)h9>5>H zGSE3dn7oylFm>uRSo_%*JipFl!ryJ-uMXJ-Ubb3fzK1E(aC#rM56=YjQ$yuNhK#;e zHGVk~146^6u>OfSGiN2mS*Hi!z`b^?k-QGs6Jqh&YBh!*`3b#-{kghX2ebdlGt(O1 zqg~B!;*=Xltp$erSN3d2$#QIg_{oP5!>ZEV8&D<{F02g zY-SUG{e{gycD|%3H(z5}=oS8i3U}y?J4>|dU*b7CDPn6C1VMjJg5U8d4A?peqOTl> z)2lfTN|!uMa=#2=?T_JQvJO*z^DpT(_ydaEOzokw7Lzq)8+bH{GYx^=pnLc?49Xq{ zzi%Rp*~D`Y)0q!h5C`uIK7m_Y1l*Ti0k`ZWpy$CA_{^O*>$z;p&a7PS-ZhVV=bV92 zr#iTE$^k`sj_{&QYe1v23l85njg?O=sO_y{NWHri#3G*Hxf6-J&i;8Yp4mzNw7kTj zw0?37*3q5~PdENc+N69UKfILKJ13{JZ&y@c@97ofMzAG2 z$y1+g*>MZ)&Q)8x>@{M$)4pI`V+Gy_jpkpt{T(&d1DA12z`%ff-kJF682U{Dk~m)L zzOPM0J6;7w1@D59|4%A`=7HBQ38wSV1nl&l3I7?NfCUpI7#Gt6Xyu{+W6NB@ zwx$I>+IFL>!d=q*T?^y~BS5f0nk}7EN@SEBAW^o8Ot`Af=8m|Lryoy)VObP~T6tCg zB6v2h``|&3IG%TF$FKvPyuJT^0#%YmbdEub$%W+I=U1@$(i{4lorT?f6XAMhHQ2`N zr_uRCbg`>Gh#roBi>qJrS}Rx5`Onm0^>Q8fs;2|uS%thW$`jyQ^fjW?o(G?tO9^jZ zAL+|XBQe(=0{^-Um^91<(-8^ivKoW~QR*PGZ8bzKa0Jt@@1Tz-3nHG2!M=0@bjub& zU%e>I@j47U9TLH7G>$Y$s(}XE40V!KaOn9qTs7N~+trj~67}wIOf5;)5A+ z5aEN)>a}ogy8wN%ZZezTnvP?uM8HPJ5(Cye#Pnb5(Q5Wds%3K(cgz@~Qd@V^)c)<5 z;3~$tOg|557y60ale1hdU4<3xcVUu5Ho%zdImA(UHh(OexiPp40)qFTy&9M6e0qVY zj9mlu@BUDSwJa`;FJ-JRuZOIsEPq2_DE1_uU<$5AaeprzVzP1?eyvSpFrfy9B9Q#q zJxZHyhcm9vgqRw0VQ7_=LEAs8n5{QVm|E#MF#nJxeo~yqxU?>2lqyr9ZMG>I2Z}Jb z#eo@|*b2U@UeLb@P4HPmi}~<%2o_}s(X@>z!05KZph+M2O3WhL^mak3ogHjSs)j(Z zo8+Bp1!UPh<2~*^0$T5D$@qOyM!+_=M#RGj%5#nrkT+w7K86#k;4L6k@|axkaAr=W z$%CG40ZbH@1pn7=%(2ed!0&$m=F1d;xJ5EMVxz#W{1*(#?uEVlFeWs_2ZS>Rp>Ekx zINJ}5RK^-I?tKm-|1hA%mtuNXY4f|UYys16?PSShj%hn21)feiAf_2kBEIP`L&}FC zh}(_swLZu%nmUD9FZ+u;zNN*#7Nmu0vv0#ltQj`g+@|5OS(r0VjFg!_LYo*le34#< zVyRp|X8jwyI9CSiS6stnmoTgx&P3sSH9E@~^-u_PR+&Co<)6;Jd z_lpyGqKg|b_CP+ACNGAK9DAXpzYLFb^ukQ32q>flFx4as&pQ5s9}?-X;j%bmb0d;2 zt{Md1uN=7Os>RGdYywdiy5Rfm3=n%D&&aJS1m)-Np#9$gNa^hc@uf+iy+@iky~Po1 zxCIc`G)cm!=rVKE=76_=5WM{ng(aJ`nKQA0(D~?X?U?H?%oZDmD{*IGQRWBwvqYNx zKmVip*&S$|u^fZLKVrCbny*eQe9U=94u5xkfjEmYD?z@?{VW9sJAsQ!pW66t9g<$Ekjm^kTX>wlz&+eU>NVY~F3UQfDu&{XK$9gl?m= zVX#KkUUn7pL+*2|J@+VjeCh*Y`&kAfN(`CAA1U-(nG7h(Ct$0$5fhnF zPid_#>~7`yLh{m#VMiqu7?8B*~q|uLt9|)3n%Dvjf6;ZWjMQh3X@^H7}m}20G{y}FZQ7$6Ly~axsD$} zU`)ko*1joBELThLlwZQ{pOO6i3ge*n(1UdPJO(xzN$bsmSz3b8zj}sU-W2E$$oL0l&;&kr7E# z6rR49_C4~V#hcxrYB&vZUv}_5Zg#~q-AH)Yl7?RU6!=aB%D7j58#FkHFzccjSu%SMZlB&u z$81C)^mZ57JvRvrjSA5Ev_7nwwH_KdM(m3J`q80tK5U$p41t-Ec;}fQt2a>_qHFiV zId3ogwCEf5J!>IbWh|gaXI?LP|k zRxfdih%qy_;W&9GxDtZiJ;pa`B8+KB1^KkQncVJsj2^}xAflj)v?c*{G;Br3@lnwF zdy7Om8es9=a$NmQj+tOPpFA9K#Sy2CsI9Hbq(ukw&%CfeC9_!+^Uau-ogwtt>v7s` zF&R2EfpfaW(X+4KQUCMF5LF0_^0b}w-)t2u`FsI#q-QYgA6@CXZJfhvY96;2AkMT% zm{5U5$@oZT6V!%W2FZg@`45Zpx&6=Ar2D5L2s}7fo9L3kc^9pD>jh+JL(*2VYvUo* z%r~RGPF-}_n_v=kMH?&C?@`lx_I$HX8~GI}`)Oq1Oq5MMO$LL!X?g7u`bf(cFISY4 ze4q6+Bl|7Kr4GZS5izo_IG%np(ZiGXV(`B44BF_>Lsc?<(F6P9G4u9iDlnvgVfF%a zZ{a>H{LQ&mx%YIIssi-KS>V1?QT&$vPgL`21@N+3sBQiw-rBGAboGRJ6LNh^1b(N0#gRm;n`{I3 z70!~{*cIG+dJ#_d7lN_hF{I0os}-##z#u*BSO~?Zp)Dk639MNTyrL zLSTzDTi7GYF7mG8yZ%ukJLM*^e+-7OgEvgW23UT`MhSL9V-mt{3#_&hq|M(y;OmP> z-=^B3|HADw;a?^CO5fqPt)GGR;*aR_vr%~dLpJ^IdKXna^PQ?6=a^i}7NBvpJ&ELM z`k5ld*gL%(U&bpyTeu)Ol9+*2x-W6((wWevI~j^EoW&=fu5)u<0*2R2VL+iD?V}tq zn4JfU_IxEu3nf@zu{T!cjp|V6n?l|Pin9B^3qr=sy(Dvg3DJ)FikDv)f`Hm#dQD#f z_Iw+_C3UJGV>m=@MD$^2{uufl`AYuF%cX95uSkRSD27};O{VZuc$_qk_uT(Je!F;@ zXRkdK=G=QvZ*^`&gJCC{7ah(KRgCjc@|&As`sKv@^3 zq-=z_=FjOgnVaAyyo~>H;xKR0!c6+auLYzpyd$4y#&TR|RRo<52=JN)`FGUVYy(X! zJMtbhA{RjUE-!XZ<{zrv_Z`BkHbCOOP3-DMS(N+u17c-1z|A>kY{qPN91{Nm=l`1z ztF5kJift@bM|}cGGhj^WNVCXc~v7hE$-y6m=S$j?L_g%q;nwMMp-nBAino;W%hFQlbSrBR50D_0 z=^u#m`x#{WUsuSlc|vaNKFaalC{f^hf|@ZNK|S7r;*SBiypqdg!b%rUz8{#GCM zZw0;1Ea=;t0lbbRUd*R5(z*E}j4AuVK@qM--e^n5%C5n+3nCC76A9~YexiF58i44F z5xLl0P%u!#g8Fjk@^z%f?pvWi(-Q4HLZMprCtcByNEYd@K?A|x#C*sSt3H=g*_{V* z-^4MV(L;{gU1yHFOOK$OW=8G%!UZVz%M1@+TY>E%O8lSg##mJHjP6))iz@0%@Pnnb zFgUf9Z{)`1`*LLY8{b=_M~o*a?2m!1dk3hbdmz5$p5I!{W-#aag=V4g_&916Y}Bvk zxR@ufEaecE&xrw%+&i%M=0ViA)-}I+C zYtwnGm>Ea*q~^iI!y+J_UW$zi14!cFKFFP!0cy{KutzVI^14#d*=QshqzejP8B_TNXxQ+))8st0fIgaS=$~ za=_`~cj?Qv9%B6?2Eqd;qVMP^zh|B=S?;tIOrFieunR)G8}loztiGCpLd6jbJGYNt z|K6QOyvrpQ)||j0SO+slyokI126ObV5Hj*M3_f$YwrxNq4d%5G~_K<=?zTpXmZ5ZAT+?PEqEk zPd%NF>B#6)aSRObJBMrl{pEd3g_VhC3)1}a};_bggKt_N9wTjn5jUIrZ)6QM|15zThu@MGjvqC`{TT~h zdEwmh=VWKR156(3MYm`dWJV*&t8ou<@4z_D%lSnG5+_;rNi@?W~XbP3;cc5pe zIU2Q$VDBk8p5(z#G@Wh~tALq$1B8a<(7DV_OA4?*Id}ss})d*^G;2 zO<-bDGvvre!^MvbX6J2z@|TmDo4@_wkhu*;nC=BnD+@*|L>0>H_TjR}%V9#GHFGU+ zD)}$@6fWmHD2egYn3_^bHyk;SGUDbuCm%f~V(A)`i_FF|hc3|l`}G;KzcIMf)Enn^ zjnMnm8q7?tKUbvkoqEMyp_yS5m;swzSS-$EJ2d9g6J@u+rqT(Q=_S)S8uD~!^${3e zF-{r#>$Lg9BD%3e(lz@EjNy2w6w#~kn7g6 z-1jdVxXW9R`W*(p`SJptbm&1LpwDFc0p3RQ1FWTS&X;nN+cOtE(#t0sz6Cr9~#~EfrJRhvz`?Xwh4g{n#AotalW$k_5i0tH^HWN z5)dC|!sYjLVd4=-I5-+kNT?;#KCl)>a;HICr9Ro9qt0mX6W}8~P3oRS^2BNoUi9Ze zTxLAoBfE(@K93+P<4=P3UOQZP!3;0#F~RX;9JB1KFPDc)!qT{-IAC%NWFu@a$)XT9 z^@ZZ&lz1r2HbiszGJH6~<+%4J!;-aAF>N=CCmc9tNOcMp<({G7RdxJvKN3pv zjZlW06?6=FVR^F~JSvgI^1ZECbAAJkt>D4rdueo%YXn+NeMF~fE`}Rx;%etQ6WW*a zg7`k!!@1rb^R(XUK*+;B^EMSpSU=a^DR>-`tqkD9wVUeaRrVej5x}$wElg6JEGn zDy&hfhE(6V5Onh|^@$FLqaT{Ne5?-S*?Qvgv+^)-kmDJp+$QEf{BZmGTfA)hdRVx2 zG12tX!I@Kz(=^3QFlv0ucX*aUHRPnR(pL&vCy&s|#(JK~2SMC+CdazYRu~;zuM)#m z3H(RDukznjq|+PMBe}W7DI%(*Nmg(MmcSzq_$A7%WCd48kH!jtbh|i4g(s5o?Wv$7 zy%FM*H{i`LL;UgIH(?;cr;*aw=Xn(`&e5pQuk&}9}o!0b~8**Ebb_L7n*{Crz%Zih?J|vM;@|q1{9n<+`U) z<2#etEh=+)H#r_jf#q_PJ~53Q{h-V_KT9w}@d)}qS8|E&OCle4gW&ogo`mSc0ASO;dkg&1Zug=6u*#@{(t$@Cc)uyK|t zgv{e(irH-{d6sjt>@)&FxojK?_rZI8UYL1T1>7?dQG3d9l%iu)vPBt=zxWSN3|8Vu zW&xG@rwA5ZN?72+qW8{D>M^T>gx6=$wH@d1aey%n^FHw0c6!s8^J&=5ot0jzQ)$(< z^;F?aFt*L!j81$(e4&<08^aw@cAYyO3#_86=eE%sb%xktDTQ)VPtuf6Kk0a<0oFOV z(h2J);827-ZkTU?zE`AZX!TOGm$Js*qL(zt>=P|q63MyR{BYU#RO`DNj&i%YX_)pV z1m!Is5Z0yTt zvpoOMxf2b6cuZqg1=OQTc08@y#e;`Yo7koEDGDbvQcG`5h&MaJUexSB=X4LWRgwmU zm`L{JvX40Myb^1bWx=$5H@j|L6}EIfM91YuuU9({_xpw`#*8k?BhiK zG+_&DizV>4?<^XW8NxBqbY79(e3;oDhfk9-!SjOvsPGprOuE1tX6WlU# z2kzb7NHsj%;K$ULbiK+QIG%YL17=Sl+S_aCU`rR|D4DaToxf0*?{Da-agN13J)G6B z4Z%K@zZCyUGY{k9*v9d4{POG`t*H@XCTHwtBWJha{1tX|M%PD}lHkZXdNiT0xpeK$ z>YMP{UY6A^PPp90Yb?gYa`lFTPnVzZr z`Q$uxG~zf81&-DCdveINKZ@wupA0aZMf2<>h~%oHxS78XG;}(sYJo9LNw|g)Z&yR_ zxiPxTX&udS?#4ISVc=pThu5n%T6ZPXV+gm;xBi+5ehzNn=CB+$yQ~cQ?*-wT6DuI4 zeF?1%_e{{i{?iCPtFdhb?^cAGs6tH?ITM|b2wLTMK4Kpbf)1{u3s1zq0C8i zBq?*5GrZ5*g;c1c;*^L)rIC^}s3e4TX3CH$6{18bywBP)G@(*R>XZ}8kd#zXQT*Tk z_x)w>>)O}#thMg@_tQPbj4Nco)5CmbSEnMho7)oCv@z1po*`~aCCGf?I8xwrgnhr$ z1D+&i)0Yirh>Bt7!`zo>o)N<12{rIO*NJnY$}s`oq_Hn06KwoUF#lmC zmGS#TMT?g~v(FekA$|l^NBY@?T8hNc=p^k}e-9%scN21;nsy%aVz#^(!sL+cpshMV zgRC2fN%u#*^^p&C-xW~dhB`PLyN*|G#X(P%3%ahdfftGL_{%sM8snp|>iOUB9`ebR z)oI{<>=Z}Y599cDyJ5m45$aksFl9|PnAzQchhx!DtrW_pa15L1x<}x3<`R_j3Ny15 zFN5XBV#qJO3EPS`)AXvV5ZLPm3IoMZJYn)!c^7I9s~0>W-V-}0 zn-IdHlnbaN+FDE0eT!)Tilj5(f;C1pINz1)Ra>SY$-${x*`EmiN^ob|dbqVIy+J?ODRbfQ_ zxIFQlEK80F8S@gfL>SjdUAAd{J~*e8s5Ecx%xV>PJ>zkmZRDttaeP@d~rihqS&`ne%Ij zR21DL>`(Oxxy$d(HI7R;fqSXrVICcP+;^s+kGt37@E+Zz3d2ddog(2?aT3 z9^T+D=2!rHLQY?R@V%0_((o_A4_z5zXse@k#Ru7Td7zoFdQSfF)& zHq8B}1q$S@Lh4`}cFwaS#Xpv5n^lRyq|{X-)%onAT|3VqIt=sM|bEDS25V5b~-Sjl1YL^3h3 zR>Atct?asM1X+FVySVS9P~wD!s#4gt+#S@SI5*G5QtU84&Gqy+c5h%G3I4YOPh7mf zj;pPJ;x;6{t2@y>D2lmhbf4H3zGp_xTtThgLC~<@M7V+5BI8 z8e-x_M@?Ml8?Wp1VERcKVtd3~S8Nf*Pk^S3I;rs*o&jHHKeo>eonS#v3 zOe*deftC+4@w4AWY^u>=rR`qRlZ%ot<3J>a1csTf&KMwd=Ew2CgdD29C@(jgasnPZ z2VvTAL&~o$x5(Rj68;nJ+PQ{`T_qA+Ch0h7Tb6*Pg?+5e z9~o>6@S;1C{=${t%GmmONf@nSj|qMi%&#?`%>Es(ajc^Tf6ME_%5;Xz-!w%q5j<7! zeezydxab38@24opRa_uAR=x!sq~qD;A~OU5bMyr}Beme+k3u^AA8|o;|181Ld*8^4 z<6Sg6<^$R+{ft3;aTt`1q4gT~@cBS4y0+@U_{u4ycWDMH{kBBrsuh@QQiE+*x1pax zI=g6x4YbdV1Oo*IZ~XFv!QHc9{zxY1t1m$B93kGwvrA;&lN5+4P{9*ljd|DY1DFuC zQ!u)1I_3p1Ja+D8_HwEd9N;ZR=_hk|t7c`h$G%L3c!@1oI<*g!E8N*8MLyZMelgl_ zivl%ixeB8YFQ#EhJiY3)5SqHp*n+FK+4j;ja`%ue6!Goo?+flU;k^xXu>R00(oE5r zrM=5f!?km;Hb9_S5)(cDT{mCy%f>7MAhfb;e2 z^?(KW9CP}pDmpkF0rLm(L}HmFPiRIBYY^cM8t0C(FXxN!9{8LS1)A$ku z+xTD+Z3tu4DU4HA4p=pwh5DZdpy@ABIJAzNy)W(ntu?7IaMKmscb0;fUme-Zea7(S zL3r|P1RR~$QT>}c;YL6xteQWI*A$|Guc#_mYjE9UO=Vtp`4)WsC7FyTQHbBbxtO1Y zB0qBjJ9w)QVuPw6do%!L_fjK#vYr!$g<}?_;D14u`*^1)K?J ze2y|N{gDxOyZA(B_a?!&-wHg_8(Nra^dEgYejE;e83vbOVN7Oosn@bBcsTC~?7bC8 ze{VIQX2Q>*O!7VmIj&}R7TB_io4TMaPsXF1DMC7BBsz6P_cmC#fjKxcMGnUCqj zfn;b5JoqtAy%I{x;`&BO+lmV2{A&dKN#Jg19X3- z8TRV$X5TNdBk|7SIHVLd(`cN_P;Noa?4dgHOoM-8K+B?u?65vgteP6ykoF2OnRuROn^x!qyLG zNZG6(q%&+T(_@!L-bwvOCVw?1abH=o-$4;QbwRu{iGIM28d4o}V6y5$cm;irnc?lmbl?k1NM7%2hia3heIagrWAQ3X$LuZM||Ltta#M)nl`$K|e?iI0C01l?DFS$|%E zS@JEWvM379rb~l-PZ7xTcT>w#1myJ<$;$j?&^7XbHri{D%*VUvzH3{FyOI=_l|D_& zhHulMm+@>*pCqnLy-2$?YiZ@HSyWGDD(1XSt9Wy+hJIvD)43O=@lwPK;?h=4ZPFC* zPq7Y8f3OmICyvvfXO81$5epQ$um=Rsh1rZ(g(%zVj%T+z!d+1x(vxx%U9@)MGL^GL z!EiE=nj}2j>VW@dwo{1(CNR(30;?q#qgq5H1}1BOh%(aPug(}@EWj&LrQ~ws5vtb^ zg44L!T*x__&GV_GTe~>s)}#Bluy2^QJ=4aDe^RhOhU*ME@$h_K2r7j|qN=()F5%|K zfaEePS`dp~*BvTak{9EE*<+6NeG?tN-zEdqrZ~#wPO9q~&}fGejGvfEHUZvsCbzX;Y z_R*t{<4$n?gySG{em`|<6HsSuM>H}t%BKBPzG!TDPZvy!!>wa-dnyfy;{oDULLMTUL&>Lv_43kAdKCNjBF zg}gd;6^ul!AcV_R25CJb6_--MmV71B&8;Nq;3U}l)(?it^ht(GA$choOLkkRgX}pk z`fgzXd0VMP2X!xy!N2Bn@0nLbG*uoqz3yWs%SL0zCk4=Gv_`qmXvWj%8uA*=f#>gz zyX#t*27~LEp|SybemP;Ej1_s(o`ebhe$X!Fgg}=V?xd~$Ms#rSN?<0(h zk1vPv@kcoP=Nog|sEiIRA0{#tqv*QgD~q*HX<~;H>6tl#(`JTHVkwC!d*sO34HTpL zw$YY%1O->pn9Nise8lyq;v1|HKAMme;YvCrqh+zaigQZH+#@qzXwenz4aAO@LbF~O zL5o{?_JAi{>OEuz6&gvXIb#+KN~VhV?KPW>t}UOSxmB(W+_Vh`qqG5wodY?Hw)9>Ye#6~WDq1Y+<+rkm)giDuVkNM3 zHmtQ{j+!{C9%PZ?KtYZN-~R<7&z7kU#R*#6Q1jRISkjxkC*E}BA1&H_wR z`NDH!(`2fE_1*v@H_f5$P99iKpC~b``mru1sf=M6iA+iz=#q#NF*V71P#) z-&_aF^_e-b>(#_vuEOM{5>ngnNU}=L2it>ZvS&R^Y3!ft+?*SQISZ>9Baz7!cQshj z{C)!tJms=mdy|-8kz67wI)yqpmJ+WPUG|TEFab5QB(~^t@LEasSLh$tE#6 zkMBUEMvF+|Y$2X(tTcLsc~*R|5T>=?pizY)JU#a^+FL5b?ag}Wq$2|$Q&`I0 z`cY4EoT6#T;WiKo4!6*9en3i+f^raA@X z^Q56rS{RNv_h5WpGL2iQ4!=KrXQ#@~5k&QP&?hp6&>65AJx1mVqD{onbEYvIvnatC z#liwJw_v2ltUi?1dK0M)A%OR?c`2W)%Gc z7e$kCmr`cMb)|7?&D}vC$);n=f5KUKQ#qSI&iRcN*i-pW$r#;poh}&3q&Y7vDymLKp?Rz@stN~_7=xcowEA&$F;v8E z?rjsOr%&E=x#9iBan5)o3#!v)$&jliw*JtjUwxv90d_OvDz|Ckv{F)2aF^AP)+QfI zS@!m)R9c626 zcA{P%m#dsQO7_R4(xEU-{BG}s{O4H}7F8l>(j|&_vW)Q1FGbWh(#3_rzv!%1bqvvr zL-T-HnDK~n5*jW+e$X8p(&?ruZ>C^M+A)+8?!fNP=jhW&L-Z1y!2-Frm}zpFw)-qZ z?HoS7k!i)x4%KuYNTc!_9(HQiVdpQuPBof^V70*nbgTEXFT-Up z`pzygFfxUA&J<}`ffbg%kRV5;rt(fnOu?=^Uo4L;A#vs-kn(ewPRWhMtD2UOU{McM z!%6gE?irjsh2!dNJ`1-VJY$=TLNP%k1x7Y|K%BZeb4hI-p4ze--n^d$Bi2C1j+$X> znK(Stjv)sAMdX+8T-;N2j5MsQXGeBOLbT8j-99OVbY1?J&hO-Uz^*maQ?i$AOrDOy zhx6d7!4o>_xE8D`7vlMfJ21)d9_8&wfp~2nEG~Kiazd*n{{cg!w_N}L literal 0 HcmV?d00001 diff --git a/test/test_ir_simplefdn_biquadallpass.tsc b/test/test_ir_simplefdn_biquadallpass.tsc new file mode 100644 index 00000000..030c3835 --- /dev/null +++ b/test/test_ir_simplefdn_biquadallpass.tsc @@ -0,0 +1,9 @@ + + + + + + + + + From edab450b070623d823e95ccef61206d9ea94a75a Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Wed, 13 Nov 2024 12:17:44 +0100 Subject: [PATCH 10/34] add mean and standard correction gains to plot --- scripts/tascar_plot_speakerresp.m | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/scripts/tascar_plot_speakerresp.m b/scripts/tascar_plot_speakerresp.m index d5b7fe88..011273b7 100644 --- a/scripts/tascar_plot_speakerresp.m +++ b/scripts/tascar_plot_speakerresp.m @@ -1,4 +1,4 @@ -function tascar_plot_speakerresp( layoutname ) +function gains = tascar_plot_speakerresp( layoutname ) % tascar_plot_speakerresp - plot loudspeaker frequency response stored in layout file % % tascar_plot_speakerresp( layoutname ) @@ -7,24 +7,37 @@ function tascar_plot_speakerresp( layoutname ) csLabels = {}; fh = figure(); vgains = []; + mgains = []; for k=1:numel(hspk) vf = str2num(tascar_xml_get_attribute( hspk{k}, 'eqfreq' )); vg = str2num(tascar_xml_get_attribute( hspk{k}, 'eqgain' )); l = char(tascar_xml_get_attribute( hspk{k}, 'label' )); + mgains(end+1,:) = vg; if isempty(l) l = num2str(k); end csLabels{end+1} = l; g = str2num(tascar_xml_get_attribute( hspk{k}, 'gain' )); vgains(end+1) = g; - plot(vf,vg); - hold on; end + mgains_mean = mean(mgains); + mgains_pstd = mgains_mean + std(mgains); + mgains_mstd = mgains_mean - std(mgains); + ph = patch([vf,vf(end:-1:1)],[mgains_pstd,mgains_mstd(end:-1:1)],0,'facecolor',[0.9,0.9,0.9]); + hold on; + plot(vf,mean(mgains),'-','linewidth',4,'Color',0.7*ones(1,3)); + ph = plot(vf,mgains); hold off; vxf = round(1000*2.^(-4:1:4)); - legend(csLabels,'Location','BestOutside'); + xlim([min(vf),max(vf)]); + grid('on'); + legend(ph, csLabels,'Location','BestOutside'); set(gca,'XScale','log','XTick',vxf,'XTickLabel',num2str(vxf')); xlabel('frequency / Hz'); ylabel('correction gain / dB'); - vgains + [tmp_p,layout_base,layout_ext] = fileparts(layoutname); + title(layout_base); + if nargout > 0 + gains = vgains; + end end \ No newline at end of file From c50ae39e25d307cf5fcbc1717772a0a078b2694b Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Thu, 14 Nov 2024 17:16:39 +0100 Subject: [PATCH 11/34] add tool to convert speaker gains into minimum phase IR --- scripts/spkgains2minphaseir.m | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 scripts/spkgains2minphaseir.m diff --git a/scripts/spkgains2minphaseir.m b/scripts/spkgains2minphaseir.m new file mode 100644 index 00000000..e1fdaf3f --- /dev/null +++ b/scripts/spkgains2minphaseir.m @@ -0,0 +1,34 @@ +function [mgains,vf,X,ir] = spkgains2minphaseir( layoutname, fs, fftlen ) +% spkgains2minphaseir +% + doc = tascar_xml_open( layoutname ); + hspk = tascar_xml_get_element( doc, 'speaker' ); + csLabels = {}; + vgains = []; + mgains = []; + for k=1:numel(hspk) + vf = str2num(tascar_xml_get_attribute( hspk{k}, 'eqfreq' )); + vg = str2num(tascar_xml_get_attribute( hspk{k}, 'eqgain' )); + l = char(tascar_xml_get_attribute( hspk{k}, 'label' )); + mgains(end+1,:) = vg; + if isempty(l) + l = num2str(k); + end + csLabels{end+1} = l; + g = str2num(tascar_xml_get_attribute( hspk{k}, 'gain' )); + vgains(end+1) = g; + end + mgains = mgains'; + vf = vf'; + X = realfft(zeros(fftlen,1)); + nbins = numel(X); + vflin = linspace(0,0.5*fs,nbins)'; + X = interp1([0;vf;fs],[mgains(1,:);mgains;mgains(end,:)],vflin,'linear','extrap'); + X = 10.^(0.05*X); + plot(X) + phase = log(max(1e-10,X)); + phase(end+1:fftlen,:) = 0; + phase = -imag(hilbert(phase)); + X = X .* exp(i*phase(1:nbins,:)); + ir = realifft(X,fftlen); +end \ No newline at end of file From 54d87b0a46d03815089a70f43fdd685712267ea4 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Fri, 15 Nov 2024 10:34:07 +0100 Subject: [PATCH 12/34] replace generic xyzgain by diffusedecoder with detection of 2d/3d --- libtascar/include/speakerarray.h | 5 ++-- libtascar/src/speakerarray.cc | 37 ++++++++++++++++++++++++++-- manual/manual.tex | 2 +- test/36ch.spk | 2 +- test/4ch.spk | 2 +- test/4ch_decorr.spk | 2 +- test/4ch_nodecorr.spk | 2 +- test/8ch.spk | 2 +- test/8ch_calib.spk | 2 +- test/8ch_diffuse.spk | 2 +- test/8ch_diffuse_zero.spk | 2 +- test/8ch_nodecorr.spk | 2 +- test/8ch_sub.spk | 2 +- test/expected_snd_diffuse_maxre.wav | Bin 176480 -> 176480 bytes test/irreg.spk | 2 +- test/lidhan_3d.spk | 2 +- test/lidhan_3d45.spk | 2 +- test/lidhan_3d45_sub.spk | 2 +- test/test_snd_diffuse_basic.tsc | 2 +- test/test_snd_diffuse_inphase.tsc | 2 +- test/test_snd_diffuse_maxre.tsc | 2 +- test/vbap6.spk | 2 +- 22 files changed, 56 insertions(+), 24 deletions(-) diff --git a/libtascar/include/speakerarray.h b/libtascar/include/speakerarray.h index 2c772908..b6f87470 100644 --- a/libtascar/include/speakerarray.h +++ b/libtascar/include/speakerarray.h @@ -99,6 +99,7 @@ namespace TASCAR { spk_array_t(const spk_array_t&); public: + enum dffusedecoder_t { basic, maxre, inphase }; double get_rmax() const { return rmax; }; double get_rmin() const { return rmin; }; class didx_t { @@ -116,7 +117,7 @@ namespace TASCAR { private: double rmax; double rmin; - float xyzgain; + dffusedecoder_t diffusedecoder_enum = maxre; std::string onload; std::string onunload; std::vector didx; @@ -212,8 +213,6 @@ namespace TASCAR { // lowpass filters for subwoofer (24 dB/Oct): std::vector flt_lowp1; std::vector flt_lowp2; - // allpass filters for transition phase matching, broad band speakers: - //std::vector flt_allp; std::vector> subweight; std::vector convolution_ir; //< file name of impulse response for convolution diff --git a/libtascar/src/speakerarray.cc b/libtascar/src/speakerarray.cc index 81a439bd..b65f84ea 100644 --- a/libtascar/src/speakerarray.cc +++ b/libtascar/src/speakerarray.cc @@ -79,7 +79,7 @@ spk_array_cfg_t::~spk_array_cfg_t() spk_array_t::spk_array_t(tsccfg::node_t e, bool use_parent_xml, const std::string& elementname_, bool allow_empty) : spk_array_cfg_t(e, use_parent_xml), elayout(e_layout), rmax(0), rmin(0), - xyzgain(1.0), elementname(elementname_), mean_rotation(0) + elementname(elementname_), mean_rotation(0) { clear(); { @@ -119,12 +119,23 @@ spk_array_t::spk_array_t(tsccfg::node_t e, bool use_parent_xml, } for(auto& sn : tsccfg::node_get_children(e_layout, elementname)) emplace_back(sn); - elayout.GET_ATTRIBUTE(xyzgain, "", "XYZ-gain for FOA decoding"); elayout.GET_ATTRIBUTE(name, "", "Name of layout, for documentation only"); elayout.GET_ATTRIBUTE(onload, "", "system command to be executed when layout is loaded"); elayout.GET_ATTRIBUTE( onunload, "", "system command to be executed when layout is unloaded"); + std::string diffusedecoder = "maxre"; + elayout.GET_ATTRIBUTE(diffusedecoder, "basic|maxre|inphase", + "Diffuse-decoder method"); + if(diffusedecoder == "maxre") + diffusedecoder_enum = maxre; + else if(diffusedecoder == "basic") + diffusedecoder_enum = basic; + else if(diffusedecoder == "inphase") + diffusedecoder_enum = inphase; + else + throw TASCAR::ErrMsg("Invalid diffuse decoder type \"" + diffusedecoder + + "\". Valid values are basic, maxre or inphase."); // if(empty() && (!allow_empty)) throw TASCAR::ErrMsg("Invalid " + elementname_ + " array (no " + @@ -141,13 +152,35 @@ spk_array_t::spk_array_t(tsccfg::node_t e, bool use_parent_xml, rmin = r; } } + bool isflat = true; std::complex p_xy(0); for(uint32_t k = 0; k < size(); k++) { + if(fabsf(operator[](k).unitvector.z) > 1e-6f) + isflat = false; operator[](k).spkgain *= operator[](k).norm() / rmax; operator[](k).dr = rmax - operator[](k).norm(); p_xy += std::exp(-(double)k * TASCAR_2PI * i / (double)(size())) * (operator[](k).unitvector.x + i * operator[](k).unitvector.y); } + // calculate xyzgain according to Daniel (2001), table 3.10: + double xyzgain = 1.0; + switch(diffusedecoder_enum) { + case basic: + xyzgain = 1.0; + break; + case maxre: + if(isflat) + xyzgain = sqrt(0.5); + else + xyzgain = 0.577; + break; + case inphase: + if(isflat) + xyzgain = 0.5; + else + xyzgain = 1.0/3.0; + break; + } mean_rotation = std::arg(p_xy); didx.resize(size()); for(uint32_t k = 0; k < size(); ++k) { diff --git a/manual/manual.tex b/manual/manual.tex index ce059b03..9fcadf3c 100644 --- a/manual/manual.tex +++ b/manual/manual.tex @@ -1181,7 +1181,7 @@ \subsection{The {\tt } element}\label{sec:receiver}\index{receive first order Ambisonics signal of the $l$-th diffuse sound field; $L$ is the number of all diffuse sound fields, including diffuse reverberation inputs. -For all loudspeaker-based receiver types, $D$ is a first order Ambisonics decoder matrix, with optional loudspeaker density compensation and decorrelation filters. To get a $\max rE$-decoder, set the \indattr{xyzgain} attribute to 0.707 for regular horizontal loudspeaker layouts and to 0.577 for regular 3D loudspeaker layouts. For Ambisonics based receiver types, $D$ is a diagonal matrix. By default, the decoded output of the first order Ambisonics rendering is de-correlated using FIR all-pass filters to achieve diffuse sound fields and avoid coloration artifacts (see \indattr{decorr} and \indattr{decorr\_length} for details). +For all loudspeaker-based receiver types, $D$ is a first order Ambisonics decoder matrix, with optional loudspeaker density compensation and decorrelation filters. By default, a $\max rE$-decoder is used. The order gain $g_{xyz}$ is set according to Table 3.10 of \citet{Daniel2001}. A loudspeaker layout is assumed to reproduce in 3D when at least one loudspeaker has non-zero elevation. For Ambisonics based receiver types, $D$ is a diagonal matrix. By default, the decoded output of the first order Ambisonics rendering is de-correlated using FIR all-pass filters to achieve diffuse sound fields and avoid coloration artifacts (see \indattr{decorr} and \indattr{decorr\_length} for details). Figure \ref{fig:renderer} presents the typical connections in \tascar{} and may help to visualize the role of the receiver. diff --git a/test/36ch.spk b/test/36ch.spk index b121ceaf..7d361a0d 100644 --- a/test/36ch.spk +++ b/test/36ch.spk @@ -1,5 +1,5 @@ - + diff --git a/test/4ch.spk b/test/4ch.spk index 3842d303..9d5f484f 100644 --- a/test/4ch.spk +++ b/test/4ch.spk @@ -1,5 +1,5 @@ - + diff --git a/test/4ch_decorr.spk b/test/4ch_decorr.spk index 1f55f1ba..84b148f5 100644 --- a/test/4ch_decorr.spk +++ b/test/4ch_decorr.spk @@ -1,5 +1,5 @@ - + diff --git a/test/4ch_nodecorr.spk b/test/4ch_nodecorr.spk index 339aac07..8df748a1 100644 --- a/test/4ch_nodecorr.spk +++ b/test/4ch_nodecorr.spk @@ -1,5 +1,5 @@ - + diff --git a/test/8ch.spk b/test/8ch.spk index 0a242194..2804e98a 100644 --- a/test/8ch.spk +++ b/test/8ch.spk @@ -1,5 +1,5 @@ - + diff --git a/test/8ch_calib.spk b/test/8ch_calib.spk index ad918e54..6441427c 100644 --- a/test/8ch_calib.spk +++ b/test/8ch_calib.spk @@ -1,5 +1,5 @@ - + diff --git a/test/8ch_diffuse.spk b/test/8ch_diffuse.spk index 0a43eace..e7026354 100644 --- a/test/8ch_diffuse.spk +++ b/test/8ch_diffuse.spk @@ -1,5 +1,5 @@ - + diff --git a/test/8ch_diffuse_zero.spk b/test/8ch_diffuse_zero.spk index 1c666cdf..4c529e56 100644 --- a/test/8ch_diffuse_zero.spk +++ b/test/8ch_diffuse_zero.spk @@ -1,5 +1,5 @@ - + diff --git a/test/8ch_nodecorr.spk b/test/8ch_nodecorr.spk index cd9eceb7..3af667ec 100644 --- a/test/8ch_nodecorr.spk +++ b/test/8ch_nodecorr.spk @@ -1,5 +1,5 @@ - + diff --git a/test/8ch_sub.spk b/test/8ch_sub.spk index 21a26e5f..e384caea 100644 --- a/test/8ch_sub.spk +++ b/test/8ch_sub.spk @@ -1,5 +1,5 @@ - + diff --git a/test/expected_snd_diffuse_maxre.wav b/test/expected_snd_diffuse_maxre.wav index 89bf6170d1060b19def7b31966ad0222fc49466e..7367b8b6b0f1fc4731a69adb137785ca434e8700 100644 GIT binary patch literal 176480 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%0D%L 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 literal 176480 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%0Dx67pM@ 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 W_EPU||2`sd5~tIX-t?t_BK{u~j$|qT diff --git a/test/irreg.spk b/test/irreg.spk index 88b8f075..7cb8c7c4 100644 --- a/test/irreg.spk +++ b/test/irreg.spk @@ -1,5 +1,5 @@ - + diff --git a/test/lidhan_3d.spk b/test/lidhan_3d.spk index 63b169e8..7c527b7a 100644 --- a/test/lidhan_3d.spk +++ b/test/lidhan_3d.spk @@ -1,5 +1,5 @@ - + diff --git a/test/lidhan_3d45.spk b/test/lidhan_3d45.spk index 75b18333..1b5925c4 100644 --- a/test/lidhan_3d45.spk +++ b/test/lidhan_3d45.spk @@ -1,5 +1,5 @@ - + diff --git a/test/lidhan_3d45_sub.spk b/test/lidhan_3d45_sub.spk index b788ad6b..5e794655 100644 --- a/test/lidhan_3d45_sub.spk +++ b/test/lidhan_3d45_sub.spk @@ -1,5 +1,5 @@ - + diff --git a/test/test_snd_diffuse_basic.tsc b/test/test_snd_diffuse_basic.tsc index 554a57fd..740f4c27 100644 --- a/test/test_snd_diffuse_basic.tsc +++ b/test/test_snd_diffuse_basic.tsc @@ -10,7 +10,7 @@ - + diff --git a/test/test_snd_diffuse_inphase.tsc b/test/test_snd_diffuse_inphase.tsc index 44835c42..2feed5ed 100644 --- a/test/test_snd_diffuse_inphase.tsc +++ b/test/test_snd_diffuse_inphase.tsc @@ -10,7 +10,7 @@ - + diff --git a/test/test_snd_diffuse_maxre.tsc b/test/test_snd_diffuse_maxre.tsc index 997ec58d..3b674fbb 100644 --- a/test/test_snd_diffuse_maxre.tsc +++ b/test/test_snd_diffuse_maxre.tsc @@ -10,7 +10,7 @@ - + diff --git a/test/vbap6.spk b/test/vbap6.spk index a59d2eea..e8146596 100644 --- a/test/vbap6.spk +++ b/test/vbap6.spk @@ -1,5 +1,5 @@ - + From b4809bfbdf70765d80375a573e2bffb266758e8e Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Sun, 17 Nov 2024 16:15:25 +0100 Subject: [PATCH 13/34] add onset-noise to simplesynth --- libtascar/include/coordinates.h | 14 +++---- libtascar/src/coordinates.cc | 7 +++- manual/oscdoc_tascar_ap_simplesynth.tex | 3 ++ plugins/Makefile | 3 ++ plugins/src/tascar_ap_simplesynth.cc | 54 ++++++++++++++++++++++--- 5 files changed, 67 insertions(+), 14 deletions(-) diff --git a/libtascar/include/coordinates.h b/libtascar/include/coordinates.h index ab653114..01cab28b 100644 --- a/libtascar/include/coordinates.h +++ b/libtascar/include/coordinates.h @@ -66,10 +66,7 @@ template void make_friendly_number_limited(T& x) namespace TASCAR { - inline bool is_denormal(const double& a) - { - return !((a < 1.0) || (a > 0.0)); - } + inline bool is_denormal(const double& a) { return !((a < 1.0) || (a > 0.0)); } inline bool is_denormal(const float& a) { @@ -79,6 +76,9 @@ namespace TASCAR { /// Generate random number between 0 and 1 double drand(); + /// Generate random number between 0 and 1 + float frand(); + /** \brief Linear interpolation table */ @@ -896,11 +896,11 @@ namespace TASCAR { }; inline void set_euler_zyx(const zyx_euler_t& eul) { - set_rotation(eul.x, TASCAR::posf_t(1.0f, 0.0f, 0.0f)); + set_rotation((float)(eul.x), TASCAR::posf_t(1.0f, 0.0f, 0.0f)); quaternion_t q; - q.set_rotation(eul.y, TASCAR::posf_t(0.0f, 1.0f, 0.0f)); + q.set_rotation((float)(eul.y), TASCAR::posf_t(0.0f, 1.0f, 0.0f)); rmul(q); - q.set_rotation(eul.z, TASCAR::posf_t(0.0f, 0.0f, 1.0f)); + q.set_rotation((float)(eul.z), TASCAR::posf_t(0.0f, 0.0f, 1.0f)); rmul(q); }; // inline void set_euler_xyz(const zyx_euler_t& eul) diff --git a/libtascar/src/coordinates.cc b/libtascar/src/coordinates.cc index e399a00b..11484b9f 100644 --- a/libtascar/src/coordinates.cc +++ b/libtascar/src/coordinates.cc @@ -48,7 +48,12 @@ using namespace TASCAR; double TASCAR::drand() { - return (double)rand() / (double)(RAND_MAX + 1.0); + return (double)rand() / ((double)RAND_MAX + 1.0); +} + +float TASCAR::frand() +{ + return (float)rand() / ((float)RAND_MAX + 1.0f); } std::string pos_t::print_cart(const std::string& delim) const diff --git a/manual/oscdoc_tascar_ap_simplesynth.tex b/manual/oscdoc_tascar_ap_simplesynth.tex index c39755ac..8f821a45 100644 --- a/manual/oscdoc_tascar_ap_simplesynth.tex +++ b/manual/oscdoc_tascar_ap_simplesynth.tex @@ -10,10 +10,13 @@ \hline \attr{/.../decaydamping} & f & [0,10] & yes & Damping decay in s\\ \attr{/.../decay} & f & ]0,20] & yes & Decay time in s\\ +\attr{/.../decaynoise} & f & [0,4] & yes & Noise decay time in s\\ \attr{/.../decayoffset} & f & ]0,20] & yes & Offset decay time in s\\ \attr{/.../detune} & f & [-10,10] & yes & Detuning in Hz\\ \attr{/.../f0} & f & [100,1000] & yes & Tuning frequency in Hz\\ \attr{/.../level} & f & [0,100] & yes & Sound level in dB SPL\\ +\attr{/.../noiseq} & f & ]0,1[ & yes & Noise resonance filter Q factor\\ +\attr{/.../noiseweight} & f & [0,1] & yes & Noise to tone ratio\\ \attr{/.../onset} & f & [0,0.2] & yes & Onset duration in s\\ \hline \end{tabularx} diff --git a/plugins/Makefile b/plugins/Makefile index 8431cf8b..23e78bbb 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -3,6 +3,9 @@ all: build modules include ../config.mk include ../rules.mk +CXXFLAGS += -Wconversion +#-Werror + # # main targets: # diff --git a/plugins/src/tascar_ap_simplesynth.cc b/plugins/src/tascar_ap_simplesynth.cc index 0930e0bc..dcde52ed 100644 --- a/plugins/src/tascar_ap_simplesynth.cc +++ b/plugins/src/tascar_ap_simplesynth.cc @@ -21,6 +21,8 @@ #include "alsamidicc.h" #include "audioplugin.h" +#include "delayline.h" +#include "filterclass.h" #include struct snddevname_t { @@ -42,18 +44,28 @@ class tone_t { std::complex dphi = 1.0f; std::complex dphi_detune = 1.0f; float amplitude = 0.0f; + float amplitudenoise = 0.0f; int pitch = 0; float decay = 0.99f; float decaydamping = 0.99f; float decayoffset = 0.99f; + float decaynoise = 0.99f; + float srate_ = 1.0f; + uint32_t fbdelay = 1; + float feedback = 1.0f; + float noisestate = 0.0f; + float noisedamp = 0.0f; + TASCAR::varidelay_t noiseflt = TASCAR::varidelay_t(10000, 1.0, 1.0, 0, 0); void set_srate(float srate) { fscale = TASCAR_2PIf / srate; dt = 1.0f / srate; + srate_ = srate; } void set_pitch(int p, float a, float f0, float tau, float tauoff, float onset, - float detune, float taupitch) + float detune, float taupitch, float taunoise, float anoise, + float q) { if(onset < 1e-4f) onset = 1e-4f; @@ -78,17 +90,35 @@ class tone_t { decayoffset = exp(-fscale / tauoff); else decayoffset = 1.0f; + if(taunoise > 0) + decaynoise = exp(-fscale / taunoise); + else + decaynoise = 1.0f; + noisedamp = exp(-TASCAR_2PIf * fscale * f); amplitude = a; + amplitudenoise = anoise; + fbdelay = uint32_t(srate_ / f); + feedback = q; + noisestate = 0.0f; } void unset_pitch(int p) { if(pitch == p) decay = decayoffset; - // amplitude = 0.0f; } void add(float& val) { + if(amplitudenoise > 1e-8f) { + float oval = noiseflt.get(fbdelay); + noisestate = + noisedamp * noisestate + (1.0f - noisedamp) * TASCAR::frand(); + noiseflt.push(oval * feedback + amplitudenoise * noisestate); + val += oval; + amplitudenoise *= decaynoise; + } else + amplitudenoise = 0.0f; if(amplitude > 1e-8f) { + // float oval = 0.0f; std::complex ldphi = dphi; float compdecay = 1.0f; @@ -101,7 +131,7 @@ class tone_t { } amplitude *= decay; if(rampt > 0) { - oval *= 0.5 + 0.5 * cosf(TASCAR_PIf * rampt * rampdur_1); + oval *= 0.5f + 0.5f * cosf(TASCAR_PIf * rampt * rampdur_1); rampt -= dt; } val += oval; @@ -138,6 +168,9 @@ class simplesynth_t : public TASCAR::audioplugin_base_t, float level = 0.06f; float detune = 1.0f; float decaydamping = 8.0f; + float decaynoise = 0.5f; + float noiseweight = 0.0f; + float noiseq = 0.5f; std::string connect; }; @@ -157,6 +190,9 @@ simplesynth_t::simplesynth_t(const TASCAR::audioplugin_cfg_t& cfg) GET_ATTRIBUTE_BOOL(autoconnect, "Autoconnect to input ports"); GET_ATTRIBUTE(connect, "", "ALSA device name to connect to"); GET_ATTRIBUTE(detune, "Hz", "Detuning frequency in Hz"); + GET_ATTRIBUTE(noiseweight, "", "Noise to tone ratio"); + GET_ATTRIBUTE(decaynoise, "s", "Noise decay time"); + GET_ATTRIBUTE(noiseq, "", "Noise resonace filter Q factor"); tones.resize(maxvoices); if(!connect.empty()) { connect_input(connect, true); @@ -190,6 +226,10 @@ void simplesynth_t::add_variables(TASCAR::osc_server_t* srv) srv->add_float("/f0", &f0, "[100,1000]", "Tuning frequency in Hz"); srv->add_float("/onset", &onset, "[0,0.2]", "Onset duration in s"); srv->add_float("/detune", &detune, "[-10,10]", "Detuning in Hz"); + srv->add_float("/noiseq", &noiseq, "]0,1[", + "Noise resonance filter Q factor"); + srv->add_float("/decaynoise", &decaynoise, "[0,4]", "Noise decay time in s"); + srv->add_float("/noiseweight", &noiseweight, "[0,1]", "Noise to tone ratio"); srv->unset_variable_owner(); } @@ -214,8 +254,10 @@ void simplesynth_t::emit_event_note(int channel, int pitch, int velocity) if(midichannel == channel) { if(velocity > 0) { tones[nexttone].set_pitch( - pitch, velocity / 127.0f, f0, decay, decayoffset, - onset * (1.0f - (float)velocity / 127.0f), detune, decaydamping); + pitch, (float)velocity / 127.0f * (1.0f - noiseweight), f0, decay, + decayoffset, onset * (1.0f - (float)velocity / 127.0f), detune, + decaydamping, decaynoise, ((float)velocity / 127.0f) * noiseweight, + noiseq); ++nexttone; if(nexttone >= tones.size()) nexttone = 0; @@ -235,7 +277,7 @@ void simplesynth_t::configure() for(auto& p : t.phase) p = 1.0f; t.amplitudes.resize(partialweights.size()); - t.set_srate(f_sample); + t.set_srate((float)f_sample); t.N = partialweights.size(); } start_service(); From e2f088b81379872d9b14b6b9454589e49438d246 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Sun, 17 Nov 2024 17:38:18 +0100 Subject: [PATCH 14/34] add support for tuning temperaments in simplesynth --- examples/chestorgan.tsc | 15 +++++++++++++++ plugins/src/tascar_ap_simplesynth.cc | 27 ++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 examples/chestorgan.tsc diff --git a/examples/chestorgan.tsc b/examples/chestorgan.tsc new file mode 100644 index 00000000..740d5cab --- /dev/null +++ b/examples/chestorgan.tsc @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/plugins/src/tascar_ap_simplesynth.cc b/plugins/src/tascar_ap_simplesynth.cc index dcde52ed..56eff557 100644 --- a/plugins/src/tascar_ap_simplesynth.cc +++ b/plugins/src/tascar_ap_simplesynth.cc @@ -30,6 +30,8 @@ struct snddevname_t { std::string desc; }; +std::vector pitchcorr = {0.0f}; + class tone_t { public: tone_t(){}; @@ -75,7 +77,11 @@ class tone_t { for(auto& ph : phase) ph = 1.0f; amplitudes = partialweights; - float f = powf(2.0f, (float)(p - 69) / 12.0f) * f0; + auto pitchidx = div(p, (int)(pitchcorr.size())); + float pitch_corr_semi = + 0.01f * pitchcorr[pitchidx.rem] - (float)(pitchidx.rem); + float pitch_log_semi = (float)(p - 69) + pitch_corr_semi; + float f = powf(2.0f, pitch_log_semi / 12.0f) * f0; dphi = std::polar(1.0f, fscale * f); dphi_detune = std::polar(1.0f, fscale * detune); if(tau > 0) @@ -193,6 +199,25 @@ simplesynth_t::simplesynth_t(const TASCAR::audioplugin_cfg_t& cfg) GET_ATTRIBUTE(noiseweight, "", "Noise to tone ratio"); GET_ATTRIBUTE(decaynoise, "s", "Noise decay time"); GET_ATTRIBUTE(noiseq, "", "Noise resonace filter Q factor"); + std::string tuning = "equal"; + GET_ATTRIBUTE(tuning, "equal|werkmeister3|meantone4|meantone6|valotti", + "Tuning"); + if(tuning == "equal") + pitchcorr = {0.0f}; + else if(tuning == "werkmeister3") + pitchcorr = {0.0f, 96.0f, 204.0f, 300.0f, 396.0f, 504.0f, + 600.0f, 702.0f, 792.0f, 900.0f, 1002.0f, 1098.0f}; + else if(tuning == "meantone4") + pitchcorr = {0.0f, 75.5f, 193.0f, 310.5f, 386.0f, 503.5f, + 579.0f, 696.5f, 772.0f, 889.5f, 1007.0f, 1082.5f}; + else if(tuning == "meantone6") + pitchcorr = {0.0f, 92.0f, 196.0f, 294.0f, 392.0f, 502.0f, + 588.0f, 698.0f, 796.0f, 894.0f, 998.0f, 1090.0f}; + else if(tuning == "valotti") + pitchcorr = {0.0f, 94.0f, 196.0f, 298.0f, 392.0f, 502.0f, + 592.0f, 698.0f, 796.0f, 894.0f, 1000.0f, 1090.0f}; + else + throw TASCAR::ErrMsg("Unsupported tuning: \"" + tuning + "\"."); tones.resize(maxvoices); if(!connect.empty()) { connect_input(connect, true); From b8cabf85b8fb7197cbf8250cfc7cc3f0b319af56 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Sun, 17 Nov 2024 17:44:54 +0100 Subject: [PATCH 15/34] do not add chestorgan config file to avoid failure on MacOS --- examples/chestorgan.tsc | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 examples/chestorgan.tsc diff --git a/examples/chestorgan.tsc b/examples/chestorgan.tsc deleted file mode 100644 index 740d5cab..00000000 --- a/examples/chestorgan.tsc +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - From b842f3e902e94a5de79e2753aacd0c940a6686bd Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Sun, 17 Nov 2024 18:33:14 +0100 Subject: [PATCH 16/34] add support for sensitivity curve --- plugins/src/tascar_ap_simplesynth.cc | 11 ++++++++--- plugins/src/tascarmod_timedisplay.cc | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/src/tascar_ap_simplesynth.cc b/plugins/src/tascar_ap_simplesynth.cc index 56eff557..36181c01 100644 --- a/plugins/src/tascar_ap_simplesynth.cc +++ b/plugins/src/tascar_ap_simplesynth.cc @@ -177,6 +177,7 @@ class simplesynth_t : public TASCAR::audioplugin_base_t, float decaynoise = 0.5f; float noiseweight = 0.0f; float noiseq = 0.5f; + float gamma = 1.0f; std::string connect; }; @@ -199,6 +200,7 @@ simplesynth_t::simplesynth_t(const TASCAR::audioplugin_cfg_t& cfg) GET_ATTRIBUTE(noiseweight, "", "Noise to tone ratio"); GET_ATTRIBUTE(decaynoise, "s", "Noise decay time"); GET_ATTRIBUTE(noiseq, "", "Noise resonace filter Q factor"); + GET_ATTRIBUTE(gamma, "", "Velocity gamma value"); std::string tuning = "equal"; GET_ATTRIBUTE(tuning, "equal|werkmeister3|meantone4|meantone6|valotti", "Tuning"); @@ -255,6 +257,7 @@ void simplesynth_t::add_variables(TASCAR::osc_server_t* srv) "Noise resonance filter Q factor"); srv->add_float("/decaynoise", &decaynoise, "[0,4]", "Noise decay time in s"); srv->add_float("/noiseweight", &noiseweight, "[0,1]", "Noise to tone ratio"); + srv->add_float("/gamma", &gamma, "[0,10]", "Sensitivity curve gamma"); srv->unset_variable_owner(); } @@ -278,10 +281,12 @@ void simplesynth_t::emit_event_note(int channel, int pitch, int velocity) { if(midichannel == channel) { if(velocity > 0) { + float inputvel = (float)velocity / 127.0f; + inputvel = powf(inputvel, gamma); tones[nexttone].set_pitch( - pitch, (float)velocity / 127.0f * (1.0f - noiseweight), f0, decay, - decayoffset, onset * (1.0f - (float)velocity / 127.0f), detune, - decaydamping, decaynoise, ((float)velocity / 127.0f) * noiseweight, + pitch, inputvel * (1.0f - noiseweight), f0, decay, + decayoffset, onset * (1.0f - inputvel), detune, + decaydamping, decaynoise, inputvel * noiseweight, noiseq); ++nexttone; if(nexttone >= tones.size()) diff --git a/plugins/src/tascarmod_timedisplay.cc b/plugins/src/tascarmod_timedisplay.cc index a97359f3..0107c4be 100644 --- a/plugins/src/tascarmod_timedisplay.cc +++ b/plugins/src/tascarmod_timedisplay.cc @@ -96,7 +96,7 @@ timedisplay_t::timedisplay_t(const TASCAR::module_cfg_t& cfg) GET_WIDGET(label); timedisplaywindow->show(); connection_timeout = Glib::signal_timeout().connect( - sigc::mem_fun(*this, &timedisplay_t::on_timeout), 1000 / fps); + sigc::mem_fun(*this, &timedisplay_t::on_timeout), 1000.0 / fps); Pango::AttrList attrlist; Pango::Attribute fscale(Pango::Attribute::create_attr_scale(fontscale)); attrlist.insert(fscale); From a7702389b9084304e0f294a017517bcec275cd94 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Mon, 18 Nov 2024 20:04:19 +0100 Subject: [PATCH 17/34] optionally add noise to sustained part --- plugins/src/tascar_ap_simplesynth.cc | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/plugins/src/tascar_ap_simplesynth.cc b/plugins/src/tascar_ap_simplesynth.cc index 36181c01..a383b05f 100644 --- a/plugins/src/tascar_ap_simplesynth.cc +++ b/plugins/src/tascar_ap_simplesynth.cc @@ -57,6 +57,7 @@ class tone_t { float feedback = 1.0f; float noisestate = 0.0f; float noisedamp = 0.0f; + float noisemin = 0.0f; TASCAR::varidelay_t noiseflt = TASCAR::varidelay_t(10000, 1.0, 1.0, 0, 0); void set_srate(float srate) @@ -67,8 +68,9 @@ class tone_t { } void set_pitch(int p, float a, float f0, float tau, float tauoff, float onset, float detune, float taupitch, float taunoise, float anoise, - float q) + float q, float nmin) { + noisemin = nmin; if(onset < 1e-4f) onset = 1e-4f; pitch = p; @@ -109,16 +111,19 @@ class tone_t { } void unset_pitch(int p) { - if(pitch == p) + if(pitch == p){ decay = decayoffset; + decaynoise = decayoffset; + noisemin = 0.0f; + } } void add(float& val) { - if(amplitudenoise > 1e-8f) { + if((amplitudenoise > 1e-8f)||(amplitude > 1e-8f)) { float oval = noiseflt.get(fbdelay); noisestate = noisedamp * noisestate + (1.0f - noisedamp) * TASCAR::frand(); - noiseflt.push(oval * feedback + amplitudenoise * noisestate); + noiseflt.push(oval * feedback + (amplitudenoise + noisemin) * noisestate); val += oval; amplitudenoise *= decaynoise; } else @@ -178,6 +183,7 @@ class simplesynth_t : public TASCAR::audioplugin_base_t, float noiseweight = 0.0f; float noiseq = 0.5f; float gamma = 1.0f; + float noisemin = 0.0f; std::string connect; }; @@ -201,6 +207,7 @@ simplesynth_t::simplesynth_t(const TASCAR::audioplugin_cfg_t& cfg) GET_ATTRIBUTE(decaynoise, "s", "Noise decay time"); GET_ATTRIBUTE(noiseq, "", "Noise resonace filter Q factor"); GET_ATTRIBUTE(gamma, "", "Velocity gamma value"); + GET_ATTRIBUTE(noisemin, "", "Minimum noise amplitude during sustain"); std::string tuning = "equal"; GET_ATTRIBUTE(tuning, "equal|werkmeister3|meantone4|meantone6|valotti", "Tuning"); @@ -258,6 +265,8 @@ void simplesynth_t::add_variables(TASCAR::osc_server_t* srv) srv->add_float("/decaynoise", &decaynoise, "[0,4]", "Noise decay time in s"); srv->add_float("/noiseweight", &noiseweight, "[0,1]", "Noise to tone ratio"); srv->add_float("/gamma", &gamma, "[0,10]", "Sensitivity curve gamma"); + srv->add_float("/noisemin", &noisemin, "[0,1]", + "Minimum noise amplitude during sustain"); srv->unset_variable_owner(); } @@ -283,11 +292,10 @@ void simplesynth_t::emit_event_note(int channel, int pitch, int velocity) if(velocity > 0) { float inputvel = (float)velocity / 127.0f; inputvel = powf(inputvel, gamma); - tones[nexttone].set_pitch( - pitch, inputvel * (1.0f - noiseweight), f0, decay, - decayoffset, onset * (1.0f - inputvel), detune, - decaydamping, decaynoise, inputvel * noiseweight, - noiseq); + tones[nexttone].set_pitch(pitch, inputvel * (1.0f - noiseweight), f0, + decay, decayoffset, onset * (1.0f - inputvel), + detune, decaydamping, decaynoise, + inputvel * noiseweight, noiseq, noisemin); ++nexttone; if(nexttone >= tones.size()) nexttone = 0; From 5d34a2c547370f62d50e7793788c44e9b7d8f385 Mon Sep 17 00:00:00 2001 From: "Giso Grimm (thetys)" Date: Wed, 27 Nov 2024 15:15:29 +0100 Subject: [PATCH 18/34] cleanup code of skyfall plugin --- plugins/src/tascarmod_skyfall.cc | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/plugins/src/tascarmod_skyfall.cc b/plugins/src/tascarmod_skyfall.cc index 9902c68b..b6c6cb1d 100644 --- a/plugins/src/tascarmod_skyfall.cc +++ b/plugins/src/tascarmod_skyfall.cc @@ -18,7 +18,7 @@ * 02110-1301, USA. */ -#include +#include "session.h" /* The best way to implement an actor module is to derive from the @@ -35,28 +35,21 @@ class skyfall_t : public TASCAR::actor_module_t { // Declare your module parameters here. Access via XML file and OSC // can be set up in the contructor. // - double gravitation; - double deceleration; - double vmax; // max fall speed - double z0; - bool bypass; - double wx; - double wy; - double wz; - double friction_fall; - double friction_jump; + double gravitation = -9.81; + double deceleration = 40; + double vmax = 40; // max fall speed + double z0 = 2; + bool bypass = true; + double wx = 0; + double wy = 11 * DEG2RAD; + double wz = 45 * DEG2RAD; + double friction_fall = 1; + double friction_jump = 0.3; std::vector vspeed; }; skyfall_t::skyfall_t(const TASCAR::module_cfg_t& cfg) - : actor_module_t(cfg, true), // initialize base class, fail if no - // matching object was found - // - // Default values of module parameters: - // - gravitation(-9.81), deceleration(40), vmax(40), z0(2), bypass(true), - wx(0), wy(11 * DEG2RAD), wz(45 * DEG2RAD), friction_fall(1), - friction_jump(0.3) + : actor_module_t(cfg, true) { std::string prefix("/skyfall"); // From 31603f31b943e206b8968aa2f53e1564556f7830 Mon Sep 17 00:00:00 2001 From: "Giso Grimm (thetys)" Date: Tue, 3 Dec 2024 16:51:37 +0100 Subject: [PATCH 19/34] update of libsd --- scripts/libfuncs/sd_plotwizard.m | 467 +++++++++++++++++++++++++ scripts/libfuncs/sd_plotwizzard.m | 125 ++++--- scripts/libsd.m | 547 ++++++++++++++++++++++++++++-- 3 files changed, 1038 insertions(+), 101 deletions(-) create mode 100644 scripts/libfuncs/sd_plotwizard.m diff --git a/scripts/libfuncs/sd_plotwizard.m b/scripts/libfuncs/sd_plotwizard.m new file mode 100644 index 00000000..d972bdae --- /dev/null +++ b/scripts/libfuncs/sd_plotwizard.m @@ -0,0 +1,467 @@ +function s = sd_plotwizard( s ) +% Interactively create data plots based on structured data input. +% +% Usage: +% s = sd_plotwizard( s ) +% +% s : structure containing the data +% +% Author: Giso Grimm +% Date: 11/2006 + fh = figure('NumberTitle','off','MenuBar','none',... + 'Position',[250 400 740 620],... + 'Name','Structured data plotting wizzard'); + set(fh,'UserData',s); + %movegui(fh,'center'); + uicontrol('style','text','fontweight','bold',... + 'string','Y fields',... + 'position',[40 580 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','Y axis',... + 'position',[270 580 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','Available fields',... + 'position',[40 460 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','X axis',... + 'position',[270 460 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','Average model',... + 'position',[500 580 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','Parameter',... + 'position',[270 370 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','Restrictions',... + 'position',[270 280 180 20]); + uicontrol('style','listbox','string',s.fields(length(s.values)+1:end),... + 'Position',[40 500 180 80],'tag','avail_fields_y',... + 'value',1); + uicontrol('style','listbox','string',s.fields(1:length(s.values)),... + 'Position',[40 110 180 350],'tag','avail_fields',... + 'value',1); + uicontrol('style','listbox','string',{},... + 'Position',[270 500 180 80],'tag','ydata_selection'); + uicontrol('style','text','string','',... + 'Position',[270 410 180 50],'tag','xdata_selection',... + 'HorizontalAlignment','left'); + uicontrol('style','text','string','',... + 'Position',[270 320 180 50],'tag','pdata_selection',... + 'HorizontalAlignment','left'); + uicontrol('style','listbox','string',{},... + 'Position',[270 200 180 80],'tag','restrictions',... + 'Callback',@select_restriction); + uicontrol('style','listbox','string',{},... + 'Position',[270 110 180 80],'tag','restriction_vals',... + 'Callback',@select_restriction_val); + uicontrol('style','listbox','string',{'mean','median'},... + 'Position',[500 500 180 80],'tag','average_model'); + uicontrol('style','PushButton','string','>>',... + 'Position',[230 560 30 20],'callback',@add_y_data); + uicontrol('style','PushButton','string','<<',... + 'Position',[230 530 30 20],'callback',@rm_y_data); + uicontrol('style','PushButton','string','>>',... + 'Position',[230 440 30 20],'callback',@add_x_data); + uicontrol('style','PushButton','string','<<',... + 'Position',[230 410 30 20],'callback',@rm_x_data); + uicontrol('style','PushButton','string','>>',... + 'Position',[230 350 30 20],'callback',@add_p_data); + uicontrol('style','PushButton','string','<<',... + 'Position',[230 320 30 20],'callback',@rm_p_data); + uicontrol('style','PushButton','string','>>',... + 'Position',[230 260 30 20],'callback',@add_restriction); + uicontrol('style','PushButton','string','<<',... + 'Position',[230 230 30 20],'callback',@rm_restriction); + uicontrol('style','PushButton','string','plot',... + 'Position',[40 40 130 40],'callback',@plot_this); + uicontrol('style','PushButton','string','quit',... + 'Position',[240 40 130 40],'callback','uiresume(gcf)'); + %% plot style controls: + uicontrol('style','frame','position',[470 100 260 280],... + 'tag','plotpar'); + create_plotpar_ui( 'fontsize', 1, @validate_positive, 14 ); + create_plotpar_ui( 'markersize', 2, @validate_positive, 3 ); + create_plotpar_ui( 'linewidth', 3, @validate_positive, 1 ); + create_plotpar_ui( 'linestyles', 4, @validate_string_or_cellarray, '''''' ); + create_plotpar_ui( 'gridfreq', 5, @validate_positive, 1 ); + create_plotpar_ui( 'firstgrid', 6, @validate_positive, 1 ); + create_plotpar_ui( 'colors', 7, @validate_string_or_cellarray, '''''' ); + create_plotpar_ui( 'markers', 8, @validate_string_or_cellarray, '''''' ); + if isfield( s, 'datapars' ) + set_all_datapars( s.datapars, fh ); + end + if isfield( s, 'plotpars' ) + set_all_plotpar_svals( s.plotpars, fh ); + end + uiwait(fh); + if ishandle(fh) + s.datapars = get_data_pars( fh ); + plotpars = struct; + for fn={'fontsize','markersize','linewidth','linestyles', ... + 'gridfreq','firstgrid','colors','markers'} + name = fn{:}; + plotpars = get_plotpar_sval( plotpars, name, fh ); + end + s.plotpars = plotpars; + close(fh); + end + if nargout == 0 + clear s + end + + + +function add_y_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields_y'); + h_ydata = findobj(gcf,'tag','ydata_selection'); + csfields = get(h_fields,'string'); + if length(csfields) + val = get(h_fields,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_fields,'value',length(csfields),'string',csfields); + csfields = get(h_ydata,'string'); + csfields{end+1} = field; + set(h_ydata,'string',csfields,'value',length(csfields)); + end + +function rm_y_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields_y'); + h_ydata = findobj(gcf,'tag','ydata_selection'); + csfields = get(h_ydata,'string'); + if length(csfields) + val = get(h_ydata,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_ydata,'value',length(csfields),'string',csfields); + csfields = get(h_fields,'string'); + csfields{end+1} = field; + set(h_fields,'string',csfields,'value',length(csfields)); + end + +function add_x_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_xdata = findobj(gcf,'tag','xdata_selection'); + csfields = get(h_fields,'string'); + if length(csfields) + if length(get(h_xdata,'String')) == 0 + val = get(h_fields,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_fields,'value',length(csfields),'string',csfields); + set(h_xdata,'string',field); + end + end + +function rm_x_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_xdata = findobj(gcf,'tag','xdata_selection'); + field = get(h_xdata,'string'); + if length(field) + set(h_xdata,'string',''); + csfields = get(h_fields,'string'); + csfields{end+1} = field; + set(h_fields,'string',csfields,'value',length(csfields)); + end + +function add_p_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_xdata = findobj(gcf,'tag','pdata_selection'); + csfields = get(h_fields,'string'); + if length(csfields) + if length(get(h_xdata,'String')) == 0 + val = get(h_fields,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_fields,'value',length(csfields),'string',csfields); + set(h_xdata,'string',field); + end + end + +function rm_p_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_xdata = findobj(gcf,'tag','pdata_selection'); + field = get(h_xdata,'string'); + if length(field) + set(h_xdata,'string',''); + csfields = get(h_fields,'string'); + csfields{end+1} = field; + set(h_fields,'string',csfields,'value',length(csfields)); + end + +function add_restriction(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_ydata = findobj(gcf,'tag','restrictions'); + csfields = get(h_fields,'string'); + if length(csfields) + val = get(h_fields,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_fields,'value',min(1,length(csfields)),'string',csfields); + csfields = get(h_ydata,'string'); + csfields{end+1} = field; + set(h_ydata,'string',csfields,'value',length(csfields)); + select_restriction; + end + +function rm_restriction(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_ydata = findobj(gcf,'tag','restrictions'); + csfields = get(h_ydata,'string'); + if length(csfields) + val = get(h_ydata,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_ydata,'value',min(1,length(csfields)),'string',csfields); + csfields = get(h_fields,'string'); + csfields{end+1} = field; + set(h_fields,'string',csfields,'value',length(csfields)); + select_restriction; + k = get_restriction_idx( field ); + s = get(gcf,'UserData'); + s.restrictions(k) = []; + set(gcf,'UserData',s); + end + +function k = get_restriction_idx( sRes ) + s = get(gcf,'UserData'); + kRes = strmatch(sRes,s.fields,'exact'); + if ~isfield(s,'restrictions') + s.restrictions = {}; + end + k = 0; + for kResL=1:length(s.restrictions) + cRes = s.restrictions{kResL}; + if cRes{1} == kRes + k = kResL; + end + end + if ~k + s.restrictions{end+1} = {kRes,1}; + k = length(s.restrictions); + end + set(gcf,'UserData',s); + return + +function select_restriction(varargin) + h_restr = findobj(gcf,'tag','restrictions'); + h_rval = findobj(gcf,'tag','restriction_vals'); + csRes = get(h_restr,'String'); + if length(csRes) + kRes = get(h_restr,'Value'); + kRes = get_restriction_idx( csRes{kRes} ); + s = get(gcf,'UserData'); + name_res = s.values{s.restrictions{kRes}{1}}; + if isnumeric(name_res) + temp_res = name_res; + name_res = {}; + for k=1:numel(temp_res) + name_res{k} = num2str(temp_res(k)); + end + end + set(h_rval,'String',name_res,... + 'Value',s.restrictions{kRes}{2}); + else + set(h_rval,'String',{},'Value',0); + end + +function select_restriction_val(varargin) + h_restr = findobj(gcf,'tag','restrictions'); + h_rval = findobj(gcf,'tag','restriction_vals'); + csRes = get(h_restr,'String'); + kRes = get(h_restr,'Value'); + kRes = get_restriction_idx( csRes{kRes} ); + s = get(gcf,'UserData'); + val = get(h_rval,'Value'); + s.restrictions{kRes}{2} = val; + set(gcf,'UserData',s); + +function sDataPar = get_data_pars( fh ) + sDataPar = struct; + s = get(fh,'UserData'); + if ~isfield(s,'restrictions') + s.restrictions = {}; + end + h_ydata = findobj(fh,'tag','ydata_selection'); + h_xdata = findobj(fh,'tag','xdata_selection'); + h_pdata = findobj(fh,'tag','pdata_selection'); + ydata_name = get(h_ydata,'String'); + xdata_name = get(h_xdata,'String'); + pdata_name = get(h_pdata,'String'); + k_ydata = []; + for sYdata=ydata_name' + k_ydata(end+1) = strmatch(sYdata{:},s.fields,'exact'); + end + k_xdata = strmatch(xdata_name,s.fields,'exact'); + k_pdata = strmatch(pdata_name,s.fields,'exact'); + sDataPar.y_data = k_ydata; + sDataPar.x_data = k_xdata; + sDataPar.par_data = k_pdata; + sDataPar.restrictions = s.restrictions; + + +function plot_this(varargin) + sDataPar = get_data_pars( gcf ) + s = get(gcf,'UserData'); + if ~isfield(s,'restrictions') + s.restrictions = {}; + end + h_ydata = findobj(gcf,'tag','ydata_selection'); + h_xdata = findobj(gcf,'tag','xdata_selection'); + h_pdata = findobj(gcf,'tag','pdata_selection'); + ydata_name = get(h_ydata,'String'); + xdata_name = get(h_xdata,'String'); + pdata_name = get(h_pdata,'String'); + k_ydata = []; + for sYdata=ydata_name' + k_ydata(end+1) = strmatch(sYdata{:},s.fields,'exact'); + end + k_xdata = strmatch(xdata_name,s.fields,'exact'); + k_pdata = strmatch(pdata_name,s.fields,'exact'); + s_ydata = sprintf('%d ',k_ydata); + s_ydata = s_ydata(1:end-1); + sPlotPars = get_plot_params( gcf ) + s_restr = ''; + for k=1:length(s.restrictions) + s_restr = sprintf('%s{%d,%d},',s_restr,s.restrictions{k}{1},s.restrictions{k}{2}); + end + s_restr = s_restr(1:end-1); + %pcmd = sprintf('plot_struct_data(s,%d,[%s],%d,{%s})',... + % k_xdata,s_ydata, ... + % k_pdata,... + % s_restr); + % disp(pcmd); + h_avg = findobj(gcf,'tag','average_model'); + avg_model = get(h_avg,'String'); + avg_model = avg_model{get(h_avg,'Value')}; + try + sd_plot(s,k_xdata,k_ydata,sPlotPars,... + 'parameter',k_pdata,... + 'restrictions',s.restrictions,... + 'average',avg_model); + catch + err = lasterror; + disp(err.message); + for k=1:length(err.stack) + disp(sprintf('%s:%d %s\n',... + err.stack(k).file,... + err.stack(k).line,... + err.stack(k).name)); + end + rethrow(err); + end + +function sPlotPar = get_plot_params( fh ) + sPlotPar = struct; + sPlotPar = set_plotpar_field( sPlotPar, 'fontsize', fh ); + sPlotPar = set_plotpar_field( sPlotPar, 'markersize', fh ); + sPlotPar = set_plotpar_field( sPlotPar, 'linewidth', fh ); + sPlotPar = set_plotpar_field( sPlotPar, 'gridfreq', fh ); + sPlotPar = set_plotpar_field( sPlotPar, 'firstgrid', fh ); + sPlotPar = set_plotpar_field( sPlotPar, 'colors', fh ); + sPlotPar = set_plotpar_field( sPlotPar, 'linestyles', fh ); + sPlotPar = set_plotpar_field( sPlotPar, 'markers', fh ); + +function sPlotParS = get_plotpar_sval( sPlotParS, name, fh ) + h = findall(fh,'tag',sprintf('plotpar_%s',name)); + sPlotParS.(name) = get(h,'String'); + +function set_all_plotpar_svals( sPlotParS, fh ) + for fn = fieldnames(sPlotParS)' + name = fn{:}; + h = findall(fh,'tag',sprintf('plotpar_%s',name)); + set(h,'String',sPlotParS.(name)); + end + +function sPlotPar = set_plotpar_field( sPlotPar, name, fh ) + h = findall(fh,'tag',sprintf('plotpar_%s',name)); + s = get(h,'String'); + if ~isempty(s) + eval(sprintf('val=%s;',s)); + if ~isempty(val) + sPlotPar.(name) = val; + end + end + + +function err = validate_string_or_cellarray( bo, varargin ) + s = get(bo, 'String' ); + if isempty( s ) + s = ''''''; + elseif strcmp([s(1) s(end)],'''''') + s = s; + elseif strcmp([s(1) s(end)],'{}') + s = s; + else + s = ['''' s '''']; + end + set(bo, 'String', s ); + +function err = validate_positive( bo, varargin ) + s = get(bo, 'String' ); + v_def = get(bo,'UserData'); + try + val = str2num(s); + if isempty(val) + val = v_def; + end + catch + val = v_def; + end + if val < eps + val = eps; + end + s = num2str(val); + set(bo, 'String', s ); + +function create_plotpar_ui( name, idx, validator, default ) + pos = get(findall(gcf,'tag','plotpar'),'Position'); + x = pos(1)+10; + y = pos(2)+pos(4)-10; + uicontrol('style','text','string',name,... + 'position',[x y-25*idx-2 85 20],... + 'fontweight','bold','horizontalalignment','right'); + h = uicontrol('tag',sprintf('plotpar_%s',name),... + 'style','edit',... + 'position',[x+90 y-25*idx 150 20],... + 'callback',validator,... + 'userdata',default,... + 'BackgroundColor',[1 1 1]); + validator(h); + +function set_all_datapars( sPars, fh ) + s = get(fh,'UserData'); + % y axis: + idx_yavail = 1:length(s.fields); + idx_yavail(sPars.y_data) = []; + idx_yavail(find(idx_yavail<=length(s.values))) = []; + h_fields = findobj(fh,'tag','avail_fields_y'); + h_ydata = findobj(fh,'tag','ydata_selection'); + set(h_fields,'String',s.fields(idx_yavail),'Value',length(idx_yavail)); + set(h_ydata,'String',s.fields(sPars.y_data)); + % x axis: + h_xdata = findobj(fh,'tag','xdata_selection'); + set(h_xdata,'String',s.fields(sPars.x_data)); + % p axis: + h_pdata = findobj(fh,'tag','pdata_selection'); + set(h_pdata,'String',s.fields(sPars.par_data)); + % restrictions: + s.restrictions = sPars.restrictions; + h_rdata = findobj(fh,'tag','restrictions'); + vRestr = []; + for k=1:length(s.restrictions) + vRestr(end+1) = s.restrictions{k}{1}; + end + set(h_rdata,'String',s.fields(vRestr)); + set(fh,'UserData',s); + select_restriction; + % remove used fields: + idx_xavail = 1:length(s.fields); + idx_xavail(find(idx_xavail==sPars.x_data)) = []; + idx_xavail(find(idx_xavail==sPars.par_data)) = []; + for k=1:length(vRestr) + idx_xavail(find(idx_xavail==vRestr(k))) = []; + end + idx_xavail(find(idx_xavail>length(s.values))) = []; + h_fields = findobj(fh,'tag','avail_fields'); + set(h_fields,'String',s.fields(idx_xavail),'Value',length(idx_xavail)); diff --git a/scripts/libfuncs/sd_plotwizzard.m b/scripts/libfuncs/sd_plotwizzard.m index 0cd235a9..14398a9b 100644 --- a/scripts/libfuncs/sd_plotwizzard.m +++ b/scripts/libfuncs/sd_plotwizzard.m @@ -51,57 +51,57 @@ 'HorizontalAlignment','left'); uicontrol('style','listbox','string',{},... 'Position',[270 200 180 80],'tag','restrictions',... - 'Callback',@select_restriction); + 'Callback',@zz_select_restriction); uicontrol('style','listbox','string',{},... 'Position',[270 110 180 80],'tag','restriction_vals',... - 'Callback',@select_restriction_val); + 'Callback',@zz_select_restriction_val); uicontrol('style','listbox','string',{'mean','median'},... 'Position',[500 500 180 80],'tag','average_model'); uicontrol('style','PushButton','string','>>',... - 'Position',[230 560 30 20],'callback',@add_y_data); + 'Position',[230 560 30 20],'callback',@zz_add_y_data); uicontrol('style','PushButton','string','<<',... - 'Position',[230 530 30 20],'callback',@rm_y_data); + 'Position',[230 530 30 20],'callback',@zz_rm_y_data); uicontrol('style','PushButton','string','>>',... - 'Position',[230 440 30 20],'callback',@add_x_data); + 'Position',[230 440 30 20],'callback',@zz_add_x_data); uicontrol('style','PushButton','string','<<',... - 'Position',[230 410 30 20],'callback',@rm_x_data); + 'Position',[230 410 30 20],'callback',@zz_rm_x_data); uicontrol('style','PushButton','string','>>',... - 'Position',[230 350 30 20],'callback',@add_p_data); + 'Position',[230 350 30 20],'callback',@zz_add_p_data); uicontrol('style','PushButton','string','<<',... - 'Position',[230 320 30 20],'callback',@rm_p_data); + 'Position',[230 320 30 20],'callback',@zz_rm_p_data); uicontrol('style','PushButton','string','>>',... - 'Position',[230 260 30 20],'callback',@add_restriction); + 'Position',[230 260 30 20],'callback',@zz_add_restriction); uicontrol('style','PushButton','string','<<',... - 'Position',[230 230 30 20],'callback',@rm_restriction); + 'Position',[230 230 30 20],'callback',@zz_rm_restriction); uicontrol('style','PushButton','string','plot',... - 'Position',[40 40 130 40],'callback',@plot_this); + 'Position',[40 40 130 40],'callback',@zz_plot_this); uicontrol('style','PushButton','string','quit',... 'Position',[240 40 130 40],'callback','uiresume(gcf)'); %% plot style controls: uicontrol('style','frame','position',[470 100 260 280],... 'tag','plotpar'); - create_plotpar_ui( 'fontsize', 1, @validate_positive, 14 ); - create_plotpar_ui( 'markersize', 2, @validate_positive, 3 ); - create_plotpar_ui( 'linewidth', 3, @validate_positive, 1 ); - create_plotpar_ui( 'linestyles', 4, @validate_string_or_cellarray, '''''' ); - create_plotpar_ui( 'gridfreq', 5, @validate_positive, 1 ); - create_plotpar_ui( 'firstgrid', 6, @validate_positive, 1 ); - create_plotpar_ui( 'colors', 7, @validate_string_or_cellarray, '''''' ); - create_plotpar_ui( 'markers', 8, @validate_string_or_cellarray, '''''' ); + zz_create_plotpar_ui( 'fontsize', 1, @zz_validate_positive, 14 ); + zz_create_plotpar_ui( 'markersize', 2, @zz_validate_positive, 3 ); + zz_create_plotpar_ui( 'linewidth', 3, @zz_validate_positive, 1 ); + zz_create_plotpar_ui( 'linestyles', 4, @zz_validate_string_or_cellarray, '''''' ); + zz_create_plotpar_ui( 'gridfreq', 5, @zz_validate_positive, 1 ); + zz_create_plotpar_ui( 'firstgrid', 6, @zz_validate_positive, 1 ); + zz_create_plotpar_ui( 'colors', 7, @zz_validate_string_or_cellarray, '''''' ); + zz_create_plotpar_ui( 'markers', 8, @zz_validate_string_or_cellarray, '''''' ); if isfield( s, 'datapars' ) - set_all_datapars( s.datapars, fh ); + zz_set_all_datapars( s.datapars, fh ); end if isfield( s, 'plotpars' ) - set_all_plotpar_svals( s.plotpars, fh ); + zz_set_all_plotpar_svals( s.plotpars, fh ); end uiwait(fh); if ishandle(fh) - s.datapars = get_data_pars( fh ); + s.datapars = zz_get_data_pars( fh ); plotpars = struct; for fn={'fontsize','markersize','linewidth','linestyles', ... 'gridfreq','firstgrid','colors','markers'} name = fn{:}; - plotpars = get_plotpar_sval( plotpars, name, fh ); + plotpars = zz_get_plotpar_sval( plotpars, name, fh ); end s.plotpars = plotpars; close(fh); @@ -110,9 +110,7 @@ clear s end - - -function add_y_data(varargin) +function zz_add_y_data(varargin) h_fields = findobj(gcf,'tag','avail_fields_y'); h_ydata = findobj(gcf,'tag','ydata_selection'); csfields = get(h_fields,'string'); @@ -126,7 +124,7 @@ function add_y_data(varargin) set(h_ydata,'string',csfields,'value',length(csfields)); end -function rm_y_data(varargin) +function zz_rm_y_data(varargin) h_fields = findobj(gcf,'tag','avail_fields_y'); h_ydata = findobj(gcf,'tag','ydata_selection'); csfields = get(h_ydata,'string'); @@ -140,7 +138,7 @@ function rm_y_data(varargin) set(h_fields,'string',csfields,'value',length(csfields)); end -function add_x_data(varargin) +function zz_add_x_data(varargin) h_fields = findobj(gcf,'tag','avail_fields'); h_xdata = findobj(gcf,'tag','xdata_selection'); csfields = get(h_fields,'string'); @@ -154,7 +152,7 @@ function add_x_data(varargin) end end -function rm_x_data(varargin) +function zz_rm_x_data(varargin) h_fields = findobj(gcf,'tag','avail_fields'); h_xdata = findobj(gcf,'tag','xdata_selection'); field = get(h_xdata,'string'); @@ -165,7 +163,7 @@ function rm_x_data(varargin) set(h_fields,'string',csfields,'value',length(csfields)); end -function add_p_data(varargin) +function zz_add_p_data(varargin) h_fields = findobj(gcf,'tag','avail_fields'); h_xdata = findobj(gcf,'tag','pdata_selection'); csfields = get(h_fields,'string'); @@ -179,7 +177,7 @@ function add_p_data(varargin) end end -function rm_p_data(varargin) +function zz_rm_p_data(varargin) h_fields = findobj(gcf,'tag','avail_fields'); h_xdata = findobj(gcf,'tag','pdata_selection'); field = get(h_xdata,'string'); @@ -190,7 +188,7 @@ function rm_p_data(varargin) set(h_fields,'string',csfields,'value',length(csfields)); end -function add_restriction(varargin) +function zz_add_restriction(varargin) h_fields = findobj(gcf,'tag','avail_fields'); h_ydata = findobj(gcf,'tag','restrictions'); csfields = get(h_fields,'string'); @@ -202,10 +200,10 @@ function add_restriction(varargin) csfields = get(h_ydata,'string'); csfields{end+1} = field; set(h_ydata,'string',csfields,'value',length(csfields)); - select_restriction; + zz_select_restriction; end -function rm_restriction(varargin) +function zz_rm_restriction(varargin) h_fields = findobj(gcf,'tag','avail_fields'); h_ydata = findobj(gcf,'tag','restrictions'); csfields = get(h_ydata,'string'); @@ -217,14 +215,14 @@ function rm_restriction(varargin) csfields = get(h_fields,'string'); csfields{end+1} = field; set(h_fields,'string',csfields,'value',length(csfields)); - select_restriction; - k = get_restriction_idx( field ); + zz_select_restriction; + k = zz_get_restriction_idx( field ); s = get(gcf,'UserData'); s.restrictions(k) = []; set(gcf,'UserData',s); end -function k = get_restriction_idx( sRes ) +function k = zz_get_restriction_idx( sRes ) s = get(gcf,'UserData'); kRes = strmatch(sRes,s.fields,'exact'); if ~isfield(s,'restrictions') @@ -244,13 +242,13 @@ function rm_restriction(varargin) set(gcf,'UserData',s); return -function select_restriction(varargin) +function zz_select_restriction(varargin) h_restr = findobj(gcf,'tag','restrictions'); h_rval = findobj(gcf,'tag','restriction_vals'); csRes = get(h_restr,'String'); if length(csRes) kRes = get(h_restr,'Value'); - kRes = get_restriction_idx( csRes{kRes} ); + kRes = zz_get_restriction_idx( csRes{kRes} ); s = get(gcf,'UserData'); name_res = s.values{s.restrictions{kRes}{1}}; if isnumeric(name_res) @@ -266,18 +264,18 @@ function select_restriction(varargin) set(h_rval,'String',{},'Value',0); end -function select_restriction_val(varargin) +function zz_select_restriction_val(varargin) h_restr = findobj(gcf,'tag','restrictions'); h_rval = findobj(gcf,'tag','restriction_vals'); csRes = get(h_restr,'String'); kRes = get(h_restr,'Value'); - kRes = get_restriction_idx( csRes{kRes} ); + kRes = zz_get_restriction_idx( csRes{kRes} ); s = get(gcf,'UserData'); val = get(h_rval,'Value'); s.restrictions{kRes}{2} = val; set(gcf,'UserData',s); -function sDataPar = get_data_pars( fh ) +function sDataPar = zz_get_data_pars( fh ) sDataPar = struct; s = get(fh,'UserData'); if ~isfield(s,'restrictions') @@ -300,9 +298,8 @@ function select_restriction_val(varargin) sDataPar.par_data = k_pdata; sDataPar.restrictions = s.restrictions; - -function plot_this(varargin) - sDataPar = get_data_pars( gcf ) +function zz_plot_this(varargin) + sDataPar = zz_get_data_pars( gcf ) s = get(gcf,'UserData'); if ~isfield(s,'restrictions') s.restrictions = {}; @@ -321,7 +318,7 @@ function plot_this(varargin) k_pdata = strmatch(pdata_name,s.fields,'exact'); s_ydata = sprintf('%d ',k_ydata); s_ydata = s_ydata(1:end-1); - sPlotPars = get_plot_params( gcf ) + sPlotPars = zz_get_plot_params( gcf ) s_restr = ''; for k=1:length(s.restrictions) s_restr = sprintf('%s{%d,%d},',s_restr,s.restrictions{k}{1},s.restrictions{k}{2}); @@ -349,32 +346,32 @@ function plot_this(varargin) err.stack(k).line,... err.stack(k).name)); end - rethrow err; + rethrow(err); end -function sPlotPar = get_plot_params( fh ) +function sPlotPar = zz_get_plot_params( fh ) sPlotPar = struct; - sPlotPar = set_plotpar_field( sPlotPar, 'fontsize', fh ); - sPlotPar = set_plotpar_field( sPlotPar, 'markersize', fh ); - sPlotPar = set_plotpar_field( sPlotPar, 'linewidth', fh ); - sPlotPar = set_plotpar_field( sPlotPar, 'gridfreq', fh ); - sPlotPar = set_plotpar_field( sPlotPar, 'firstgrid', fh ); - sPlotPar = set_plotpar_field( sPlotPar, 'colors', fh ); - sPlotPar = set_plotpar_field( sPlotPar, 'linestyles', fh ); - sPlotPar = set_plotpar_field( sPlotPar, 'markers', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'fontsize', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'markersize', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'linewidth', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'gridfreq', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'firstgrid', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'colors', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'linestyles', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'markers', fh ); -function sPlotParS = get_plotpar_sval( sPlotParS, name, fh ) +function sPlotParS = zz_get_plotpar_sval( sPlotParS, name, fh ) h = findall(fh,'tag',sprintf('plotpar_%s',name)); sPlotParS.(name) = get(h,'String'); -function set_all_plotpar_svals( sPlotParS, fh ) +function zz_set_all_plotpar_svals( sPlotParS, fh ) for fn = fieldnames(sPlotParS)' name = fn{:}; h = findall(fh,'tag',sprintf('plotpar_%s',name)); set(h,'String',sPlotParS.(name)); end -function sPlotPar = set_plotpar_field( sPlotPar, name, fh ) +function sPlotPar = zz_set_plotpar_field( sPlotPar, name, fh ) h = findall(fh,'tag',sprintf('plotpar_%s',name)); s = get(h,'String'); if ~isempty(s) @@ -385,7 +382,7 @@ function set_all_plotpar_svals( sPlotParS, fh ) end -function err = validate_string_or_cellarray( bo, varargin ) +function err = zz_validate_string_or_cellarray( bo, varargin ) s = get(bo, 'String' ); if isempty( s ) s = ''''''; @@ -398,7 +395,7 @@ function set_all_plotpar_svals( sPlotParS, fh ) end set(bo, 'String', s ); -function err = validate_positive( bo, varargin ) +function err = zz_validate_positive( bo, varargin ) s = get(bo, 'String' ); v_def = get(bo,'UserData'); try @@ -415,7 +412,7 @@ function set_all_plotpar_svals( sPlotParS, fh ) s = num2str(val); set(bo, 'String', s ); -function create_plotpar_ui( name, idx, validator, default ) +function zz_create_plotpar_ui( name, idx, validator, default ) pos = get(findall(gcf,'tag','plotpar'),'Position'); x = pos(1)+10; y = pos(2)+pos(4)-10; @@ -430,7 +427,7 @@ function create_plotpar_ui( name, idx, validator, default ) 'BackgroundColor',[1 1 1]); validator(h); -function set_all_datapars( sPars, fh ) +function zz_set_all_datapars( sPars, fh ) s = get(fh,'UserData'); % y axis: idx_yavail = 1:length(s.fields); @@ -455,7 +452,7 @@ function set_all_datapars( sPars, fh ) end set(h_rdata,'String',s.fields(vRestr)); set(fh,'UserData',s); - select_restriction; + zz_select_restriction; % remove used fields: idx_xavail = 1:length(s.fields); idx_xavail(find(idx_xavail==sPars.x_data)) = []; diff --git a/scripts/libsd.m b/scripts/libsd.m index c5e8bd77..b92ea361 100644 --- a/scripts/libsd.m +++ b/scripts/libsd.m @@ -18,7 +18,7 @@ % This file was generated by "bake_mlib sd". % Do not edit! Edit sources sd_*.m instead. % -% Date: 16-Feb-2022 12:59:58 +% Date: 03-Dec-2024 16:49:50 sLib = struct; sLib.anovan = @sd_anovan; sLib.help.anovan = @help_anovan; @@ -60,6 +60,8 @@ sLib.help.plot = @help_plot; sLib.plot_histo = @sd_plot_histo; sLib.help.plot_histo = @help_plot_histo; +sLib.plotwizard = @sd_plotwizard; +sLib.help.plotwizard = @help_plotwizard; sLib.plotwizzard = @sd_plotwizzard; sLib.help.plotwizzard = @help_plotwizzard; sLib.posthoc = @sd_posthoc; @@ -163,7 +165,7 @@ s = sOut; function help_average -disp([' AVERAGE - calculate across specified fields',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [s, csFields] = sd.average( s, vkField, cFunc );',char(10),'',char(10),'',char(10),' Usage:',char(10),' [s, csFields] = sd_average( s, vkField [, cFunc ] )',char(10),'',char(10),' Input arguments:',char(10),' s : data structure.',char(10),' vkField : field numbers to calculate the average.',char(10),' cFunc : cell array of function handles (optional).',char(10),' Functions may take a matrix of values as input and must',char(10),' return a row vector (e.g., ''{@mean}'').',char(10),'',char(10),' Return values:',char(10),' s : modified data structure with extra data fields ''N'' (number',char(10),' of fields) and the processed value of each data column for ',char(10),' each specified function (e.g., ''mean(Y)'').',char(10),' csFields : names of processed fields.',char(10),'',char(10),' Author: Giso Grimm',char(10),' Date: 11/2008',char(10),'']); +disp([' AVERAGE - calculate across specified fields',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [s, csFields] = sd.average( s, vkField, cFunc );',char(10),'',char(10),' ',char(10),' Usage:',char(10),' [s, csFields] = sd_average( s, vkField [, cFunc ] )',char(10),' ',char(10),' Input arguments:',char(10),' s : data structure.',char(10),' vkField : field numbers to calculate the average.',char(10),' cFunc : cell array of function handles (optional).',char(10),' Functions may take a matrix of values as input and must',char(10),' return a row vector (e.g., ''{@mean}'').',char(10),' ',char(10),' Return values:',char(10),' s : modified data structure with extra data fields ''N'' (number',char(10),' of fields) and the processed value of each data column for ',char(10),' each specified function (e.g., ''mean(Y)'').',char(10),' csFields : names of processed fields.',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 11/2008',char(10),'']); function [sOut,mFields] = sd_col2par( sIn, sParName, cValues, csDataNames ) @@ -217,7 +219,7 @@ mFields = reshape(sIn.fields(nParIn+[1:nDataIn]),[nParElem,nDataOut]); function help_col2par -disp([' COL2PAR - convert multiple data columns into a new parameter column',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [sOut,mFields] = sd.col2par( sIn, sParName, cValues, csDataNames );',char(10),'',char(10),' sParName : new parameter column name',char(10),' cValues : values of new parameter column',char(10),' csDataNames : names of resulting data columns',char(10),'',char(10),'Note: ',char(10),' If field order is {''a1'',''a2'',''a3'',''b1'',''b2'',''b3''} then use',char(10),'',char(10),' sOut = sd.col2par(sIn,''numeric'',{''1'',''2'',''3''},{''a'',''b''});',char(10),'',char(10),' or',char(10),'',char(10),' sOut = sd.col2par(sIn,''numeric'',1:3,{''a'',''b''});',char(10),'',char(10),' This means that the new parameter field must be in interleaved',char(10),' order in the input data fields.',char(10),'',char(10),' Check consistency of mFields to avoid unexpected results.',char(10),'']); +disp([' COL2PAR - convert multiple data columns into a new parameter column',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [sOut,mFields] = sd.col2par( sIn, sParName, cValues, csDataNames );',char(10),'',char(10),' sParName : new parameter column name',char(10),' cValues : values of new parameter column',char(10),' csDataNames : names of resulting data columns',char(10),' ',char(10),' Note: ',char(10),' If field order is {''a1'',''a2'',''a3'',''b1'',''b2'',''b3''} then use',char(10),' ',char(10),' sOut = sd.col2par(sIn,''numeric'',{''1'',''2'',''3''},{''a'',''b''});',char(10),' ',char(10),' or',char(10),' ',char(10),' sOut = sd.col2par(sIn,''numeric'',1:3,{''a'',''b''});',char(10),' ',char(10),' This means that the new parameter field must be in interleaved',char(10),' order in the input data fields.',char(10),' ',char(10),' Check consistency of mFields to avoid unexpected results.',char(10),'']); function s = sd_compactval( s ) @@ -254,7 +256,7 @@ function help_compactval -disp([' COMPACTVAL - remove unused entries from value vectors',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.compactval( s );',char(10),'',char(10),'',char(10),' Author: Giso Grimm',char(10),' Date: 11/2008',char(10),'']); +disp([' COMPACTVAL - remove unused entries from value vectors',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.compactval( s );',char(10),'',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 11/2008',char(10),'']); function sd_csv( data, fname ) @@ -291,7 +293,7 @@ function sd_csv( data, fname ) function help_csv -disp([' CSV - sd_ - export data structure as file',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' sd.csv( data, fname );',char(10),'',char(10),'',char(10),' Usage:',char(10),' sd_csv( data, fname )',char(10),'',char(10),'']); +disp([' CSV - sd_ - export data structure as file',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' sd.csv( data, fname );',char(10),'',char(10),' ',char(10),' Usage:',char(10),' sd_csv( data, fname )',char(10),'']); function s = sd_deltaref( s, field, val ) @@ -323,7 +325,7 @@ function sd_csv( data, fname ) function help_deltaref -disp([' DELTAREF - calculate difference to reference condition',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.deltaref( s, field, val );',char(10),'',char(10),' field : field number or name of reference paremeter',char(10),' val : value of reference condition',char(10),'']); +disp([' DELTAREF - calculate difference to reference condition',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.deltaref( s, field, val );',char(10),'',char(10),' field : field number or name of reference paremeter',char(10),' val : value of reference condition',char(10),'']); function s = sd_eval( s, fun, varargin ) @@ -509,7 +511,7 @@ function sd_csv( data, fname ) function help_eval -disp([' EVAL - uation of parameterized data',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.eval( s, fun, varargin );',char(10),'',char(10),'',char(10),' Syntax:',char(10),'',char(10),' sOut = sd.eval( sIn, fun, ... )',char(10),'',char(10),' sIn : structure containing a ''values'' field (cell array, each',char(10),' entry defines valid values in one dimension)',char(10),' fun : evaluation function with one or more return values,',char(10),' one input argument for each dimension, plus an optional',char(10),' input argument for additional paramters ''par''.',char(10),'',char(10),' Optional param-value pairs:',char(10),'',char(10),' display : flag to show progress information',char(10),' nrep : number of re-evaluations, in case that ''fun'' describes a',char(10),' stochastic process',char(10),' brand : bool: randomize trials',char(10),' cell array of integer: keep given columns ordered',char(10),' structarg: pass parameter values as structure to eval function',char(10),' param : additional parameters for ''fun''',char(10),'',char(10),' Author: Giso Grimm',char(10),' Date: 11/2006',char(10),'']); +disp([' EVAL - uation of parameterized data',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.eval( s, fun, varargin );',char(10),'',char(10),' ',char(10),' Syntax:',char(10),' ',char(10),' sOut = sd.eval( sIn, fun, ... )',char(10),' ',char(10),' sIn : structure containing a ''values'' field (cell array, each',char(10),' entry defines valid values in one dimension)',char(10),' fun : evaluation function with one or more return values,',char(10),' one input argument for each dimension, plus an optional',char(10),' input argument for additional paramters ''par''.',char(10),' ',char(10),' Optional param-value pairs:',char(10),' ',char(10),' display : flag to show progress information',char(10),' nrep : number of re-evaluations, in case that ''fun'' describes a',char(10),' stochastic process',char(10),' brand : bool: randomize trials',char(10),' cell array of integer: keep given columns ordered',char(10),' structarg: pass parameter values as structure to eval function',char(10),' param : additional parameters for ''fun''',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 11/2006',char(10),'']); function sData = sd_eval2( sData, fun, varargin ) @@ -672,7 +674,7 @@ function sd_csv( data, fname ) function help_eval2 -disp([' EVAL2 - evaluation of parameterized data',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' sData = sd.eval2( sData, fun, varargin );',char(10),'',char(10),'',char(10),' sData : structure containing a ''values'' field (cell array, each',char(10),' entry defines valid values in one dimension)',char(10),' fun : evaluation function handle, of type',char(10),' mOut = fun( sPar ) or ',char(10),' mOut = fun( sPar, extra )',char(10),' sPar is a structure with evaluation parameters',char(10),' ''extra'' is the optional extra function parameter (see below)',char(10),'',char(10),' Optional param-value pairs:',char(10),'',char(10),' display : flag to show progress information',char(10),' nrep : number of re-evaluations, in case that ''fun'' describes a',char(10),' stochastic process',char(10),' brand : bool: randomize trials',char(10),' cell array of integer: keep given columns ordered',char(10),' param : extra parameters for ''fun''',char(10),'',char(10),' Author: Giso Grimm',char(10),' Date: 11/2006, 3/2014',char(10),'']); +disp([' EVAL2 - evaluation of parameterized data',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' sData = sd.eval2( sData, fun, varargin );',char(10),'',char(10),' ',char(10),' sData : structure containing a ''values'' field (cell array, each',char(10),' entry defines valid values in one dimension)',char(10),' fun : evaluation function handle, of type',char(10),' mOut = fun( sPar ) or ',char(10),' mOut = fun( sPar, extra )',char(10),' sPar is a structure with evaluation parameters',char(10),' ''extra'' is the optional extra function parameter (see below)',char(10),' ',char(10),' Optional param-value pairs:',char(10),' ',char(10),' display : flag to show progress information',char(10),' nrep : number of re-evaluations, in case that ''fun'' describes a',char(10),' stochastic process',char(10),' brand : bool: randomize trials',char(10),' cell array of integer: keep given columns ordered',char(10),' param : extra parameters for ''fun''',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 11/2006, 3/2014',char(10),'']); function s = sd_fun( s, kCol, fun ) @@ -702,7 +704,7 @@ function sd_csv( data, fname ) s.data(:,end+1) = vRes; function help_fun -disp([' FUN - apply a ction to a data column and add result as new',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.fun( s, kCol, fun );',char(10),'',char(10),' column',char(10),'',char(10),' Usage:',char(10),' s = sdlib.fun( s, kCol, fun )',char(10),'',char(10),' s: struct data',char(10),' kCol: column number or name',char(10),' fun: function handle (must return numeric value)',char(10),'',char(10),' Author: Giso Grimm, 9/2010',char(10),'']); +disp([' FUN - apply a ction to a data column and add result as new',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.fun( s, kCol, fun );',char(10),'',char(10),' column',char(10),' ',char(10),' Usage:',char(10),' s = sdlib.fun( s, kCol, fun )',char(10),' ',char(10),' s: struct data',char(10),' kCol: column number or name',char(10),' fun: function handle (must return numeric value)',char(10),' ',char(10),' Author: Giso Grimm, 9/2010',char(10),'']); function vDim = sd_getdim( s ) @@ -721,7 +723,7 @@ function sd_csv( data, fname ) function help_getdim -disp([' GETDIM - STRUCT_DATA_return dimension of data',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' vDim = sd.getdim( s );',char(10),'',char(10),'',char(10),' Usage:',char(10),' vDim = sd_getdim( s )',char(10),'',char(10),' Author: Giso Grimm',char(10),' Date: 11/2008',char(10),'']); +disp([' GETDIM - STRUCT_DATA_return dimension of data',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' vDim = sd.getdim( s );',char(10),'',char(10),' ',char(10),' Usage:',char(10),' vDim = sd_getdim( s )',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 11/2008',char(10),'']); function [v,field] = sd_getfield( s, field ) @@ -741,7 +743,7 @@ function sd_csv( data, fname ) function help_getfield -disp([' GETFIELD - return name and number of a field',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [v,field] = sd.getfield( s, field );',char(10),'',char(10),' field : field number or field name',char(10),'',char(10),' Returns field name "v" and field number "field".',char(10),'']); +disp([' GETFIELD - return name and number of a field',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [v,field] = sd.getfield( s, field );',char(10),'',char(10),' field : field number or field name',char(10),' ',char(10),' Returns field name "v" and field number "field".',char(10),'']); function v = sd_getvalues( s, field ) @@ -755,7 +757,7 @@ function sd_csv( data, fname ) function help_getvalues -disp([' GETVALUES - return value set of a given parameter field',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' v = sd.getvalues( s, field );',char(10),'',char(10),' field : parameter field number or field name',char(10),'']); +disp([' GETVALUES - return value set of a given parameter field',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' v = sd.getvalues( s, field );',char(10),'',char(10),' field : parameter field number or field name',char(10),'']); function sd_latex_tabular( s, format ) @@ -813,7 +815,7 @@ function sd_latex_tabular( s, format ) disp('\end{tabular}'); function help_latex_tabular -disp([' LATEX_TABULAR - sd_ - print structured data as LaTeX',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' sd.latex_tabular( s, format );',char(10),'',char(10),' tabular ',char(10),'',char(10),' Usage:',char(10),' sd_latex_tabular( s [, format ] )',char(10),'',char(10),' s : structured data',char(10),' format: optional format string (default: %g)',char(10),'',char(10),' Author: Giso Grimm',char(10),' Date: 12/2008',char(10),'']); +disp([' LATEX_TABULAR - sd_ - print structured data as LaTeX',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' sd.latex_tabular( s, format );',char(10),'',char(10),' tabular ',char(10),' ',char(10),' Usage:',char(10),' sd_latex_tabular( s [, format ] )',char(10),' ',char(10),' s : structured data',char(10),' format: optional format string (default: %g)',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 12/2008',char(10),'']); function s = sd_merge( s, varargin ) @@ -846,7 +848,7 @@ function sd_latex_tabular( s, format ) s = sd_compactval( s ); function help_merge -disp([' MERGE - multiple data structures into one',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.merge( s, varargin );',char(10),'',char(10),'',char(10),' s : data structure',char(10),' varargin : one or more other data structures',char(10),'',char(10),' requires field names and parameter types to be equal for all',char(10),' structures.',char(10),'']); +disp([' MERGE - multiple data structures into one',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.merge( s, varargin );',char(10),'',char(10),' ',char(10),' s : data structure',char(10),' varargin : one or more other data structures',char(10),' ',char(10),' requires field names and parameter types to be equal for all',char(10),' structures.',char(10),'']); function s = sd_merge_addpar( s, varargin ) @@ -884,7 +886,7 @@ function sd_latex_tabular( s, format ) function help_merge_addpar -disp([' MERGE_ADDPAR - merge multiple data structures into one',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.merge_addpar( s, varargin );',char(10),'',char(10),'',char(10),' s : data structure',char(10),' varargin : one or more other data structures',char(10),'',char(10),' requires field names and parameter types to be equal for all',char(10),' structures.',char(10),'']); +disp([' MERGE_ADDPAR - merge multiple data structures into one',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.merge_addpar( s, varargin );',char(10),'',char(10),' ',char(10),' s : data structure',char(10),' varargin : one or more other data structures',char(10),' ',char(10),' requires field names and parameter types to be equal for all',char(10),' structures.',char(10),'']); function s = sd_mergepar( s, vCol ) @@ -929,7 +931,7 @@ function sd_latex_tabular( s, format ) s = sd_compactval( s ); function help_mergepar -disp([' MERGEPAR - merge multiple parameter columns',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.mergepar( s, vCol );',char(10),'',char(10),'',char(10),' s : struct data',char(10),' vCol : number of clumns or cell array of column names',char(10),'']); +disp([' MERGEPAR - merge multiple parameter columns',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.mergepar( s, vCol );',char(10),'',char(10),' ',char(10),' s : struct data',char(10),' vCol : number of clumns or cell array of column names',char(10),'']); function s = sd_par2col( s, kPar ) @@ -1020,7 +1022,7 @@ function sd_latex_tabular( s, format ) s.data = [par_out,data_out]; function help_par2col -disp([' PAR2COL - convert a parameter field into additional data columns',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.par2col( s, kPar );',char(10),'',char(10),' s : data structure',char(10),' kPar : number or name of parameter column',char(10),'',char(10),'Example:',char(10),' sIn.fields = {''p'',''numeric'',''a'',''b''};',char(10),' sIn.values = {{''X'',''Y''},[1:3]};',char(10),' sIn.data = [...',char(10),' 1, 1, 0.81, 0.27;...',char(10),' 2, 1, 0.90, 0.54;...',char(10),' 1, 2, 0.12, 0.95;...',char(10),' 2, 2, 0.91, 0.96;...',char(10),' 1, 3, 0.63, 0.15;...',char(10),' 2, 3, 0.09, 0.97;...',char(10),' ];',char(10),' sOut = sd.par2col( sIn, ''numeric'' );',char(10),'',char(10),'Note:',char(10),' This operation can not be inverted by col2par. The fields ',char(10),' need to be reordered first.',char(10),'',char(10),'Author: ',char(10),' Giso Grimm, 12/2008',char(10),'']); +disp([' PAR2COL - convert a parameter field into additional data columns',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.par2col( s, kPar );',char(10),'',char(10),' s : data structure',char(10),' kPar : number or name of parameter column',char(10),' ',char(10),' Example:',char(10),' sIn.fields = {''p'',''numeric'',''a'',''b''};',char(10),' sIn.values = {{''X'',''Y''},[1:3]};',char(10),' sIn.data = [...',char(10),' 1, 1, 0.81, 0.27;...',char(10),' 2, 1, 0.90, 0.54;...',char(10),' 1, 2, 0.12, 0.95;...',char(10),' 2, 2, 0.91, 0.96;...',char(10),' 1, 3, 0.63, 0.15;...',char(10),' 2, 3, 0.09, 0.97;...',char(10),' ];',char(10),' sOut = sd.par2col( sIn, ''numeric'' );',char(10),' ',char(10),' Note:',char(10),' This operation can not be inverted by col2par. The fields ',char(10),' need to be reordered first.',char(10),' ',char(10),' Author: ',char(10),' Giso Grimm, 12/2008',char(10),'']); function s = sd_permval( s, field, vPerm ) @@ -1043,7 +1045,7 @@ function sd_latex_tabular( s, format ) function help_permval -disp([' PERMVAL - permute value order in a given column',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.permval( s, field, vPerm );',char(10),'',char(10),'',char(10),' s: struct data',char(10),' field: name or number of column',char(10),' vPerm: permutation vector',char(10),'',char(10),' Author: Giso Grimm 10/2010',char(10),'']); +disp([' PERMVAL - permute value order in a given column',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.permval( s, field, vPerm );',char(10),'',char(10),' ',char(10),' s: struct data',char(10),' field: name or number of column',char(10),' vPerm: permutation vector',char(10),' ',char(10),' Author: Giso Grimm 10/2010',char(10),'']); function [fh, fname, ph] = sd_plot( sData, xidx, yidx, sPlotPars, varargin ) @@ -1157,7 +1159,8 @@ function sd_latex_tabular( s, format ) csLeg = any2cell( csLeg ); h = legend(csLeg,'Interpreter','none','Location','NorthEast'); set(get(h,'title'),'string',par_name,'interpreter','none',... - 'fontsize',sPlotPars.fontsize,'fontweight','bold'); + 'fontsize',sPlotPars.fontsize,'fontweight','bold',... + 'position',[0.5 1.02 1]); set(h,'Position',[0.73 0.05 0.19 0.12]); set(h,'Position',[0.73 0.05 0.19 0.12]); set(findobj(h,'type','line'),'linewidth',0.8); @@ -1327,7 +1330,7 @@ function sd_latex_tabular( s, format ) function help_plot -disp([' PLOT - data from data structures',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [fh, fname, ph] = sd.plot( sData, xidx, yidx, sPlotPars, varargin );',char(10),'',char(10),'',char(10),' Mandatory paramaters:',char(10),' sData : Data structure with fields ''fields'', ''values'' and ''data''',char(10),' (additional fields are ignored)',char(10),' xidx : column number of X-data',char(10),' yidx : column number of Y-data',char(10),'',char(10),' Optional parameters:',char(10),' sPlotPars : plot parameter structure, with optional fields:',char(10),' - fontsize (14)',char(10),' - markersize (fontsize/1.8)',char(10),' - linewidth (1)',char(10),' - gridfreq (1)',char(10),' - firstgrid (1)',char(10),' - colors ({''b'',''g'',''r'',''k'',''m'',''c'',''y''})',char(10),' - linestyles ({''-'',''--'',''-.'','':'',''-'',''-'',''-''})',char(10),' - markers ({''d'',''x'',''*'',''o'',''+'',''*'',''d''})',char(10),' - errorscale (1)',char(10),' - xshift (0)',char(10),'',char(10),' Possible optional parameters (tag-value-pairs) :',char(10),' ''average'' : ''mean'' or ''median'' (mean)',char(10),' ''restrictions'': list of data restrictions',char(10),' ''parameter'' : column number of parameter field',char(10),' ''xorder'' : re-ordering index vector',char(10),' ''errorscale'' : error scale (1)',char(10),' ',char(10),' Return values:',char(10),' fh : figure handle',char(10),' fname : Unique file name containing information on data selection ',char(10),'',char(10),' Author: Giso Grimm',char(10),' Date: 11/2006',char(10),' Modified: 6/2007',char(10),'']); +disp([' PLOT - data from data structures',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [fh, fname, ph] = sd.plot( sData, xidx, yidx, sPlotPars, varargin );',char(10),'',char(10),' ',char(10),' Mandatory paramaters:',char(10),' sData : Data structure with fields ''fields'', ''values'' and ''data''',char(10),' (additional fields are ignored)',char(10),' xidx : column number of X-data',char(10),' yidx : column number of Y-data',char(10),' ',char(10),' Optional parameters:',char(10),' sPlotPars : plot parameter structure, with optional fields:',char(10),' - fontsize (14)',char(10),' - markersize (fontsize/1.8)',char(10),' - linewidth (1)',char(10),' - gridfreq (1)',char(10),' - firstgrid (1)',char(10),' - colors ({''b'',''g'',''r'',''k'',''m'',''c'',''y''})',char(10),' - linestyles ({''-'',''--'',''-.'','':'',''-'',''-'',''-''})',char(10),' - markers ({''d'',''x'',''*'',''o'',''+'',''*'',''d''})',char(10),' - errorscale (1)',char(10),' - xshift (0)',char(10),' ',char(10),' Possible optional parameters (tag-value-pairs) :',char(10),' ''average'' : ''mean'' or ''median'' (mean)',char(10),' ''restrictions'': list of data restrictions',char(10),' ''parameter'' : column number of parameter field',char(10),' ''xorder'' : re-ordering index vector',char(10),' ''errorscale'' : error scale (1)',char(10),' ',char(10),' Return values:',char(10),' fh : figure handle',char(10),' fname : Unique file name containing information on data selection ',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 11/2006',char(10),' Modified: 6/2007',char(10),'']); function [ph,mN] = sd_plot_histo( s, c_par, c_val, nBins ) @@ -1382,15 +1385,14 @@ function sd_latex_tabular( s, format ) function help_plot_histo -disp([' PLOT_HISTO - PLOT_STRUCT_DATA_VAL_HISTO - Histogram plotting function',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [ph,mN] = sd.plot_histo( s, c_par, c_val, nBins );',char(10),'',char(10),'',char(10),' Usage:',char(10),' plot_sd_val_histo( s, c_par, c_val )',char(10),'',char(10),' s : data structure',char(10),' c_par : parameter column',char(10),' c_val : data column',char(10),'',char(10),' Author: Giso Grimm, 2007',char(10),'']); +disp([' PLOT_HISTO - PLOT_STRUCT_DATA_VAL_HISTO - Histogram plotting function',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [ph,mN] = sd.plot_histo( s, c_par, c_val, nBins );',char(10),'',char(10),' ',char(10),' Usage:',char(10),' plot_sd_val_histo( s, c_par, c_val )',char(10),' ',char(10),' s : data structure',char(10),' c_par : parameter column',char(10),' c_val : data column',char(10),' ',char(10),' Author: Giso Grimm, 2007',char(10),'']); -function s = sd_plotwizzard( s ) -% PLOT_STRUCT_DATA_WIZZARD - Interactively create data plots based -% on structured data input. +function s = sd_plotwizard( s ) +% Interactively create data plots based on structured data input. % % Usage: -% s = plot_sd_wizzard( s ) +% s = sd_plotwizard( s ) % % s : structure containing the data % @@ -1855,8 +1857,479 @@ function set_all_datapars( sPars, fh ) set(h_fields,'String',s.fields(idx_xavail),'Value',length(idx_xavail)); +function help_plotwizard +disp([' PLOTWIZARD - Interactively create data plots based on structured data input.',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.plotwizard( s );',char(10),'',char(10),' ',char(10),' Usage:',char(10),' s = sd_plotwizard( s )',char(10),' ',char(10),' s : structure containing the data',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 11/2006',char(10),'']); + + +function s = sd_plotwizzard( s ) +% PLOT_STRUCT_DATA_WIZZARD - Interactively create data plots based +% on structured data input. +% +% Usage: +% s = plot_sd_wizzard( s ) +% +% s : structure containing the data +% +% Author: Giso Grimm +% Date: 11/2006 + fh = figure('NumberTitle','off','MenuBar','none',... + 'Position',[250 400 740 620],... + 'Name','Structured data plotting wizzard'); + set(fh,'UserData',s); + %movegui(fh,'center'); + uicontrol('style','text','fontweight','bold',... + 'string','Y fields',... + 'position',[40 580 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','Y axis',... + 'position',[270 580 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','Available fields',... + 'position',[40 460 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','X axis',... + 'position',[270 460 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','Average model',... + 'position',[500 580 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','Parameter',... + 'position',[270 370 180 20]); + uicontrol('style','text','fontweight','bold',... + 'string','Restrictions',... + 'position',[270 280 180 20]); + uicontrol('style','listbox','string',s.fields(length(s.values)+1:end),... + 'Position',[40 500 180 80],'tag','avail_fields_y',... + 'value',1); + uicontrol('style','listbox','string',s.fields(1:length(s.values)),... + 'Position',[40 110 180 350],'tag','avail_fields',... + 'value',1); + uicontrol('style','listbox','string',{},... + 'Position',[270 500 180 80],'tag','ydata_selection'); + uicontrol('style','text','string','',... + 'Position',[270 410 180 50],'tag','xdata_selection',... + 'HorizontalAlignment','left'); + uicontrol('style','text','string','',... + 'Position',[270 320 180 50],'tag','pdata_selection',... + 'HorizontalAlignment','left'); + uicontrol('style','listbox','string',{},... + 'Position',[270 200 180 80],'tag','restrictions',... + 'Callback',@zz_select_restriction); + uicontrol('style','listbox','string',{},... + 'Position',[270 110 180 80],'tag','restriction_vals',... + 'Callback',@zz_select_restriction_val); + uicontrol('style','listbox','string',{'mean','median'},... + 'Position',[500 500 180 80],'tag','average_model'); + uicontrol('style','PushButton','string','>>',... + 'Position',[230 560 30 20],'callback',@zz_add_y_data); + uicontrol('style','PushButton','string','<<',... + 'Position',[230 530 30 20],'callback',@zz_rm_y_data); + uicontrol('style','PushButton','string','>>',... + 'Position',[230 440 30 20],'callback',@zz_add_x_data); + uicontrol('style','PushButton','string','<<',... + 'Position',[230 410 30 20],'callback',@zz_rm_x_data); + uicontrol('style','PushButton','string','>>',... + 'Position',[230 350 30 20],'callback',@zz_add_p_data); + uicontrol('style','PushButton','string','<<',... + 'Position',[230 320 30 20],'callback',@zz_rm_p_data); + uicontrol('style','PushButton','string','>>',... + 'Position',[230 260 30 20],'callback',@zz_add_restriction); + uicontrol('style','PushButton','string','<<',... + 'Position',[230 230 30 20],'callback',@zz_rm_restriction); + uicontrol('style','PushButton','string','plot',... + 'Position',[40 40 130 40],'callback',@zz_plot_this); + uicontrol('style','PushButton','string','quit',... + 'Position',[240 40 130 40],'callback','uiresume(gcf)'); + %% plot style controls: + uicontrol('style','frame','position',[470 100 260 280],... + 'tag','plotpar'); + zz_create_plotpar_ui( 'fontsize', 1, @zz_validate_positive, 14 ); + zz_create_plotpar_ui( 'markersize', 2, @zz_validate_positive, 3 ); + zz_create_plotpar_ui( 'linewidth', 3, @zz_validate_positive, 1 ); + zz_create_plotpar_ui( 'linestyles', 4, @zz_validate_string_or_cellarray, '''''' ); + zz_create_plotpar_ui( 'gridfreq', 5, @zz_validate_positive, 1 ); + zz_create_plotpar_ui( 'firstgrid', 6, @zz_validate_positive, 1 ); + zz_create_plotpar_ui( 'colors', 7, @zz_validate_string_or_cellarray, '''''' ); + zz_create_plotpar_ui( 'markers', 8, @zz_validate_string_or_cellarray, '''''' ); + if isfield( s, 'datapars' ) + zz_set_all_datapars( s.datapars, fh ); + end + if isfield( s, 'plotpars' ) + zz_set_all_plotpar_svals( s.plotpars, fh ); + end + uiwait(fh); + if ishandle(fh) + s.datapars = zz_get_data_pars( fh ); + plotpars = struct; + for fn={'fontsize','markersize','linewidth','linestyles', ... + 'gridfreq','firstgrid','colors','markers'} + name = fn{:}; + plotpars = zz_get_plotpar_sval( plotpars, name, fh ); + end + s.plotpars = plotpars; + close(fh); + end + if nargout == 0 + clear s + end + +function zz_add_y_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields_y'); + h_ydata = findobj(gcf,'tag','ydata_selection'); + csfields = get(h_fields,'string'); + if length(csfields) + val = get(h_fields,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_fields,'value',length(csfields),'string',csfields); + csfields = get(h_ydata,'string'); + csfields{end+1} = field; + set(h_ydata,'string',csfields,'value',length(csfields)); + end + +function zz_rm_y_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields_y'); + h_ydata = findobj(gcf,'tag','ydata_selection'); + csfields = get(h_ydata,'string'); + if length(csfields) + val = get(h_ydata,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_ydata,'value',length(csfields),'string',csfields); + csfields = get(h_fields,'string'); + csfields{end+1} = field; + set(h_fields,'string',csfields,'value',length(csfields)); + end + +function zz_add_x_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_xdata = findobj(gcf,'tag','xdata_selection'); + csfields = get(h_fields,'string'); + if length(csfields) + if length(get(h_xdata,'String')) == 0 + val = get(h_fields,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_fields,'value',length(csfields),'string',csfields); + set(h_xdata,'string',field); + end + end + +function zz_rm_x_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_xdata = findobj(gcf,'tag','xdata_selection'); + field = get(h_xdata,'string'); + if length(field) + set(h_xdata,'string',''); + csfields = get(h_fields,'string'); + csfields{end+1} = field; + set(h_fields,'string',csfields,'value',length(csfields)); + end + +function zz_add_p_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_xdata = findobj(gcf,'tag','pdata_selection'); + csfields = get(h_fields,'string'); + if length(csfields) + if length(get(h_xdata,'String')) == 0 + val = get(h_fields,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_fields,'value',length(csfields),'string',csfields); + set(h_xdata,'string',field); + end + end + +function zz_rm_p_data(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_xdata = findobj(gcf,'tag','pdata_selection'); + field = get(h_xdata,'string'); + if length(field) + set(h_xdata,'string',''); + csfields = get(h_fields,'string'); + csfields{end+1} = field; + set(h_fields,'string',csfields,'value',length(csfields)); + end + +function zz_add_restriction(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_ydata = findobj(gcf,'tag','restrictions'); + csfields = get(h_fields,'string'); + if length(csfields) + val = get(h_fields,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_fields,'value',min(1,length(csfields)),'string',csfields); + csfields = get(h_ydata,'string'); + csfields{end+1} = field; + set(h_ydata,'string',csfields,'value',length(csfields)); + zz_select_restriction; + end + +function zz_rm_restriction(varargin) + h_fields = findobj(gcf,'tag','avail_fields'); + h_ydata = findobj(gcf,'tag','restrictions'); + csfields = get(h_ydata,'string'); + if length(csfields) + val = get(h_ydata,'value'); + field = csfields{val}; + csfields(val) = []; + set(h_ydata,'value',min(1,length(csfields)),'string',csfields); + csfields = get(h_fields,'string'); + csfields{end+1} = field; + set(h_fields,'string',csfields,'value',length(csfields)); + zz_select_restriction; + k = zz_get_restriction_idx( field ); + s = get(gcf,'UserData'); + s.restrictions(k) = []; + set(gcf,'UserData',s); + end + +function k = zz_get_restriction_idx( sRes ) + s = get(gcf,'UserData'); + kRes = strmatch(sRes,s.fields,'exact'); + if ~isfield(s,'restrictions') + s.restrictions = {}; + end + k = 0; + for kResL=1:length(s.restrictions) + cRes = s.restrictions{kResL}; + if cRes{1} == kRes + k = kResL; + end + end + if ~k + s.restrictions{end+1} = {kRes,1}; + k = length(s.restrictions); + end + set(gcf,'UserData',s); + return + +function zz_select_restriction(varargin) + h_restr = findobj(gcf,'tag','restrictions'); + h_rval = findobj(gcf,'tag','restriction_vals'); + csRes = get(h_restr,'String'); + if length(csRes) + kRes = get(h_restr,'Value'); + kRes = zz_get_restriction_idx( csRes{kRes} ); + s = get(gcf,'UserData'); + name_res = s.values{s.restrictions{kRes}{1}}; + if isnumeric(name_res) + temp_res = name_res; + name_res = {}; + for k=1:numel(temp_res) + name_res{k} = num2str(temp_res(k)); + end + end + set(h_rval,'String',name_res,... + 'Value',s.restrictions{kRes}{2}); + else + set(h_rval,'String',{},'Value',0); + end + +function zz_select_restriction_val(varargin) + h_restr = findobj(gcf,'tag','restrictions'); + h_rval = findobj(gcf,'tag','restriction_vals'); + csRes = get(h_restr,'String'); + kRes = get(h_restr,'Value'); + kRes = zz_get_restriction_idx( csRes{kRes} ); + s = get(gcf,'UserData'); + val = get(h_rval,'Value'); + s.restrictions{kRes}{2} = val; + set(gcf,'UserData',s); + +function sDataPar = zz_get_data_pars( fh ) + sDataPar = struct; + s = get(fh,'UserData'); + if ~isfield(s,'restrictions') + s.restrictions = {}; + end + h_ydata = findobj(fh,'tag','ydata_selection'); + h_xdata = findobj(fh,'tag','xdata_selection'); + h_pdata = findobj(fh,'tag','pdata_selection'); + ydata_name = get(h_ydata,'String'); + xdata_name = get(h_xdata,'String'); + pdata_name = get(h_pdata,'String'); + k_ydata = []; + for sYdata=ydata_name' + k_ydata(end+1) = strmatch(sYdata{:},s.fields,'exact'); + end + k_xdata = strmatch(xdata_name,s.fields,'exact'); + k_pdata = strmatch(pdata_name,s.fields,'exact'); + sDataPar.y_data = k_ydata; + sDataPar.x_data = k_xdata; + sDataPar.par_data = k_pdata; + sDataPar.restrictions = s.restrictions; + +function zz_plot_this(varargin) + sDataPar = zz_get_data_pars( gcf ) + s = get(gcf,'UserData'); + if ~isfield(s,'restrictions') + s.restrictions = {}; + end + h_ydata = findobj(gcf,'tag','ydata_selection'); + h_xdata = findobj(gcf,'tag','xdata_selection'); + h_pdata = findobj(gcf,'tag','pdata_selection'); + ydata_name = get(h_ydata,'String'); + xdata_name = get(h_xdata,'String'); + pdata_name = get(h_pdata,'String'); + k_ydata = []; + for sYdata=ydata_name' + k_ydata(end+1) = strmatch(sYdata{:},s.fields,'exact'); + end + k_xdata = strmatch(xdata_name,s.fields,'exact'); + k_pdata = strmatch(pdata_name,s.fields,'exact'); + s_ydata = sprintf('%d ',k_ydata); + s_ydata = s_ydata(1:end-1); + sPlotPars = zz_get_plot_params( gcf ) + s_restr = ''; + for k=1:length(s.restrictions) + s_restr = sprintf('%s{%d,%d},',s_restr,s.restrictions{k}{1},s.restrictions{k}{2}); + end + s_restr = s_restr(1:end-1); + %pcmd = sprintf('plot_struct_data(s,%d,[%s],%d,{%s})',... + % k_xdata,s_ydata, ... + % k_pdata,... + % s_restr); + % disp(pcmd); + h_avg = findobj(gcf,'tag','average_model'); + avg_model = get(h_avg,'String'); + avg_model = avg_model{get(h_avg,'Value')}; + try + sd_plot(s,k_xdata,k_ydata,sPlotPars,... + 'parameter',k_pdata,... + 'restrictions',s.restrictions,... + 'average',avg_model); + catch + err = lasterror; + disp(err.message); + for k=1:length(err.stack) + disp(sprintf('%s:%d %s\n',... + err.stack(k).file,... + err.stack(k).line,... + err.stack(k).name)); + end + rethrow(err); + end + +function sPlotPar = zz_get_plot_params( fh ) + sPlotPar = struct; + sPlotPar = zz_set_plotpar_field( sPlotPar, 'fontsize', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'markersize', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'linewidth', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'gridfreq', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'firstgrid', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'colors', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'linestyles', fh ); + sPlotPar = zz_set_plotpar_field( sPlotPar, 'markers', fh ); + +function sPlotParS = zz_get_plotpar_sval( sPlotParS, name, fh ) + h = findall(fh,'tag',sprintf('plotpar_%s',name)); + sPlotParS.(name) = get(h,'String'); + +function zz_set_all_plotpar_svals( sPlotParS, fh ) + for fn = fieldnames(sPlotParS)' + name = fn{:}; + h = findall(fh,'tag',sprintf('plotpar_%s',name)); + set(h,'String',sPlotParS.(name)); + end + +function sPlotPar = zz_set_plotpar_field( sPlotPar, name, fh ) + h = findall(fh,'tag',sprintf('plotpar_%s',name)); + s = get(h,'String'); + if ~isempty(s) + eval(sprintf('val=%s;',s)); + if ~isempty(val) + sPlotPar.(name) = val; + end + end + + +function err = zz_validate_string_or_cellarray( bo, varargin ) + s = get(bo, 'String' ); + if isempty( s ) + s = ''''''; + elseif strcmp([s(1) s(end)],'''''') + s = s; + elseif strcmp([s(1) s(end)],'{}') + s = s; + else + s = ['''' s '''']; + end + set(bo, 'String', s ); + +function err = zz_validate_positive( bo, varargin ) + s = get(bo, 'String' ); + v_def = get(bo,'UserData'); + try + val = str2num(s); + if isempty(val) + val = v_def; + end + catch + val = v_def; + end + if val < eps + val = eps; + end + s = num2str(val); + set(bo, 'String', s ); + +function zz_create_plotpar_ui( name, idx, validator, default ) + pos = get(findall(gcf,'tag','plotpar'),'Position'); + x = pos(1)+10; + y = pos(2)+pos(4)-10; + uicontrol('style','text','string',name,... + 'position',[x y-25*idx-2 85 20],... + 'fontweight','bold','horizontalalignment','right'); + h = uicontrol('tag',sprintf('plotpar_%s',name),... + 'style','edit',... + 'position',[x+90 y-25*idx 150 20],... + 'callback',validator,... + 'userdata',default,... + 'BackgroundColor',[1 1 1]); + validator(h); + +function zz_set_all_datapars( sPars, fh ) + s = get(fh,'UserData'); + % y axis: + idx_yavail = 1:length(s.fields); + idx_yavail(sPars.y_data) = []; + idx_yavail(find(idx_yavail<=length(s.values))) = []; + h_fields = findobj(fh,'tag','avail_fields_y'); + h_ydata = findobj(fh,'tag','ydata_selection'); + set(h_fields,'String',s.fields(idx_yavail),'Value',length(idx_yavail)); + set(h_ydata,'String',s.fields(sPars.y_data)); + % x axis: + h_xdata = findobj(fh,'tag','xdata_selection'); + set(h_xdata,'String',s.fields(sPars.x_data)); + % p axis: + h_pdata = findobj(fh,'tag','pdata_selection'); + set(h_pdata,'String',s.fields(sPars.par_data)); + % restrictions: + s.restrictions = sPars.restrictions; + h_rdata = findobj(fh,'tag','restrictions'); + vRestr = []; + for k=1:length(s.restrictions) + vRestr(end+1) = s.restrictions{k}{1}; + end + set(h_rdata,'String',s.fields(vRestr)); + set(fh,'UserData',s); + zz_select_restriction; + % remove used fields: + idx_xavail = 1:length(s.fields); + idx_xavail(find(idx_xavail==sPars.x_data)) = []; + idx_xavail(find(idx_xavail==sPars.par_data)) = []; + for k=1:length(vRestr) + idx_xavail(find(idx_xavail==vRestr(k))) = []; + end + idx_xavail(find(idx_xavail>length(s.values))) = []; + h_fields = findobj(fh,'tag','avail_fields'); + set(h_fields,'String',s.fields(idx_xavail),'Value',length(idx_xavail)); + + function help_plotwizzard -disp([' PLOTWIZZARD - PLOT_STRUCT_DATA_WIZZARD - Interactively create data plots based',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.plotwizzard( s );',char(10),'',char(10),' on structured data input.',char(10),'',char(10),' Usage:',char(10),' s = plot_sd_wizzard( s )',char(10),'',char(10),' s : structure containing the data',char(10),'',char(10),' Author: Giso Grimm',char(10),' Date: 11/2006',char(10),'']); +disp([' PLOTWIZZARD - PLOT_STRUCT_DATA_WIZZARD - Interactively create data plots based',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.plotwizzard( s );',char(10),'',char(10),' on structured data input.',char(10),' ',char(10),' Usage:',char(10),' s = plot_sd_wizzard( s )',char(10),' ',char(10),' s : structure containing the data',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 11/2006',char(10),'']); function [COMPARISON,MEANS,H,GNAMES] = sd_posthoc( sData, DataCol,vParCol) @@ -1904,7 +2377,7 @@ function set_all_datapars( sPars, fh ) s = sd_compactval( s ); function help_readxls -disp([' READXLS - STRUCT_DATA_read an xls spreadsheet and convert to structdata',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.readxls( fname, labelrow, datarows, parcols, datacols );',char(10),'',char(10),'',char(10),' Usage:',char(10),' s = sd_readxls( fname, labelrow, datarows, parcols, datacols )',char(10),'',char(10),' Author: Giso Grimm, 9/2010',char(10),'']); +disp([' READXLS - STRUCT_DATA_read an xls spreadsheet and convert to structdata',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.readxls( fname, labelrow, datarows, parcols, datacols );',char(10),'',char(10),' ',char(10),' Usage:',char(10),' s = sd_readxls( fname, labelrow, datarows, parcols, datacols )',char(10),' ',char(10),' Author: Giso Grimm, 9/2010',char(10),'']); function s = sd_renamefield( s, field, newname ) @@ -1919,7 +2392,7 @@ function set_all_datapars( sPars, fh ) s.fields{field} = newname; function help_renamefield -disp([' RENAMEFIELD - rename a data field',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.renamefield( s, field, newname );',char(10),'',char(10),'',char(10),' s : data structure',char(10),' field : field name or number',char(10),' newname : new name',char(10),'']); +disp([' RENAMEFIELD - rename a data field',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.renamefield( s, field, newname );',char(10),'',char(10),' ',char(10),' s : data structure',char(10),' field : field name or number',char(10),' newname : new name',char(10),'']); function [s,vOrder] = sd_reorder_datacol( s, vOrder ) @@ -1946,7 +2419,7 @@ function set_all_datapars( sPars, fh ) s.data = s.data(:,vOrder); function help_reorder_datacol -disp([' REORDER_DATACOL - reorder data fields',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [s,vOrder] = sd.reorder_datacol( s, vOrder );',char(10),'',char(10),' s : input data structure',char(10),' vOrder : new order (vector), or new stride (scalar)',char(10),'']); +disp([' REORDER_DATACOL - reorder data fields',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [s,vOrder] = sd.reorder_datacol( s, vOrder );',char(10),'',char(10),' s : input data structure',char(10),' vOrder : new order (vector), or new stride (scalar)',char(10),'']); function s = sd_restrict( s, kField, vValueIdx, bInvert ) @@ -1994,7 +2467,7 @@ function set_all_datapars( sPars, fh ) s.data = s.data(idx,:); function help_restrict -disp([' RESTRICT - select a data subset by applying ions',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.restrict( s, kField, vValueIdx, bInvert );',char(10),'',char(10),'',char(10),' s : data structure',char(10),' kField : array of field number indices, or',char(10),' single field name',char(10),' vValueIdx : array of value indices, or ',char(10),' cell array of arrays of values',char(10),' bInvert : invert restrictions (keep unmatched values)',char(10),'',char(10),' Author: Giso Grimm, 2007',char(10),'']); +disp([' RESTRICT - select a data subset by applying ions',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.restrict( s, kField, vValueIdx, bInvert );',char(10),'',char(10),' ',char(10),' s : data structure',char(10),' kField : array of field number indices, or',char(10),' single field name',char(10),' vValueIdx : array of value indices, or ',char(10),' cell array of arrays of values',char(10),' bInvert : invert restrictions (keep unmatched values)',char(10),' ',char(10),' Author: Giso Grimm, 2007',char(10),'']); function s = sd_result2par( s, kField ) @@ -2031,7 +2504,7 @@ function set_all_datapars( sPars, fh ) function help_result2par -disp([' RESULT2PAR - convert a result column into a parameter column.',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.result2par( s, kField );',char(10),'',char(10),'',char(10),' s : Data structure.',char(10),' kField : Field number or name to be converted.',char(10),'',char(10),' Author: Giso Grimm',char(10),' Date: 12/2008',char(10),'']); +disp([' RESULT2PAR - convert a result column into a parameter column.',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.result2par( s, kField );',char(10),'',char(10),' ',char(10),' s : Data structure.',char(10),' kField : Field number or name to be converted.',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 12/2008',char(10),'']); function s = sd_rmcol( s, vField ) @@ -2057,7 +2530,7 @@ function set_all_datapars( sPars, fh ) s.data(:,vField ) = []; function help_rmcol -disp([' RMCOL - remove columns from a data structure',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.rmcol( s, vField );',char(10),'',char(10),'',char(10),' s : data structure',char(10),' vField : column numbers or column name to delete (can be cell',char(10),' string to delete multiple columns by name)',char(10),'']); +disp([' RMCOL - remove columns from a data structure',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.rmcol( s, vField );',char(10),'',char(10),' ',char(10),' s : data structure',char(10),' vField : column numbers or column name to delete (can be cell',char(10),' string to delete multiple columns by name)',char(10),'']); function s = sd_rminf( s ) @@ -2082,7 +2555,7 @@ function set_all_datapars( sPars, fh ) s.data = s.data(idx,:); function help_rmnan -disp([' RMNAN - remove rows with NaN values in data section',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.rmnan( s );',char(10),'',char(10),'',char(10),' Usage:',char(10),' s = sd.rmnan(s);',char(10),'']); +disp([' RMNAN - remove rows with NaN values in data section',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.rmnan( s );',char(10),'',char(10),' ',char(10),' Usage:',char(10),' s = sd.rmnan(s);',char(10),'']); function [s,csFields,cValues] = sd_squeeze( s ) @@ -2109,7 +2582,7 @@ function set_all_datapars( sPars, fh ) function help_squeeze -disp([' SQUEEZE - remove singleton dimensions (parameter and data fields)',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [s,csFields,cValues] = sd.squeeze( s );',char(10),'',char(10),'',char(10),' Author: Giso Grimm',char(10),' Date: 11/2008',char(10),'']); +disp([' SQUEEZE - remove singleton dimensions (parameter and data fields)',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [s,csFields,cValues] = sd.squeeze( s );',char(10),'',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 11/2008',char(10),'']); function s = sd_strrepval( s, field, pattern, replacement ) @@ -2138,7 +2611,7 @@ function set_all_datapars( sPars, fh ) function help_strrepval -disp([' STRREPVAL - replace strings in value list of one or more fields',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.strrepval( s, field, pattern, replacement );',char(10),'',char(10),'',char(10),' s: struct data',char(10),' field: column numbers or cellstr with field names, or string with',char(10),' field name',char(10),'']); +disp([' STRREPVAL - replace strings in value list of one or more fields',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.strrepval( s, field, pattern, replacement );',char(10),'',char(10),' ',char(10),' s: struct data',char(10),' field: column numbers or cellstr with field names, or string with',char(10),' field name',char(10),'']); function s = sd_struct2parstruct( sIn ) @@ -2164,7 +2637,7 @@ function set_all_datapars( sPars, fh ) end function help_struct2parstruct -disp([' STRUCT2PARSTRUCT - - convert field names of structure into param',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.struct2parstruct( sIn );',char(10),'',char(10),' control structure',char(10),'',char(10),' Usage:',char(10),' s = sd_struct2parstruct( sIn )',char(10),' ',char(10),' Fieldnames of sIn are converted into content of ''s.fields'',',char(10),' contents of sIn are converted into content of ''s.values''.',char(10),'',char(10),' Example:',char(10),' sIn.par1 = {''apple'',''orange'',''lemmon''}',char(10),' sIn.par2 = [1:3]',char(10),' s = sd.struct2parstruct( sIn )',char(10),'',char(10),'']); +disp([' STRUCT2PARSTRUCT - - convert field names of structure into param',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' s = sd.struct2parstruct( sIn );',char(10),'',char(10),' control structure',char(10),' ',char(10),' Usage:',char(10),' s = sd_struct2parstruct( sIn )',char(10),' ',char(10),' Fieldnames of sIn are converted into content of ''s.fields'',',char(10),' contents of sIn are converted into content of ''s.values''.',char(10),' ',char(10),' Example:',char(10),' sIn.par1 = {''apple'',''orange'',''lemmon''}',char(10),' sIn.par2 = [1:3]',char(10),' s = sd.struct2parstruct( sIn )',char(10),'']); function [s1, s2] = sd_uniquepar( s1, kField1, s2, kField2, func ) @@ -2217,6 +2690,6 @@ function set_all_datapars( sPars, fh ) function help_uniquepar -disp([' UNIQUEPAR - sd_unify_param - combine parameter values and use common',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [s1, s2] = sd.uniquepar( s1, kField1, s2, kField2, func );',char(10),'',char(10),' index',char(10),'',char(10),' Usage:',char(10),' [s1, s2] = sd_unify_param( s1, kField1, s2, kField2 [, func ] )',char(10),'',char(10),' s1 : first data structure',char(10),' kField1: field number in first structure to be merged',char(10),' s2 : second data structure',char(10),' kField2: field number in second structure to be merged',char(10),' func : Merge function to be used (optional)',char(10),' Typically ''@intersect'' (default) or ''@union''.',char(10),'',char(10),' Author: Giso Grimm',char(10),' Date: 11/2008',char(10),'']); +disp([' UNIQUEPAR - sd_unify_param - combine parameter values and use common',char(10),'',char(10),' Usage:',char(10),' sd = libsd();',char(10),' [s1, s2] = sd.uniquepar( s1, kField1, s2, kField2, func );',char(10),'',char(10),' index',char(10),' ',char(10),' Usage:',char(10),' [s1, s2] = sd_unify_param( s1, kField1, s2, kField2 [, func ] )',char(10),' ',char(10),' s1 : first data structure',char(10),' kField1: field number in first structure to be merged',char(10),' s2 : second data structure',char(10),' kField2: field number in second structure to be merged',char(10),' func : Merge function to be used (optional)',char(10),' Typically ''@intersect'' (default) or ''@union''.',char(10),' ',char(10),' Author: Giso Grimm',char(10),' Date: 11/2008',char(10),'']); From 4457fa06e778675e1e89340107b9fdd87802d86d Mon Sep 17 00:00:00 2001 From: "Giso Grimm (thetys)" Date: Tue, 3 Dec 2024 17:06:56 +0100 Subject: [PATCH 20/34] add basic skyfall plugin documentation --- manual/actmodskyfall.tex | 41 +++++++++++++++++++++++++++++ manual/documentation.tsc | 1 + manual/oscdoc_tascarmod_skyfall.tex | 25 ++++++++++++++++++ plugins/src/tascarmod_skyfall.cc | 4 +-- 4 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 manual/actmodskyfall.tex create mode 100644 manual/oscdoc_tascarmod_skyfall.tex diff --git a/manual/actmodskyfall.tex b/manual/actmodskyfall.tex new file mode 100644 index 00000000..f0cdeaa1 --- /dev/null +++ b/manual/actmodskyfall.tex @@ -0,0 +1,41 @@ +Simple physical simulation of sky dive + +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:skyfall} +Attributes of element {\bf skyfall}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{actor} & pattern to match actor objects (string array) & \\ +\hline +\indattr{bypass} & Bypass plugin (bool) & true\\ +\hline +\indattr{deceleration} & Deceleration during sprung phase (double, m/s$^2$) & 40\\ +\hline +\indattr{friction\_fall} & friction during falling phase (double) & 1\\ +\hline +\indattr{friction\_jump} & friction during jumping phase (double) & 0.3\\ +\hline +\indattr{gravitation} & Gravitation constant (double, m/s$^2$) & -9.81\\ +\hline +\indattr{prefix} & OSC prefix (string) & /skyfall\\ +\hline +\indattr{vmax} & maximum velocity (double, m/s) & 40\\ +\hline +\indattr{wx} & deg/s (double, angular velocity around x axis) & 0\\ +\hline +\indattr{wy} & deg/s (double, angular velocity around y axis) & 11\\ +\hline +\indattr{wz} & deg/s (double, angular velocity around z axis) & 45\\ +\hline +\indattr{z0} & starting point (double, m) & 2\\ +\hline +\end{tabularx} +} +\end{snugshade} + +\input{oscdoc_tascarmod_skyfall.tex} diff --git a/manual/documentation.tsc b/manual/documentation.tsc index e50d988c..516e8658 100644 --- a/manual/documentation.tsc +++ b/manual/documentation.tsc @@ -154,6 +154,7 @@ + diff --git a/manual/oscdoc_tascarmod_skyfall.tex b/manual/oscdoc_tascarmod_skyfall.tex new file mode 100644 index 00000000..3ad6f490 --- /dev/null +++ b/manual/oscdoc_tascarmod_skyfall.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{236,236,255}\begin{snugshade} +{\footnotesize +\label{osctab:tascarmodskyfall} +OSC variables: +\nopagebreak + +\begin{tabularx}{\textwidth}{llllX} +\hline +path & fmt. & range & r. & description\\ +\hline +\attr{/.../bypass} & i & bool & yes & \\ +\attr{/.../deceleration} & f & & yes & \\ +\attr{/.../friction\_fall} & f & & yes & \\ +\attr{/.../friction\_jump} & f & & yes & \\ +\attr{/.../gravitation} & f & & yes & \\ +\attr{/.../vmax} & f & & yes & \\ +\attr{/.../wx} & f & [0,360] & yes & \\ +\attr{/.../wy} & f & [0,360] & yes & \\ +\attr{/.../wz} & f & [0,360] & yes & \\ +\attr{/.../z0} & f & & yes & \\ +\hline +\end{tabularx} +} +\end{snugshade} +\definecolor{shadecolor}{RGB}{255,230,204} diff --git a/plugins/src/tascarmod_skyfall.cc b/plugins/src/tascarmod_skyfall.cc index b6c6cb1d..89c3fd55 100644 --- a/plugins/src/tascarmod_skyfall.cc +++ b/plugins/src/tascarmod_skyfall.cc @@ -56,8 +56,8 @@ skyfall_t::skyfall_t(const TASCAR::module_cfg_t& cfg) // Register module parameters for access via XML file: // GET_ATTRIBUTE(prefix, "", "OSC prefix"); - GET_ATTRIBUTE(gravitation, "m/s^2", "Gravitation constant"); - GET_ATTRIBUTE(deceleration, "m/s^2", "Deceleration during sprung phase"); + GET_ATTRIBUTE(gravitation, "m/s$^2$", "Gravitation constant"); + GET_ATTRIBUTE(deceleration, "m/s$^2$", "Deceleration during sprung phase"); GET_ATTRIBUTE(vmax, "m/s", "maximum velocity"); GET_ATTRIBUTE(z0, "m", "starting point"); GET_ATTRIBUTE_BOOL(bypass, "Bypass plugin"); From fe7a87591a8856f58e3436f87726206a07457bdc Mon Sep 17 00:00:00 2001 From: "Giso Grimm (thetys)" Date: Tue, 3 Dec 2024 17:07:06 +0100 Subject: [PATCH 21/34] improve error messages --- libtascar/src/jackclient.cc | 9 ++++++--- libtascar/src/session.cc | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libtascar/src/jackclient.cc b/libtascar/src/jackclient.cc index 3d176764..32590ee6 100644 --- a/libtascar/src/jackclient.cc +++ b/libtascar/src/jackclient.cc @@ -39,7 +39,7 @@ static std::string errmsg(""); static void jack_report_error(const char* msg) { std::cerr << msg << std::endl; - if( errmsg.size() ) + if(errmsg.size()) errmsg += "\n"; errmsg += msg; } @@ -80,7 +80,7 @@ jackc_portless_t::jackc_portless_t(const std::string& clientname) err += "Unable to access shared memory. "; if(jstat & JackVersionError) err += "Client's protocol version does not match. "; - if( errmsg.size() ) + if(errmsg.size()) err += "\n" + errmsg; throw TASCAR::ErrMsg(err); } @@ -606,7 +606,10 @@ jackc_db_t::jackc_db_t(const std::string& clientname, jack_nframes_t infragsize) if(0 != jack_client_create_thread(jc, &inner_thread, std::max(-1, rtprio - 1), (rtprio > 0), service, this)) - throw TASCAR::ErrMsg("Unable to create inner processing thread."); + throw TASCAR::ErrMsg( + "Unable to create inner processing thread with priority " + + TASCAR::to_string(std::max(-1, rtprio - 1)) + + " - check realtime settings."); } else { // check for integer ratio: ratio = fragsize / inner_fragsize; diff --git a/libtascar/src/session.cc b/libtascar/src/session.cc index ba30d52d..5c154f37 100644 --- a/libtascar/src/session.cc +++ b/libtascar/src/session.cc @@ -1000,7 +1000,7 @@ TASCAR::actor_module_t::actor_module_t(const TASCAR::module_cfg_t& cfg, obj = session->find_objects(actor); if(fail_on_empty && obj.empty()) throw TASCAR::ErrMsg("No object matches actor pattern \"" + - vecstr2str(actor) + "\"."); + vecstr2str(actor) + "\" (attribute \"actor\")"); } TASCAR::actor_module_t::~actor_module_t() {} From 4a672befbbd0a9e7fbf22b075ea14de43bd69815 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Thu, 5 Dec 2024 15:26:28 +0100 Subject: [PATCH 22/34] basic skyfall example --- examples/skyfall.tsc | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 examples/skyfall.tsc diff --git a/examples/skyfall.tsc b/examples/skyfall.tsc new file mode 100644 index 00000000..9460a0d9 --- /dev/null +++ b/examples/skyfall.tsc @@ -0,0 +1,13 @@ + + + + + + + + + + + + + From 1bcc5f69cff4be7a59d96ed3da52461dba05f6ee Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Wed, 11 Dec 2024 08:42:35 +0100 Subject: [PATCH 23/34] improve note buffer selection by finding note with lowest amplitude --- plugins/src/tascar_ap_simplesynth.cc | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/plugins/src/tascar_ap_simplesynth.cc b/plugins/src/tascar_ap_simplesynth.cc index a383b05f..a165c31b 100644 --- a/plugins/src/tascar_ap_simplesynth.cc +++ b/plugins/src/tascar_ap_simplesynth.cc @@ -111,7 +111,7 @@ class tone_t { } void unset_pitch(int p) { - if(pitch == p){ + if(pitch == p) { decay = decayoffset; decaynoise = decayoffset; noisemin = 0.0f; @@ -119,7 +119,7 @@ class tone_t { } void add(float& val) { - if((amplitudenoise > 1e-8f)||(amplitude > 1e-8f)) { + if((amplitudenoise > 1e-8f) || (amplitude > 1e-8f)) { float oval = noiseflt.get(fbdelay); noisestate = noisedamp * noisestate + (1.0f - noisedamp) * TASCAR::frand(); @@ -166,7 +166,6 @@ class simplesynth_t : public TASCAR::audioplugin_base_t, private: int midichannel = 0; - size_t nexttone = 0; float f0 = 440.0; uint32_t maxvoices = 8; std::vector partialweights = {1.0f, 0.562f, 0.316f, 0.355f, @@ -292,13 +291,20 @@ void simplesynth_t::emit_event_note(int channel, int pitch, int velocity) if(velocity > 0) { float inputvel = (float)velocity / 127.0f; inputvel = powf(inputvel, gamma); - tones[nexttone].set_pitch(pitch, inputvel * (1.0f - noiseweight), f0, - decay, decayoffset, onset * (1.0f - inputvel), - detune, decaydamping, decaynoise, - inputvel * noiseweight, noiseq, noisemin); - ++nexttone; - if(nexttone >= tones.size()) - nexttone = 0; + // search for tone with lowest amplitude and replace that: + size_t kmin = 0; + float amp_min = 2.0f; + for(size_t k = 0; k < tones.size(); ++k) { + float amp_sum = tones[k].amplitudenoise + tones[k].amplitude; + if(amp_sum < amp_min) { + amp_min = amp_sum; + kmin = k; + } + } + tones[kmin].set_pitch(pitch, inputvel * (1.0f - noiseweight), f0, decay, + decayoffset, onset * (1.0f - inputvel), detune, + decaydamping, decaynoise, inputvel * noiseweight, + noiseq, noisemin); } else { for(auto& t : tones) t.unset_pitch(pitch); From a9c2ebbdf5900681a4392283dd733fba82a02f7a Mon Sep 17 00:00:00 2001 From: "Giso Grimm (thetys)" Date: Wed, 18 Dec 2024 09:56:32 +0100 Subject: [PATCH 24/34] optionally add double parameter to string OSC messages --- manual/Makefile | 2 +- manual/moddatalogging.tex | 96 +------------------ manual/modtabtex/taballpass.tex | 21 ++++ manual/modtabtex/tabbandlevel2osc.tex | 29 ++++++ manual/modtabtex/tabbandpass.tex | 19 ++++ manual/modtabtex/tabboundingbox.tex | 19 ++++ manual/modtabtex/tabccmsg.tex | 25 +++++ manual/modtabtex/tabconnect.tex | 19 ++++ manual/modtabtex/tabconst.tex | 15 +++ manual/modtabtex/tabdatalogging.tex | 33 +++++++ manual/modtabtex/tabdelay.tex | 15 +++ manual/modtabtex/tabdiffuse.tex | 17 ++++ manual/modtabtex/tabechoc.tex | 35 +++++++ manual/modtabtex/tabemergency.tex | 27 ++++++ manual/modtabtex/tabepicycles.tex | 23 +++++ manual/modtabtex/tabf.tex | 15 +++ manual/modtabtex/tabface.tex | 31 ++++++ manual/modtabtex/tabfacegroup.tex | 31 ++++++ manual/modtabtex/tabfeedbackdelay.tex | 23 +++++ manual/modtabtex/tabfence.tex | 21 ++++ manual/modtabtex/tabfilter.tex | 23 +++++ manual/modtabtex/tabfilterequalizer.tex | 29 ++++++ manual/modtabtex/tabfilterhighshelf.tex | 27 ++++++ manual/modtabtex/tabfixture.tex | 41 ++++++++ manual/modtabtex/tabflanger.tex | 25 +++++ manual/modtabtex/tabgain.tex | 17 ++++ manual/modtabtex/tabgainramp.tex | 19 ++++ manual/modtabtex/tabgate.tex | 27 ++++++ manual/modtabtex/tabglabsensors.tex | 27 ++++++ manual/modtabtex/tabgranularsynth.tex | 57 +++++++++++ manual/modtabtex/tabhannenv.tex | 23 +++++ manual/modtabtex/tabhoafdnrot.tex | 41 ++++++++ manual/modtabtex/tabhossustain.tex | 35 +++++++ manual/modtabtex/tabi.tex | 15 +++ manual/modtabtex/tabjackrec.tex | 33 +++++++ manual/modtabtex/tablayout.tex | 49 ++++++++++ manual/modtabtex/tablevel2hsv.tex | 37 +++++++ manual/modtabtex/tablevel2osc.tex | 33 +++++++ manual/modtabtex/tablightctl.tex | 43 +++++++++ manual/modtabtex/tablightscene.tex | 37 +++++++ manual/modtabtex/tablinearmovement.tex | 21 ++++ manual/modtabtex/tablipsync.tex | 39 ++++++++ manual/modtabtex/tablipsync_paper.tex | 39 ++++++++ manual/modtabtex/tablookatme.tex | 33 +++++++ manual/modtabtex/tabloopmachine.tex | 27 ++++++ manual/modtabtex/tablsl.tex | 19 ++++ manual/modtabtex/tablsl2osc.tex | 19 ++++ manual/modtabtex/tabltcgen.tex | 25 +++++ manual/modtabtex/tabmask.tex | 19 ++++ manual/modtabtex/tabmaskpluginfig8.tex | 17 ++++ manual/modtabtex/tabmaskpluginmultibeam.tex | 31 ++++++ manual/modtabtex/tabmaterial.tex | 19 ++++ manual/modtabtex/tabmetronome.tex | 35 +++++++ manual/modtabtex/tabmic.tex | 23 +++++ manual/modtabtex/tabmidicc2osc.tex | 31 ++++++ manual/modtabtex/tabmididispatch.tex | 27 ++++++ manual/modtabtex/tabmsg.tex | 15 +++ manual/modtabtex/tabnavmesh.tex | 19 ++++ manual/modtabtex/tabnoise.tex | 15 +++ manual/modtabtex/tabobjects.tex | 25 +++++ manual/modtabtex/tabobstacle.tex | 21 ++++ manual/modtabtex/tabonsetdetector.tex | 25 +++++ manual/modtabtex/tabosc.tex | 21 ++++ manual/modtabtex/tabosc2lsl.tex | 27 ++++++ manual/modtabtex/taboscactor.tex | 29 ++++++ manual/modtabtex/tabosceog.tex | 27 ++++++ manual/modtabtex/taboscheadtracker.tex | 49 ++++++++++ manual/modtabtex/taboscjacktime.tex | 23 +++++ manual/modtabtex/taboscrelay.tex | 25 +++++ manual/modtabtex/taboscs.tex | 17 ++++ manual/modtabtex/tabpink.tex | 27 ++++++ manual/modtabtex/tabplugins.tex | 15 +++ manual/modtabtex/tabports.tex | 23 +++++ manual/modtabtex/tabpos2osc.tex | 45 +++++++++ manual/modtabtex/tabposition.tex | 19 ++++ manual/modtabtex/tabpulse.tex | 17 ++++ manual/modtabtex/tabqualisys.tex | 27 ++++++ manual/modtabtex/tabrange.tex | 19 ++++ manual/modtabtex/tabreceiver.tex | 65 +++++++++++++ manual/modtabtex/tabreceiveramb1h1v.tex | 17 ++++ manual/modtabtex/tabreceiverchmap.tex | 15 +++ manual/modtabtex/tabreceiverdebugpos.tex | 15 +++ manual/modtabtex/tabreceiverfakebf.tex | 25 +++++ manual/modtabtex/tabreceiverhann.tex | 15 +++ manual/modtabtex/tabreceiverhoa2d.tex | 29 ++++++ manual/modtabtex/tabreceiverhoa2d_fuma.tex | 27 ++++++ manual/modtabtex/tabreceiverhoa3d.tex | 23 +++++ manual/modtabtex/tabreceiverhoa3d_enc.tex | 15 +++ manual/modtabtex/tabreceiverhrtf.tex | 61 ++++++++++++ .../modtabtex/tabreceiverintensityvector.tex | 15 +++ manual/modtabtex/tabreceiveritu51.tex | 19 ++++ manual/modtabtex/tabreceivermicarray.tex | 15 +++ manual/modtabtex/tabreceivernsp.tex | 15 +++ manual/modtabtex/tabreceiverortf.tex | 35 +++++++ manual/modtabtex/tabreceivervmic.tex | 15 +++ manual/modtabtex/tabreceiverwfs.tex | 17 ++++ manual/modtabtex/tabreverb.tex | 73 ++++++++++++++ manual/modtabtex/tabreverbfoaconv.tex | 23 +++++ manual/modtabtex/tabreverbsimplefdn.tex | 47 +++++++++ manual/modtabtex/tabrotator.tex | 27 ++++++ manual/modtabtex/tabroute.tex | 41 ++++++++ manual/modtabtex/tabroutes.tex | 27 ++++++ manual/modtabtex/tabs.tex | 15 +++ manual/modtabtex/tabscene.tex | 29 ++++++ manual/modtabtex/tabsession.tex | 57 +++++++++++ manual/modtabtex/tabsimplecontroller.tex | 25 +++++ manual/modtabtex/tabsine.tex | 17 ++++ manual/modtabtex/tabskyfall.tex | 37 +++++++ manual/modtabtex/tabsnapangle.tex | 23 +++++ manual/modtabtex/tabsndfile.tex | 55 +++++++++++ manual/modtabtex/tabsndfileasync.tex | 31 ++++++ manual/modtabtex/tabsound.tex | 53 ++++++++++ manual/modtabtex/tabsoundcardioidmod.tex | 17 ++++ manual/modtabtex/tabsounddoor.tex | 23 +++++ manual/modtabtex/tabsoundfarsrc.tex | 17 ++++ manual/modtabtex/tabsoundgeneric1storder.tex | 15 +++ manual/modtabtex/tabspeaker.tex | 39 ++++++++ manual/modtabtex/tabspeakerbased.tex | 19 ++++ manual/modtabtex/tabspeechactivity.tex | 25 +++++ manual/modtabtex/tabspkcalib.tex | 15 +++ manual/modtabtex/tabspksim.tex | 25 +++++ manual/modtabtex/tabsystem.tex | 35 +++++++ manual/modtabtex/tabsystime.tex | 21 ++++ manual/modtabtex/tabtimedisplay.tex | 43 +++++++++ manual/modtabtex/tabtransportramp.tex | 19 ++++ manual/modtabtex/tabtubesim.tex | 25 +++++ manual/modtabtex/tabwaitforjackport.tex | 19 ++++ manual/modtabtex/tabwaitforlslstream.tex | 19 ++++ manual/tabdatalogging.tex | 33 +++++++ plugins/src/tascarmod_datalogging.cc | 92 ++++++++++-------- 130 files changed, 3495 insertions(+), 132 deletions(-) create mode 100644 manual/modtabtex/taballpass.tex create mode 100644 manual/modtabtex/tabbandlevel2osc.tex create mode 100644 manual/modtabtex/tabbandpass.tex create mode 100644 manual/modtabtex/tabboundingbox.tex create mode 100644 manual/modtabtex/tabccmsg.tex create mode 100644 manual/modtabtex/tabconnect.tex create mode 100644 manual/modtabtex/tabconst.tex create mode 100644 manual/modtabtex/tabdatalogging.tex create mode 100644 manual/modtabtex/tabdelay.tex create mode 100644 manual/modtabtex/tabdiffuse.tex create mode 100644 manual/modtabtex/tabechoc.tex create mode 100644 manual/modtabtex/tabemergency.tex create mode 100644 manual/modtabtex/tabepicycles.tex create mode 100644 manual/modtabtex/tabf.tex create mode 100644 manual/modtabtex/tabface.tex create mode 100644 manual/modtabtex/tabfacegroup.tex create mode 100644 manual/modtabtex/tabfeedbackdelay.tex create mode 100644 manual/modtabtex/tabfence.tex create mode 100644 manual/modtabtex/tabfilter.tex create mode 100644 manual/modtabtex/tabfilterequalizer.tex create mode 100644 manual/modtabtex/tabfilterhighshelf.tex create mode 100644 manual/modtabtex/tabfixture.tex create mode 100644 manual/modtabtex/tabflanger.tex create mode 100644 manual/modtabtex/tabgain.tex create mode 100644 manual/modtabtex/tabgainramp.tex create mode 100644 manual/modtabtex/tabgate.tex create mode 100644 manual/modtabtex/tabglabsensors.tex create mode 100644 manual/modtabtex/tabgranularsynth.tex create mode 100644 manual/modtabtex/tabhannenv.tex create mode 100644 manual/modtabtex/tabhoafdnrot.tex create mode 100644 manual/modtabtex/tabhossustain.tex create mode 100644 manual/modtabtex/tabi.tex create mode 100644 manual/modtabtex/tabjackrec.tex create mode 100644 manual/modtabtex/tablayout.tex create mode 100644 manual/modtabtex/tablevel2hsv.tex create mode 100644 manual/modtabtex/tablevel2osc.tex create mode 100644 manual/modtabtex/tablightctl.tex create mode 100644 manual/modtabtex/tablightscene.tex create mode 100644 manual/modtabtex/tablinearmovement.tex create mode 100644 manual/modtabtex/tablipsync.tex create mode 100644 manual/modtabtex/tablipsync_paper.tex create mode 100644 manual/modtabtex/tablookatme.tex create mode 100644 manual/modtabtex/tabloopmachine.tex create mode 100644 manual/modtabtex/tablsl.tex create mode 100644 manual/modtabtex/tablsl2osc.tex create mode 100644 manual/modtabtex/tabltcgen.tex create mode 100644 manual/modtabtex/tabmask.tex create mode 100644 manual/modtabtex/tabmaskpluginfig8.tex create mode 100644 manual/modtabtex/tabmaskpluginmultibeam.tex create mode 100644 manual/modtabtex/tabmaterial.tex create mode 100644 manual/modtabtex/tabmetronome.tex create mode 100644 manual/modtabtex/tabmic.tex create mode 100644 manual/modtabtex/tabmidicc2osc.tex create mode 100644 manual/modtabtex/tabmididispatch.tex create mode 100644 manual/modtabtex/tabmsg.tex create mode 100644 manual/modtabtex/tabnavmesh.tex create mode 100644 manual/modtabtex/tabnoise.tex create mode 100644 manual/modtabtex/tabobjects.tex create mode 100644 manual/modtabtex/tabobstacle.tex create mode 100644 manual/modtabtex/tabonsetdetector.tex create mode 100644 manual/modtabtex/tabosc.tex create mode 100644 manual/modtabtex/tabosc2lsl.tex create mode 100644 manual/modtabtex/taboscactor.tex create mode 100644 manual/modtabtex/tabosceog.tex create mode 100644 manual/modtabtex/taboscheadtracker.tex create mode 100644 manual/modtabtex/taboscjacktime.tex create mode 100644 manual/modtabtex/taboscrelay.tex create mode 100644 manual/modtabtex/taboscs.tex create mode 100644 manual/modtabtex/tabpink.tex create mode 100644 manual/modtabtex/tabplugins.tex create mode 100644 manual/modtabtex/tabports.tex create mode 100644 manual/modtabtex/tabpos2osc.tex create mode 100644 manual/modtabtex/tabposition.tex create mode 100644 manual/modtabtex/tabpulse.tex create mode 100644 manual/modtabtex/tabqualisys.tex create mode 100644 manual/modtabtex/tabrange.tex create mode 100644 manual/modtabtex/tabreceiver.tex create mode 100644 manual/modtabtex/tabreceiveramb1h1v.tex create mode 100644 manual/modtabtex/tabreceiverchmap.tex create mode 100644 manual/modtabtex/tabreceiverdebugpos.tex create mode 100644 manual/modtabtex/tabreceiverfakebf.tex create mode 100644 manual/modtabtex/tabreceiverhann.tex create mode 100644 manual/modtabtex/tabreceiverhoa2d.tex create mode 100644 manual/modtabtex/tabreceiverhoa2d_fuma.tex create mode 100644 manual/modtabtex/tabreceiverhoa3d.tex create mode 100644 manual/modtabtex/tabreceiverhoa3d_enc.tex create mode 100644 manual/modtabtex/tabreceiverhrtf.tex create mode 100644 manual/modtabtex/tabreceiverintensityvector.tex create mode 100644 manual/modtabtex/tabreceiveritu51.tex create mode 100644 manual/modtabtex/tabreceivermicarray.tex create mode 100644 manual/modtabtex/tabreceivernsp.tex create mode 100644 manual/modtabtex/tabreceiverortf.tex create mode 100644 manual/modtabtex/tabreceivervmic.tex create mode 100644 manual/modtabtex/tabreceiverwfs.tex create mode 100644 manual/modtabtex/tabreverb.tex create mode 100644 manual/modtabtex/tabreverbfoaconv.tex create mode 100644 manual/modtabtex/tabreverbsimplefdn.tex create mode 100644 manual/modtabtex/tabrotator.tex create mode 100644 manual/modtabtex/tabroute.tex create mode 100644 manual/modtabtex/tabroutes.tex create mode 100644 manual/modtabtex/tabs.tex create mode 100644 manual/modtabtex/tabscene.tex create mode 100644 manual/modtabtex/tabsession.tex create mode 100644 manual/modtabtex/tabsimplecontroller.tex create mode 100644 manual/modtabtex/tabsine.tex create mode 100644 manual/modtabtex/tabskyfall.tex create mode 100644 manual/modtabtex/tabsnapangle.tex create mode 100644 manual/modtabtex/tabsndfile.tex create mode 100644 manual/modtabtex/tabsndfileasync.tex create mode 100644 manual/modtabtex/tabsound.tex create mode 100644 manual/modtabtex/tabsoundcardioidmod.tex create mode 100644 manual/modtabtex/tabsounddoor.tex create mode 100644 manual/modtabtex/tabsoundfarsrc.tex create mode 100644 manual/modtabtex/tabsoundgeneric1storder.tex create mode 100644 manual/modtabtex/tabspeaker.tex create mode 100644 manual/modtabtex/tabspeakerbased.tex create mode 100644 manual/modtabtex/tabspeechactivity.tex create mode 100644 manual/modtabtex/tabspkcalib.tex create mode 100644 manual/modtabtex/tabspksim.tex create mode 100644 manual/modtabtex/tabsystem.tex create mode 100644 manual/modtabtex/tabsystime.tex create mode 100644 manual/modtabtex/tabtimedisplay.tex create mode 100644 manual/modtabtex/tabtransportramp.tex create mode 100644 manual/modtabtex/tabtubesim.tex create mode 100644 manual/modtabtex/tabwaitforjackport.tex create mode 100644 manual/modtabtex/tabwaitforlslstream.tex create mode 100644 manual/tabdatalogging.tex diff --git a/manual/Makefile b/manual/Makefile index eb148864..50be8853 100644 --- a/manual/Makefile +++ b/manual/Makefile @@ -172,7 +172,7 @@ cli_%.tex: ../apps/build/tascar_% Makefile modtabs: - jackd -d dummy & PID=$$!; sleep 1;(export LD_LIBRARY_PATH="../libtascar/build/:../plugins/build/";export TASCARGENDOC="true";../gui/build/tascar documentation.tsc);kill -9 $$PID + jackd -d dummy & PID=$$!; sleep 1;(export LD_LIBRARY_PATH="../libtascar/build/:../plugins/build/";export TASCARGENDOC="true";../gui/build/tascar documentation.tsc);kill -9 $$PID; mkdir -p modtabtex; cp tab*.tex modtabtex/ showundoc: for i in oscdoc_*.tex; do grep -q -e "input.$${i}" *.tex || echo "\input{$${i}}"; done diff --git a/manual/moddatalogging.tex b/manual/moddatalogging.tex index ab126f2b..56639137 100644 --- a/manual/moddatalogging.tex +++ b/manual/moddatalogging.tex @@ -45,101 +45,13 @@ \end{lstlisting} +\input{modtabtex/tabdatalogging.tex} -\begin{snugshade} -{\footnotesize -\label{attrtab:datalogging} -Attributes of element {\bf datalogging}\nopagebreak +\input{modtabtex/tabosc.tex} -\begin{tabularx}{\textwidth}{lXl} -\hline -name & description (type, unit) & def.\\ -\hline -\hline -\indattr{controltransport} & Control transport with recording session control (bool) & true\\ -\hline -\indattr{displaydc} & Display DC components (bool) & true\\ -\hline -\indattr{fileformat} & File format, can be either ``mat'', ``matcell'' or ``txt'' (string) & matcell\\ -\hline -\indattr{headless} & Use without GUI (bool) & false\\ -\hline -\indattr{lsltimeout} & Number of seconds to scan for LSL streams (double, s) & 10\\ -\hline -\indattr{multicast} & OSC multicasting address (string) & \\ -\hline -\indattr{outputdir} & Data output directory (string) & \\ -\hline -\indattr{port} & OSC port, or empty to use session server (string) & \\ -\hline -\indattr{srv\_proto} & Server protocol, UDP or TCP (string) & UDP\\ -\hline -\indattr{usetransport} & Record only while transport is rolling (bool) & false\\ -\hline -\end{tabularx} -} -\end{snugshade} +\input{modtabtex/taboscs.tex} -%%% osc -\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} -{\footnotesize -\label{attrtab:osc} -Attributes of element {\bf osc}\nopagebreak - -\begin{tabularx}{\textwidth}{lXl} -\hline -name & description (type, unit) & def.\\ -\hline -\hline -\indattr{ignorefirst} & Ignore first value in visualization. (bool) & false\\ -\hline -\indattr{path} & OSC path name, expecting messages with 'd' format (usedouble=true) or 'f' format. (string) & \\ -\hline -\indattr{size} & Numer of double/float values per sample. (uint32) & 1\\ -\hline -\indattr{usedouble} & Use double precision OSC variable instead of single precision. (bool) & true\\ -\hline -\end{tabularx} -} -\end{snugshade} - -%%% oscs -\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} -{\footnotesize -\label{attrtab:oscs} -Attributes of element {\bf oscs}\nopagebreak - -\begin{tabularx}{\textwidth}{lXl} -\hline -name & description (type, unit) & def.\\ -\hline -\hline -\indattr{path} & OSC path name, expecting messages with 's' format (string) & \\ -\hline -\end{tabularx} -} -\end{snugshade} - -%%% lsl -\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} -{\footnotesize -\label{attrtab:lsl} -Attributes of element {\bf lsl}\nopagebreak - -\begin{tabularx}{\textwidth}{lXl} -\hline -name & description (type, unit) & def.\\ -\hline -\hline -\indattr{predicate} & LSL stream resolving predicate, e.g., "name='EEG'" (string) & \\ -\hline -\indattr{required} & Require this stream. If true, then loading will fail if stream is not available. (bool) & true\\ -\hline -\indattr{tctimeout} & Time correction timeout (double, s) & 2\\ -\hline -\end{tabularx} -} -\end{snugshade} +\input{modtabtex/tablsl.tex} The window size and position of the datalogging GUI can be controlled diff --git a/manual/modtabtex/taballpass.tex b/manual/modtabtex/taballpass.tex new file mode 100644 index 00000000..9b5e94cf --- /dev/null +++ b/manual/modtabtex/taballpass.tex @@ -0,0 +1,21 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:allpass} +Attributes of element {\bf allpass}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{bypass} & Bypass plugin (bool) & false\\ +\hline +\indattr{f} & Phase jump frequency (double, Hz) & 1000\\ +\hline +\indattr{nstages} & Number of biquad-stages (uint32) & 3\\ +\hline +\indattr{r} & Allpass pole radius (double) & 0.9\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabbandlevel2osc.tex b/manual/modtabtex/tabbandlevel2osc.tex new file mode 100644 index 00000000..22bb38d9 --- /dev/null +++ b/manual/modtabtex/tabbandlevel2osc.tex @@ -0,0 +1,29 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:bandlevel2osc} +Attributes of element {\bf bandlevel2osc}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{bandwidth} & band width (float, octaves) & 1\\ +\hline +\indattr{f} & Center frequencies (float array, Hz) & 250 500 1000 2000\\ +\hline +\indattr{mode} & Level mode [dbspl|rms|max] (string) & dbspl\\ +\hline +\indattr{path} & Target path (string) & /level\\ +\hline +\indattr{sendwhilestopped} & Send also when transport is stopped (bool) & false\\ +\hline +\indattr{skip} & Skip frames (uint32) & 0\\ +\hline +\indattr{threaded} & Use additional thread for sending data (bool) & true\\ +\hline +\indattr{url} & Target URL (string) & {\tiny osc.udp://localhost:9999/}\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabbandpass.tex b/manual/modtabtex/tabbandpass.tex new file mode 100644 index 00000000..f7f310cf --- /dev/null +++ b/manual/modtabtex/tabbandpass.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:bandpass} +Attributes of element {\bf bandpass}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{bypass} & bypass plugin (bool) & false\\ +\hline +\indattr{fmax} & Maximum frequency (float, Hz) & 20000\\ +\hline +\indattr{fmin} & Minimum frequency (float, Hz) & 100\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabboundingbox.tex b/manual/modtabtex/tabboundingbox.tex new file mode 100644 index 00000000..fd844e51 --- /dev/null +++ b/manual/modtabtex/tabboundingbox.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:boundingbox} +Attributes of element {\bf boundingbox}, inheriting from \hyperref[attrtab:objects]{{\bf objects}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{active} & use bounding box (bool) & false\\ +\hline +\indattr{falloff} & fade-out ramp length at boundaries (float, m) & 1\\ +\hline +\indattr{size} & dimension of bounding box (pos, m) & 0 0 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabccmsg.tex b/manual/modtabtex/tabccmsg.tex new file mode 100644 index 00000000..2276c8f7 --- /dev/null +++ b/manual/modtabtex/tabccmsg.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:ccmsg} +Attributes of element {\bf ccmsg}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{channel} & MIDI channel (uint32) & 0\\ +\hline +\indattr{max} & upper bound (float) & 127\\ +\hline +\indattr{min} & lower bound (float) & 0\\ +\hline +\indattr{mode} & message mode, float|trigger (string) & trigger\\ +\hline +\indattr{param} & MIDI CC parameter (uint32) & 0\\ +\hline +\indattr{path} & OSC path (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabconnect.tex b/manual/modtabtex/tabconnect.tex new file mode 100644 index 00000000..c03463cf --- /dev/null +++ b/manual/modtabtex/tabconnect.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:connect} +Attributes of element {\bf connect}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{dest} & jack destination port (string) & \\ +\hline +\indattr{failonerror} & create an error if connection failed, alternatively just warn (bool) & false\\ +\hline +\indattr{src} & jack source port (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabconst.tex b/manual/modtabtex/tabconst.tex new file mode 100644 index 00000000..9d7370e5 --- /dev/null +++ b/manual/modtabtex/tabconst.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:const} +Attributes of element {\bf const}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{a} & amplitude, one entry per channel (float array, Pa) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabdatalogging.tex b/manual/modtabtex/tabdatalogging.tex new file mode 100644 index 00000000..b664d50f --- /dev/null +++ b/manual/modtabtex/tabdatalogging.tex @@ -0,0 +1,33 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:datalogging} +Attributes of element {\bf datalogging}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{controltransport} & Control transport with recording session control (bool) & true\\ +\hline +\indattr{displaydc} & Display DC components (bool) & true\\ +\hline +\indattr{fileformat} & File format, can be either ``mat'', ``matcell'' or ``txt'' (string) & matcell\\ +\hline +\indattr{headless} & Use without GUI (bool) & false\\ +\hline +\indattr{lsltimeout} & Number of seconds to scan for LSL streams (double, s) & 10\\ +\hline +\indattr{multicast} & OSC multicasting address (string) & \\ +\hline +\indattr{outputdir} & Data output directory (string) & \\ +\hline +\indattr{port} & OSC port, or empty to use session server (string) & \\ +\hline +\indattr{srv\_proto} & Server protocol, UDP or TCP (string) & UDP\\ +\hline +\indattr{usetransport} & Record only while transport is rolling (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabdelay.tex b/manual/modtabtex/tabdelay.tex new file mode 100644 index 00000000..fd2a53f9 --- /dev/null +++ b/manual/modtabtex/tabdelay.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:delay} +Attributes of element {\bf delay}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{delay} & Delays in seconds (double array, s) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabdiffuse.tex b/manual/modtabtex/tabdiffuse.tex new file mode 100644 index 00000000..befcac89 --- /dev/null +++ b/manual/modtabtex/tabdiffuse.tex @@ -0,0 +1,17 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:diffuse} +Attributes of element {\bf diffuse}, inheriting from \hyperref[attrtab:objects]{{\bf objects}} \hyperref[attrtab:ports]{{\bf ports}} \hyperref[attrtab:routes]{{\bf routes}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{falloff} & falloff ramp length at boundaries (float, m) & 1\\ +\hline +\indattr{size} & size in which sound field is rendered. (pos, m) & 1 1 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabechoc.tex b/manual/modtabtex/tabechoc.tex new file mode 100644 index 00000000..a8fabbe1 --- /dev/null +++ b/manual/modtabtex/tabechoc.tex @@ -0,0 +1,35 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:echoc} +Attributes of element {\bf echoc}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{autoreconnect} & Automatically re-connect ports after jack port change (bool) & false\\ +\hline +\indattr{bypass} & Bypass filter stage (bool) & false\\ +\hline +\indattr{filterlen} & Minimal length of filters (uint32, samples) & 65\\ +\hline +\indattr{level} & Playback level (float, dB SPL) & 70\\ +\hline +\indattr{loudspeakerports} & Loudspeaker ports (string array) & {\tiny system:playback\_1 system:playback\_2}\\ +\hline +\indattr{maxdist} & Maximum distance between microphone and loudspeaker (float, m) & 2\\ +\hline +\indattr{measureatstart} & Perform a measurement when the plugin is loaded (bool) & false\\ +\hline +\indattr{micports} & Microphone ports (string array) & system:capture\_1\\ +\hline +\indattr{name} & Client name, used for jack and IR file name (string) & echoc\\ +\hline +\indattr{nrep} & Number of measurement repetitions (uint32) & 16\\ +\hline +\indattr{premax} & Time before to maximum to add to filter (uint32, samples) & 8\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabemergency.tex b/manual/modtabtex/tabemergency.tex new file mode 100644 index 00000000..60035851 --- /dev/null +++ b/manual/modtabtex/tabemergency.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:emergency} +Attributes of element {\bf emergency}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{alivetimeout} & undocumented (double) & 1\\ +\hline +\indattr{name} & undocumented (string) & emergency\\ +\hline +\indattr{on\_alive} & Command to be executed when sensor is alive again (string) & \\ +\hline +\indattr{on\_timeout} & Command to be executed on timeout (string) & \\ +\hline +\indattr{path} & OSC path on which messages are arriving (string) & /noemergency\\ +\hline +\indattr{startlock} & Lock detecting at start for this amount of time (double, s) & 5\\ +\hline +\indattr{timeout} & Timeout after which an emergency is detected (double, s) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabepicycles.tex b/manual/modtabtex/tabepicycles.tex new file mode 100644 index 00000000..5a85821f --- /dev/null +++ b/manual/modtabtex/tabepicycles.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:epicycles} +Attributes of element {\bf epicycles}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{actor} & pattern to match actor objects (string array) & \\ +\hline +\indattr{home} & Home direction of sound source (float, deg) & 0\\ +\hline +\indattr{path} & Path prefix of plugin (string) & \\ +\hline +\indattr{targetaddr} & Target url where the current position is sent to on trigger (string) & \\ +\hline +\indattr{use\_transport} & Update traces only while transport is running (bool) & true\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabf.tex b/manual/modtabtex/tabf.tex new file mode 100644 index 00000000..d2d86015 --- /dev/null +++ b/manual/modtabtex/tabf.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:f} +Attributes of element {\bf f}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{v} & float value (double) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabface.tex b/manual/modtabtex/tabface.tex new file mode 100644 index 00000000..869dd374 --- /dev/null +++ b/manual/modtabtex/tabface.tex @@ -0,0 +1,31 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:face} +Attributes of element {\bf face}, inheriting from \hyperref[attrtab:objects]{{\bf objects}} \hyperref[attrtab:routes]{{\bf routes}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{damping} & Damping coefficient (float) & 0\\ +\hline +\indattr{edgereflection} & Apply edge reflection in case of not directly visible image source (bool) & true\\ +\hline +\indattr{height} & Height of reflector (double, m) & 1\\ +\hline +\indattr{layers} & render layers (bits32) & all\\ +\hline +\indattr{material} & Material name, or empty to use coefficients (string) & \\ +\hline +\indattr{reflectivity} & Reflectivity coefficient (float) & 1\\ +\hline +\indattr{scattering} & Relative amount of scattering (float) & 0\\ +\hline +\indattr{vertices} & List of Cartesian coordinates to define polygon surface (pos array, m) & \\ +\hline +\indattr{width} & Width of reflector (double, m) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabfacegroup.tex b/manual/modtabtex/tabfacegroup.tex new file mode 100644 index 00000000..4e49b84e --- /dev/null +++ b/manual/modtabtex/tabfacegroup.tex @@ -0,0 +1,31 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:facegroup} +Attributes of element {\bf facegroup}, inheriting from \hyperref[attrtab:objects]{{\bf objects}} \hyperref[attrtab:routes]{{\bf routes}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{damping} & Damping coefficient (float) & 0\\ +\hline +\indattr{edgereflection} & Apply edge reflection in case of not directly visible image source (bool) & true\\ +\hline +\indattr{importraw} & File name of raw file containing list of polygon surfaces (string) & \\ +\hline +\indattr{layers} & render layers (bits32) & all\\ +\hline +\indattr{material} & Material name, or empty to use coefficients (string) & \\ +\hline +\indattr{reflectivity} & Reflectivity coefficient (float) & 1\\ +\hline +\indattr{scattering} & Relative amount of scattering (float) & 0\\ +\hline +\indattr{shoebox} & Generate a shoebox room of these dimensions (pos, m) & 0 0 0\\ +\hline +\indattr{shoeboxwalls} & generate shoebox room without floor and ceiling (pos, m) & 0 0 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabfeedbackdelay.tex b/manual/modtabtex/tabfeedbackdelay.tex new file mode 100644 index 00000000..48d4c505 --- /dev/null +++ b/manual/modtabtex/tabfeedbackdelay.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:feedbackdelay} +Attributes of element {\bf feedbackdelay}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{dry} & Linear gain of direct input (float) & 1\\ +\hline +\indattr{f} & Resonance frequency (float, Hz) & 1000\\ +\hline +\indattr{feedback} & Linear feedback gain (float) & 0.5\\ +\hline +\indattr{maxdelay} & Maximum delay line length (uint64, samples) & 44100\\ +\hline +\indattr{wet} & Linear gain of input to delayline (float) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabfence.tex b/manual/modtabtex/tabfence.tex new file mode 100644 index 00000000..d8e14506 --- /dev/null +++ b/manual/modtabtex/tabfence.tex @@ -0,0 +1,21 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:fence} +Attributes of element {\bf fence}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{alpha} & alpha (float) & 1\\ +\hline +\indattr{origin} & origin (pos, m) & 0 0 0\\ +\hline +\indattr{r} & r (float, m) & 1\\ +\hline +\indattr{range} & range (float, m) & 0.1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabfilter.tex b/manual/modtabtex/tabfilter.tex new file mode 100644 index 00000000..49cfb234 --- /dev/null +++ b/manual/modtabtex/tabfilter.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:filter} +Attributes of element {\bf filter}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{Q} & quality factor (float) & 1\\ +\hline +\indattr{fc} & Cut-off frequncy (float, Hz) & 1000\\ +\hline +\indattr{gain} & equalizer gain (float, dB) & 0\\ +\hline +\indattr{highpass} & Highpass filter (true) or lowpass filter (false) (bool) & false\\ +\hline +\indattr{mode} & filter mode: lohi, lowpass, highpass, equalizer (string) & lohi\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabfilterequalizer.tex b/manual/modtabtex/tabfilterequalizer.tex new file mode 100644 index 00000000..8f307f3e --- /dev/null +++ b/manual/modtabtex/tabfilterequalizer.tex @@ -0,0 +1,29 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:filterequalizer} +Attributes of filter element {\bf equalizer}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{Q} & quality factor (double) & nan\\ +\hline +\indattr{axis} & orientation axis for filter parameter variation relative to receiver orientation (pos) & 0 0 0\\ +\hline +\indattr{gain\_end} & gain applied for all theta >= theta\_end (double, dB) & nan\\ +\hline +\indattr{gain\_st} & gain applied at theta = 0 rad (double, dB) & nan\\ +\hline +\indattr{omega\_end} & center frequency for theta >= theta\_end (double, Hz) & nan\\ +\hline +\indattr{omega\_st} & center frequency at theta = 0 rad (double, Hz) & nan\\ +\hline +\indattr{theta\_end} & angle until which the gain is varied (double, rad) & nan\\ +\hline +\indattr{type} & filter model type (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabfilterhighshelf.tex b/manual/modtabtex/tabfilterhighshelf.tex new file mode 100644 index 00000000..66944eef --- /dev/null +++ b/manual/modtabtex/tabfilterhighshelf.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:filterhighshelf} +Attributes of filter element {\bf highshelf}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{alpha\_m} & alpha at theta = beta*(pi-theta\_st) (double) & nan\\ +\hline +\indattr{alpha\_st} & alpha for all theta < theta\_st (double) & nan\\ +\hline +\indattr{axis} & orientation axis for filter parameter variation relative to receiver orientation (pos) & 0 0 0\\ +\hline +\indattr{beta} & parameter to determine angle at which alpha = alpha\_m (double) & nan\\ +\hline +\indattr{omega} & cut-off frequency of high-shelf (double, Hz) & nan\\ +\hline +\indattr{theta\_st} & angle at which the zero position starts to vary (double, rad) & nan\\ +\hline +\indattr{type} & filter model type (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabfixture.tex b/manual/modtabtex/tabfixture.tex new file mode 100644 index 00000000..3228458c --- /dev/null +++ b/manual/modtabtex/tabfixture.tex @@ -0,0 +1,41 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:fixture} +Attributes of element {\bf fixture}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{addr} & start address (uint32) & 1\\ +\hline +\indattr{az} & Azimuth (double, deg) & 0\\ +\hline +\indattr{calibrate} & Use this loudspeaker during calibration (bool) & true\\ +\hline +\indattr{compB} & FIR filter coefficients for speaker calibration (double array) & \\ +\hline +\indattr{connect} & Connection to jack port (string) & \\ +\hline +\indattr{delay} & Static delay (double, s) & 0\\ +\hline +\indattr{dmxval} & start DMX value (int32 array) & \\ +\hline +\indattr{el} & Elevation (double, deg) & 0\\ +\hline +\indattr{eqfreq} & Frequencies for IIR filter design (float array, Hz) & \\ +\hline +\indattr{eqgain} & Gains for IIR filter design (float array, dB) & \\ +\hline +\indattr{eqstages} & Number of biquad-stages in IIR frequency correction (0 = disable) (uint32) & 0\\ +\hline +\indattr{gain} & Broadband gain correction (double, dB) & 0\\ +\hline +\indattr{label} & fixture label (string) & \\ +\hline +\indattr{r} & Distance (double, m) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabflanger.tex b/manual/modtabtex/tabflanger.tex new file mode 100644 index 00000000..755a9196 --- /dev/null +++ b/manual/modtabtex/tabflanger.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:flanger} +Attributes of element {\bf flanger}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{dmax} & Upper bound of delay (float, s) & 0.01\\ +\hline +\indattr{dmin} & Lower bound of delay (float, s) & 0\\ +\hline +\indattr{feedback} & Feedback, must be between 0 and 0.999 (float) & 0\\ +\hline +\indattr{maxdelay} & Maximum delay line length (uint64, samples) & 44100\\ +\hline +\indattr{modf} & Modulation frequency (float, Hz) & 1\\ +\hline +\indattr{wet} & Linear gain of input to delayline (float) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabgain.tex b/manual/modtabtex/tabgain.tex new file mode 100644 index 00000000..4dd22401 --- /dev/null +++ b/manual/modtabtex/tabgain.tex @@ -0,0 +1,17 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:gain} +Attributes of element {\bf gain}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{gain} & gain (float, dB) & 0\\ +\hline +\indattr{lingain} & lingain (float) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabgainramp.tex b/manual/modtabtex/tabgainramp.tex new file mode 100644 index 00000000..f1bf0391 --- /dev/null +++ b/manual/modtabtex/tabgainramp.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:gainramp} +Attributes of element {\bf gainramp}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{gain} & Set current gain (double, dB) & 0\\ +\hline +\indattr{maxgain} & Set maximal gain (double, dB) & 0\\ +\hline +\indattr{slope} & Set gain slope in dB/s (double, dB) & -inf\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabgate.tex b/manual/modtabtex/tabgate.tex new file mode 100644 index 00000000..3913c490 --- /dev/null +++ b/manual/modtabtex/tabgate.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:gate} +Attributes of element {\bf gate}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{bypass} & Start in bypass mode (bool) & true\\ +\hline +\indattr{fadeinlen} & Duration of von-Hann fade in (double, s) & 0.01\\ +\hline +\indattr{fadeoutlen} & Duration of von-Hann fade out (double, s) & 0.125\\ +\hline +\indattr{holdlen} & Time to keep output after level decay below threshold (double, s) & 0.125\\ +\hline +\indattr{taurms} & RMS level estimation time constant (double, s) & 0.005\\ +\hline +\indattr{tautrack} & Min/max tracking time constant (double, s) & 30\\ +\hline +\indattr{threshold} & Threshold value between 0 and 1 (double) & 0.125\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabglabsensors.tex b/manual/modtabtex/tabglabsensors.tex new file mode 100644 index 00000000..4a9275db --- /dev/null +++ b/manual/modtabtex/tabglabsensors.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:glabsensors} +Attributes of element {\bf glabsensors}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{h} & Window height (uint32, px) & 1080\\ +\hline +\indattr{ontop} & Keep window on top of other windows (bool) & true\\ +\hline +\indattr{url\_critical} & OSC URL to send critical messages to (string) & \\ +\hline +\indattr{url\_warning} & OSC URL to send warning messages to (string) & \\ +\hline +\indattr{w} & Window width (uint32, px) & 320\\ +\hline +\indattr{x} & Screen x position (uint32, px) & 0\\ +\hline +\indattr{y} & Screen y position (uint32, px) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabgranularsynth.tex b/manual/modtabtex/tabgranularsynth.tex new file mode 100644 index 00000000..c9683998 --- /dev/null +++ b/manual/modtabtex/tabgranularsynth.tex @@ -0,0 +1,57 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:granularsynth} +Attributes of element {\bf granularsynth}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{active} & active (bool) & true\\ +\hline +\indattr{bpm} & Tempo (double, bpm) & 120\\ +\hline +\indattr{bypass} & bypass (bool) & false\\ +\hline +\indattr{durations} & Durations (double array, beats) & \\ +\hline +\indattr{f0} & frequency of pitch 0 (double, Hz) & 415\\ +\hline +\indattr{fillthreshold} & Minimum number of grains per frequency in fill counter (uint32) & 5\\ +\hline +\indattr{gain} & Gain (double, dB) & 0\\ +\hline +\indattr{hue} & Hue component (0-360) (float, degree) & 0\\ +\hline +\indattr{id} & ID used in jack name and OSC path (string) & granularsynth\\ +\hline +\indattr{loop} & Time when to loop (double, beats) & 64\\ +\hline +\indattr{numgrains} & Number of grains to keep (uint32) & 100\\ +\hline +\indattr{oscactive} & Activate OSC sending on start (bool) & false\\ +\hline +\indattr{path} & Grainstore fill path (string) & /grainstorefill\\ +\hline +\indattr{pitches} & Pitch numbers (double array, semitones) & \\ +\hline +\indattr{ponset} & Onset playback probabbility (double) & 1\\ +\hline +\indattr{prefix} & prefix used in OSC path (string) & /c/\\ +\hline +\indattr{psustain} & Sustained sound probability (double) & 0\\ +\hline +\indattr{saturation} & Saturation component (0-1) (float) & 1\\ +\hline +\indattr{t0} & Melody start time (double, s) & 0\\ +\hline +\indattr{url} & Grainstore fill URL (string) & \\ +\hline +\indattr{wet} & Mixing gain (float) & 1\\ +\hline +\indattr{wlen} & window length (uint32, samples) & 8192\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabhannenv.tex b/manual/modtabtex/tabhannenv.tex new file mode 100644 index 00000000..673487f4 --- /dev/null +++ b/manual/modtabtex/tabhannenv.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:hannenv} +Attributes of element {\bf hannenv}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{period} & Period time (double, s) & 2\\ +\hline +\indattr{ramp1} & First ramp length (double, s) & 0.25\\ +\hline +\indattr{ramp2} & Second ramp length (double, s) & 0.25\\ +\hline +\indattr{steady} & Duration of steady state (double, s) & 0.5\\ +\hline +\indattr{t0} & Start time (double, s) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabhoafdnrot.tex b/manual/modtabtex/tabhoafdnrot.tex new file mode 100644 index 00000000..a4f7d694 --- /dev/null +++ b/manual/modtabtex/tabhoafdnrot.tex @@ -0,0 +1,41 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:hoafdnrot} +Attributes of element {\bf hoafdnrot}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{amborder} & Ambisonics order (uint32) & 3\\ +\hline +\indattr{damping} & Damping coefficient (float) & 0.3\\ +\hline +\indattr{decay} & Decay time (float, s) & 1\\ +\hline +\indattr{dry} & Dry signal ratio (float) & 0\\ +\hline +\indattr{dt} & Delay line spread (float, s) & 0.002\\ +\hline +\indattr{dumpmatrix} & Dump feedback matrix on console (bool) & false\\ +\hline +\indattr{dw} & Angular spread (float, rps) & 0.1\\ +\hline +\indattr{fdnorder} & FDN order (uint32) & 5\\ +\hline +\indattr{id} & Jack / OSC id (string) & fdn\\ +\hline +\indattr{logdelays} & Use logarithmic delay distribution between dt and t (bool) & false\\ +\hline +\indattr{prefilt} & Use pre-filters (bool) & false\\ +\hline +\indattr{t} & Average delay line length (float, s) & 0.01\\ +\hline +\indattr{w} & Rotation velocity in rounds per second (float, rps) & 1\\ +\hline +\indattr{wet} & Wet signal ratio (float) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabhossustain.tex b/manual/modtabtex/tabhossustain.tex new file mode 100644 index 00000000..37b5e98d --- /dev/null +++ b/manual/modtabtex/tabhossustain.tex @@ -0,0 +1,35 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:hossustain} +Attributes of element {\bf hossustain}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{bass} & Linear gain of subsonic component (float) & 0\\ +\hline +\indattr{bassratio} & Frequency ratio of subsonic component (float) & 2\\ +\hline +\indattr{delayenvelope} & Delay envelope to match processed signal (bool) & false\\ +\hline +\indattr{fcut} & Low-cut edge frequency (float, Hz) & 40\\ +\hline +\indattr{gain} & Gain (double, dB) & 0\\ +\hline +\indattr{id} & ID used for jack and OSC (string) & sustain\\ +\hline +\indattr{oscprefix} & Prefix used in OSC (string) & \\ +\hline +\indattr{tau\_envelope} & Envelope tracking time constant (float, s) & 1\\ +\hline +\indattr{tau\_sustain} & Clustering time constant (float, s) & 20\\ +\hline +\indattr{wet} & Wet-dry ratio (float) & 1\\ +\hline +\indattr{wlen} & Window length (uint32, samples) & 8192\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabi.tex b/manual/modtabtex/tabi.tex new file mode 100644 index 00000000..93a88f4e --- /dev/null +++ b/manual/modtabtex/tabi.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:i} +Attributes of element {\bf i}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{v} & int value (int32) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabjackrec.tex b/manual/modtabtex/tabjackrec.tex new file mode 100644 index 00000000..d55ff88a --- /dev/null +++ b/manual/modtabtex/tabjackrec.tex @@ -0,0 +1,33 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:jackrec} +Attributes of element {\bf jackrec}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{buflen} & audio buffer length (double, s) & 10\\ +\hline +\indattr{fileformat} & File format (string, WAV AIFF AU RAW PAF SVX NIST VOC IRCAM W64 MAT4 MAT5 PVF XI HTK SDS AVR WAVEX SD2 FLAC CAF WVE OGG MPC2K RF64) & WAV\\ +\hline +\indattr{name} & Name used for OSC prefix and jack (string) & jackrec\\ +\hline +\indattr{path} & File path where to store and search for files (string) & \\ +\hline +\indattr{pattern} & search pattern (string) & rec*.wav\\ +\hline +\indattr{ports} & List of ports to record (string array) & \\ +\hline +\indattr{prefix} & file prefix (string) & rec\\ +\hline +\indattr{sampleformat} & Audio sample format (string, PCM\_S8 PCM\_16 PCM\_24 PCM\_32 PCM\_U8 FLOAT DOUBLE ULAW ALAW IMA\_ADPCM MS\_ADPCM GSM610 VOX\_ADPCM G721\_32 G723\_24 G723\_40 DWVW\_12 DWVW\_16 DWVW\_24 DWVW\_N DPCM\_8 DPCM\_16 VORBIS) & PCM\_16\\ +\hline +\indattr{url} & URL of OSC controller interface (string) & \\ +\hline +\indattr{usetransport} & Record only when transport is rolling (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablayout.tex b/manual/modtabtex/tablayout.tex new file mode 100644 index 00000000..4c91f439 --- /dev/null +++ b/manual/modtabtex/tablayout.tex @@ -0,0 +1,49 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:layout} +Attributes of element {\bf layout}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{addring} & Create a circular layout with this number of speakers (uint32) & 0\\ +\hline +\indattr{addsphere} & Create a spherical layout with at least this number of speakers by barycentric subdivision (uint32) & 0\\ +\hline +\indattr{calibdate} & Calibration date in format YYYY-MM-DD (string) & \\ +\hline +\indattr{calibfor} & Summary of receiver parameters (string) & \\ +\hline +\indattr{caliblevel} & Calibration level (double, dB SPL) & 93.9794\\ +\hline +\indattr{checksum} & autogenerated value for validation of calibration (uint64) & 0\\ +\hline +\indattr{convlabels} & Space-separated list of labels of convolution output channels (string array) & \\ +\hline +\indattr{convprecalib} & Apply convolution before calibration (true) or after (false). (bool) & true\\ +\hline +\indattr{decorr} & Decorrelate speaker signals in diffuse sound field rendering (bool) & true\\ +\hline +\indattr{decorr\_length} & Length of decorrelation filter (double, s) & 0.05\\ +\hline +\indattr{densitycorr} & In diffuse rendering, correct gains locally for loudspeaker density (bool) & true\\ +\hline +\indattr{diffusedecoder} & Diffuse-decoder method (string, basic|maxre|inphase) & maxre\\ +\hline +\indattr{diffusegain} & Calibration gain of diffuse sound fields (double, dB) & 0\\ +\hline +\indattr{fcsub} & Cross-over frequency, used only if subwoofers are defined (double, Hz) & 80\\ +\hline +\indattr{name} & Name of layout, for documentation only (string) & \\ +\hline +\indattr{onload} & system command to be executed when layout is loaded (string) & \\ +\hline +\indattr{onunload} & system command to be executed when layout is unloaded (string) & \\ +\hline +\indattr{sofa\_file} & SOFA convolution file (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablevel2hsv.tex b/manual/modtabtex/tablevel2hsv.tex new file mode 100644 index 00000000..42798584 --- /dev/null +++ b/manual/modtabtex/tablevel2hsv.tex @@ -0,0 +1,37 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:level2hsv} +Attributes of element {\bf level2hsv}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{active} & start activated (bool) & true\\ +\hline +\indattr{decay} & decay filter coefficient (double) & 0\\ +\hline +\indattr{frange} & Frequency range in bandpass mode (float array, Hz) & 62.5 4000\\ +\hline +\indattr{hue} & Hue component (0-360) (float, degree) & 0\\ +\hline +\indattr{lrange} & Level range (float array, dB) & 40 90\\ +\hline +\indattr{mode} & Level mode [dbspl|rms|max] (string) & dbspl\\ +\hline +\indattr{path} & Target path (string array) & /hsv\\ +\hline +\indattr{saturation} & Saturation component (0-1) (float) & 1\\ +\hline +\indattr{skip} & Skip frames (uint32) & 0\\ +\hline +\indattr{tau} & Leq duration, or 0 to use block size (float, s) & 0\\ +\hline +\indattr{url} & Target URL (string) & {\tiny osc.udp://localhost:9999/}\\ +\hline +\indattr{weight} & Level meter weight (f-weight) & Z\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablevel2osc.tex b/manual/modtabtex/tablevel2osc.tex new file mode 100644 index 00000000..d6b3cdca --- /dev/null +++ b/manual/modtabtex/tablevel2osc.tex @@ -0,0 +1,33 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:level2osc} +Attributes of element {\bf level2osc}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{firstpar} & First parameter, or -1 to use current session time. (double) & -1\\ +\hline +\indattr{frange} & Frequency range in bandpass mode (float array, Hz) & 62.5 4000\\ +\hline +\indattr{mode} & Level mode [dbspl|rms|max] (string) & dbspl\\ +\hline +\indattr{path} & Target path (string) & /level\\ +\hline +\indattr{sendwhilestopped} & Send also when transport is stopped (bool) & false\\ +\hline +\indattr{skip} & Skip frames (uint32) & 0\\ +\hline +\indattr{tau} & Leq duration, or 0 to use block size (float, s) & 0\\ +\hline +\indattr{threaded} & Use additional thread for sending data (bool) & true\\ +\hline +\indattr{url} & Target URL (string) & {\tiny osc.udp://localhost:9999/}\\ +\hline +\indattr{weights} & Level meter weights (f-weight array) & Z\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablightctl.tex b/manual/modtabtex/tablightctl.tex new file mode 100644 index 00000000..f9c724b1 --- /dev/null +++ b/manual/modtabtex/tablightctl.tex @@ -0,0 +1,43 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:lightctl} +Attributes of element {\bf lightctl}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{driver} & Driver name (string, ``artnetdmx'', ``opendmxusb'', or ``osc'') & \\ +\hline +\indattr{fps} & Frames per second (double, Hz) & 30\\ +\hline +\indattr{hostname} & Hostname of OSC destination (``osc'' driver only) (string) & localhost\\ +\hline +\indattr{hue\_warp\_rot} & Hue warping rotation (double, deg) & 0\\ +\hline +\indattr{hue\_warp\_x} & Hue warping x offset (double) & 0\\ +\hline +\indattr{hue\_warp\_y} & Hue warping y offset (double) & 0\\ +\hline +\indattr{maxchannels} & Maximum number of channels to transmit (``osc'' driver only) (uint32) & 512\\ +\hline +\indattr{path} & Destination path (``osc'' driver only) (string) & /dmx\\ +\hline +\indattr{port} & Port number of OSC destination (``osc'' driver only) (uint32) & 9000\\ +\hline +\indattr{rawsrvchannels} & Number of channels to receive as RAW DMX (uint32) & 0\\ +\hline +\indattr{rawsrvhost} & multicast address for raw DX OSC server (string) & \\ +\hline +\indattr{rawsrvpath} & Path for raw DMX OSC server, empty for no raw DMX OSC server (string) & \\ +\hline +\indattr{rawsrvport} & Port of raw DMX OSC server, or empty to use session OSC server (string) & \\ +\hline +\indattr{rawsrvproto} & Protocol of raw DMX OSC server (string) & UDP\\ +\hline +\indattr{universe} & DMX universe (uint32) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablightscene.tex b/manual/modtabtex/tablightscene.tex new file mode 100644 index 00000000..d1f2cd2a --- /dev/null +++ b/manual/modtabtex/tablightscene.tex @@ -0,0 +1,37 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:lightscene} +Attributes of element {\bf lightscene}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{channels} & Number of DMX channels per fixture (uint32) & 3\\ +\hline +\indattr{layout} & name of speaker layout file (string) & \\ +\hline +\indattr{master} & undocumented (float) & 1\\ +\hline +\indattr{method} & undocumented (string) & \\ +\hline +\indattr{mixmax} & undocumented (bool) & false\\ +\hline +\indattr{name} & Scene name (string) & lightscene\\ +\hline +\indattr{objects} & Pattern of objects to track (string array) & \\ +\hline +\indattr{objval} & DMX value of objects (float array) & \\ +\hline +\indattr{objw} & weight of objects (float array) & \\ +\hline +\indattr{parent} & Name of parent object for relative position measurement (string) & \\ +\hline +\indattr{sendsquared} & Send squared values for smoother intensity fades (bool) & false\\ +\hline +\indattr{usecalib} & Use calibrated values instead of raw values (bool) & true\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablinearmovement.tex b/manual/modtabtex/tablinearmovement.tex new file mode 100644 index 00000000..9dd674dc --- /dev/null +++ b/manual/modtabtex/tablinearmovement.tex @@ -0,0 +1,21 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:linearmovement} +Attributes of element {\bf linearmovement}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{actor} & pattern to match actor objects (string array) & \\ +\hline +\indattr{p0} & start position at time t0 (pos, m) & 0 0 0\\ +\hline +\indattr{t0} & start time t0 (double, s) & 4.788e-310\\ +\hline +\indattr{v} & velocity vector (pos, m/s) & 1 1 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablipsync.tex b/manual/modtabtex/tablipsync.tex new file mode 100644 index 00000000..b1090f09 --- /dev/null +++ b/manual/modtabtex/tablipsync.tex @@ -0,0 +1,39 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:lipsync} +Attributes of element {\bf lipsync}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{dynamicrange} & Mapped dynamic range (double, dB) & 165\\ +\hline +\indattr{energypath} & OSC destination for sending format energies, or empty for no energy messages (string) & \\ +\hline +\indattr{maxspeechlevel} & Level normalization (double, dB) & 48\\ +\hline +\indattr{onchangecount} & Maximum number of repetitions of equal messages in ``onchange'' mode (uint32) & 3\\ +\hline +\indattr{path} & OSC destination of blendshape messages (empty: use parent name) (string) & \\ +\hline +\indattr{scale} & Scaling factor of blend shapes; 3 values: kiss, jaw, lipsclosed (pos) & 1 1 1\\ +\hline +\indattr{sendmode} & Sending mode, one of ``always'', ``transport'', or ``onchange'' (string) & always\\ +\hline +\indattr{smoothing} & Smoothing time constant (double, s) & 0.02\\ +\hline +\indattr{strmsg} & Message string to be added to OSC messages before blend shapes (string) & /lipsync\\ +\hline +\indattr{threaded} & Use additional thread for sending data (bool) & true\\ +\hline +\indattr{threshold} & Noise threshold, range 0-1 (double) & 0.5\\ +\hline +\indattr{url} & Target OSC URL (string) & {\tiny osc.udp://localhost:9999/}\\ +\hline +\indattr{vocalTract} & Vocal tract scaling factor (double) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablipsync_paper.tex b/manual/modtabtex/tablipsync_paper.tex new file mode 100644 index 00000000..185e4f9a --- /dev/null +++ b/manual/modtabtex/tablipsync_paper.tex @@ -0,0 +1,39 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:lipsync_paper} +Attributes of element {\bf lipsync\_paper}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{dynamicrange} & Mapped dynamic range (double, dB) & 165\\ +\hline +\indattr{energypath} & OSC destination for sending format energies, or empty for no energy messages (string) & \\ +\hline +\indattr{maxspeechlevel} & Level normalization (double, dB) & 48\\ +\hline +\indattr{onchangecount} & Maximum number of repetitions of equal messages in ``onchange'' mode (uint32) & 3\\ +\hline +\indattr{path} & OSC destination of blendshape messages (empty: use parent name) (string) & \\ +\hline +\indattr{scale} & Scaling factor of blend shapes; 3 values: kiss, jaw, lipsclosed (pos) & 1 1 1\\ +\hline +\indattr{sendmode} & Sending mode, one of ``always'', ``transport'', or ``onchange'' (string) & always\\ +\hline +\indattr{smoothing} & Smoothing time constant (double, s) & 0.04\\ +\hline +\indattr{strmsg} & Message string to be added to OSC messages before blend shapes (string) & /lipsync\\ +\hline +\indattr{threaded} & Use additional thread for sending data (bool) & true\\ +\hline +\indattr{threshold} & Noise threshold, range 0-1 (double) & 0.5\\ +\hline +\indattr{url} & Target OSC URL (string) & {\tiny osc.udp://localhost:9999/}\\ +\hline +\indattr{vocalTract} & Vocal tract scaling factor (double) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablookatme.tex b/manual/modtabtex/tablookatme.tex new file mode 100644 index 00000000..5eafea06 --- /dev/null +++ b/manual/modtabtex/tablookatme.tex @@ -0,0 +1,33 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:lookatme} +Attributes of element {\bf lookatme}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{animation} & Animation name (or empty for no animation) (string) & \\ +\hline +\indattr{fadelen} & Motion duration after threshold (double, s) & 1\\ +\hline +\indattr{levelpath} & Destination path of level logging (or empty) (string) & \\ +\hline +\indattr{paths} & Space-separated list of target paths (string array) & \\ +\hline +\indattr{pos\_offset} & Position to look at on offset (or empty for no change of look direction) (pos, m) & 0 0 0\\ +\hline +\indattr{pos\_onset} & Position to look at on onset (or empty to look at vertex position) (pos, m) & 0 0 0\\ +\hline +\indattr{tau} & Time constant of level estimation (double, s) & 1\\ +\hline +\indattr{threshold} & Level threshold (double, dB SPL) & 53.9794\\ +\hline +\indattr{thresholdpath} & Destination path of threshold criterion (or empty) (string) & \\ +\hline +\indattr{url} & Target OSC URL (string) & {\tiny osc.udp://localhost:9999/}\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabloopmachine.tex b/manual/modtabtex/tabloopmachine.tex new file mode 100644 index 00000000..65df5ddb --- /dev/null +++ b/manual/modtabtex/tabloopmachine.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:loopmachine} +Attributes of element {\bf loopmachine}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{bpm} & Beats per minute (double) & 120\\ +\hline +\indattr{bypass} & Start in bypass mode (bool) & false\\ +\hline +\indattr{delaycomp} & Delay compensation (double, s) & 0\\ +\hline +\indattr{durationbeats} & Record duration (double, beats) & 4\\ +\hline +\indattr{gain} & Playback gain (float, dB) & 0\\ +\hline +\indattr{muteinput} & Mute input while not recording (bool) & false\\ +\hline +\indattr{ramplen} & Ramp length (double, s) & 0.01\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablsl.tex b/manual/modtabtex/tablsl.tex new file mode 100644 index 00000000..30656b02 --- /dev/null +++ b/manual/modtabtex/tablsl.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:lsl} +Attributes of element {\bf lsl}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{predicate} & LSL stream resolving predicate, e.g., "name='EEG'" (string) & \\ +\hline +\indattr{required} & Require this stream. If true, then loading will fail if stream is not available. (bool) & true\\ +\hline +\indattr{tctimeout} & Time correction timeout (double, s) & 2\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablsl2osc.tex b/manual/modtabtex/tablsl2osc.tex new file mode 100644 index 00000000..64d22b03 --- /dev/null +++ b/manual/modtabtex/tablsl2osc.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:lsl2osc} +Attributes of element {\bf lsl2osc}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{prefix} & OSC path prefix, "/" + name will be appended. (string) & /lsl2osc\\ +\hline +\indattr{streams} & List of stream names to transmit (string array) & \\ +\hline +\indattr{url} & OSC target URL, or empty to dispatch locally. (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabltcgen.tex b/manual/modtabtex/tabltcgen.tex new file mode 100644 index 00000000..b2056b0a --- /dev/null +++ b/manual/modtabtex/tabltcgen.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:ltcgen} +Attributes of element {\bf ltcgen}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{addtime} & Add time, e.g., for time zone compensation (double, s) & 0\\ +\hline +\indattr{connect} & Space-separated list of output port connections (string array) & \\ +\hline +\indattr{fpsden} & Frames per second, denominator (double) & 1\\ +\hline +\indattr{fpsnum} & Frames per second, numerator (double) & 25\\ +\hline +\indattr{usewallclock} & Use wallclock time instead of session time (bool) & false\\ +\hline +\indattr{volume} & Signal volume (double, dB re FS) & -18\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabmask.tex b/manual/modtabtex/tabmask.tex new file mode 100644 index 00000000..62ddc540 --- /dev/null +++ b/manual/modtabtex/tabmask.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:mask} +Attributes of element {\bf mask}, inheriting from \hyperref[attrtab:objects]{{\bf objects}} \hyperref[attrtab:routes]{{\bf routes}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{falloff} & ramp length at boundaries (double, m) & 1\\ +\hline +\indattr{inside} & mask inner objects (bool) & false\\ +\hline +\indattr{size} & dimension of mask (pos, m) & 0 0 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabmaskpluginfig8.tex b/manual/modtabtex/tabmaskpluginfig8.tex new file mode 100644 index 00000000..f9375dba --- /dev/null +++ b/manual/modtabtex/tabmaskpluginfig8.tex @@ -0,0 +1,17 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:maskpluginfig8} +Attributes of maskplugin element {\bf fig8}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{drawradius} & Draw mask plugin with this radius in TASCAR GUI, 0 for no drawing. (float, m) & 0\\ +\hline +\indattr{type} & mask plugin type (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabmaskpluginmultibeam.tex b/manual/modtabtex/tabmaskpluginmultibeam.tex new file mode 100644 index 00000000..81776756 --- /dev/null +++ b/manual/modtabtex/tabmaskpluginmultibeam.tex @@ -0,0 +1,31 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:maskpluginmultibeam} +Attributes of maskplugin element {\bf multibeam}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{az} & Azimuth of steering vectors (float array, deg) & 0\\ +\hline +\indattr{drawradius} & Draw mask plugin with this radius in TASCAR GUI, 0 for no drawing. (float, m) & 0\\ +\hline +\indattr{el} & Elevation of steering vectors (float array, deg) & 0\\ +\hline +\indattr{gain} & On-axis gain (float array, dB) & 0\\ +\hline +\indattr{maxgain} & Maximum gain (float, dB) & 0\\ +\hline +\indattr{mingain} & Minimum gain (float, dB) & -inf\\ +\hline +\indattr{numbeams} & Number of beams (uint32) & 1\\ +\hline +\indattr{selectivity} & Selectivity, 0 = omni, 1 = cardioid (6 dB threshold) (float array, 1/pi) & 1\\ +\hline +\indattr{type} & mask plugin type (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabmaterial.tex b/manual/modtabtex/tabmaterial.tex new file mode 100644 index 00000000..1f967254 --- /dev/null +++ b/manual/modtabtex/tabmaterial.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:material} +Attributes of element {\bf material}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{alpha} & Absorption coefficients (float array) & {\tiny 0.013 0.015 0.02 0.03 0.04 0.05}\\ +\hline +\indattr{f} & Frequencies at which alpha is provided (float array, Hz) & {\tiny 125 250 500 1000 2000 4000}\\ +\hline +\indattr{name} & Name of material (string) & plaster\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabmetronome.tex b/manual/modtabtex/tabmetronome.tex new file mode 100644 index 00000000..554e88b0 --- /dev/null +++ b/manual/modtabtex/tabmetronome.tex @@ -0,0 +1,35 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:metronome} +Attributes of element {\bf metronome}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{a1} & Amplitude of first beat (double, dB SPL) & 40\\ +\hline +\indattr{ao} & Amplitude of other beats (double, dB SPL) & 33.9794\\ +\hline +\indattr{bpb} & Beats per bar (int32 array) & 4\\ +\hline +\indattr{bpm} & Beats per minute (double) & 120\\ +\hline +\indattr{bypass} & Load in bypass mode (bool) & false\\ +\hline +\indattr{changeonone} & Apply OSC parameter changes on next bar (bool) & false\\ +\hline +\indattr{fres1} & Resonance frequency of first beat (double, Hz) & 1000\\ +\hline +\indattr{freso} & Resonance frequency of other beats (double, Hz) & 600\\ +\hline +\indattr{q1} & Filter resonance of first beat (double) & 0.997\\ +\hline +\indattr{qo} & Filter resonance of other beats (double) & 0.997\\ +\hline +\indattr{sync} & Use object time synchronization (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabmic.tex b/manual/modtabtex/tabmic.tex new file mode 100644 index 00000000..46f8a5f8 --- /dev/null +++ b/manual/modtabtex/tabmic.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:mic} +Attributes of element {\bf mic}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{delay} & delay line model type, "freefield" or "sphere" (string) & freefield\\ +\hline +\indattr{name} & microphone label (string) & \\ +\hline +\indattr{position} & microphone position relative to receiver origin (pos, m) & 0 0 0\\ +\hline +\indattr{sincorder} & Sinc interpolation order of delay line (double) & 0\\ +\hline +\indattr{sincsampling} & Sampling of sinc table, or 0 for direct calculation (uint32) & 64\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabmidicc2osc.tex b/manual/modtabtex/tabmidicc2osc.tex new file mode 100644 index 00000000..07cc34e2 --- /dev/null +++ b/manual/modtabtex/tabmidicc2osc.tex @@ -0,0 +1,31 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:midicc2osc} +Attributes of element {\bf midicc2osc}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{connect} & name of input ALSA MIDI source (string) & \\ +\hline +\indattr{controllers} & List of controllers, in ``channel/param'' form (e.g., 0/13 0/28) (string array) & \\ +\hline +\indattr{dumpmsg} & Dump unprocessed messages to console (bool) & false\\ +\hline +\indattr{dumppath} & Path to send unprocessed messages (string) & \\ +\hline +\indattr{max} & maximum output value (corresponding to MIDI 127) (double) & 1\\ +\hline +\indattr{min} & minimum output value (corresponding to MIDI 0) (double) & 0\\ +\hline +\indattr{name} & name of MIDI client (string) & \\ +\hline +\indattr{path} & OSC path (string) & /midicc\\ +\hline +\indattr{url} & OSC destination URL (string) & {\tiny osc.udp://localhost:7777/}\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabmididispatch.tex b/manual/modtabtex/tabmididispatch.tex new file mode 100644 index 00000000..14f33cdb --- /dev/null +++ b/manual/modtabtex/tabmididispatch.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:mididispatch} +Attributes of element {\bf mididispatch}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{connect} & ALSA device name to connect to (string) & \\ +\hline +\indattr{copyccpath} & OSC path for copied CC events (string) & /cc\\ +\hline +\indattr{copynotepath} & OSC path for copied note events (string) & /note\\ +\hline +\indattr{copyurl} & OSC URL to copy outgoing MIDI messages to. (string) & \\ +\hline +\indattr{dumpmsg} & Dump all unrecognized messages to console (bool) & true\\ +\hline +\indattr{name} & ALSA MIDI name (string) & mididispatch\\ +\hline +\indattr{oscinput} & Create additional OSC inputs (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabmsg.tex b/manual/modtabtex/tabmsg.tex new file mode 100644 index 00000000..4fad0265 --- /dev/null +++ b/manual/modtabtex/tabmsg.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:msg} +Attributes of element {\bf msg}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{path} & OSC path name (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabnavmesh.tex b/manual/modtabtex/tabnavmesh.tex new file mode 100644 index 00000000..b6783f2c --- /dev/null +++ b/manual/modtabtex/tabnavmesh.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:navmesh} +Attributes of element {\bf navmesh}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{importraw} & file name of vertex list (string) & \\ +\hline +\indattr{maxstep} & maximum step height of object (double, m) & 0.5\\ +\hline +\indattr{zshift} & shift object vertically (double, m) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabnoise.tex b/manual/modtabtex/tabnoise.tex new file mode 100644 index 00000000..8970a980 --- /dev/null +++ b/manual/modtabtex/tabnoise.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:noise} +Attributes of element {\bf noise}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{a} & Noise level (double, dB SPL) & 33.9794\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabobjects.tex b/manual/modtabtex/tabobjects.tex new file mode 100644 index 00000000..867f7548 --- /dev/null +++ b/manual/modtabtex/tabobjects.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:objects} +Attributes of element {\bf objects} ({\hyperref[attrtab:boundingbox]{boundingbox}} {\hyperref[attrtab:diffuse]{diffuse}} {\hyperref[attrtab:face]{face}} {\hyperref[attrtab:facegroup]{facegroup}} {\hyperref[attrtab:mask]{mask}} {\hyperref[attrtab:obstacle]{obstacle}} {\hyperref[attrtab:receiver]{receiver}} {\hyperref[attrtab:reverb]{reverb}} {\hyperref[attrtab:source]{source}})\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{dlocation} & delta location (pos, m) & 0 0 0\\ +\hline +\indattr{dorientation} & delta orientation (Euler rot, deg) & 0 0 0\\ +\hline +\indattr{localpos} & local position (pos, m) & 0 0 0\\ +\hline +\indattr{parent} & Name of parent object from same scene (string) & \\ +\hline +\indattr{sampledorientation} & sample orientation by line fit into curve (double, m) & 0\\ +\hline +\indattr{start} & time when rendering of object starts (double, s) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabobstacle.tex b/manual/modtabtex/tabobstacle.tex new file mode 100644 index 00000000..4908f421 --- /dev/null +++ b/manual/modtabtex/tabobstacle.tex @@ -0,0 +1,21 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:obstacle} +Attributes of element {\bf obstacle}, inheriting from \hyperref[attrtab:objects]{{\bf objects}} \hyperref[attrtab:routes]{{\bf routes}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{aperture} & Override aperture of airy disk calculation, zero for calculation from area (float, m) & 0\\ +\hline +\indattr{importraw} & file name of vertex list (string) & \\ +\hline +\indattr{ishole} & Simulate infinite plane with hole instead of finite surface (bool) & false\\ +\hline +\indattr{transmission} & transmission coefficient (float) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabonsetdetector.tex b/manual/modtabtex/tabonsetdetector.tex new file mode 100644 index 00000000..c28ad326 --- /dev/null +++ b/manual/modtabtex/tabonsetdetector.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:onsetdetector} +Attributes of element {\bf onsetdetector}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{path} & Destination OSC path (string) & \\ +\hline +\indattr{side} & (string) & \\ +\hline +\indattr{tau} & Level estimator time constant (double, s) & 1\\ +\hline +\indattr{taumin} & Trigger blocking time (double, s) & 0.05\\ +\hline +\indattr{threshold} & Detection threshold (double, dB SPL) & 53.9794\\ +\hline +\indattr{url} & Destination OSC URL (string) & {\tiny osc.udp://localhost:9999/}\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabosc.tex b/manual/modtabtex/tabosc.tex new file mode 100644 index 00000000..810e247c --- /dev/null +++ b/manual/modtabtex/tabosc.tex @@ -0,0 +1,21 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:osc} +Attributes of element {\bf osc}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{ignorefirst} & Ignore first value in visualization. (bool) & false\\ +\hline +\indattr{path} & OSC path name, expecting messages with 'd' format (usedouble=true) or 'f' format. (string) & \\ +\hline +\indattr{size} & Numer of double/float values per sample. (uint32) & 1\\ +\hline +\indattr{usedouble} & Use double precision OSC variable instead of single precision. (bool) & true\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabosc2lsl.tex b/manual/modtabtex/tabosc2lsl.tex new file mode 100644 index 00000000..5b96e9c4 --- /dev/null +++ b/manual/modtabtex/tabosc2lsl.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:osc2lsl} +Attributes of element {\bf osc2lsl}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{first\_row\_is\_timestamp} & Use data of first row as LSL time stamp (bool) & false\\ +\hline +\indattr{lslname} & LSL name (string) & osc2lsl\\ +\hline +\indattr{lsltype} & LSL type (string) & osc2lsl\\ +\hline +\indattr{path} & OSC path name (string) & /osc2lsl\\ +\hline +\indattr{retval} & OSC return value: 0 = handle messages also locally, non-0 = mark message as handled, do not handle locally (int32) & 1\\ +\hline +\indattr{size} & Dimension of variable (uint32) & 1\\ +\hline +\indattr{source\_id} & LSL source ID (string) & osc2lsl29\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/taboscactor.tex b/manual/modtabtex/taboscactor.tex new file mode 100644 index 00000000..599b436e --- /dev/null +++ b/manual/modtabtex/taboscactor.tex @@ -0,0 +1,29 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:oscactor} +Attributes of element {\bf oscactor}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{actor} & pattern to match actor objects (string array) & \\ +\hline +\indattr{channels} & Which channels to use (int32 array) & \\ +\hline +\indattr{incremental} & Add transformation to current delta transformation, e.g., when used together with other motion controllers (bool) & false\\ +\hline +\indattr{influence} & Influence of OSC values on the selected movement channels (float array) & \\ +\hline +\indattr{inputchannels} & Number of OSC channels (uint32) & 6\\ +\hline +\indattr{local} & Use transformations in local coordinates (bool) & false\\ +\hline +\indattr{path} & OSC path (string) & \\ +\hline +\indattr{prefix} & OSC prefix for influence variable (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabosceog.tex b/manual/modtabtex/tabosceog.tex new file mode 100644 index 00000000..99e4da0e --- /dev/null +++ b/manual/modtabtex/tabosceog.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:osceog} +Attributes of element {\bf osceog}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{connectwlan} & connect to sensor to external WLAN (bool) & false\\ +\hline +\indattr{eogpath} & OSC target path for EOG data, or empty for no EOG (string) & /eog\\ +\hline +\indattr{name} & Prefix in OSC control variables (string) & osceog\\ +\hline +\indattr{srate} & Sensor sampling rate (8, 16, 32, 64, 128, 250, 475, 860) (uint32, Hz) & 128\\ +\hline +\indattr{targetip} & target IP address when using external WLAN (string) & \\ +\hline +\indattr{wlanpass} & passphrase of external WLAN (string) & \\ +\hline +\indattr{wlanssid} & SSID of external WLAN (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/taboscheadtracker.tex b/manual/modtabtex/taboscheadtracker.tex new file mode 100644 index 00000000..ccdd89ce --- /dev/null +++ b/manual/modtabtex/taboscheadtracker.tex @@ -0,0 +1,49 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:oscheadtracker} +Attributes of element {\bf oscheadtracker}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{actor} & pattern to match actor objects (string array) & \\ +\hline +\indattr{apply\_loc} & Apply translation based on accelerometer (not implemented) (bool) & false\\ +\hline +\indattr{apply\_rot} & Apply rotation based on gyroscope and accelerometer (bool) & true\\ +\hline +\indattr{autoref} & Filter coefficient for estimating reference orientation from average direction, or zero for no auto-referencing (double) & 1e-05\\ +\hline +\indattr{autoref\_zonly} & Compensate z-rotation only, requires sensor alignment (bool) & true\\ +\hline +\indattr{combinegyr} & Combine quaternions with gyroscope based second estimate for increased resolution of pose estimation. (bool) & true\\ +\hline +\indattr{connectwlan} & connect to sensor to external WLAN (bool) & false\\ +\hline +\indattr{eogpath} & OSC target path for EOG data, or empty for no EOG (string) & \\ +\hline +\indattr{name} & Prefix in OSC control variables (string) & oscheadtracker\\ +\hline +\indattr{rawpath} & OSC target path for raw data, or empty for no raw data (string) & \\ +\hline +\indattr{rotpath} & OSC target path for rotation data (string) & \\ +\hline +\indattr{roturl} & OSC target URL for rotation data (string) & \\ +\hline +\indattr{smooth} & Filter coefficient for smoothing of quaternions (double) & 0.1\\ +\hline +\indattr{targetip} & target IP address when using external WLAN (string) & \\ +\hline +\indattr{ttl} & Time-to-live of OSC multicast data (uint32) & 1\\ +\hline +\indattr{url} & Target URL for OSC data logging, or empty for no datalogging (string) & \\ +\hline +\indattr{wlanpass} & passphrase of external WLAN (string) & \\ +\hline +\indattr{wlanssid} & SSID of external WLAN (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/taboscjacktime.tex b/manual/modtabtex/taboscjacktime.tex new file mode 100644 index 00000000..dfe9f650 --- /dev/null +++ b/manual/modtabtex/taboscjacktime.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:oscjacktime} +Attributes of element {\bf oscjacktime}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{path} & Destination OSC path (string) & /time\\ +\hline +\indattr{skip} & Skip this number of blocks between sending (uint32, blocks) & 0\\ +\hline +\indattr{threaded} & Use additional thread for sending data (bool) & true\\ +\hline +\indattr{ttl} & Time-to-live of UDP messages (uint32) & 1\\ +\hline +\indattr{url} & Destination URL (string) & {\tiny osc.udp://localhost:9999/}\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/taboscrelay.tex b/manual/modtabtex/taboscrelay.tex new file mode 100644 index 00000000..1c7221f4 --- /dev/null +++ b/manual/modtabtex/taboscrelay.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:oscrelay} +Attributes of element {\bf oscrelay}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{newpath} & Replace incoming path with this path, or empty for no replacement (string) & \\ +\hline +\indattr{path} & Path filter, or empty to match any path (string) & \\ +\hline +\indattr{retval} & Return value: 0 = handle messages also locally, non-0 = do not handle locally (int32) & 1\\ +\hline +\indattr{startswith} & Forward only messags which start with this path (string) & \\ +\hline +\indattr{trimstart} & Trim startswith part of the path before forwarding (bool) & false\\ +\hline +\indattr{url} & Target OSC URL (string) & {\tiny osc.udp://localhost:9000/}\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/taboscs.tex b/manual/modtabtex/taboscs.tex new file mode 100644 index 00000000..506078dd --- /dev/null +++ b/manual/modtabtex/taboscs.tex @@ -0,0 +1,17 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:oscs} +Attributes of element {\bf oscs}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{ds\_format} & Use ds format, i.e., a double in addition to the string. (bool) & false\\ +\hline +\indattr{path} & OSC path name, expecting messages with 's' format (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabpink.tex b/manual/modtabtex/tabpink.tex new file mode 100644 index 00000000..eb48921d --- /dev/null +++ b/manual/modtabtex/tabpink.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:pink} +Attributes of element {\bf pink}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{alpha} & Frequency exponent alpha, 1 = pink (double) & 2\\ +\hline +\indattr{fmax} & Maximum frequency (double, Hz) & 4000\\ +\hline +\indattr{fmin} & Minimum frequency (double, Hz) & 62.5\\ +\hline +\indattr{level} & RMS level (double, dB SPL) & 33.9794\\ +\hline +\indattr{mute} & load muted (bool) & false\\ +\hline +\indattr{period} & Period time of frozen noise (double, s) & 4\\ +\hline +\indattr{use\_transport} & Play only if transport is running (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabplugins.tex b/manual/modtabtex/tabplugins.tex new file mode 100644 index 00000000..31b04074 --- /dev/null +++ b/manual/modtabtex/tabplugins.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:plugins} +Attributes of element {\bf plugins}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{profilingpath} & OSC path to dispatch profiling information to (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabports.tex b/manual/modtabtex/tabports.tex new file mode 100644 index 00000000..e5d18b46 --- /dev/null +++ b/manual/modtabtex/tabports.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:ports} +Attributes of element {\bf ports} ({\hyperref[attrtab:diffuse]{diffuse}} {\hyperref[attrtab:receiver]{receiver}} {\hyperref[attrtab:sound]{sound}})\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{caliblevel} & calibration level (float, dB SPL) & 93.9794\\ +\hline +\indattr{connect} & Regular expressions of port names for connections (string array) & \\ +\hline +\indattr{gain} & port gain (float, dB) & 0\\ +\hline +\indattr{inv} & phase invert (bool) & false\\ +\hline +\indattr{layers} & render layers (bits32) & all\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabpos2osc.tex b/manual/modtabtex/tabpos2osc.tex new file mode 100644 index 00000000..c1e57f25 --- /dev/null +++ b/manual/modtabtex/tabpos2osc.tex @@ -0,0 +1,45 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:pos2osc} +Attributes of element {\bf pos2osc}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{addparentname} & When sending sound vertex positions, add parent name to vertex name (bool) & false\\ +\hline +\indattr{avatar} & Name of object to be controlled (for control of game engines) (string) & \\ +\hline +\indattr{ignoreorientation} & Ignore delta-orientation of source, send zeros instead (bool) & false\\ +\hline +\indattr{lookatlen} & Duration of look-at animation (for control of game engines) (double, s) & 1\\ +\hline +\indattr{mode} & Message format mode (uint32) & 0\\ +\hline +\indattr{name} & Default name used in OSC variables (string) & pos2osc\\ +\hline +\indattr{orientationname} & Name for orientation variables (string) & /headGaze\\ +\hline +\indattr{oscale} & Scaling factor for orientations (float) & 1\\ +\hline +\indattr{pattern} & Pattern of TASCAR object names; see actor module documentation for details. (string array) & /*/*\\ +\hline +\indattr{sendsounds} & Send also position of sound vertices (bool) & false\\ +\hline +\indattr{skip} & Skip frames to reduce network traffic (uint32) & 0\\ +\hline +\indattr{threaded} & Use additional thread for sending data to avoid blocking of real-time audio thread (bool) & true\\ +\hline +\indattr{transport} & Send only while transport is rolling (bool) & true\\ +\hline +\indattr{triggered} & Send data only when triggered via OSC (bool) & false\\ +\hline +\indattr{ttl} & Time to live of OSC multicast messages (uint32) & 1\\ +\hline +\indattr{url} & Target URL (string) & {\tiny osc.udp://localhost:9999/}\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabposition.tex b/manual/modtabtex/tabposition.tex new file mode 100644 index 00000000..7bc3dbaf --- /dev/null +++ b/manual/modtabtex/tabposition.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:position} +Attributes of element {\bf position}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{importcsv} & Read position track from the {\tt .csv}-file as comma-separated values. The file name can contain absolute or relative path. Relative paths are relative to the session's {\tt .tsc}-file. Default: position track is contained as space-separated text between opening and closing \refelem{position} tags. (string) & \\ +\hline +\indattr{interpolation} & Coordinate system in which positions are linearly interpolated between given positions. Possible values are cartesian and spherical. (string) & cartesian\\ +\hline +\indattr{loop} & The value, if greater than 0, specifies the time when this position track is repeated from 0 (double, s) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabpulse.tex b/manual/modtabtex/tabpulse.tex new file mode 100644 index 00000000..3f8762f7 --- /dev/null +++ b/manual/modtabtex/tabpulse.tex @@ -0,0 +1,17 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:pulse} +Attributes of element {\bf pulse}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{a} & Pulse amplitude (double, Pa) & 0.001\\ +\hline +\indattr{f} & Pulse frequency (double, Hz) & 1000\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabqualisys.tex b/manual/modtabtex/tabqualisys.tex new file mode 100644 index 00000000..43bbb23c --- /dev/null +++ b/manual/modtabtex/tabqualisys.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:qualisys} +Attributes of element {\bf qualisys}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{alivetimeout} & undocumented (double) & 1\\ +\hline +\indattr{dataprefix} & OSC path prefix, will be followed by slash + rigid names (string) & \\ +\hline +\indattr{dataurl} & OSC URL where data is sent to (or empty for no OSC sending) (string) & \\ +\hline +\indattr{name} & undocumented (string) & qualisys\\ +\hline +\indattr{qtmurl} & Qualisys Track Manager URL of USC interface (string) & {\tiny osc.udp://localhost:22225/}\\ +\hline +\indattr{timeout} & Timeout (double, s) & 1\\ +\hline +\indattr{uselsl} & Create LSL output stream (bool) & true\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabrange.tex b/manual/modtabtex/tabrange.tex new file mode 100644 index 00000000..b977e6b8 --- /dev/null +++ b/manual/modtabtex/tabrange.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:range} +Attributes of element {\bf range}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{end} & end time (double, s) & 0\\ +\hline +\indattr{name} & range name (string) & \\ +\hline +\indattr{start} & start time (double, s) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiver.tex b/manual/modtabtex/tabreceiver.tex new file mode 100644 index 00000000..7965ac88 --- /dev/null +++ b/manual/modtabtex/tabreceiver.tex @@ -0,0 +1,65 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiver} +Attributes of element {\bf receiver} ({\hyperref[attrtab:receiveramb1h0v]{amb1h0v}} {\hyperref[attrtab:receiveramb1h1v]{amb1h1v}} {\hyperref[attrtab:receiveramb3h0v]{amb3h0v}} {\hyperref[attrtab:receiveramb3h3v]{amb3h3v}} {\hyperref[attrtab:receivercardioid]{cardioid}} {\hyperref[attrtab:receiverchmap]{chmap}} {\hyperref[attrtab:receiverdebugpos]{debugpos}} {\hyperref[attrtab:receiverfakebf]{fakebf}} {\hyperref[attrtab:receiverhann]{hann}} {\hyperref[attrtab:receiverhoa2d]{hoa2d}} {\hyperref[attrtab:receiverhoa2d_fuma]{hoa2d\_fuma}} {\hyperref[attrtab:receiverhoa3d]{hoa3d}} {\hyperref[attrtab:receiverhoa3d_enc]{hoa3d\_enc}} {\hyperref[attrtab:receiverhrtf]{hrtf}} {\hyperref[attrtab:receiverintensityvector]{intensityvector}} {\hyperref[attrtab:receiveritu51]{itu51}} {\hyperref[attrtab:receivermicarray]{micarray}} {\hyperref[attrtab:receivernsp]{nsp}} {\hyperref[attrtab:receiveromni]{omni}} {\hyperref[attrtab:receiverortf]{ortf}} {\hyperref[attrtab:receivervbap]{vbap}} {\hyperref[attrtab:receivervbap3d]{vbap3d}} {\hyperref[attrtab:receivervmic]{vmic}} {\hyperref[attrtab:receiverwfs]{wfs}}), inheriting from \hyperref[attrtab:objects]{{\bf objects}} \hyperref[attrtab:ports]{{\bf ports}} \hyperref[attrtab:routes]{{\bf routes}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{avgdist} & Average distance which is assumed inside receiver boxes, or 0 to use $(\frac18 V)^{1/3}$ (float, m) & 0\\ +\hline +\indattr{delaycomp} & subtract this value from delay in delay lines (float, s) & 0\\ +\hline +\indattr{diffuse} & render diffuse sources (bool) & true\\ +\hline +\indattr{diffusegain} & gain of diffuse sources (float, dB) & 0\\ +\hline +\indattr{fade\_gain} & linear fade gain (float) & 1\\ +\hline +\indattr{falloff} & Length of von-Hann ramp at volume boundaries, or -1 for normal distance model (float, m) & -1\\ +\hline +\indattr{globalmask} & use global mask (bool) & true\\ +\hline +\indattr{image} & render image sources (bool) & true\\ +\hline +\indattr{ismmax} & maximal ISM order to render (uint32) & 2147483647\\ +\hline +\indattr{ismmin} & minimal ISM order to render (uint32) & 0\\ +\hline +\indattr{layerfadelen} & duration of fades between layers (float, s) & 1\\ +\hline +\indattr{muteonstop} & mute when transport stopped to prevent playback of sounds from delaylines and reverb (bool) & false\\ +\hline +\indattr{point} & render point sources (bool) & true\\ +\hline +\indattr{proxy\_airabsorption} & Use proxy position for air absorption (bool) & false\\ +\hline +\indattr{proxy\_delay} & Use proxy position for delay (bool) & false\\ +\hline +\indattr{proxy\_direction} & Use proxy position for direction (bool) & false\\ +\hline +\indattr{proxy\_gain} & Use proxy position for gain (bool) & false\\ +\hline +\indattr{proxy\_is\_relative} & Proxy is relative to receiver (true) or in absolute coordinates (false) (bool) & false\\ +\hline +\indattr{proxy\_position} & Proxy position (pos, m) & 0 0 0\\ +\hline +\indattr{scatterdamping} & damping of scatter reflection filter (float) & 0\\ +\hline +\indattr{scatterreflections} & Number of reflections created by scattering filter (uint32) & 0\\ +\hline +\indattr{scatterspread} & Spatial spread of scattering (float, deg) & 22.5\\ +\hline +\indattr{scatterstructuresize} & size of scatter structure (float, m) & 1\\ +\hline +\indattr{type} & receiver type (string) & omni\\ +\hline +\indattr{volumetric} & volume in which receiver does not apply distance based gain model (pos, m) & 0 0 0\\ +\hline +\indattr{volumetricgainwithdistance} & For volumetric receivers, increase gain with distance (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiveramb1h1v.tex b/manual/modtabtex/tabreceiveramb1h1v.tex new file mode 100644 index 00000000..a632a8cd --- /dev/null +++ b/manual/modtabtex/tabreceiveramb1h1v.tex @@ -0,0 +1,17 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiveramb1h1v} +Attributes of receiver element {\bf amb1h1v}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{channelorder} & Channel order, either ``ACN'' (wyzx) or ``FuMa'' (wxyz) (string) & ACN\\ +\hline +\indattr{normalization} & Normalization, either ``FuMa'' or ``SN3D'' (string) & FuMa\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverchmap.tex b/manual/modtabtex/tabreceiverchmap.tex new file mode 100644 index 00000000..b73cb6b8 --- /dev/null +++ b/manual/modtabtex/tabreceiverchmap.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverchmap} +Attributes of receiver element {\bf chmap}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{channels} & number of output channels (uint32) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverdebugpos.tex b/manual/modtabtex/tabreceiverdebugpos.tex new file mode 100644 index 00000000..d8a7d542 --- /dev/null +++ b/manual/modtabtex/tabreceiverdebugpos.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverdebugpos} +Attributes of receiver element {\bf debugpos}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{sources} & number of sources to output (uint32) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverfakebf.tex b/manual/modtabtex/tabreceiverfakebf.tex new file mode 100644 index 00000000..e524b7b7 --- /dev/null +++ b/manual/modtabtex/tabreceiverfakebf.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverfakebf} +Attributes of receiver element {\bf fakebf}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{angle} & Angular distance between microphone axes (double, deg) & 110\\ +\hline +\indattr{c} & Speed of sound (double, m/s) & 340\\ +\hline +\indattr{distance} & Microphone distance (double, m) & 0.17\\ +\hline +\indattr{sincorder} & Sinc interpolation order of ITD delay line (uint32) & 0\\ +\hline +\indattr{start\_angle} & Angle at which attenutation ramp starts (double, deg) & 0\\ +\hline +\indattr{stop\_angle} & Angle at which full attenutation is reached (double, deg) & 90\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverhann.tex b/manual/modtabtex/tabreceiverhann.tex new file mode 100644 index 00000000..743e3ec9 --- /dev/null +++ b/manual/modtabtex/tabreceiverhann.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverhann} +Attributes of receiver element {\bf hann}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}} \hyperref[attrtab:speakerbased]{{\bf speakerbased}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{wexp} & window exponent $\gamma$ (double) & 0.5\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverhoa2d.tex b/manual/modtabtex/tabreceiverhoa2d.tex new file mode 100644 index 00000000..c3e9982a --- /dev/null +++ b/manual/modtabtex/tabreceiverhoa2d.tex @@ -0,0 +1,29 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverhoa2d} +Attributes of receiver element {\bf hoa2d}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}} \hyperref[attrtab:speakerbased]{{\bf speakerbased}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{diffup} & Use diffuse upsampling similar to \citet{Zotter2014} (bool) & false\\ +\hline +\indattr{diffup\_delay} & Decorrelation delay (double, s) & 0.01\\ +\hline +\indattr{diffup\_maxorder} & Maximum order of diffuse sound fields (uint32) & 100\\ +\hline +\indattr{diffup\_rot} & Decorrelation rotation (double, deg) & 45\\ +\hline +\indattr{filterperiod} & Filter period for source width encoding (double, s) & 0.005\\ +\hline +\indattr{filtershape} & De-correlation filter shape for source width encoding, one of ``none'', ``notch'', ``sine'', ``tria'', ``triald'' (string) & none\\ +\hline +\indattr{maxre} & Use $\max r\_E$ decoder (true) or basic decoder (false) (bool) & false\\ +\hline +\indattr{order} & Ambisonics order; 0: use maximum possible (uint32) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverhoa2d_fuma.tex b/manual/modtabtex/tabreceiverhoa2d_fuma.tex new file mode 100644 index 00000000..d696f448 --- /dev/null +++ b/manual/modtabtex/tabreceiverhoa2d_fuma.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverhoa2d_fuma} +Attributes of receiver element {\bf hoa2d\_fuma}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{diffup} & Use diffuse upsampling similar to \citet{Zotter2014} (bool) & false\\ +\hline +\indattr{diffup\_delay} & Decorrelation delay (double, s) & 0.01\\ +\hline +\indattr{diffup\_maxorder} & Maximum order of diffuse sound fields (uint32) & 100\\ +\hline +\indattr{diffup\_rot} & Decorrelation rotation (double, deg) & 45\\ +\hline +\indattr{filterperiod} & Filter period for source width encoding (double, s) & 0.005\\ +\hline +\indattr{filtershape} & De-correlation filter shape for source width encoding, one of ``none'', ``notch'', ``sine'', ``tria'', ``triald'' (string) & none\\ +\hline +\indattr{order} & Ambisonics order; 0: use maximum possible (uint32) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverhoa3d.tex b/manual/modtabtex/tabreceiverhoa3d.tex new file mode 100644 index 00000000..02fdfec2 --- /dev/null +++ b/manual/modtabtex/tabreceiverhoa3d.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverhoa3d} +Attributes of receiver element {\bf hoa3d}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}} \hyperref[attrtab:speakerbased]{{\bf speakerbased}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{dectype} & Decoder type, ``basic'', ``maxre'' or ``inphase'' (string) & maxre\\ +\hline +\indattr{decwarnthreshold} & Warning threshold for decoder matrix abs/rms ratio (double) & 8\\ +\hline +\indattr{method} & Decoder generation method, ``pinv'' or ``allrad'' (string) & pinv\\ +\hline +\indattr{order} & Ambisonics order (int32) & 3\\ +\hline +\indattr{savedec} & Save Octave/Matlab script for decoder matrix debugging (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverhoa3d_enc.tex b/manual/modtabtex/tabreceiverhoa3d_enc.tex new file mode 100644 index 00000000..56540be0 --- /dev/null +++ b/manual/modtabtex/tabreceiverhoa3d_enc.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverhoa3d_enc} +Attributes of receiver element {\bf hoa3d\_enc}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{order} & Ambisonics order (int32) & 3\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverhrtf.tex b/manual/modtabtex/tabreceiverhrtf.tex new file mode 100644 index 00000000..bdce6cdc --- /dev/null +++ b/manual/modtabtex/tabreceiverhrtf.tex @@ -0,0 +1,61 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverhrtf} +Attributes of receiver element {\bf hrtf}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{Q\_notch} & quality factor of the notch filter (float) & 2.3\\ +\hline +\indattr{alphamin} & parameter which determines the depth of the high-shelf realizing the SHM (float) & 0.14\\ +\hline +\indattr{alphamin\_front} & parameter which determines the depth of the second high-shelf (float) & 0.39\\ +\hline +\indattr{alphamin\_up} & parameter which determines the depth of the second high-shelf (float) & 0.1\\ +\hline +\indattr{angle} & Position of the ears on the sphere (float, deg) & 90\\ +\hline +\indattr{c} & Speed of sound (float, m/s) & 340\\ +\hline +\indattr{decorr} & Flag to use decorrelation of diffuse sounds (bool) & false\\ +\hline +\indattr{decorr\_length} & Decorrelation length (float, s) & 0.05\\ +\hline +\indattr{diffuse\_hrtf} & apply hrtf model also to diffuse rendering (bool) & false\\ +\hline +\indattr{freq\_end} & notch center frequency at [0 0 1] (float, Hz) & 650\\ +\hline +\indattr{freq\_start} & notch center frequency at \attr{startangle\_notch} (float, Hz) & 1300\\ +\hline +\indattr{gaincorr} & channel-wise gain correction (float array, dB) & 0 0\\ +\hline +\indattr{maxgain} & gain applied at [0 0 1] -- gain is 0 dB at \attr{startangle\_notch} and increases linearly (float, dB) & -5.4\\ +\hline +\indattr{omega} & cut-off frequency of the high-self realizing the SHM (float, Hz) & 3100\\ +\hline +\indattr{omega\_front} & cut-off frequency of the second high-self (float, Hz) & 11200\\ +\hline +\indattr{omega\_up} & cut-off frequency of the second high-shelf in Hz (float, Hz) & 2125\\ +\hline +\indattr{prewarpingmode} & Azimuth pre-warping mode, 0 = original, 1 = none, 2 = corrected (uint32) & 0\\ +\hline +\indattr{radius} & Radius of sphere modeling the head (float, m) & 0.08\\ +\hline +\indattr{sincorder} & Sinc interpolation order of ITD delay line (uint32) & 0\\ +\hline +\indattr{sincsampling} & Sinc table sampling of ITD delay line, or 0 for no table. (uint32) & 64\\ +\hline +\indattr{startangle\_front} & the second high-shelf, e.g. to model pinna shadow effect, is applied when the angle with respect to front direction [1 0 0] is larger than \attr{startangle\_front} (float, deg) & 0\\ +\hline +\indattr{startangle\_notch} & notch filter to model concha notch is applied if angle with respect to up direction [0 0 1] is smaller than \attr{startangle\_notch} (float, deg) & 102\\ +\hline +\indattr{startangle\_up} & the third high-shelf which models the shadow effect of the torso is applied when the angle with respect to up direction [0 0 1] is larger than \attr{startangle\_up} (float, deg) & 135\\ +\hline +\indattr{thetamin} & angle with respect to the position of the ears at which the maximum depth of the high-shelf realizing the SHM is reached (float, deg) & 160\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverintensityvector.tex b/manual/modtabtex/tabreceiverintensityvector.tex new file mode 100644 index 00000000..ea78ca25 --- /dev/null +++ b/manual/modtabtex/tabreceiverintensityvector.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverintensityvector} +Attributes of receiver element {\bf intensityvector}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{tau} & intensity integration time constant (double, s) & 0.125\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiveritu51.tex b/manual/modtabtex/tabreceiveritu51.tex new file mode 100644 index 00000000..e47fcf61 --- /dev/null +++ b/manual/modtabtex/tabreceiveritu51.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiveritu51} +Attributes of receiver element {\bf itu51}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{diffusegainfront} & Diffuse gain for frontal speakers (double, dB) & -6.0206\\ +\hline +\indattr{diffusegainrear} & Diffuse gain for rear speakers (double, dB) & 0\\ +\hline +\indattr{fc} & LFE cut off frequency (double, Hz) & 80\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceivermicarray.tex b/manual/modtabtex/tabreceivermicarray.tex new file mode 100644 index 00000000..6c64f84b --- /dev/null +++ b/manual/modtabtex/tabreceivermicarray.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receivermicarray} +Attributes of receiver element {\bf micarray}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{c} & speed of sound (double, m/s) & 340\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceivernsp.tex b/manual/modtabtex/tabreceivernsp.tex new file mode 100644 index 00000000..bcc02147 --- /dev/null +++ b/manual/modtabtex/tabreceivernsp.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receivernsp} +Attributes of receiver element {\bf nsp}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}} \hyperref[attrtab:speakerbased]{{\bf speakerbased}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{useall} & activate all speakers independent of source position (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverortf.tex b/manual/modtabtex/tabreceiverortf.tex new file mode 100644 index 00000000..89c81fa5 --- /dev/null +++ b/manual/modtabtex/tabreceiverortf.tex @@ -0,0 +1,35 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverortf} +Attributes of receiver element {\bf ortf}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{angle} & Angular distance between microphone axes (double, deg) & 110\\ +\hline +\indattr{attscale} & Scaling factor for cosine attenuation function (double) & 1\\ +\hline +\indattr{broadband} & Use broadband cardioid characteristics (bool) & false\\ +\hline +\indattr{c} & Speed of sound (double, m/s) & 340\\ +\hline +\indattr{decorr} & Flag to use decorrelatin of diffuse sounds (bool) & false\\ +\hline +\indattr{decorr\_length} & Decorrelation length (double, s) & 0.05\\ +\hline +\indattr{distance} & Microphone distance (double, m) & 0.17\\ +\hline +\indattr{f6db} & 6 dB cutoff frequency for 90 degrees (double, Hz) & 3000\\ +\hline +\indattr{fmin} & Cutoff frequency for 180 degrees sounds (double, Hz) & 800\\ +\hline +\indattr{sincorder} & Sinc interpolation order of ITD delay line (uint32) & 0\\ +\hline +\indattr{sincsampling} & Sinc table sampling of ITD delay line, or 0 for no table. (uint32) & 64\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceivervmic.tex b/manual/modtabtex/tabreceivervmic.tex new file mode 100644 index 00000000..faa5ac43 --- /dev/null +++ b/manual/modtabtex/tabreceivervmic.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receivervmic} +Attributes of receiver element {\bf vmic}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{a} & directivity coefficient (double) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreceiverwfs.tex b/manual/modtabtex/tabreceiverwfs.tex new file mode 100644 index 00000000..a674affd --- /dev/null +++ b/manual/modtabtex/tabreceiverwfs.tex @@ -0,0 +1,17 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:receiverwfs} +Attributes of receiver element {\bf wfs}, inheriting from \hyperref[attrtab:receiver]{{\bf receiver}} \hyperref[attrtab:speakerbased]{{\bf speakerbased}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{c} & Speed of sound (float, m/s) & 340\\ +\hline +\indattr{planewave} & Simlate always plane waves independent of distance (bool) & true\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreverb.tex b/manual/modtabtex/tabreverb.tex new file mode 100644 index 00000000..2390dae6 --- /dev/null +++ b/manual/modtabtex/tabreverb.tex @@ -0,0 +1,73 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:reverb} +Attributes of element {\bf reverb} ({\hyperref[attrtab:reverbfoaconv]{foaconv}} {\hyperref[attrtab:reverbsimplefdn]{simplefdn}}), inheriting from \hyperref[attrtab:objects]{{\bf objects}} \hyperref[attrtab:routes]{{\bf routes}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{avgdist} & Average distance which is assumed inside receiver boxes, or 0 to use $(\frac18 V)^{1/3}$ (float, m) & 0\\ +\hline +\indattr{caliblevel} & calibration level (float, dB SPL) & 93.9794\\ +\hline +\indattr{connect} & Regular expressions of port names for connections (string array) & \\ +\hline +\indattr{delaycomp} & subtract this value from delay in delay lines (float, s) & 0\\ +\hline +\indattr{diffuse} & render diffuse input sound fields (bool) & false\\ +\hline +\indattr{fade\_gain} & linear fade gain (float) & 1\\ +\hline +\indattr{falloff} & Length of von-Hann ramp at volume boundaries, or -1 for normal distance model (float, m) & -1\\ +\hline +\indattr{gain} & port gain (float, dB) & 0\\ +\hline +\indattr{globalmask} & use global mask (bool) & true\\ +\hline +\indattr{image} & render image sources (bool) & true\\ +\hline +\indattr{inv} & phase invert (bool) & false\\ +\hline +\indattr{ismmax} & maximal ISM order to render (uint32) & 2147483647\\ +\hline +\indattr{ismmin} & minimal ISM order to render (uint32) & 0\\ +\hline +\indattr{layerfadelen} & duration of fades between layers (float, s) & 1\\ +\hline +\indattr{layers} & render layers (bits32) & all\\ +\hline +\indattr{muteonstop} & mute when transport stopped to prevent playback of sounds from delaylines and reverb (bool) & false\\ +\hline +\indattr{outputlayers} & output layers (bits32) & all\\ +\hline +\indattr{proxy\_airabsorption} & Use proxy position for air absorption (bool) & false\\ +\hline +\indattr{proxy\_delay} & Use proxy position for delay (bool) & false\\ +\hline +\indattr{proxy\_direction} & Use proxy position for direction (bool) & false\\ +\hline +\indattr{proxy\_gain} & Use proxy position for gain (bool) & false\\ +\hline +\indattr{proxy\_is\_relative} & Proxy is relative to receiver (true) or in absolute coordinates (false) (bool) & false\\ +\hline +\indattr{proxy\_position} & Proxy position (pos, m) & 0 0 0\\ +\hline +\indattr{scatterdamping} & damping of scatter reflection filter (float) & 0\\ +\hline +\indattr{scatterreflections} & Number of reflections created by scattering filter (uint32) & 0\\ +\hline +\indattr{scatterspread} & Spatial spread of scattering (float, deg) & 22.5\\ +\hline +\indattr{scatterstructuresize} & size of scatter structure (float, m) & 1\\ +\hline +\indattr{type} & receiver type (string) & omni\\ +\hline +\indattr{volumetric} & volume in which receiver does not apply distance based gain model (pos, m) & 0 0 0\\ +\hline +\indattr{volumetricgainwithdistance} & For volumetric receivers, increase gain with distance (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreverbfoaconv.tex b/manual/modtabtex/tabreverbfoaconv.tex new file mode 100644 index 00000000..d1a00106 --- /dev/null +++ b/manual/modtabtex/tabreverbfoaconv.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:reverbfoaconv} +Attributes of reverb element {\bf foaconv}, inheriting from \hyperref[attrtab:reverb]{{\bf reverb}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{channelorder} & Channel order of FOA response, either ``FuMa'' (wxyz) or ``ACN'' (wyzx) (string) & ACN\\ +\hline +\indattr{irsname} & Name of IRS sound file (string) & \\ +\hline +\indattr{maxlen} & Maximum length of IRS, or 0 to use full sound file (uint32, samples) & 0\\ +\hline +\indattr{normalization} & Normalization of FOA response, either ``FuMa'' or ``SN3D'' (string) & FuMa\\ +\hline +\indattr{offset} & Offset of IR in sound file (uint32, samples) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabreverbsimplefdn.tex b/manual/modtabtex/tabreverbsimplefdn.tex new file mode 100644 index 00000000..0ebf0fc1 --- /dev/null +++ b/manual/modtabtex/tabreverbsimplefdn.tex @@ -0,0 +1,47 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:reverbsimplefdn} +Attributes of reverb element {\bf simplefdn}, inheriting from \hyperref[attrtab:reverb]{{\bf reverb}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{absorption} & Absorption used in Sabine's equation (float) & 0.6\\ +\hline +\indattr{c} & Speed of sound (float, m/s) & 340\\ +\hline +\indattr{damping} & Damping (first order lowpass) coefficient to control spectral tilt of T60 (float) & 0.3\\ +\hline +\indattr{dw} & Spatial spread of rotation (float, rounds/s) & 60\\ +\hline +\indattr{fdnorder} & Order of FDN (number of recursive paths) (uint32) & 5\\ +\hline +\indattr{fixcirculantmat} & Apply fix to correctly initialize circulant feedback matrix (bool) & false\\ +\hline +\indattr{forwardstages} & Number of feed forward stages (uint32) & 0\\ +\hline +\indattr{gainmethod} & Gain calculation method (string, original mean schroeder) & original\\ +\hline +\indattr{lowcut} & low cut off frequency, or zero for no low cut (float, Hz) & 0\\ +\hline +\indattr{numiter} & Number of iterations in T60 optimization (uint32) & 100\\ +\hline +\indattr{prefilt} & Apply additional filter before inserting audio into FDN (bool) & true\\ +\hline +\indattr{rallpass} & Allpass filter radius vector (requires four entries) (float array, [0,1]) & 0.96 0.95 0.951 0.93\\ +\hline +\indattr{t60} & T60, or zero to use Sabine's equation (float, s) & 0\\ +\hline +\indattr{truncate\_forward} & Truncate delays of feed forward path (bool) & false\\ +\hline +\indattr{use\_biquad\_allpass} & Use biquad allpass filters instead of first order filters (bool) & false\\ +\hline +\indattr{vcf} & Center frequencies for T60 optimization, or empty for no optimization (float array, Hz) & \\ +\hline +\indattr{vt60} & T60 at specified center frequencies (float array, s) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabrotator.tex b/manual/modtabtex/tabrotator.tex new file mode 100644 index 00000000..dae588fc --- /dev/null +++ b/manual/modtabtex/tabrotator.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:rotator} +Attributes of element {\bf rotator}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{actor} & pattern to match actor objects (string array) & \\ +\hline +\indattr{mode} & Operation mode (uint32, 0|1|2|3) & 0\\ +\hline +\indattr{phi0} & Start angle (sigmoid/cosine movement) (double, deg) & -90\\ +\hline +\indattr{phi1} & End angle (sigmoid/cosine movement) (double, deg) & 90\\ +\hline +\indattr{t0} & Start time (double, s) & 0\\ +\hline +\indattr{t1} & End time (sigmoid/cosine movement) (double, s) & 1\\ +\hline +\indattr{w} & Angular velocity (double, deg/s) & 10\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabroute.tex b/manual/modtabtex/tabroute.tex new file mode 100644 index 00000000..7de17867 --- /dev/null +++ b/manual/modtabtex/tabroute.tex @@ -0,0 +1,41 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:route} +Attributes of element {\bf route}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{caliblevel} & calibration level (float, dB SPL) & 93.9794\\ +\hline +\indattr{caliblevel\_in} & Input calibration levels (float array, dB SPL) & \\ +\hline +\indattr{channels} & Number of channels (uint32) & 1\\ +\hline +\indattr{connect} & Regular expressions of port names for connections (string array) & \\ +\hline +\indattr{connect\_out} & Regular expressions of output port names (string array) & \\ +\hline +\indattr{gain} & port gain (float, dB) & 0\\ +\hline +\indattr{id} & Unique route id, empty to autogenerate (string) & 28\\ +\hline +\indattr{inv} & phase invert (bool) & false\\ +\hline +\indattr{levelmeter\_tc} & Leq level metering time constant (double, s) & 2\\ +\hline +\indattr{levelmeter\_weight} & level meter weighting (f-weight) & Z\\ +\hline +\indattr{lingain} & linear gain (float) & 1\\ +\hline +\indattr{mute} & Mute flag of route (bool) & false\\ +\hline +\indattr{name} & Route name (string) & \\ +\hline +\indattr{solo} & Solo flag of route (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabroutes.tex b/manual/modtabtex/tabroutes.tex new file mode 100644 index 00000000..ea05087f --- /dev/null +++ b/manual/modtabtex/tabroutes.tex @@ -0,0 +1,27 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:routes} +Attributes of element {\bf routes} ({\hyperref[attrtab:diffuse]{diffuse}} {\hyperref[attrtab:face]{face}} {\hyperref[attrtab:facegroup]{facegroup}} {\hyperref[attrtab:mask]{mask}} {\hyperref[attrtab:obstacle]{obstacle}} {\hyperref[attrtab:receiver]{receiver}} {\hyperref[attrtab:reverb]{reverb}} {\hyperref[attrtab:source]{source}})\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{color} & html color string (string) & \\ +\hline +\indattr{end} & end of render activity, or 0 to render always (double, s) & 0\\ +\hline +\indattr{id} & Unique route id, empty to autogenerate (string) & 22\\ +\hline +\indattr{mute} & Mute flag of route (bool) & false\\ +\hline +\indattr{name} & Route name (string) & \\ +\hline +\indattr{scale} & scale of local coordinates (float) & 1\\ +\hline +\indattr{solo} & Solo flag of route (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabs.tex b/manual/modtabtex/tabs.tex new file mode 100644 index 00000000..7d3a76d5 --- /dev/null +++ b/manual/modtabtex/tabs.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:s} +Attributes of element {\bf s}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{v} & string value (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabscene.tex b/manual/modtabtex/tabscene.tex new file mode 100644 index 00000000..6abf102c --- /dev/null +++ b/manual/modtabtex/tabscene.tex @@ -0,0 +1,29 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:scene} +Attributes of element {\bf scene}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{active} & render scene (bool) & true\\ +\hline +\indattr{c} & speed of sound (double, m/s) & 340\\ +\hline +\indattr{guicenter} & origin of GUI window (pos, m) & 0 0 0\\ +\hline +\indattr{guiscale} & scale of GUI window of this scene (double, m) & 200\\ +\hline +\indattr{guitracking} & object name for scene tracking (string) & \\ +\hline +\indattr{id} & scene id, or empty to auto-generate id (string) & 1\\ +\hline +\indattr{ismorder} & order of image source model (uint32) & 1\\ +\hline +\indattr{name} & scene name (string) & scene\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsession.tex b/manual/modtabtex/tabsession.tex new file mode 100644 index 00000000..434bd862 --- /dev/null +++ b/manual/modtabtex/tabsession.tex @@ -0,0 +1,57 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:session} +Attributes of element {\bf session}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{attribution} & attribution of license, if applicable (string) & \\ +\hline +\indattr{duration} & session duration (double, s) & 60\\ +\hline +\indattr{initcmd} & Command to be executed before first connection to jack. Can be used to start jack server. (string) & \\ +\hline +\indattr{initcmdsleep} & Time to wait for initcmd to start up, in seconds. (double, s) & 0\\ +\hline +\indattr{levelmeter\_min} & Level meter minimum (double, dB SPL) & 30\\ +\hline +\indattr{levelmeter\_mode} & Level meter mode (rms, rmspeak, percentile) (string) & \\ +\hline +\indattr{levelmeter\_range} & Level range of level meters (double, dB) & 70\\ +\hline +\indattr{levelmeter\_tc} & level meter time constant (double, s) & 2\\ +\hline +\indattr{levelmeter\_weight} & level meter weighting (f-weight) & Z\\ +\hline +\indattr{license} & license type (string) & \\ +\hline +\indattr{loop} & loop session at end (bool) & false\\ +\hline +\indattr{name} & session name (string) & tascar\\ +\hline +\indattr{playonload} & start playing when session is loaded (bool) & false\\ +\hline +\indattr{profilingpath} & OSC path to dispatch module profiling information to (string) & \\ +\hline +\indattr{requirefragsize} & Session fragment size, stop loading the session if the system fragment size doesn't match (int32) & 0\\ +\hline +\indattr{requiresrate} & Session sampling rate, stop loading the session if the system sampling rate doesn't match (double, Hz) & 0\\ +\hline +\indattr{srv\_addr} & OSC multicast address in case of UDP transport (string) & \\ +\hline +\indattr{srv\_port} & OSC port number (string) & 9877\\ +\hline +\indattr{srv\_proto} & OSC protocol, UDP or TCP (string) & UDP\\ +\hline +\indattr{starturl} & URL of start page for display (string) & \\ +\hline +\indattr{warnfragsize} & Session fragment size, print a warning if the system fragment size doesn't match (int32) & 0\\ +\hline +\indattr{warnsrate} & Session sampling rate, print a warning if the system sampling rate doesn't match (double, Hz) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsimplecontroller.tex b/manual/modtabtex/tabsimplecontroller.tex new file mode 100644 index 00000000..f9aebca3 --- /dev/null +++ b/manual/modtabtex/tabsimplecontroller.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:simplecontroller} +Attributes of element {\bf simplecontroller}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{actor} & pattern to match actor objects (string array) & \\ +\hline +\indattr{maxnorm} & Maximum distance of object from origin, or zero for no limit. (double, m) & 0\\ +\hline +\indattr{vr} & Angular velocity (double, deg/s) & 90\\ +\hline +\indattr{vx} & Velocity in x direction (double, m/s) & 1\\ +\hline +\indattr{vy} & Velocity in y direction (double, m/s) & 1\\ +\hline +\indattr{vz} & Velocity in y direction (double, m/s) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsine.tex b/manual/modtabtex/tabsine.tex new file mode 100644 index 00000000..4938afe5 --- /dev/null +++ b/manual/modtabtex/tabsine.tex @@ -0,0 +1,17 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:sine} +Attributes of element {\bf sine}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{a} & Amplitude (double, dB SPL) & 33.9794\\ +\hline +\indattr{f} & Frequency (double, Hz) & 1000\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabskyfall.tex b/manual/modtabtex/tabskyfall.tex new file mode 100644 index 00000000..8d082a04 --- /dev/null +++ b/manual/modtabtex/tabskyfall.tex @@ -0,0 +1,37 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:skyfall} +Attributes of element {\bf skyfall}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{actor} & pattern to match actor objects (string array) & \\ +\hline +\indattr{bypass} & Bypass plugin (bool) & true\\ +\hline +\indattr{deceleration} & Deceleration during sprung phase (double, m/s$^2$) & 40\\ +\hline +\indattr{friction\_fall} & friction during falling phase (double) & 1\\ +\hline +\indattr{friction\_jump} & friction during jumping phase (double) & 0.3\\ +\hline +\indattr{gravitation} & Gravitation constant (double, m/s$^2$) & -9.81\\ +\hline +\indattr{prefix} & OSC prefix (string) & /skyfall\\ +\hline +\indattr{vmax} & maximum velocity (double, m/s) & 40\\ +\hline +\indattr{wx} & deg/s (double, angular velocity around x axis) & 0\\ +\hline +\indattr{wy} & deg/s (double, angular velocity around y axis) & 11\\ +\hline +\indattr{wz} & deg/s (double, angular velocity around z axis) & 45\\ +\hline +\indattr{z0} & starting point (double, m) & 2\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsnapangle.tex b/manual/modtabtex/tabsnapangle.tex new file mode 100644 index 00000000..cf18bdee --- /dev/null +++ b/manual/modtabtex/tabsnapangle.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:snapangle} +Attributes of element {\bf snapangle}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{actor} & pattern to match actor objects (string array) & \\ +\hline +\indattr{bypass} & Bypass algorithm (bool) & false\\ +\hline +\indattr{candidates} & Path of target candidates (string array) & \\ +\hline +\indattr{name} & Default name used in OSC variables (string) & snapangle\\ +\hline +\indattr{srcobj} & Path of source object (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsndfile.tex b/manual/modtabtex/tabsndfile.tex new file mode 100644 index 00000000..e743ccae --- /dev/null +++ b/manual/modtabtex/tabsndfile.tex @@ -0,0 +1,55 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:sndfile} +Attributes of element {\bf sndfile}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{attribution} & attribution of license, if applicable (string) & \\ +\hline +\indattr{channel} & First sound file channel to be used, zero-base (uint32) & 0\\ +\hline +\indattr{channelorder} & Channel order in case of First Order Ambisonics files, ``FuMa'', ``ACN'' or ``none'' (string, FuMa|ACN|none) & \\ +\hline +\indattr{length} & length of sound sample, or 0 to use whole file length (double, s) & 0\\ +\hline +\indattr{level} & level, meaning depends on \attr{levelmode} (double, dB) & -inf\\ +\hline +\indattr{levelmode} & level mode, ``rms'', ``peak'' or ``calib'' (string) & rms\\ +\hline +\indattr{license} & license type (string) & \\ +\hline +\indattr{loop} & loop count or 0 for infinite looping (uint32) & 1\\ +\hline +\indattr{loopcrossexp} & exponent of von-Hann crossfade for seamless loop (float) & 1\\ +\hline +\indattr{loopcrosslen} & duration of crossfade for seamless loop (float, s) & 0\\ +\hline +\indattr{mute} & Load muted (bool) & false\\ +\hline +\indattr{name} & Sound file name (string) & \\ +\hline +\indattr{normalization} & Normalization in case of First Order Ambisonics files. (string, FuMa|SN3D) & FuMa\\ +\hline +\indattr{position} & Start position within the scene (double, s) & 0\\ +\hline +\indattr{rampend} & von-Hann ramp duration at end of sound (float, s) & 0\\ +\hline +\indattr{rampstart} & von-Hann ramp duration at start of sound (float, s) & 0\\ +\hline +\indattr{resample} & Allow resampling to current session sample rate (bool) & false\\ +\hline +\indattr{start} & Start position within the file (double, s) & 0\\ +\hline +\indattr{transport} & Use session time base (bool) & true\\ +\hline +\indattr{triggered} & Use OSC variable `/loop' to trigger playback (ignores attributes `position' and `loop') (bool) & false\\ +\hline +\indattr{weighting} & level weighting for RMS mode (f-weight) & Z\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsndfileasync.tex b/manual/modtabtex/tabsndfileasync.tex new file mode 100644 index 00000000..e4f7b0a5 --- /dev/null +++ b/manual/modtabtex/tabsndfileasync.tex @@ -0,0 +1,31 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:sndfileasync} +Attributes of element {\bf sndfileasync}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{attribution} & attribution of license, if applicable (string) & \\ +\hline +\indattr{caliblevel} & Calibration level (double, dB SPL) & 93.9794\\ +\hline +\indattr{channel} & First sound file channel to be used, zero-base (uint32) & 0\\ +\hline +\indattr{license} & license type (string) & \\ +\hline +\indattr{loop} & loop count or 0 for infinite looping (uint32) & 1\\ +\hline +\indattr{mute} & Load muted (bool) & false\\ +\hline +\indattr{name} & Sound file name (string) & \\ +\hline +\indattr{position} & Start position within the scene (double, s) & 0\\ +\hline +\indattr{transport} & Use session time base (bool) & true\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsound.tex b/manual/modtabtex/tabsound.tex new file mode 100644 index 00000000..429ed01b --- /dev/null +++ b/manual/modtabtex/tabsound.tex @@ -0,0 +1,53 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:sound} +Attributes of element {\bf sound} ({\hyperref[attrtab:soundcardioidmod]{cardioidmod}} {\hyperref[attrtab:sounddoor]{door}} {\hyperref[attrtab:soundfarsrc]{farsrc}} {\hyperref[attrtab:soundgeneric1storder]{generic1storder}} {\hyperref[attrtab:soundomni]{omni}}), inheriting from \hyperref[attrtab:ports]{{\bf ports}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{airabsorption} & apply air absorption filter (bool) & true\\ +\hline +\indattr{d} & distance to next sound along trajectory, or 0 for normal mode (double, m) & 0\\ +\hline +\indattr{delayline} & use delayline (bool) & true\\ +\hline +\indattr{gainmodel} & gain rule, valid gain models: "1/r", "1" (string) & 1/r\\ +\hline +\indattr{id} & id of sound vertex (string) & 6\\ +\hline +\indattr{ismmax} & maximal ISM order to render (uint32) & 2147483647\\ +\hline +\indattr{ismmin} & minimal ISM order to render (uint32) & 0\\ +\hline +\indattr{maxdist} & maximum distance to be used in delay lines (float, m) & 3700\\ +\hline +\indattr{minlevel} & Level threshold for rendering (float, dB SPL) & -inf\\ +\hline +\indattr{name} & name of sound vertex (string) & \\ +\hline +\indattr{nearfieldlimit} & distance arond 1/r source where the gain is constant (float, m) & 0.1\\ +\hline +\indattr{rx} & Euler orientation (X) relative to parent (double, deg) & 0\\ +\hline +\indattr{ry} & Euler orientation (Y) relative to parent (double, deg) & 0\\ +\hline +\indattr{rz} & Euler orientation (Z) relative to parent (double, deg) & 0\\ +\hline +\indattr{sincorder} & order of sinc interpolation in delayline (uint32) & 0\\ +\hline +\indattr{size} & physical size of sound source (effect depends on rendering method) (float, m) & 0\\ +\hline +\indattr{type} & source directivity type, e.g., omni, cardioid (string) & omni\\ +\hline +\indattr{x} & position relative to parent (double, m) & 0\\ +\hline +\indattr{y} & position relative to parent (double, m) & 0\\ +\hline +\indattr{z} & position relative to parent (double, m) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsoundcardioidmod.tex b/manual/modtabtex/tabsoundcardioidmod.tex new file mode 100644 index 00000000..d35af68b --- /dev/null +++ b/manual/modtabtex/tabsoundcardioidmod.tex @@ -0,0 +1,17 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:soundcardioidmod} +Attributes of sound element {\bf cardioidmod}, inheriting from \hyperref[attrtab:sound]{{\bf sound}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{f6db} & Frequency in Hz, at which a 6~dB attenuation at 90 degrees is achieved (double, Hz) & 1000\\ +\hline +\indattr{fmin} & Low-end limit for stabilization (double, Hz) & 60\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsounddoor.tex b/manual/modtabtex/tabsounddoor.tex new file mode 100644 index 00000000..64d9d7e6 --- /dev/null +++ b/manual/modtabtex/tabsounddoor.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:sounddoor} +Attributes of sound element {\bf door}, inheriting from \hyperref[attrtab:sound]{{\bf sound}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{distance} & Distance by which the source is shifted behind the door (double, m) & 1\\ +\hline +\indattr{falloff} & Distance at which the gain attenuation starts (double, m) & 1\\ +\hline +\indattr{height} & Door height (double, m) & 2\\ +\hline +\indattr{width} & Door width (double, m) & 1\\ +\hline +\indattr{wndsqrt} & Flag to control von-Hann fall-off (false, default) or square-root of von-Hann fall-off (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsoundfarsrc.tex b/manual/modtabtex/tabsoundfarsrc.tex new file mode 100644 index 00000000..dbf2f442 --- /dev/null +++ b/manual/modtabtex/tabsoundfarsrc.tex @@ -0,0 +1,17 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:soundfarsrc} +Attributes of sound element {\bf farsrc}, inheriting from \hyperref[attrtab:sound]{{\bf sound}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{distance} & Distance at which the fade-in starts (float, m) & 1\\ +\hline +\indattr{falloff} & Length of fade-in area (float, m) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsoundgeneric1storder.tex b/manual/modtabtex/tabsoundgeneric1storder.tex new file mode 100644 index 00000000..4583ae15 --- /dev/null +++ b/manual/modtabtex/tabsoundgeneric1storder.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:soundgeneric1storder} +Attributes of sound element {\bf generic1storder}, inheriting from \hyperref[attrtab:sound]{{\bf sound}}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{a} & undocumented (double) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabspeaker.tex b/manual/modtabtex/tabspeaker.tex new file mode 100644 index 00000000..fb392cd1 --- /dev/null +++ b/manual/modtabtex/tabspeaker.tex @@ -0,0 +1,39 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:speaker} +Attributes of element {\bf speaker}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{az} & Azimuth (double, deg) & 0\\ +\hline +\indattr{calibrate} & Use this loudspeaker during calibration (bool) & true\\ +\hline +\indattr{compB} & FIR filter coefficients for speaker calibration (double array) & \\ +\hline +\indattr{connect} & Connection to jack port (string) & \\ +\hline +\indattr{conv} & Name of impulse response for convolution (string) & \\ +\hline +\indattr{delay} & Static delay (double, s) & 0\\ +\hline +\indattr{el} & Elevation (double, deg) & 0\\ +\hline +\indattr{eqfreq} & Frequencies for IIR filter design (float array, Hz) & \\ +\hline +\indattr{eqgain} & Gains for IIR filter design (float array, dB) & \\ +\hline +\indattr{eqstages} & Number of biquad-stages in IIR frequency correction (0 = disable) (uint32) & 0\\ +\hline +\indattr{gain} & Broadband gain correction (double, dB) & 0\\ +\hline +\indattr{label} & Additional port label (string) & \\ +\hline +\indattr{r} & Distance (double, m) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabspeakerbased.tex b/manual/modtabtex/tabspeakerbased.tex new file mode 100644 index 00000000..348a3fe9 --- /dev/null +++ b/manual/modtabtex/tabspeakerbased.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:speakerbased} +Attributes of element {\bf speakerbased} ({\hyperref[attrtab:receiverhann]{hann}} {\hyperref[attrtab:receiverhoa2d]{hoa2d}} {\hyperref[attrtab:receiverhoa3d]{hoa3d}} {\hyperref[attrtab:receivernsp]{nsp}} {\hyperref[attrtab:receivervbap]{vbap}} {\hyperref[attrtab:receivervbap3d]{vbap3d}} {\hyperref[attrtab:receiverwfs]{wfs}})\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{layout} & name of speaker layout file (string) & \\ +\hline +\indattr{showspatialerror} & show absolute and angular error for rE and rV for 2D and 3D rendering, given the actual speaker layout and settings (bool) & false\\ +\hline +\indattr{spatialerrorpos} & Additional point list in Cartesian coordinates for testing spatial error (pos array, m) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabspeechactivity.tex b/manual/modtabtex/tabspeechactivity.tex new file mode 100644 index 00000000..13d3062a --- /dev/null +++ b/manual/modtabtex/tabspeechactivity.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:speechactivity} +Attributes of element {\bf speechactivity}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{path} & OSC destination path (string) & /in.0\\ +\hline +\indattr{tauenv} & Envelope tracking time constant (double, s) & 1\\ +\hline +\indattr{tauonset} & Onset detection time constant (double, s) & 1\\ +\hline +\indattr{threshold} & Envelope threshold (double, dB SPL) & 48.9794\\ +\hline +\indattr{transitionsonly} & Send only when a transition occurs (bool) & false\\ +\hline +\indattr{url} & OSC destination URL (string) & {\tiny osc.udp://localhost:9999/}\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabspkcalib.tex b/manual/modtabtex/tabspkcalib.tex new file mode 100644 index 00000000..512c8e3b --- /dev/null +++ b/manual/modtabtex/tabspkcalib.tex @@ -0,0 +1,15 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:spkcalib} +Attributes of element {\bf spkcalib}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{layout} & name of speaker layout file (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabspksim.tex b/manual/modtabtex/tabspksim.tex new file mode 100644 index 00000000..b897b1a9 --- /dev/null +++ b/manual/modtabtex/tabspksim.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:spksim} +Attributes of element {\bf spksim}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{bypass} & Bypass plugin (bool) & false\\ +\hline +\indattr{fres} & Resonance frequency (double, Hz) & 1200\\ +\hline +\indattr{gain} & Post-gain $g$ (double, dB) & 0\\ +\hline +\indattr{q} & $q$-factor of the resonance filter (double) & 0.8\\ +\hline +\indattr{scale} & Distortion factor $s$ (double) & 0.5\\ +\hline +\indattr{wet} & Wet (1) - dry (0) mixture gain (float) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsystem.tex b/manual/modtabtex/tabsystem.tex new file mode 100644 index 00000000..f1caa49d --- /dev/null +++ b/manual/modtabtex/tabsystem.tex @@ -0,0 +1,35 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:system} +Attributes of element {\bf system}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{allowoscmod} & allow modifications of timed commands via OSC (bool) & false\\ +\hline +\indattr{command} & command to be executed (string) & \\ +\hline +\indattr{id} & undocumented (string) & system\\ +\hline +\indattr{noshell} & do not use shell to spawn subprocess (bool) & true\\ +\hline +\indattr{onunload} & command to be executed when unloading session (string) & \\ +\hline +\indattr{relaunch} & relaunch process if ended before session unload (bool) & false\\ +\hline +\indattr{relaunchwait} & Time to wait before relaunching subprocess (double, s) & 0\\ +\hline +\indattr{sleep} & wait after starting the command before continuing to load session (double, s) & 0\\ +\hline +\indattr{timedcmdpipe} & start timed commands using a pipe (true) or fork (false) (bool) & true\\ +\hline +\indattr{timedprefix} & Prefix for timed commands added via OSC (string) & \\ +\hline +\indattr{triggered} & command to be executed upon trigger signal (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabsystime.tex b/manual/modtabtex/tabsystime.tex new file mode 100644 index 00000000..9518b570 --- /dev/null +++ b/manual/modtabtex/tabsystime.tex @@ -0,0 +1,21 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:systime} +Attributes of element {\bf systime}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{addtime} & Add time to seconds since midnight, e.g., for time zone compensation (double, s) & 0\\ +\hline +\indattr{path} & OSC path where time stamps (calendar) are dispatched (string) & /systime\\ +\hline +\indattr{secpath} & OSC path where time stamps (seconds since midnight) are dispatched (string) & /seconds\\ +\hline +\indattr{sendsessiontime} & Send session time in first data field (bool) & true\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabtimedisplay.tex b/manual/modtabtex/tabtimedisplay.tex new file mode 100644 index 00000000..fc2c8f60 --- /dev/null +++ b/manual/modtabtex/tabtimedisplay.tex @@ -0,0 +1,43 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:timedisplay} +Attributes of element {\bf timedisplay}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{colbg} & background color (string, html color) & \#ffffff\\ +\hline +\indattr{colneg} & font color for negative times (string, html color) & \#cc1a1a\\ +\hline +\indattr{colpos} & font color for positive times (string, html color) & \#000000\\ +\hline +\indattr{digits} & Number of decimals (uint32) & 1\\ +\hline +\indattr{fontscale} & font scale (double) & 1\\ +\hline +\indattr{fps} & Display update rate (not granted) (double, Hz) & 10\\ +\hline +\indattr{h} & window height (int32, px) & 18\\ +\hline +\indattr{prefix} & OSC variable prefix (string) & /timedisplay\\ +\hline +\indattr{remaining} & show remaining time (bool) & false\\ +\hline +\indattr{showtc} & Show time code (bool) & false\\ +\hline +\indattr{threshold} & Change color to red if displayed time is below this value (double, s) & 0\\ +\hline +\indattr{times} & List of time thresholds (double array, s) & \\ +\hline +\indattr{w} & window width (int32, px) & 100\\ +\hline +\indattr{x} & window x position (int32, px) & 0\\ +\hline +\indattr{y} & window y position (int32, px) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabtransportramp.tex b/manual/modtabtex/tabtransportramp.tex new file mode 100644 index 00000000..7078fc94 --- /dev/null +++ b/manual/modtabtex/tabtransportramp.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:transportramp} +Attributes of element {\bf transportramp}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{endduration} & Duration of ramp when transport is switched from ``rolling'' to ``stopped'' (float, s) & 0.025\\ +\hline +\indattr{precalc} & Operation mode, to switch between precalculated and online-generated ramps (bool) & true\\ +\hline +\indattr{startduration} & Duration of ramp when transport is switched from ``stopped'' to ``rolling'' (float, s) & 0.025\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabtubesim.tex b/manual/modtabtex/tabtubesim.tex new file mode 100644 index 00000000..9853f3ac --- /dev/null +++ b/manual/modtabtex/tabtubesim.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:tubesim} +Attributes of element {\bf tubesim}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{bypass} & Bypass plugin (bool) & false\\ +\hline +\indattr{offset} & Input offset $x\_0$ (float) & 0.5\\ +\hline +\indattr{postgain} & Post-gain $g\_o$ (float, dB) & 0\\ +\hline +\indattr{pregain} & Pre-gain $g\_i$ (float, dB) & 0\\ +\hline +\indattr{saturation} & Saturation parameter $s$ (float, dB) & -6.0206\\ +\hline +\indattr{wet} & Wet (1) - dry (0) mixture gain (float) & 1\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabwaitforjackport.tex b/manual/modtabtex/tabwaitforjackport.tex new file mode 100644 index 00000000..08b320fd --- /dev/null +++ b/manual/modtabtex/tabwaitforjackport.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:waitforjackport} +Attributes of element {\bf waitforjackport}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{name} & Name used in jack (string) & waitforjackport\\ +\hline +\indattr{ports} & List of port names to wait for (string array) & \\ +\hline +\indattr{timeout} & Timeout after which execution will not block any longer (double, s) & 30\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabwaitforlslstream.tex b/manual/modtabtex/tabwaitforlslstream.tex new file mode 100644 index 00000000..e22b2fb6 --- /dev/null +++ b/manual/modtabtex/tabwaitforlslstream.tex @@ -0,0 +1,19 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:waitforlslstream} +Attributes of element {\bf waitforlslstream}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{showgui} & Show GUI (bool) & true\\ +\hline +\indattr{streams} & List of stream names to wait for (string array) & \\ +\hline +\indattr{timeout} & Timeout (double, s) & 30\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/tabdatalogging.tex b/manual/tabdatalogging.tex new file mode 100644 index 00000000..b664d50f --- /dev/null +++ b/manual/tabdatalogging.tex @@ -0,0 +1,33 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:datalogging} +Attributes of element {\bf datalogging}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{controltransport} & Control transport with recording session control (bool) & true\\ +\hline +\indattr{displaydc} & Display DC components (bool) & true\\ +\hline +\indattr{fileformat} & File format, can be either ``mat'', ``matcell'' or ``txt'' (string) & matcell\\ +\hline +\indattr{headless} & Use without GUI (bool) & false\\ +\hline +\indattr{lsltimeout} & Number of seconds to scan for LSL streams (double, s) & 10\\ +\hline +\indattr{multicast} & OSC multicasting address (string) & \\ +\hline +\indattr{outputdir} & Data output directory (string) & \\ +\hline +\indattr{port} & OSC port, or empty to use session server (string) & \\ +\hline +\indattr{srv\_proto} & Server protocol, UDP or TCP (string) & UDP\\ +\hline +\indattr{usetransport} & Record only while transport is rolling (bool) & false\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/plugins/src/tascarmod_datalogging.cc b/plugins/src/tascarmod_datalogging.cc index 00e5ab73..92f88c31 100644 --- a/plugins/src/tascarmod_datalogging.cc +++ b/plugins/src/tascarmod_datalogging.cc @@ -114,9 +114,10 @@ class oscsvar_t : public var_base_t { lo_arg** argv, int argc, lo_message msg, void* user_data); std::string path; + bool ds_format = false; private: - void osc_receive_sample(const char* path, const char* msg); + void osc_receive_sample(const char* path, double t, const char* msg); }; /** @@ -224,9 +225,9 @@ class data_draw_t : public Gtk::DrawingArea { void set_alive() { timeout_cnt = 10u; }; private: - void get_valuerange(const std::vector& data, uint32_t channels, - uint32_t firstchannel, uint32_t n1, uint32_t n2, - double& dmin, double& dmax); + void get_valuerange(const std::vector& data, size_t channels, + size_t firstchannel, size_t n1, size_t n2, double& dmin, + double& dmax); std::mutex drawlock; std::mutex plotdatalock; std::vector plotdata_; @@ -264,17 +265,17 @@ void data_draw_t::store_msg(double t1, double t2, const std::string& msg) } void data_draw_t::get_valuerange(const std::vector& data, - uint32_t channels, uint32_t firstchannel, - uint32_t n1, uint32_t n2, double& dmin, + size_t channels, size_t firstchannel, + size_t n1, size_t n2, double& dmin, double& dmax) { dmin = std::numeric_limits::max(); dmax = std::numeric_limits::lowest(); - for(uint32_t dim = firstchannel; dim < channels; dim++) { + for(size_t dim = firstchannel; dim < channels; dim++) { vdc[dim] = 0; if(!displaydc_) { - uint32_t cnt(0); - for(uint32_t k = n1; k < n2; k++) { + size_t cnt(0); + for(size_t k = n1; k < n2; k++) { double v(data[dim + k * channels]); if((v > std::numeric_limits::lowest()) && (v < std::numeric_limits::max())) { @@ -285,7 +286,7 @@ void data_draw_t::get_valuerange(const std::vector& data, if(cnt) vdc[dim] /= (double)cnt; } - for(uint32_t k = n1; k < n2; k++) { + for(size_t k = n1; k < n2; k++) { double v(data[dim + k * channels]); if((v > std::numeric_limits::lowest()) && (v < std::numeric_limits::max())) { @@ -340,7 +341,7 @@ class recorder_t { std::lock_guard lock(dlock); return xdata_; }; - uint32_t get_size() const { return size_; }; + size_t get_size() const { return size_; }; const std::string& get_name() const { return name_; }; void clear(); void set_textdata() { b_textdata = true; }; @@ -356,7 +357,7 @@ class recorder_t { private: std::mutex dlock; // bool displaydc_; - uint32_t size_; + size_t size_; bool b_textdata; std::vector xdata_; std::vector xmessages; @@ -529,6 +530,8 @@ void lslvar_t::set_delta(double deltatime) oscsvar_t::oscsvar_t(tsccfg::node_t xmlsrc) : var_base_t(xmlsrc) { GET_ATTRIBUTE(path, "", "OSC path name, expecting messages with 's' format"); + GET_ATTRIBUTE_BOOL( + ds_format, "Use ds format, i.e., a double in addition to the string."); } oscvar_t::oscvar_t(tsccfg::node_t xmlsrc) : var_base_t(xmlsrc) @@ -650,10 +653,10 @@ double sqr(double x) \retval n1 Start index, included \retval n2 End index, excluded */ -void find_timeinterval(const std::vector& data, uint32_t channels, - double t1, double t2, uint32_t& n1, uint32_t& n2) +void find_timeinterval(const std::vector& data, size_t channels, + double t1, double t2, size_t& n1, size_t& n2) { - uint32_t N(data.size() / channels); + size_t N(data.size() / channels); n1 = 0; n2 = 0; if(N == 0) @@ -743,28 +746,31 @@ bool data_draw_t::on_draw(const Cairo::RefPtr& cr) } } else { // N is number of (multi-dimensional) samples: - uint32_t N(plotdata_.size() / num_channels); + size_t N(plotdata_.size() / num_channels); if(N > 1) { // double tmax(0); - uint32_t n1(0); - uint32_t n2(0); + size_t n1(0); + size_t n2(0); ltime = plotdata_[(N - 1) * num_channels]; find_timeinterval(plotdata_, num_channels, ltime - 30, ltime, n1, n2); get_valuerange(plotdata_, num_channels, 1 + ignore_first_, n1, n2, dmin, dmax); - uint32_t stepsize((n2 - n1) / 2048); + size_t stepsize((n2 - n1) / 2048); ++stepsize; drange = dmax - dmin; dscale = height / drange; - for(uint32_t dim = 1 + ignore_first_; dim < num_channels; ++dim) { + for(size_t dim = 1 + ignore_first_; dim < num_channels; ++dim) { cr->save(); - double size_norm(1.0 / (num_channels - 1.0 - ignore_first_)); - TASCAR::Scene::rgb_color_t rgb(set_hsv( - (dim - 1.0 - ignore_first_) * size_norm * 360, 0.8, 0.7)); + double size_norm(1.0 / + ((double)num_channels - 1.0 - ignore_first_)); + TASCAR::Scene::rgb_color_t rgb( + set_hsv((float)(((double)dim - 1.0 - ignore_first_) * + size_norm * 360.0), + 0.8f, 0.7f)); cr->set_source_rgb(rgb.r, rgb.g, rgb.b); bool restart_line(true); // limit number of lines: - for(uint32_t k = n1; k < n2; k += stepsize) { + for(size_t k = n1; k < n2; k += stepsize) { double t(plotdata_[k * num_channels] - ltime); double v(plotdata_[dim + k * num_channels]); if(std::isfinite(v)) { @@ -1143,7 +1149,10 @@ void datalogging_t::configure() session->jc, session->srate, false, headless)); var->set_recorder(recorder.back()); - osc->add_method(var->path, "s", &oscsvar_t::osc_receive_sample, var); + if(var->ds_format) + osc->add_method(var->path, "ds", &oscsvar_t::osc_receive_sample, var); + else + osc->add_method(var->path, "s", &oscsvar_t::osc_receive_sample, var); } #ifdef HAS_LSL // finally, add all LSL variables: @@ -1162,8 +1171,9 @@ void datalogging_t::configure() // connection_timeout = Glib::signal_timeout().connect( sigc::mem_fun(*this, &datalogging_t::on_100ms), 100); - uint32_t num_cols(ceil(sqrt(recorder.size()))); - uint32_t num_rows(ceil(recorder.size() / std::max(1u, num_cols))); + uint32_t num_cols((uint32_t)(ceil(sqrt(recorder.size())))); + uint32_t num_rows( + (uint32_t)(ceil(recorder.size() / std::max(1u, num_cols)))); for(uint32_t k = 0; k < num_cols; k++) draw_grid->insert_column(0); for(uint32_t k = 0; k < num_rows; k++) @@ -1363,16 +1373,20 @@ void oscvar_t::osc_receive_sample(const char*, uint32_t size, double* data) } int oscsvar_t::osc_receive_sample(const char* path, const char*, lo_arg** argv, - int, lo_message, void* user_data) + int argc, lo_message, void* user_data) { - ((oscsvar_t*)user_data)->osc_receive_sample(path, &(argv[0]->s)); + if(argc == 1) + ((oscsvar_t*)user_data)->osc_receive_sample(path, 0.0, &(argv[0]->s)); + else + ((oscsvar_t*)user_data) + ->osc_receive_sample(path, argv[0]->d, &(argv[1]->s)); return 0; } -void oscsvar_t::osc_receive_sample(const char*, const char* msg) +void oscsvar_t::osc_receive_sample(const char*, double t, const char* msg) { if(datarecorder) { - datarecorder->store_msg(datarecorder->get_session_time(), 0, msg); + datarecorder->store_msg(datarecorder->get_session_time(), t, msg); } } @@ -1464,10 +1478,10 @@ void datalogging_t::save_text(const std::string& filename) ofs << it->t1 << " " << it->t2 << " \"" << it->msg << "\"\n"; } else { std::vector data(rec->get_data()); - uint32_t N(rec->get_size()); - uint32_t M(data.size()); - uint32_t cnt(N - 1); - for(uint32_t l = 0; l < M; l++) { + size_t N(rec->get_size()); + size_t M(data.size()); + size_t cnt(N - 1); + for(size_t l = 0; l < M; l++) { ofs << data[l]; if(cnt--) { ofs << " "; @@ -1626,8 +1640,8 @@ void datalogging_t::save_mat(const std::string& filename) mvar = create_message_struct(recorder[k]->get_textdata(), name); } else { std::vector data(recorder[k]->get_data()); - uint32_t N(recorder[k]->get_size()); - uint32_t M(data.size()); + size_t N(recorder[k]->get_size()); + size_t M(data.size()); dims[1] = M / N; dims[0] = N; double* x(&(data[0])); @@ -1697,8 +1711,8 @@ void datalogging_t::save_matcell(const std::string& filename) mData = create_message_struct(recorder[k]->get_textdata(), name); } else { std::vector data(recorder[k]->get_data()); - uint32_t N(recorder[k]->get_size()); - uint32_t M(data.size()); + size_t N(recorder[k]->get_size()); + size_t M(data.size()); dims[1] = M / N; dims[0] = N; double* x(&(data[0])); From e6b53209971c09805495049cab817f36ef1af287 Mon Sep 17 00:00:00 2001 From: "Giso Grimm (thetys)" Date: Thu, 19 Dec 2024 11:44:07 +0100 Subject: [PATCH 25/34] improve and add documentation --- manual/.gitignore | 2 +- manual/actmodlinearmovement.tex | 22 +----------- manual/documentation.tsc | 2 ++ manual/modjackrec.tex | 45 ++---------------------- manual/modlightcolorpicker.tex | 3 ++ manual/modtabtex/tablightcolorpicker.tex | 17 +++++++++ manual/modtabtex/tablinearmovement.tex | 2 +- manual/oscdoc_tascarmod_jackrec.tex | 25 +++++++++++++ manual/tabdatalogging.tex | 33 ----------------- plugins/src/tascarmod_jackrec.cc | 31 +++++++++++----- plugins/src/tascarmod_linearmovement.cc | 2 +- 11 files changed, 76 insertions(+), 108 deletions(-) create mode 100644 manual/modlightcolorpicker.tex create mode 100644 manual/modtabtex/tablightcolorpicker.tex create mode 100644 manual/oscdoc_tascarmod_jackrec.tex delete mode 100644 manual/tabdatalogging.tex diff --git a/manual/.gitignore b/manual/.gitignore index f7bb26c9..fdebc656 100644 --- a/manual/.gitignore +++ b/manual/.gitignore @@ -33,7 +33,7 @@ secrecspeaker.tex secmaskmodules.tex version.tex *.run* -tab*.tex +/tab*.tex autodoc.txt cli_*.tex clihelp.tex diff --git a/manual/actmodlinearmovement.tex b/manual/actmodlinearmovement.tex index a4e9524d..4440e59f 100644 --- a/manual/actmodlinearmovement.tex +++ b/manual/actmodlinearmovement.tex @@ -5,27 +5,7 @@ \end{lstlisting} -\begin{snugshade} -{\footnotesize -\label{attrtab:linearmovement} -Attributes of element {\bf linearmovement}\nopagebreak - -\begin{tabularx}{\textwidth}{lXl} -\hline -name & description (type, unit) & def.\\ -\hline -\hline -\indattr{actor} & pattern to match actor objects (string array) & \\ -\hline -\indattr{p0} & start position at time t0 (pos, m) & 0 0 0\\ -\hline -\indattr{t0} & start time t0 (double, s) & 0\\ -\hline -\indattr{v} & velocity vector (pos, m/s) & 1 1 0\\ -\hline -\end{tabularx} -} -\end{snugshade} +\input{modtabtex/tablinearmovement.tex} All variables can be controlled via OSC; the \attr{actor} attribute is used as path prefix. In the example above this would result in these diff --git a/manual/documentation.tsc b/manual/documentation.tsc index 516e8658..2298859e 100644 --- a/manual/documentation.tsc +++ b/manual/documentation.tsc @@ -142,6 +142,8 @@ + + diff --git a/manual/modjackrec.tex b/manual/modjackrec.tex index 49269368..6a1f4710 100644 --- a/manual/modjackrec.tex +++ b/manual/modjackrec.tex @@ -2,49 +2,8 @@ List of configuration variables: -\begin{snugshade} -{\footnotesize -\label{attrtab:jackrec} -Attributes of element {\bf jackrec}\nopagebreak -\begin{tabularx}{\textwidth}{lXl} -\hline -name & description (type, unit) & def. \\ -\hline -\hline -\indattr{name} & Name used for OSC prefix and jack (string) & jackrec \\ -\hline -\indattr{buflen} & audio buffer length (double, s) & 10 \\ -\hline -\indattr{url} & URL of OSC controller interface (string) & \\ -\hline -\indattr{path} & File path where to store and search for files (string) & \\ -\hline -\indattr{pattern} & search pattern (string) & rec*.wav \\ -\hline -\indattr{fileformat} & File format (string, WAV AIFF AU RAW PAF SVX NIST VOC IRCAM W64 MAT4 MAT5 PVF XI HTK SDS AVR WAVEX SD2 FLAC CAF WVE OGG MPC2K RF64) & WAV\\ -\hline -\indattr{sampleformat} & Audio sample format (string, PCM\_S8 PCM\_16 PCM\_24 PCM\_32 PCM\_U8 FLOAT DOUBLE ULAW ALAW IMA\_ADPCM MS\_ADPCM GSM610 VOX\_ADPCM G721\_32 G723\_24 G723\_40 DWVW\_12 DWVW\_16 DWVW\_24 DWVW\_N DPCM\_8 DPCM\_16 VORBIS) & PCM\_16\\ -\hline -\indattr{prefix} & file prefix (string) & rec \\ -\hline -\indattr{usetransport} & Record only when transport is rolling (bool) & false \\ -\hline -\indattr{ports} & List of ports to record (string array) & \\ -\hline -\end{tabularx} -} -\end{snugshade} -%\begin{tscattributes} -% \attr{name} & jack client name (jackrec) \\ -% \attr{buflen} & recording buffer in seconds (10) \\ -% \attr{url} & OSC url where response messages are sent \\ -% \attr{path} & File path where to store and search for files \\ -% \attr{pattern} & File pattern of files to show in list (``rec*.wav'') \\ -% \attr{fileformat} & File format (``WAV'') \\ -% \attr{sampleformat} & Sample format (``PCM\_S16'') \\ -% \attr{prefix} & file prefix, may contain path delimiters if path already exists (``rec'') \\ -%\end{tscattributes} +\input{modtabtex/tabjackrec.tex} Here is an example of the communication protocol: @@ -125,3 +84,5 @@ DWVW_12 DWVW_16 DWVW_24 DWVW_N DPCM_8 DPCM_16 VORBIS \end{verbatim} + +\input{oscdoc_tascarmod_jackrec.tex} diff --git a/manual/modlightcolorpicker.tex b/manual/modlightcolorpicker.tex new file mode 100644 index 00000000..a429e55f --- /dev/null +++ b/manual/modlightcolorpicker.tex @@ -0,0 +1,3 @@ +Provide a simple color picking dialog to control HSV values via OSC. + +\input{modtabtex/tablightcolorpicker.tex} diff --git a/manual/modtabtex/tablightcolorpicker.tex b/manual/modtabtex/tablightcolorpicker.tex new file mode 100644 index 00000000..2c0653ec --- /dev/null +++ b/manual/modtabtex/tablightcolorpicker.tex @@ -0,0 +1,17 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:lightcolorpicker} +Attributes of element {\bf lightcolorpicker}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{color} & undocumented (string) & \\ +\hline +\indattr{path} & undocumented (string) & \\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tablinearmovement.tex b/manual/modtabtex/tablinearmovement.tex index 9dd674dc..35103635 100644 --- a/manual/modtabtex/tablinearmovement.tex +++ b/manual/modtabtex/tablinearmovement.tex @@ -12,7 +12,7 @@ \hline \indattr{p0} & start position at time t0 (pos, m) & 0 0 0\\ \hline -\indattr{t0} & start time t0 (double, s) & 4.788e-310\\ +\indattr{t0} & start time t0 (double, s) & 0\\ \hline \indattr{v} & velocity vector (pos, m/s) & 1 1 0\\ \hline diff --git a/manual/oscdoc_tascarmod_jackrec.tex b/manual/oscdoc_tascarmod_jackrec.tex new file mode 100644 index 00000000..056f178c --- /dev/null +++ b/manual/oscdoc_tascarmod_jackrec.tex @@ -0,0 +1,25 @@ +\definecolor{shadecolor}{RGB}{236,236,255}\begin{snugshade} +{\footnotesize +\label{osctab:tascarmodjackrec} +OSC variables: +\nopagebreak + +\begin{tabularx}{\textwidth}{llllX} +\hline +path & fmt. & range & r. & description\\ +\hline +\attr{/.../addport} & s & & no & Add the given port to the list of recorder input ports\\ +\attr{/.../clear} & & & no & Clear list of ports\\ +\attr{/.../listfiles} & & & no & Send list of sound files (matching pattern provided in XML)\\ +\attr{/.../listports} & & & no & List all available jack ports\\ +\attr{/.../name} & s & string & yes & Output file name, leave empty for automatic file names\\ +\attr{/.../rmfile} & s & & no & Remove a file on disk\\ +\attr{/.../start} & & & no & Start recording (or recording standby when usetransport is set)\\ +\attr{/.../stop} & & & no & Stop recording and close output file\\ +\attr{/.../tag} & s & string & yes & Set tag of output file\\ +\attr{/.../usetransport} & i & bool & yes & Control wether to use jack transport during recording when started next\\ +\hline +\end{tabularx} +} +\end{snugshade} +\definecolor{shadecolor}{RGB}{255,230,204} diff --git a/manual/tabdatalogging.tex b/manual/tabdatalogging.tex deleted file mode 100644 index b664d50f..00000000 --- a/manual/tabdatalogging.tex +++ /dev/null @@ -1,33 +0,0 @@ -\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} -{\footnotesize -\label{attrtab:datalogging} -Attributes of element {\bf datalogging}\nopagebreak - -\begin{tabularx}{\textwidth}{lXl} -\hline -name & description (type, unit) & def.\\ -\hline -\hline -\indattr{controltransport} & Control transport with recording session control (bool) & true\\ -\hline -\indattr{displaydc} & Display DC components (bool) & true\\ -\hline -\indattr{fileformat} & File format, can be either ``mat'', ``matcell'' or ``txt'' (string) & matcell\\ -\hline -\indattr{headless} & Use without GUI (bool) & false\\ -\hline -\indattr{lsltimeout} & Number of seconds to scan for LSL streams (double, s) & 10\\ -\hline -\indattr{multicast} & OSC multicasting address (string) & \\ -\hline -\indattr{outputdir} & Data output directory (string) & \\ -\hline -\indattr{port} & OSC port, or empty to use session server (string) & \\ -\hline -\indattr{srv\_proto} & Server protocol, UDP or TCP (string) & UDP\\ -\hline -\indattr{usetransport} & Record only while transport is rolling (bool) & false\\ -\hline -\end{tabularx} -} -\end{snugshade} diff --git a/plugins/src/tascarmod_jackrec.cc b/plugins/src/tascarmod_jackrec.cc index 9f28cb85..66ba791f 100644 --- a/plugins/src/tascarmod_jackrec.cc +++ b/plugins/src/tascarmod_jackrec.cc @@ -355,21 +355,34 @@ void jackrec_t::listfiles() void jackrec_t::add_variables(TASCAR::osc_server_t* srv) { + srv->set_variable_owner( + TASCAR::strrep(TASCAR::tscbasename(__FILE__), ".cc", "")); std::string prefix_(srv->get_prefix()); srv->set_prefix(oscprefix); - srv->add_string("/name", &ofname); - srv->add_method("/start", "", &jackrec_t::start, this); - srv->add_method("/stop", "", &jackrec_t::stop, this); - srv->add_method("/clear", "", &jackrec_t::clearports, this); - srv->add_method("/addport", "s", &jackrec_t::addport, this); - srv->add_method("/listports", "", &jackrec_t::listports, this); - srv->add_method("/listfiles", "", &jackrec_t::listfiles, this); - srv->add_method("/rmfile", "s", &jackrec_t::rmfile, this); - srv->add_string("/tag", &tag); + srv->add_string("/name", &ofname, + "Output file name, leave empty for automatic file names"); + srv->add_method( + "/start", "", &jackrec_t::start, this, true, false, "", + "Start recording (or recording standby when usetransport is set)"); + srv->add_method("/stop", "", &jackrec_t::stop, this, true, false, "", + "Stop recording and close output file"); + srv->add_method("/clear", "", &jackrec_t::clearports, this, true, false, "", + "Clear list of ports"); + srv->add_method("/addport", "s", &jackrec_t::addport, this, true, false, "", + "Add the given port to the list of recorder input ports"); + srv->add_method("/listports", "", &jackrec_t::listports, this, true, false, + "", "List all available jack ports"); + srv->add_method( + "/listfiles", "", &jackrec_t::listfiles, this, true, false, "", + "Send list of sound files (matching pattern provided in XML)"); + srv->add_method("/rmfile", "s", &jackrec_t::rmfile, this, true, false, "", + "Remove a file on disk"); + srv->add_string("/tag", &tag, "Set tag of output file"); srv->add_bool("/usetransport", &usetransport, "Control wether to use jack transport during recording when " "started next"); srv->set_prefix(prefix_); + srv->unset_variable_owner(); } REGISTER_MODULE(jackrec_t); diff --git a/plugins/src/tascarmod_linearmovement.cc b/plugins/src/tascarmod_linearmovement.cc index 56e61119..133fc989 100644 --- a/plugins/src/tascarmod_linearmovement.cc +++ b/plugins/src/tascarmod_linearmovement.cc @@ -32,7 +32,7 @@ class locmod_t : public TASCAR::actor_module_t { private: TASCAR::pos_t v; TASCAR::pos_t p0; - double t0; + double t0 = 0.0; std::mutex mtx; }; From a1990a8881580d6bc54b3150b53ecfe232965e12 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Sun, 22 Dec 2024 13:32:58 +0100 Subject: [PATCH 26/34] package simplesynth plugin --- packaging/deb/tascarcli.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/deb/tascarcli.csv b/packaging/deb/tascarcli.csv index 38c21483..0cb1ca3e 100644 --- a/packaging/deb/tascarcli.csv +++ b/packaging/deb/tascarcli.csv @@ -57,6 +57,7 @@ "plugins/build/tascar_ap_pulse.so","usr/lib/" "plugins/build/tascar_ap_ringmodulator.so","usr/lib/" "plugins/build/tascar_ap_sessiontime.so","usr/lib/" +"plugins/build/tascar_ap_simplesynth.so","usr/lib/" "plugins/build/tascar_ap_sine.so","usr/lib/" "plugins/build/tascar_ap_sndfile.so","usr/lib/" "plugins/build/tascar_ap_sndfileasync.so","usr/lib/" From a202cb1dc8eb46f84610ea9357da14b1ce87d60f Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Sun, 22 Dec 2024 14:04:28 +0100 Subject: [PATCH 27/34] attempt to improve performance of simplesynth --- plugins/src/tascar_ap_simplesynth.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/src/tascar_ap_simplesynth.cc b/plugins/src/tascar_ap_simplesynth.cc index a165c31b..4946be56 100644 --- a/plugins/src/tascar_ap_simplesynth.cc +++ b/plugins/src/tascar_ap_simplesynth.cc @@ -59,6 +59,7 @@ class tone_t { float noisedamp = 0.0f; float noisemin = 0.0f; TASCAR::varidelay_t noiseflt = TASCAR::varidelay_t(10000, 1.0, 1.0, 0, 0); + std::complex ldphi = 1.0f; void set_srate(float srate) { @@ -131,15 +132,16 @@ class tone_t { if(amplitude > 1e-8f) { // float oval = 0.0f; - std::complex ldphi = dphi; + ldphi = dphi; float compdecay = 1.0f; for(size_t k = 0; k < N; ++k) { phase[k] *= ldphi * dphi_detune; ldphi *= dphi; - oval += amplitude * amplitudes[k] * std::real(phase[k]); + oval += amplitudes[k] * std::real(phase[k]); amplitudes[k] *= compdecay; compdecay *= decaydamping; } + oval *= amplitude; amplitude *= decay; if(rampt > 0) { oval *= 0.5f + 0.5f * cosf(TASCAR_PIf * rampt * rampdur_1); From b62fcf5770512e57ddd17ceb942bb058f849e910 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Sun, 22 Dec 2024 15:40:53 +0100 Subject: [PATCH 28/34] improve performance of simplesynth --- examples/simplesynth.tsc | 19 ++++++++++++------- plugins/src/tascar_ap_simplesynth.cc | 10 +++++++--- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/examples/simplesynth.tsc b/examples/simplesynth.tsc index d63b93c5..2763be2c 100644 --- a/examples/simplesynth.tsc +++ b/examples/simplesynth.tsc @@ -1,10 +1,15 @@ - - - - - - - + + + + + + + + + + + + diff --git a/plugins/src/tascar_ap_simplesynth.cc b/plugins/src/tascar_ap_simplesynth.cc index 4946be56..97016b26 100644 --- a/plugins/src/tascar_ap_simplesynth.cc +++ b/plugins/src/tascar_ap_simplesynth.cc @@ -118,7 +118,7 @@ class tone_t { noisemin = 0.0f; } } - void add(float& val) + inline void add(float& val) { if((amplitudenoise > 1e-8f) || (amplitude > 1e-8f)) { float oval = noiseflt.get(fbdelay); @@ -137,8 +137,12 @@ class tone_t { for(size_t k = 0; k < N; ++k) { phase[k] *= ldphi * dphi_detune; ldphi *= dphi; - oval += amplitudes[k] * std::real(phase[k]); - amplitudes[k] *= compdecay; + if(amplitudes[k] > 1e-8f) { + oval += amplitudes[k] * std::real(phase[k]); + amplitudes[k] *= compdecay; + } else { + amplitudes[k] = 0.0f; + } compdecay *= decaydamping; } oval *= amplitude; From 55f1543203d16483fcc621b254a3d3ca6dc2d514 Mon Sep 17 00:00:00 2001 From: Giso Grimm Date: Sun, 22 Dec 2024 16:24:09 +0100 Subject: [PATCH 29/34] revert simplesynth example --- examples/simplesynth.tsc | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/examples/simplesynth.tsc b/examples/simplesynth.tsc index 2763be2c..d63b93c5 100644 --- a/examples/simplesynth.tsc +++ b/examples/simplesynth.tsc @@ -1,15 +1,10 @@ - - - - - - - - - - - - + + + + + + + From a271997b973aa8f2ef88b8046c71f5ff4f715c67 Mon Sep 17 00:00:00 2001 From: "Giso Grimm (thetys)" Date: Tue, 7 Jan 2025 11:34:35 +0100 Subject: [PATCH 30/34] add optional locateonload feature --- libtascar/include/session.h | 25 +++++++++++++------------ libtascar/src/session.cc | 6 ++++++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/libtascar/include/session.h b/libtascar/include/session.h index e68661f3..8fde751c 100644 --- a/libtascar/include/session.h +++ b/libtascar/include/session.h @@ -118,20 +118,21 @@ namespace TASCAR { session_core_t(const std::string& filename_or_data, load_type_t t, const std::string& path); // configuration variables: - double duration; - bool loop; - bool playonload; - double levelmeter_tc; - TASCAR::levelmeter::weight_t levelmeter_weight; + double duration = 60; + bool loop = false; + bool playonload = false; + double locateonload = -1.0; + double levelmeter_tc = 2.0; + TASCAR::levelmeter::weight_t levelmeter_weight = TASCAR::levelmeter::Z; std::string levelmeter_mode; - double levelmeter_min; - double levelmeter_range; - double requiresrate; - double warnsrate; - int32_t requirefragsize; - int32_t warnfragsize; + double levelmeter_min = 30.0; + double levelmeter_range = 70.0; + double requiresrate = 0.0; + double warnsrate = 0.0; + int32_t requirefragsize = 0; + int32_t warnfragsize = 0; std::string initcmd; - double initcmdsleep; + double initcmdsleep = 0.0; private: void start_initcmd(); diff --git a/libtascar/src/session.cc b/libtascar/src/session.cc index 5c154f37..925f70d4 100644 --- a/libtascar/src/session.cc +++ b/libtascar/src/session.cc @@ -278,6 +278,8 @@ TASCAR::session_core_t::session_core_t() root.GET_ATTRIBUTE(duration, "s", "session duration"); root.GET_ATTRIBUTE_BOOL(loop, "loop session at end"); root.GET_ATTRIBUTE_BOOL(playonload, "start playing when session is loaded"); + root.GET_ATTRIBUTE(locateonload, "s", + "if >= 0, locate to this time when session is loaded"); root.GET_ATTRIBUTE(levelmeter_tc, "s", "level meter time constant"); root.GET_ATTRIBUTE_NOUNIT(levelmeter_weight, "level meter weighting"); root.GET_ATTRIBUTE(levelmeter_mode, "", @@ -421,6 +423,8 @@ TASCAR::session_t::session_t() jackc_transport_t::activate(); add_transport_methods(); osc_server_t::activate(); + if(locateonload >= 0.0) + tp_locate(locateonload); if(playonload) tp_start(); } @@ -462,6 +466,8 @@ TASCAR::session_t::session_t(const std::string& filename_or_data, load_type_t t, jackc_transport_t::activate(); add_transport_methods(); osc_server_t::activate(); + if(locateonload >= 0.0) + tp_locate(locateonload); if(playonload) tp_start(); } From 6e140e581e54c86513e41fda4dff431ed6c64133 Mon Sep 17 00:00:00 2001 From: "Giso Grimm (thetys)" Date: Tue, 7 Jan 2025 16:42:10 +0100 Subject: [PATCH 31/34] fix return value of OSC handler in datalogging to allow further handling --- plugins/src/tascarmod_datalogging.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/src/tascarmod_datalogging.cc b/plugins/src/tascarmod_datalogging.cc index 92f88c31..f02def71 100644 --- a/plugins/src/tascarmod_datalogging.cc +++ b/plugins/src/tascarmod_datalogging.cc @@ -1236,7 +1236,7 @@ int datalogging_t::osc_session_start(const char*, const char*, lo_arg**, int, ((datalogging_t*)user_data)->on_ui_start(); else ((datalogging_t*)user_data)->osc_start.emit(); - return 0; + return 1; } int datalogging_t::osc_session_stop(const char*, const char*, lo_arg**, int, @@ -1246,7 +1246,7 @@ int datalogging_t::osc_session_stop(const char*, const char*, lo_arg**, int, ((datalogging_t*)user_data)->on_ui_stop(); else ((datalogging_t*)user_data)->osc_stop.emit(); - return 0; + return 1; } int datalogging_t::osc_session_set_trialid(const char*, const char*, @@ -1256,7 +1256,7 @@ int datalogging_t::osc_session_set_trialid(const char*, const char*, ((datalogging_t*)user_data)->osc_trialid = std::string(&(argv[0]->s)); if(!((datalogging_t*)user_data)->is_headless()) ((datalogging_t*)user_data)->osc_set_trialid.emit(); - return 0; + return 1; } void datalogging_t::on_osc_set_trialid() @@ -1361,7 +1361,7 @@ int oscvar_t::osc_receive_sample(const char* path, const char* types, } } ((oscvar_t*)user_data)->osc_receive_sample(path, argc + 1, data); - return 0; + return 1; } void oscvar_t::osc_receive_sample(const char*, uint32_t size, double* data) @@ -1380,7 +1380,7 @@ int oscsvar_t::osc_receive_sample(const char* path, const char*, lo_arg** argv, else ((oscsvar_t*)user_data) ->osc_receive_sample(path, argv[0]->d, &(argv[1]->s)); - return 0; + return 1; } void oscsvar_t::osc_receive_sample(const char*, double t, const char* msg) From 5210af8025a4f50f6ecb4e3467a1e3fa862515c9 Mon Sep 17 00:00:00 2001 From: "Giso Grimm (thetys)" Date: Wed, 8 Jan 2025 10:19:55 +0100 Subject: [PATCH 32/34] add new accelerated movement plugins --- manual/actmodaccmovement.tex | 10 ++ manual/actmodaccrotator.tex | 10 ++ manual/documentation.tsc | 2 + manual/modtabtex/tabaccmovement.tex | 23 +++++ manual/modtabtex/tabaccrotator.tex | 23 +++++ manual/oscdoc_tascarmod_accmovement.tex | 26 +++++ manual/oscdoc_tascarmod_accrotator.tex | 20 ++++ packaging/deb/tascarcli.csv | 1 + plugins/Makefile | 8 +- plugins/src/tascarmod_accmovement.cc | 130 ++++++++++++++++++++++++ plugins/src/tascarmod_accrotator.cc | 107 +++++++++++++++++++ 11 files changed, 356 insertions(+), 4 deletions(-) create mode 100644 manual/actmodaccmovement.tex create mode 100644 manual/actmodaccrotator.tex create mode 100644 manual/modtabtex/tabaccmovement.tex create mode 100644 manual/modtabtex/tabaccrotator.tex create mode 100644 manual/oscdoc_tascarmod_accmovement.tex create mode 100644 manual/oscdoc_tascarmod_accrotator.tex create mode 100644 plugins/src/tascarmod_accmovement.cc create mode 100644 plugins/src/tascarmod_accrotator.cc diff --git a/manual/actmodaccmovement.tex b/manual/actmodaccmovement.tex new file mode 100644 index 00000000..a67ef4c0 --- /dev/null +++ b/manual/actmodaccmovement.tex @@ -0,0 +1,10 @@ +Accelerated movement, with the position vector $\boldsymbol{r}$ as +\begin{equation} + \boldsymbol{r} = \boldsymbol{p}_{acc\_onset} + t \boldsymbol{v} + \left\{\begin{array}{ll} \frac12 t^2 \boldsymbol{a} & t > 0\\0 & t \le 0 + \end{array}\right. +\end{equation} +with $t$ being the time since $t_\textrm{acc\_onset}$. + +\input{modtabtex/tabaccmovement.tex} + +\input{oscdoc_tascarmod_accmovement.tex} diff --git a/manual/actmodaccrotator.tex b/manual/actmodaccrotator.tex new file mode 100644 index 00000000..4e4d75a3 --- /dev/null +++ b/manual/actmodaccrotator.tex @@ -0,0 +1,10 @@ +Accelerated rotation, with Euler angle around z axis $\Omega_z$ as +\begin{equation} + \Omega_z = \theta_{acc\_onset} + t \omega + \left\{\begin{array}{ll} \frac12 t^2 a & t > 0\\0 & t \le 0 + \end{array}\right. +\end{equation} +with $t$ being the time since $t_\textrm{acc\_onset}$. + +\input{modtabtex/tabaccrotator.tex} + +\input{oscdoc_tascarmod_accrotator.tex} diff --git a/manual/documentation.tsc b/manual/documentation.tsc index 2298859e..30cf8c3b 100644 --- a/manual/documentation.tsc +++ b/manual/documentation.tsc @@ -157,6 +157,8 @@ + + diff --git a/manual/modtabtex/tabaccmovement.tex b/manual/modtabtex/tabaccmovement.tex new file mode 100644 index 00000000..58a85fee --- /dev/null +++ b/manual/modtabtex/tabaccmovement.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:accmovement} +Attributes of element {\bf accmovement}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{a} & acceleration vector (pos, $m/s^2$) & 0 0 0\\ +\hline +\indattr{actor} & pattern to match actor objects (string array) & \\ +\hline +\indattr{p\_acc\_onset} & start position at time t\_acc\_onset (pos, m) & 0 0 0\\ +\hline +\indattr{t\_acc\_onset} & onset of acceleration time t\_acc\_onset (double, s) & 0\\ +\hline +\indattr{v} & velocity vector (pos, $m/s$) & 1 1 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/modtabtex/tabaccrotator.tex b/manual/modtabtex/tabaccrotator.tex new file mode 100644 index 00000000..df051232 --- /dev/null +++ b/manual/modtabtex/tabaccrotator.tex @@ -0,0 +1,23 @@ +\definecolor{shadecolor}{RGB}{255,230,204}\begin{snugshade} +{\footnotesize +\label{attrtab:accrotator} +Attributes of element {\bf accrotator}\nopagebreak + +\begin{tabularx}{\textwidth}{lXl} +\hline +name & description (type, unit) & def.\\ +\hline +\hline +\indattr{acc} & angular acceleration at origin (double, $\textrm{rad}/s^2$) & 0\\ +\hline +\indattr{actor} & pattern to match actor objects (string array) & \\ +\hline +\indattr{omega} & angular velocity vector (double, $\textrm{rad}/s$) & 1\\ +\hline +\indattr{t\_acc\_onset} & onset of angular acceleration time t\_acc\_onset (double, s) & 0\\ +\hline +\indattr{theta\_acc\_onset} & start angular rotation at time t\_acc\_onset (double, rad) & 0\\ +\hline +\end{tabularx} +} +\end{snugshade} diff --git a/manual/oscdoc_tascarmod_accmovement.tex b/manual/oscdoc_tascarmod_accmovement.tex new file mode 100644 index 00000000..464537d9 --- /dev/null +++ b/manual/oscdoc_tascarmod_accmovement.tex @@ -0,0 +1,26 @@ +\definecolor{shadecolor}{RGB}{236,236,255}\begin{snugshade} +{\footnotesize +\label{osctab:tascarmodaccmovement} +OSC variables: +\nopagebreak + +\begin{tabularx}{\textwidth}{llllX} +\hline +path & fmt. & range & r. & description\\ +\hline +\attr{/.../a/x} & f & & yes & acceleration in x-direction in $m/s^2$\\ +\attr{/.../a/y} & f & & yes & acceleration in y-direction in $m/s^2$\\ +\attr{/.../a/z} & f & & yes & acceleration in z-direction in $m/s^2$\\ +\attr{/.../avpt} & dddddddddd & & no & \\ +\attr{/.../p\_acc\_onset/x} & f & & yes & start x-position at time $t_{acc\_onset}$ in m\\ +\attr{/.../p\_acc\_onset/y} & f & & yes & start y-position at time $t_{acc\_onset}$ in m\\ +\attr{/.../p\_acc\_onset/z} & f & & yes & start z-position at time $t_{acc\_onset}$ in m\\ +\attr{/.../t\_acc\_onset} & f & & yes & reference session time in s\\ +\attr{/.../v/x} & f & & yes & velocity in x-direction in m/s\\ +\attr{/.../v/y} & f & & yes & velocity in y-direction in m/s\\ +\attr{/.../v/z} & f & & yes & velocity in z-direction in m/s\\ +\hline +\end{tabularx} +} +\end{snugshade} +\definecolor{shadecolor}{RGB}{255,230,204} diff --git a/manual/oscdoc_tascarmod_accrotator.tex b/manual/oscdoc_tascarmod_accrotator.tex new file mode 100644 index 00000000..1556a078 --- /dev/null +++ b/manual/oscdoc_tascarmod_accrotator.tex @@ -0,0 +1,20 @@ +\definecolor{shadecolor}{RGB}{236,236,255}\begin{snugshade} +{\footnotesize +\label{osctab:tascarmodaccrotator} +OSC variables: +\nopagebreak + +\begin{tabularx}{\textwidth}{llllX} +\hline +path & fmt. & range & r. & description\\ +\hline +\attr{/.../acc} & f & & yes & angular acceleration $rad/s^2$\\ +\attr{/.../awzt} & dddd & & no & \\ +\attr{/.../omega} & f & & yes & angular velocity in $rad/s$\\ +\attr{/.../t\_acc\_onset} & f & & yes & time of acceleration onset\\ +\attr{/.../theta\_acc\_onset} & f & & yes & angular rotation at time $t_{acc\_onset}$ in rad\\ +\hline +\end{tabularx} +} +\end{snugshade} +\definecolor{shadecolor}{RGB}{255,230,204} diff --git a/packaging/deb/tascarcli.csv b/packaging/deb/tascarcli.csv index 0cb1ca3e..55f352e5 100644 --- a/packaging/deb/tascarcli.csv +++ b/packaging/deb/tascarcli.csv @@ -86,6 +86,7 @@ "plugins/build/tascar_motionpath.so","usr/lib/" "plugins/build/tascar_nearsensor.so","usr/lib/" "plugins/build/tascar_osc2lsl.so","usr/lib/" +"plugins/build/tascar_acc*.so","usr/lib/" "plugins/build/tascar_oscactor.so","usr/lib/" "plugins/build/tascar_osceog.so","usr/lib/" "plugins/build/tascar_oscevents.so","usr/lib/" diff --git a/plugins/Makefile b/plugins/Makefile index f8e059ce..649363d0 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -17,12 +17,12 @@ RECEIVERS = omni nsp amb3h0v amb3h3v amb1h0v amb1h1v cardioid hann \ SOURCES = omni cardioidmod door generic1storder farsrc head TASCARMODS = system pos2osc sampler pendulum epicycles motionpath \ - foa2hoadiff route oscevents oscjacktime nearsensor dirgain \ - dummy matrix hoafdnrot mpu6050track touchosc savegains hrirconv \ + foa2hoadiff route oscevents oscjacktime nearsensor dirgain dummy \ + matrix hoafdnrot mpu6050track touchosc savegains hrirconv \ hossustain rotator orientationmodulator locationmodulator \ linearmovement granularsynth fail waitforjackport sleep jackrec \ - oscserver oscrelay echoc snapangle oscactor \ - oscheadtracker osceog tonalenhancement systime skyfall + oscserver oscrelay echoc snapangle oscactor oscheadtracker osceog \ + tonalenhancement systime skyfall accmovement accrotator TASCARMODSGUI = tracegui simplecontroller timedisplay geopresets \ transportgui lightcolorpicker datalogging sendsvg diff --git a/plugins/src/tascarmod_accmovement.cc b/plugins/src/tascarmod_accmovement.cc new file mode 100644 index 00000000..1584626f --- /dev/null +++ b/plugins/src/tascarmod_accmovement.cc @@ -0,0 +1,130 @@ +/* License (GPL) + * + * Copyright (C) 2018 Giso Grimm + * Copyright (C) 2025 Thirsa Huisman + * + * This program is free software; you can redistribute it and/ or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "session.h" +#include + +class accmotion_t : public TASCAR::actor_module_t { +public: + accmotion_t(const TASCAR::module_cfg_t& cfg); + virtual ~accmotion_t(); + void update(uint32_t tp_frame, bool running); + void osc_set_avpt(double ax, double ay, double az, double vx, double vy, + double vz, double x, double y, double z, double t); + +private: + TASCAR::pos_t a; // acceleration m/s2 + TASCAR::pos_t v; // velocity m/s + TASCAR::pos_t p_acc_onset; // position m + double t_acc_onset; // acc onset in tascar s + std::mutex mtx; +}; + +int osc_update(const char*, const char*, lo_arg** argv, int argc, lo_message, + void* user_data) +{ + if(user_data && (argc == 10)) + ((accmotion_t*)user_data) + ->osc_set_avpt(argv[0]->d, argv[1]->d, argv[2]->d, argv[3]->d, + argv[4]->d, argv[5]->d, argv[6]->d, argv[7]->d, + argv[8]->d, argv[9]->d); + return 0; +} + +accmotion_t::accmotion_t(const TASCAR::module_cfg_t& cfg) + : actor_module_t(cfg, true), a(0.0, 0.0, 0.0), v(1.0, 1.0, 0.0), + p_acc_onset(0.0, 0.0, 0.0), t_acc_onset(0.0) +{ + actor_module_t::GET_ATTRIBUTE(a, "$m/s^2$", "acceleration vector"); + actor_module_t::GET_ATTRIBUTE(v, "$m/s$", "velocity vector"); + actor_module_t::GET_ATTRIBUTE(p_acc_onset, "m", + "start position at time t_acc_onset"); + actor_module_t::GET_ATTRIBUTE(t_acc_onset, "s", + "onset of acceleration time t_acc_onset"); + std::string oldpref(session->get_prefix()); + session->set_prefix(TASCAR::vecstr2str(actor)); + session->set_variable_owner( + TASCAR::strrep(TASCAR::tscbasename(__FILE__), ".cc", "")); + session->add_double("/a/x", &a.x, "", + "acceleration in x-direction in $m/s^2$"); + session->add_double("/a/y", &a.y, "", + "acceleration in y-direction in $m/s^2$"); + session->add_double("/a/z", &a.z, "", + "acceleration in z-direction in $m/s^2$"); + session->add_double("/v/x", &v.x, "", "velocity in x-direction in m/s"); + session->add_double("/v/y", &v.y, "", "velocity in y-direction in m/s"); + session->add_double("/v/z", &v.z, "", "velocity in z-direction in m/s"); + session->add_double("/p_acc_onset/x", &p_acc_onset.x, "", + "start x-position at time $t_{acc\\_onset}$ in m"); + session->add_double("/p_acc_onset/y", &p_acc_onset.y, "", + "start y-position at time $t_{acc\\_onset}$ in m"); + session->add_double("/p_acc_onset/z", &p_acc_onset.z, "", + "start z-position at time $t_{acc\\_onset}$ in m"); + session->add_double("/t_acc_onset", &t_acc_onset, "", + "reference session time in s"); + session->add_method("/avpt", "dddddddddd", &osc_update, this); + session->unset_variable_owner(); + session->set_prefix(oldpref); +} + +void accmotion_t::osc_set_avpt(double ax, double ay, double az, double vx, + double vy, double vz, double x, double y, + double z, double t) +{ + std::lock_guard lock(mtx); + a.x = ax; + a.y = ay; + a.z = az; + v.x = vx; + v.y = vy; + v.z = vz; + p_acc_onset.x = x; + p_acc_onset.y = y; + p_acc_onset.z = z; + t_acc_onset = t; +} + +void accmotion_t::update(uint32_t tp_frame, bool) +{ + if(mtx.try_lock()) { + double tptime(tp_frame * t_sample); + tptime -= t_acc_onset; + TASCAR::pos_t ra(a); + TASCAR::pos_t r(v); + r *= tptime; + ra *= 0.5 * tptime * tptime * (tptime >= 0); + r += p_acc_onset + ra; + set_location(r); + mtx.unlock(); + } +} + +accmotion_t::~accmotion_t() {} + +REGISTER_MODULE(accmotion_t); + +/* + * Local Variables: + * mode: c++ + * c-basic-offset: 2 + * compile-command: "make -C .." + * End: + */ diff --git a/plugins/src/tascarmod_accrotator.cc b/plugins/src/tascarmod_accrotator.cc new file mode 100644 index 00000000..ff85ba82 --- /dev/null +++ b/plugins/src/tascarmod_accrotator.cc @@ -0,0 +1,107 @@ +/* + * This file is part of the TASCAR software, see + * + * Copyright (c) 2019 Giso Grimm + * Copyright (C) 2025 Thirsa Huisman + */ +/* + * TASCAR is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation, version 3 of the License. + * + * TASCAR is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHATABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License, version 3 for more details. + * + * You should have received a copy of the GNU General Public License, + * Version 3 along with TASCAR. If not, see . + */ +#include "session.h" +#include + +class accrotation_t : public TASCAR::actor_module_t { +public: + accrotation_t(const TASCAR::module_cfg_t& cfg); + virtual ~accrotation_t(); + void update(uint32_t tp_frame, bool running); + void osc_set_awzt(double a, double w, double th, double t); + +private: + double acc; // angular acceleration in rad/s^2 + double omega; // angular velocity in rad/s + double theta_acc_onset; // rotation angle at acc onset in rad + double t_acc_onset; // time of angular acceleration onset in tascar s + std::mutex mtx; +}; + +int osc_update(const char*, const char*, lo_arg** argv, int argc, lo_message, + void* user_data) +{ + if(user_data && (argc == 4)) + ((accrotation_t*)user_data) + ->osc_set_awzt(argv[0]->d, argv[1]->d, argv[2]->d, argv[3]->d); + return 0; +} + +accrotation_t::accrotation_t(const TASCAR::module_cfg_t& cfg) + : actor_module_t(cfg, true), acc(0.0), omega(1.0), theta_acc_onset(0.0), + t_acc_onset(0.0) +{ + actor_module_t::GET_ATTRIBUTE(acc, "$\\textrm{rad}/s^2$", + "angular acceleration at origin"); + actor_module_t::GET_ATTRIBUTE(omega, "$\\textrm{rad}/s$", + "angular velocity vector"); + actor_module_t::GET_ATTRIBUTE(theta_acc_onset, "rad", + "start angular rotation at time t_acc_onset"); + actor_module_t::GET_ATTRIBUTE( + t_acc_onset, "s", "onset of angular acceleration time t_acc_onset"); + std::string oldpref(session->get_prefix()); + session->set_prefix(TASCAR::vecstr2str(actor)); + session->set_variable_owner( + TASCAR::strrep(TASCAR::tscbasename(__FILE__), ".cc", "")); + session->add_double("/acc", &acc, "", "angular acceleration $rad/s^2$"); + session->add_double("/omega", &omega, "", "angular velocity in $rad/s$"); + session->add_double("/theta_acc_onset", &theta_acc_onset, "", + "angular rotation at time $t_{acc\\_onset}$ in rad"); + session->add_double("/t_acc_onset", &t_acc_onset, "", + "time of acceleration onset"); + session->add_method("/awzt", "dddd", &osc_update, this); + session->unset_variable_owner(); + session->set_prefix(oldpref); +} + +void accrotation_t::osc_set_awzt(double a, double w, double z, double t) +{ + std::lock_guard lock(mtx); + acc = a; + omega = w; + theta_acc_onset = z; + t_acc_onset = t; +} + +void accrotation_t::update(uint32_t tp_frame, bool) +{ + if(mtx.try_lock()) { + double tptime(tp_frame * t_sample); + tptime -= t_acc_onset; + TASCAR::zyx_euler_t r; + double cv_rot = tptime * omega; + double ac_rot = 0.5 * acc * tptime * tptime * (tptime >= 0); + r.z = theta_acc_onset + cv_rot + ac_rot; + set_orientation(r); + mtx.unlock(); + } +} + +accrotation_t::~accrotation_t() {} + +REGISTER_MODULE(accrotation_t); + +/* + * Local Variables: + * mode: c++ + * c-basic-offset: 2 + * compile-command: "make -C .." + * End: + */ From bb20d92c8b4039561f026ddb7916c3c2ae187778 Mon Sep 17 00:00:00 2001 From: "Giso Grimm (thetys)" Date: Wed, 8 Jan 2025 15:35:27 +0100 Subject: [PATCH 33/34] add shelving filters --- libtascar/include/filterclass.h | 4 ++ libtascar/src/filterclass.cc | 78 ++++++++++++++++++++++++++++++--- plugins/src/tascar_ap_filter.cc | 16 ++++++- 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/libtascar/include/filterclass.h b/libtascar/include/filterclass.h index 3db94a4d..0bd59b1b 100644 --- a/libtascar/include/filterclass.h +++ b/libtascar/include/filterclass.h @@ -190,6 +190,8 @@ namespace TASCAR { b2_ = b2; }; void set_pareq(double f, double fs, double gain, double q); + void set_highshelf(double f, double fs, double gain, double s); + void set_lowshelf(double f, double fs, double gain, double s); inline double filter(double in) { double out = z1 + b0_ * in; @@ -245,6 +247,8 @@ namespace TASCAR { */ void set_butterworth(float f, float fs, bool highpass = false); void set_pareq(float f, float fs, float gain, float q); + void set_highshelf(float f, float fs, float gain, float s); + void set_lowshelf(float f, float fs, float gain, float s); inline float filter(float in) { float out = z1 + b0_ * in; diff --git a/libtascar/src/filterclass.cc b/libtascar/src/filterclass.cc index e41deb73..f436746e 100644 --- a/libtascar/src/filterclass.cc +++ b/libtascar/src/filterclass.cc @@ -460,6 +460,37 @@ void TASCAR::biquad_t::set_pareq(double f, double fs, double gain, double q) } } +void TASCAR::biquad_t::set_highshelf(double f, double fs, double gain, double s) +{ + // https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html + double A = pow(10.0, gain / 40.0); + double omega0 = TASCAR_2PI * f / fs; + double alpha = + 0.5 * sin(omega0) * sqrt((A + 1.0 / A) * (1.0 / s - 1.0) + 2.0); + double a0 = (A + 1.0) - (A - 1.0) * cos(omega0) + 2.0 * sqrt(A) * alpha; + double a1 = 2.0 * ((A - 1.0) - (A + 1.0) * cos(omega0)); + double a2 = (A + 1.0) - (A - 1.0) * cos(omega0) - 2.0 * sqrt(A) * alpha; + double b0 = A * ((A + 1.0) + (A - 1.0) * cos(omega0) + 2.0 * sqrt(A) * alpha); + double b1 = -2.0 * A * ((A - 1.0) + (A + 1.0) * cos(omega0)); + double b2 = A * ((A + 1.0) + (A - 1.0) * cos(omega0) - 2.0 * sqrt(A) * alpha); + set_coefficients(a1 / a0, a2 / a0, b0 / a0, b1 / a0, b2 / a0); +} + +void TASCAR::biquad_t::set_lowshelf(double f, double fs, double gain, double s) +{ + double A = pow(10.0, gain / 40.0); + double omega0 = TASCAR_2PI * f / fs; + double alpha = + 0.5f * sin(omega0) * sqrt((A + 1.0 / A) * (1.0 / s - 1.0) + 2.0); + double a0 = (A + 1.0) + (A - 1.0) * cos(omega0) + 2.0 * sqrt(A) * alpha; + double a1 = -2.0 * ((A - 1.0) + (A + 1.0) * cos(omega0)); + double a2 = (A + 1.0) + (A - 1.0) * cos(omega0) - 2.0 * sqrt(A) * alpha; + double b0 = A * ((A + 1.0) - (A - 1.0) * cos(omega0) + 2.0 * sqrt(A) * alpha); + double b1 = 2.0 * A * ((A - 1.0) - (A + 1.0) * cos(omega0)); + double b2 = A * ((A + 1.0) - (A - 1.0) * cos(omega0) - 2.0 * sqrt(A) * alpha); + set_coefficients(a1 / a0, a2 / a0, b0 / a0, b1 / a0, b2 / a0); +} + void bilinear(std::vector>& poles, double& gain) { std::complex prod_poles = 1.0; @@ -569,6 +600,40 @@ void TASCAR::biquadf_t::set_pareq(float f, float fs, float gain, float q) } } +void TASCAR::biquadf_t::set_highshelf(float f, float fs, float gain, float s) +{ + float A = powf(10.0f, gain / 40.0f); + float omega0 = TASCAR_2PIf * f / fs; + float alpha = + 0.5f * sinf(omega0) * sqrtf((A + 1.0f / A) * (1.0f / s - 1.0f) + 2.0f); + float a0 = (A + 1.0f) - (A - 1.0f) * cosf(omega0) + 2.0f * sqrtf(A) * alpha; + float a1 = 2.0f * ((A - 1.0f) - (A + 1.0f) * cosf(omega0)); + float a2 = (A + 1.0f) - (A - 1.0f) * cosf(omega0) - 2.0f * sqrtf(A) * alpha; + float b0 = + A * ((A + 1.0f) + (A - 1.0f) * cosf(omega0) + 2.0f * sqrtf(A) * alpha); + float b1 = -2.0f * A * ((A - 1.0f) + (A + 1.0f) * cosf(omega0)); + float b2 = + A * ((A + 1.0f) + (A - 1.0f) * cosf(omega0) - 2.0f * sqrtf(A) * alpha); + set_coefficients(a1 / a0, a2 / a0, b0 / a0, b1 / a0, b2 / a0); +} + +void TASCAR::biquadf_t::set_lowshelf(float f, float fs, float gain, float s) +{ + float A = powf(10.0f, gain / 40.0f); + float omega0 = TASCAR_2PIf * f / fs; + float alpha = + 0.5f * sinf(omega0) * sqrtf((A + 1.0f / A) * (1.0f / s - 1.0f) + 2.0f); + float a0 = (A + 1.0f) + (A - 1.0f) * cosf(omega0) + 2.0f * sqrtf(A) * alpha; + float a1 = -2.0f * ((A - 1.0f) + (A + 1.0f) * cosf(omega0)); + float a2 = (A + 1.0f) + (A - 1.0f) * cosf(omega0) - 2.0f * sqrtf(A) * alpha; + float b0 = + A * ((A + 1.0f) - (A - 1.0f) * cosf(omega0) + 2.0f * sqrtf(A) * alpha); + float b1 = 2.0f * A * ((A - 1.0f) - (A + 1.0f) * cosf(omega0)); + float b2 = + A * ((A + 1.0f) - (A - 1.0f) * cosf(omega0) - 2.0f * sqrtf(A) * alpha); + set_coefficients(a1 / a0, a2 / a0, b0 / a0, b1 / a0, b2 / a0); +} + TASCAR::aweighting_t::aweighting_t(double fs) { b1.set_analog_poles(7.39705e9, -76655.0, -76655.0, fs); @@ -801,7 +866,7 @@ float absorptionerror(const std::vector& vPar, void* data) a2 *= a2; e += a2; } - e /= pOpt->alpha.size(); + e /= (float)(pOpt->alpha.size()); if((d > 1.0f) || (d < 0.0f)) e = 1e6f; return e; @@ -882,8 +947,9 @@ std::vector dupvec_chk(std::vector vec, unsigned n) if(vec.size() == 1) vec.resize(n, vec[vec.size() - 1]); if(vec.size() != n) - throw TASCAR::ErrMsg("Invalid vector length (expected 1 or " + std::to_string(n) + - ", got " + std::to_string(vec.size()) + ")."); + throw TASCAR::ErrMsg("Invalid vector length (expected 1 or " + + std::to_string(n) + ", got " + + std::to_string(vec.size()) + ")."); return vec; } @@ -906,11 +972,11 @@ TASCAR::o1_ar_filter_t::o1_ar_filter_t(unsigned int channels, float fs_, void o1_lp_coeffs(const float tau, const float fs, float& c1, float& c2) { - if((tau > 0) && (fs > 0)) - c1 = exp(-1.0 / (tau * fs)); + if((tau > 0.0f) && (fs > 0.0f)) + c1 = expf(-1.0f / (tau * fs)); else c1 = 0; - c2 = 1.0 - c1; + c2 = 1.0f - c1; } void TASCAR::o1_ar_filter_t::set_tau_attack(unsigned int ch, float tau) diff --git a/plugins/src/tascar_ap_filter.cc b/plugins/src/tascar_ap_filter.cc index 5bdab769..ca164c22 100644 --- a/plugins/src/tascar_ap_filter.cc +++ b/plugins/src/tascar_ap_filter.cc @@ -25,7 +25,7 @@ class biquadplugin_t : public TASCAR::audioplugin_base_t { public: - enum filtertype_t { lowpass, highpass, equalizer }; + enum filtertype_t { lowpass, highpass, equalizer, highshelf, lowshelf }; biquadplugin_t(const TASCAR::audioplugin_cfg_t& cfg); void ap_process(std::vector& chunk, const TASCAR::pos_t& pos, const TASCAR::zyx_euler_t&, const TASCAR::transport_t& tp); @@ -52,7 +52,9 @@ biquadplugin_t::biquadplugin_t(const TASCAR::audioplugin_cfg_t& cfg) GET_ATTRIBUTE_BOOL(highpass, "Highpass filter (true) or lowpass filter (false)"); std::string mode("lohi"); - GET_ATTRIBUTE(mode, "", "filter mode: lohi, lowpass, highpass, equalizer"); + GET_ATTRIBUTE( + mode, "", + "filter mode: lohi, lowpass, highpass, equalizer, highshelf, lowshelf"); if(mode == "lohi") { if(highpass) ftype = biquadplugin_t::highpass; @@ -64,6 +66,10 @@ biquadplugin_t::biquadplugin_t(const TASCAR::audioplugin_cfg_t& cfg) ftype = biquadplugin_t::highpass; else if(mode == "equalizer") ftype = biquadplugin_t::equalizer; + else if(mode == "highshelf") + ftype = biquadplugin_t::highshelf; + else if(mode == "lowshelf") + ftype = biquadplugin_t::lowshelf; else throw TASCAR::ErrMsg("Invalid mode: " + mode); } @@ -113,6 +119,12 @@ void biquadplugin_t::ap_process(std::vector& chunk, case equalizer: bp[k]->set_pareq(fc, (float)f_sample, gain, Q); break; + case highshelf: + bp[k]->set_highshelf(fc, (float)f_sample, gain, Q); + break; + case lowshelf: + bp[k]->set_lowshelf(fc, (float)f_sample, gain, Q); + break; } bp[k]->filter(chunk[k]); } From 87bc915770561527dcb776c6eaa4883db49646a4 Mon Sep 17 00:00:00 2001 From: "Giso Grimm (thetys)" Date: Wed, 8 Jan 2025 15:55:23 +0100 Subject: [PATCH 34/34] release 0.234.0 --- changelog | 7 +++++++ config.mk | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/changelog b/changelog index fab8a1f5..820796c8 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,10 @@ +0.234: + - addition of accelerated movement plugins (Thirsa Huisman) + - addition of shelving filter modes in filter audio plugin + - improvement of simplefdn + - improvement of simplesynth + - replace xyzgain attribute by automatic detection of 2D/3D layout + 0.233.2: bugfix of tascar_spkcalib 0.233.1: - add layer property for faces/facegroups (fholzm) diff --git a/config.mk b/config.mk index 0415cf2e..fef3c448 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ # variables: -VERSION=0.233.2 +VERSION=0.234.0 ARCH=$(shell uname -m)