diff --git a/libtascar/include/audiochunks.h b/libtascar/include/audiochunks.h index 75025030..9b2125a8 100644 --- a/libtascar/include/audiochunks.h +++ b/libtascar/include/audiochunks.h @@ -64,7 +64,13 @@ namespace TASCAR { float gain = 1.0); float ms() const; float rms() const; - float maxabs() const; + inline float maxabs() const + { + float rv = 0.0f; + for(uint32_t k = 0; k < n; ++k) + rv = std::max(rv, fabsf(d[k])); + return rv; + }; float spldb() const; float maxabsdb() const; void append(const wave_t& src); diff --git a/libtascar/src/audiochunks.cc b/libtascar/src/audiochunks.cc index 0772b967..404e8566 100644 --- a/libtascar/src/audiochunks.cc +++ b/libtascar/src/audiochunks.cc @@ -205,14 +205,6 @@ float wave_t::ms() const return rv; } -float wave_t::maxabs() const -{ - float rv(0.0f); - for(uint32_t k = 0; k < size(); ++k) - rv = std::max(rv, fabsf(d[k])); - return rv; -} - float wave_t::spldb() const { return 10.0f * log10f(ms()) - SPLREFf; diff --git a/plugins/src/receivermod_simplefdn.cc b/plugins/src/receivermod_simplefdn.cc index 110aaaa0..4f10c163 100644 --- a/plugins/src/receivermod_simplefdn.cc +++ b/plugins/src/receivermod_simplefdn.cc @@ -32,21 +32,18 @@ const std::complex i_f(0.0f, 1.0f); /** * A First Order Ambisonics audio sample */ -class foa_sample_t { +class foa_sample_t : public TASCAR::posf_t { public: - foa_sample_t() : w(0), x(0), y(0), z(0){}; + foa_sample_t() : w(0) { set_zero(); }; foa_sample_t(float w_, float x_, float y_, float z_) - : w(w_), x(x_), y(y_), z(z_){}; - float w; - float x; - float y; - float z; - void clear() + : posf_t(x_, y_, z_), w(w_){}; + float w = 0.0f; + void set_zero() { - w = 0.0; - x = 0.0; - y = 0.0; - z = 0.0; + w = 0.0f; + x = 0.0f; + y = 0.0f; + z = 0.0f; }; inline foa_sample_t& operator+=(const foa_sample_t& b) { @@ -98,108 +95,38 @@ inline foa_sample_t operator+(foa_sample_t a, const foa_sample_t& b) return a; } -/** - * Two-dimensional container for foa_sample_t - */ -class foa_sample_matrix_2d_t { -public: - foa_sample_matrix_2d_t(uint32_t d1, uint32_t d2); - ~foa_sample_matrix_2d_t(); - inline foa_sample_t& elem(uint32_t p1, uint32_t p2) - { - return data[p1 * s2 + p2]; - }; - inline const foa_sample_t& elem(uint32_t p1, uint32_t p2) const - { - return data[p1 * s2 + p2]; - }; - inline foa_sample_t& elem00x(uint32_t p2) { return data[p2]; }; - inline const foa_sample_t& elem00x(uint32_t p2) const { return data[p2]; }; - inline void clear() - { - uint32_t l(s1 * s2); - for(uint32_t k = 0; k < l; ++k) - data[k].clear(); - }; - -protected: - uint32_t s1; - uint32_t s2; - foa_sample_t* data; -}; - -class foa_sample_array_1d_t { -public: - foa_sample_array_1d_t(uint32_t d1); - ~foa_sample_array_1d_t(); - inline foa_sample_t& elem(uint32_t p1) { return data[p1]; }; - inline const foa_sample_t& elem(uint32_t p1) const { return data[p1]; }; - inline void clear() - { - for(uint32_t k = 0; k < s1; ++k) - data[k].clear(); - }; - -protected: - uint32_t s1; - foa_sample_t* data; -}; - -foa_sample_matrix_2d_t::foa_sample_matrix_2d_t(uint32_t d1, uint32_t d2) - : s1(d1), s2(d2), data(new foa_sample_t[s1 * s2]) -{ - clear(); -} - -foa_sample_matrix_2d_t::~foa_sample_matrix_2d_t() -{ - delete[] data; -} - -foa_sample_array_1d_t::foa_sample_array_1d_t(uint32_t d1) - : s1(d1), data(new foa_sample_t[s1]) -{ - clear(); -} - -foa_sample_array_1d_t::~foa_sample_array_1d_t() -{ - delete[] data; -} - // y[n] = -g x[n] + x[n-1] + g y[n-1] class reflectionfilter_t { public: - reflectionfilter_t(uint32_t d1); - inline void filter(foa_sample_t& x, uint32_t p1) + reflectionfilter_t(); + inline void filter(foa_sample_t& x) { x *= B1; - x -= A2 * sy.elem(p1); - sy.elem(p1) = x; + x -= A2 * sy; + sy = x; // all pass section: - foa_sample_t tmp(eta[p1] * x + sapx.elem(p1)); - sapx.elem(p1) = x; - x = tmp - eta[p1] * sapy.elem(p1); - sapy.elem(p1) = x; + 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; }; protected: - float B1; ///< non-recursive filter coefficient for all channels - float A2; ///< recursive filter coefficient for all channels - std::vector - eta; ///< phase coefficient of allpass filters for each channel - foa_sample_array_1d_t sy; ///< output state buffer - foa_sample_array_1d_t sapx; ///< input state variable of allpass filter - foa_sample_array_1d_t sapy; ///< output state variable of allpass filter + float B1 = 0.0f; ///< non-recursive filter coefficient for all channels + float A2 = 0.0f; ///< recursive filter coefficient for all channels + float eta = 0.0f; ///< phase coefficient of allpass filters for each channel + 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 }; -reflectionfilter_t::reflectionfilter_t(uint32_t d1) - : B1(0), A2(0), sy(d1), sapx(d1), sapy(d1) +reflectionfilter_t::reflectionfilter_t() { - eta.resize(d1); - for(uint32_t k = 0; k < d1; ++k) - eta[k] = 0.87f * (float)k / ((float)d1 - 1.0f); + sy.set_zero(); + sapx.set_zero(); + sapy.set_zero(); } /** @@ -211,124 +138,166 @@ reflectionfilter_t::reflectionfilter_t(uint32_t d1) */ void reflectionfilter_t::set_lp(float g, float c) { - sy.clear(); - sapx.clear(); - sapy.clear(); + sy.set_zero(); + sapx.set_zero(); + sapy.set_zero(); float c2(1.0f - c); B1 = g * c2; A2 = -c; } +class fdnpath_t { +public: + fdnpath_t() + { + init(1); + set_zero(); + }; + void init(uint32_t maxdelay) + { + delayline.resize(maxdelay); + set_zero(); + }; + void update_par(uint32_t delay_, float eta, float g, float c); + void set_zero() + { + for(auto& dl : delayline) + dl.set_zero(); + dlout.set_zero(); + } + // delay line: + std::vector delayline; + // reflection filter: + reflectionfilter_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 }; - fdn_t(uint32_t fdnorder, uint32_t maxdelay, bool logdelays, gainmethod_t gm); - ~fdn_t(); - inline void process(bool b_prefilt); + fdn_t(uint32_t fdnorder, uint32_t maxdelay, bool logdelays, gainmethod_t gm, + bool feedback_); + ~fdn_t(){}; + inline void process(foa_sample_t& x, bool b_prefilt); void setpar_t60(float az, float daz, float t, float dt, float t60, float damping, bool fixcirculantmat); void set_logdelays(bool ld) { logdelays_ = ld; }; - void clear() + void set_zero() { - delayline.clear(); - outval.clear(); + for(auto& path : fdnpath) + path.set_zero(); }; - void get_ir(TASCAR::wave_t& w, bool b_prefilt); private: - bool logdelays_; - uint32_t fdnorder_; - uint32_t maxdelay_; + bool logdelays_ = true; + uint32_t fdnorder_ = 5u; + uint32_t maxdelay_ = 8u; // delayline: - foa_sample_matrix_2d_t delayline; + // foa_sample_matrix_2d_t delayline; // feedback matrix: std::vector feedbackmat; // reflection filter: - reflectionfilter_t reflection; - reflectionfilter_t prefilt; - // rotation: - std::vector rotation; - // delayline output for reflection filters: - foa_sample_array_1d_t dlout; - // delays: - uint32_t* delay; - // delayline pointer: - uint32_t* pos; + // std::vector reflection; + reflectionfilter_t prefilt0; + reflectionfilter_t prefilt1; + // FDN path: + std::vector fdnpath; // gain calculation method: - gainmethod_t gainmethod; + gainmethod_t gainmethod = original; + // use feedback matrix: + bool feedback = true; public: // input FOA sample: - foa_sample_t inval; + // foa_sample_t inval; // output FOA sample: - foa_sample_t outval; + // foa_sample_t outval; }; fdn_t::fdn_t(uint32_t fdnorder, uint32_t maxdelay, bool logdelays, - gainmethod_t gm) + gainmethod_t gm, bool feedback_) : logdelays_(logdelays), fdnorder_(fdnorder), maxdelay_(maxdelay), - delayline(fdnorder_, maxdelay_), feedbackmat(fdnorder_ * fdnorder_), - reflection(fdnorder), prefilt(2), rotation(fdnorder), dlout(fdnorder_), - delay(new uint32_t[fdnorder_]), pos(new uint32_t[fdnorder_]), - gainmethod(gm) -{ - memset(delay, 0, sizeof(uint32_t) * fdnorder_); - memset(pos, 0, sizeof(uint32_t) * fdnorder_); -} - -fdn_t::~fdn_t() -{ - delete[] delay; - delete[] pos; + feedbackmat(fdnorder_ * fdnorder_), gainmethod(gm), feedback(feedback_) +{ + for(auto& v : feedbackmat) + v = 0.0f; + prefilt0.set_eta(0.0f); + prefilt1.set_eta(0.87f); + fdnpath.resize(fdnorder); + for(size_t k = 0; k < fdnpath.size(); ++k) { + fdnpath[k].init(maxdelay); + } + // inval.set_zero(); + // outval.set_zero(); } -void fdn_t::process(bool b_prefilt) +void fdn_t::process(foa_sample_t& x, bool b_prefilt) { if(b_prefilt) { - prefilt.filter(inval, 0); - prefilt.filter(inval, 1); - } - outval.clear(); - // get output values from delayline, apply reflection filters and rotation: - for(uint32_t tap = 0; tap < fdnorder_; ++tap) { - foa_sample_t tmp(delayline.elem(tap, pos[tap])); - reflection.filter(tmp, tap); - TASCAR::posf_t p(tmp.x, tmp.y, tmp.z); - rotation[tap].rotate(p); - tmp.x = p.x; - tmp.y = p.y; - tmp.z = p.z; - dlout.elem(tap) = tmp; - outval += tmp; + prefilt0.filter(x); + prefilt1.filter(x); } - // put rotated+attenuated value to delayline, add input: - for(uint32_t tap = 0; tap < fdnorder_; ++tap) { - // first put input into delayline: - delayline.elem(tap, pos[tap]) = inval; - // now add feedback signal: - for(uint32_t otap = 0; otap < fdnorder_; ++otap) - delayline.elem(tap, pos[tap]) += - dlout.elem(otap) * feedbackmat[fdnorder_ * tap + otap]; - // iterate delayline: - if(!pos[tap]) - pos[tap] = delay[tap]; - if(pos[tap]) - --pos[tap]; - } -} - -void fdn_t::get_ir(TASCAR::wave_t& ir, bool b_prefilt) -{ - clear(); - inval.w = 1.0f; - inval.x = 1.0f; - inval.y = 0.0f; - inval.z = 0.0f; - for(size_t k = 0u; k < ir.n; ++k) { - process(b_prefilt); - ir.d[k] = outval.w; + if(feedback) { + foa_sample_t outval; + // 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] = x; + // now add feedback signal: + uint32_t otap = 0; + for(auto& opath : fdnpath) { + path.delayline[path.pos] += + opath.dlout * feedbackmat[fdnorder_ * tap + otap]; + ++otap; + } + // iterate delayline: + if(!path.pos) + path.pos = path.delay; + if(path.pos) + --path.pos; + ++tap; + } + x = outval; + } else { + foa_sample_t outval; + // 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] = x; + x *= -1.0f; + // iterate delayline: + if(!path.pos) + path.pos = path.delay; + if(path.pos) + --path.pos; + ++tap; + } + x = outval; } - clear(); } /** @@ -344,7 +313,8 @@ void fdn_t::setpar_t60(float az, float daz, float t_min, float t_max, float t60, float damping, bool fixcirculantmat) { // set delays: - delayline.clear(); + // delayline.clear(); + set_zero(); float t_mean(0); for(uint32_t tap = 0; tap < fdnorder_; ++tap) { float t_(t_min); @@ -361,8 +331,21 @@ void fdn_t::setpar_t60(float az, float daz, float t_min, float t_max, float t60, powf((float)tap / ((float)fdnorder_ - 1.0f), 0.5f); } uint32_t d((uint32_t)std::max(0.0f, t_)); - delay[tap] = std::max(2u, std::min(maxdelay_ - 1u, d)); - t_mean += (float)delay[tap]; + 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) { + 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; } t_mean /= (float)std::max(1u, fdnorder_); float g(0.0f); @@ -377,22 +360,23 @@ void fdn_t::setpar_t60(float az, float daz, float t_min, float t_max, float t60, g = powf(10.0f, -3.0f * t_mean / t60); break; } - // set reflection filters: - reflection.set_lp(g, damping); - prefilt.set_lp(g, damping); + 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_; - rotation[tap].set_rotation(laz, TASCAR::posf_t(0, 0, 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)); - rotation[tap].rmul(q); + fdnpath[tap].rotation.rmul(q); q.set_rotation(0.125f * daz * (float)(tap % 3) - 0.25f * daz, TASCAR::posf_t(1, 0, 0)); - rotation[tap].rmul(q); + fdnpath[tap].rotation.rmul(q); } // set feedback matrix: if(fdnorder_ > 1) { @@ -427,6 +411,7 @@ class simplefdn_vars_t : public TASCAR::receivermod_base_t { float t60 = 0.0; float damping = 0.3f; bool prefilt = true; + uint32_t forwardstages = 0; bool logdelays = true; float absorption = 0.6f; float c = 340.0f; @@ -449,6 +434,7 @@ simplefdn_vars_t::simplefdn_vars_t(tsccfg::node_t xmlsrc) "tilt of T60"); GET_ATTRIBUTE_BOOL(prefilt, "Apply additional filter before inserting audio into FDN"); + GET_ATTRIBUTE(forwardstages, "", "Number of feed forward stages"); GET_ATTRIBUTE(absorption, "", "Absorption used in Sabine's equation"); GET_ATTRIBUTE(c, "m/s", "Speed of sound"); GET_ATTRIBUTE(volumetric, "m", "Dimension of room x y z"); @@ -503,8 +489,9 @@ class simplefdn_t : public simplefdn_vars_t { void update_par(); void setlogdelays(bool ld); void configure(); - void optim_param(); + // void optim_param(); void release(); + void get_ir(TASCAR::wave_t& w); receivermod_base_t::data_t* create_state_data(double srate, uint32_t fragsize) const; virtual void add_variables(TASCAR::osc_server_t* srv); @@ -524,13 +511,14 @@ class simplefdn_t : public simplefdn_vars_t { float slopeerr(const std::vector& param); private: - fdn_t* fdn; - TASCAR::amb1wave_t* foa_out; + fdn_t* feedback_delay_network = NULL; + std::vector feedforward_delay_network; + TASCAR::amb1wave_t* foa_out = NULL; pthread_mutex_t mtx; - float wgain; - float distcorr; - TASCAR::wave_t* ir_bb; - TASCAR::wave_t* ir_band; + float wgain = MIN3DB; + float distcorr = 1.0f; + TASCAR::wave_t* ir_bb = NULL; + TASCAR::wave_t* ir_band = NULL; }; int simplefdn_t::osc_fixcirculantmat(const char*, const char* types, @@ -561,9 +549,7 @@ int simplefdn_t::osc_set_dim_damp_absorption(const char*, const char* types, return 0; } -simplefdn_t::simplefdn_t(tsccfg::node_t cfg) - : simplefdn_vars_t(cfg), fdn(NULL), foa_out(NULL), wgain(MIN3DB), - distcorr(1.0) +simplefdn_t::simplefdn_t(tsccfg::node_t cfg) : simplefdn_vars_t(cfg) { if(t60 <= 0.0) t60 = @@ -624,9 +610,16 @@ void simplefdn_t::configure() { receivermod_base_t::configure(); n_channels = AMB11ACN::idx::channels; - if(fdn) - delete fdn; - fdn = new fdn_t(fdnorder, (uint32_t)f_sample, logdelays, gm); + if(feedback_delay_network) + delete feedback_delay_network; + feedback_delay_network = + new fdn_t(fdnorder, (uint32_t)f_sample, logdelays, gm, true); + 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 fdn_t(fdnorder, (uint32_t)f_sample, logdelays, gm, false)); if(foa_out) delete foa_out; foa_out = new TASCAR::amb1wave_t(n_fragment); @@ -707,8 +700,10 @@ void simplefdn_t::add_variables(TASCAR::osc_server_t* srv) simplefdn_t::~simplefdn_t() { - if(fdn) - delete fdn; + if(feedback_delay_network) + delete feedback_delay_network; + for(auto& pff : feedforward_delay_network) + delete pff; if(foa_out) delete foa_out; pthread_mutex_destroy(&mtx); @@ -729,12 +724,17 @@ void simplefdn_t::update_par() if(t60 <= 0.0) t60 = 0.161f * volumetric.boxvolumef() / (absorption * volumetric.boxareaf()); - if(fdn) { + if(feedback_delay_network) { float wscale(TASCAR_2PIf * tmin); - fdn->setpar_t60(wscale * w, wscale * dw, (float)f_sample * tmin, - (float)f_sample * tmax, (float)f_sample * t60, - std::max(0.0f, std::min(0.999f, damping)), - fixcirculantmat); + feedback_delay_network->setpar_t60( + wscale * w, wscale * dw, (float)f_sample * tmin, + (float)f_sample * tmax, (float)f_sample * t60, + std::max(0.0f, std::min(0.999f, damping)), fixcirculantmat); + for(auto& pff : feedforward_delay_network) + pff->setpar_t60(wscale * w, wscale * dw, (float)f_sample * tmin, + (float)f_sample * tmax, (float)f_sample * t60, + std::max(0.0f, std::min(0.999f, damping)), + fixcirculantmat); } pthread_mutex_unlock(&mtx); } @@ -743,8 +743,10 @@ void simplefdn_t::update_par() void simplefdn_t::setlogdelays(bool ld) { if(pthread_mutex_lock(&mtx) == 0) { - if(fdn) { - fdn->set_logdelays(ld); + if(feedback_delay_network) { + feedback_delay_network->set_logdelays(ld); + for(auto& pff : feedforward_delay_network) + pff->set_logdelays(ld); float tmin(std::min((float)volumetric.x, std::min((float)volumetric.y, (float)volumetric.z)) / c); @@ -755,10 +757,15 @@ void simplefdn_t::setlogdelays(bool ld) t60 = 0.161f * volumetric.boxvolumef() / (absorption * volumetric.boxareaf()); float wscale(TASCAR_2PIf * tmin); - fdn->setpar_t60(wscale * w, wscale * dw, (float)f_sample * tmin, - (float)f_sample * tmax, (float)f_sample * t60, - std::max(0.0f, std::min(0.999f, damping)), - fixcirculantmat); + feedback_delay_network->setpar_t60( + wscale * w, wscale * dw, (float)f_sample * tmin, + (float)f_sample * tmax, (float)f_sample * t60, + std::max(0.0f, std::min(0.999f, damping)), fixcirculantmat); + for(auto& pff : feedforward_delay_network) + pff->setpar_t60(wscale * w, wscale * dw, (float)f_sample * tmin, + (float)f_sample * tmax, (float)f_sample * t60, + std::max(0.0f, std::min(0.999f, damping)), + fixcirculantmat); } pthread_mutex_unlock(&mtx); } @@ -769,17 +776,18 @@ void simplefdn_t::postproc(std::vector& output) // correct for volumetric gain: if(pthread_mutex_trylock(&mtx) == 0) { *foa_out *= distcorr; - if(fdn) { + if(feedback_delay_network) { for(uint32_t t = 0; t < n_fragment; ++t) { - fdn->inval.w = foa_out->w()[t]; - fdn->inval.x = foa_out->x()[t]; - fdn->inval.y = foa_out->y()[t]; - fdn->inval.z = foa_out->z()[t]; - fdn->process(prefilt); - output[AMB11ACN::idx::w][t] += fdn->outval.w; - output[AMB11ACN::idx::x][t] += fdn->outval.x; - output[AMB11ACN::idx::y][t] += fdn->outval.y; - output[AMB11ACN::idx::z][t] += fdn->outval.z; + foa_sample_t inval(foa_out->w()[t], foa_out->x()[t], foa_out->y()[t], + foa_out->z()[t]); + for(auto& pff : feedforward_delay_network) + pff->process(inval, prefilt); + feedback_delay_network->process(inval, prefilt); + + output[AMB11ACN::idx::w][t] += inval.w; + output[AMB11ACN::idx::x][t] += inval.x; + output[AMB11ACN::idx::y][t] += inval.y; + output[AMB11ACN::idx::z][t] += inval.z; } } foa_out->clear(); @@ -820,9 +828,9 @@ float ir_get_t60(TASCAR::wave_t& ir, float fs) void simplefdn_t::get_t60(const std::vector& cf, std::vector& t60) { if(pthread_mutex_trylock(&mtx) == 0) { - if(fdn) { + if(feedback_delay_network) { t60.clear(); - fdn->get_ir(*ir_bb, prefilt); + get_ir(*ir_bb); TASCAR::bandpass_t bp(176.78f, 353.55f, f_sample); for(auto f : cf) { bp.set_range(f * sqrtf(0.5f), f * sqrtf(2.0f)); @@ -841,12 +849,41 @@ void simplefdn_t::get_t60(const std::vector& cf, std::vector& t60) } } +void simplefdn_t::get_ir(TASCAR::wave_t& ir) +{ + if(feedback_delay_network) { + for(auto& pff : feedforward_delay_network) + pff->set_zero(); + feedback_delay_network->set_zero(); + foa_sample_t inval; + inval.w = 1.0f; + inval.x = 1.0f; + inval.y = 0.0f; + inval.z = 0.0f; + for(size_t k = 0u; k < ir.n; ++k) { + for(auto& pff : feedforward_delay_network) + pff->process(inval, prefilt); + feedback_delay_network->process(inval, prefilt); + ir.d[k] = inval.w; + inval.set_zero(); + } + for(auto& pff : feedforward_delay_network) + pff->set_zero(); + feedback_delay_network->set_zero(); + } +} + TASCAR::receivermod_base_t::data_t* simplefdn_t::create_state_data(double, uint32_t fragsize) const { return new data_t(fragsize); } +/** + * @brief add input signal to ambisonics signal container + * + * Use panning, using source direction + */ void simplefdn_t::add_pointsource(const TASCAR::pos_t& prel, double, const TASCAR::wave_t& chunk, std::vector&, diff --git a/test/Makefile b/test/Makefile index c736aa37..358a5d70 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,24 +1,26 @@ 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_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 hoa3d test_level_hoa2d_basic \ + test_ir_wfs_noplanewave test_level_hoa2d_basic \ test_level_hoa2d_maxre test_ir_reverb test_ir_simplefdn \ - test_ir_simplefdn_gain test_snd_vbap3d_speakermatch \ - test_ir_volumetric test_pos_obstacle test_pos_obstacle_inner \ + 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 \ @@ -100,7 +102,7 @@ FAILING_TESTS = test_ir5 TOL = 2e-7 RMSTOL = 2e-7 -all: $(TESTS) $(TEST_FAIL) testrec testlev +all: $(TESTS) hoa3d $(TEST_FAIL) testrec testlev jack: $(TEST_JACK) @@ -278,6 +280,9 @@ init_%: %.tsc init_test_ir%: test_ir%.tsc $(LIBPATH) valgrind --error-exitcode=1 --leak-check=no --undef-value-errors=yes --track-origins=yes $(RENDERIR) -o test_ir$*.wav $< -t 1 -l $(IRLEN) -f 44100 +init_test_pos%: test_pos%.tsc + $(LIBPATH) RECTYPE=debugpos valgrind --error-exitcode=1 --leak-check=no --undef-value-errors=yes --track-origins=yes $(RENDERFILE) -i zeros.wav -o $*.wav -f 64 $< + clean: rm -f *~ test*.wav gdbcmd @@ -311,7 +316,7 @@ jackconnect_%: test_connect%.tsc test_snd_hoa3d_enc_hor test_snd_hoa3d_enc_vert test_snd_hoa3d_pinv_hor test_snd_hoa3d_allrad_hor test_snd_hoa3d_allrad_vert test_snd_hoa3d_pinv_vert: TOL=4e-4 test_snd_hoa3d_enc_hor test_snd_hoa3d_enc_vert test_snd_hoa3d_pinv_hor test_snd_hoa3d_allrad_hor test_snd_hoa3d_allrad_vert test_snd_hoa3d_pinv_vert: RMSTOL=1e-4 -test_ir_simplefdn test_ir_simplefdn_gain test_ir_simplefdn_optimt60: IRLEN=44100 +test_ir_simplefdn test_ir_simplefdn_gain test_ir_simplefdn_optimt60 test_ir_simplefdn_feedforward: IRLEN=44100 test_ir_reverb: IRLEN=44100 test_ir_partconvrev: IRLEN=44100 test_ir_partconvrev_offset: IRLEN=44100 diff --git a/test/expected_ir_simplefdn_feedforward.wav b/test/expected_ir_simplefdn_feedforward.wav new file mode 100644 index 00000000..52e9245f Binary files /dev/null and b/test/expected_ir_simplefdn_feedforward.wav differ diff --git a/test/test_ir_simplefdn_feedforward.tsc b/test/test_ir_simplefdn_feedforward.tsc new file mode 100644 index 00000000..185a31b5 --- /dev/null +++ b/test/test_ir_simplefdn_feedforward.tsc @@ -0,0 +1,8 @@ + + + + + + + +