summaryrefslogtreecommitdiff
path: root/servers/audio/effects/reverb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'servers/audio/effects/reverb.cpp')
-rw-r--r--servers/audio/effects/reverb.cpp364
1 files changed, 364 insertions, 0 deletions
diff --git a/servers/audio/effects/reverb.cpp b/servers/audio/effects/reverb.cpp
new file mode 100644
index 0000000000..43ea0edb3a
--- /dev/null
+++ b/servers/audio/effects/reverb.cpp
@@ -0,0 +1,364 @@
+//
+// C++ Interface: reverb
+//
+// Description:
+//
+//
+// Author: Juan Linietsky <reduzio@gmail.com>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include "reverb.h"
+#include <math.h>
+#include "math_funcs.h"
+
+
+const float Reverb::comb_tunings[MAX_COMBS]={
+ //freeverb comb tunings
+ 0.025306122448979593,
+ 0.026938775510204082,
+ 0.028956916099773241,
+ 0.03074829931972789,
+ 0.032244897959183672,
+ 0.03380952380952381,
+ 0.035306122448979592,
+ 0.036666666666666667
+};
+
+const float Reverb::allpass_tunings[MAX_ALLPASS]={
+ //freeverb allpass tunings
+ 0.0051020408163265302,
+ 0.007732426303854875,
+ 0.01,
+ 0.012607709750566893
+};
+
+
+
+void Reverb::process(float *p_src,float *p_dst,int p_frames) {
+
+ if (p_frames>INPUT_BUFFER_MAX_SIZE)
+ p_frames=INPUT_BUFFER_MAX_SIZE;
+
+ int predelay_frames=lrint((params.predelay/1000.0)*params.mix_rate);
+ if (predelay_frames<10)
+ predelay_frames=10;
+ if (predelay_frames>=echo_buffer_size)
+ predelay_frames=echo_buffer_size-1;
+
+ for (int i=0;i<p_frames;i++) {
+
+ if (echo_buffer_pos>=echo_buffer_size)
+ echo_buffer_pos=0;
+
+ int read_pos=echo_buffer_pos-predelay_frames;
+ while (read_pos<0)
+ read_pos+=echo_buffer_size;
+
+ float in=undenormalise(echo_buffer[read_pos]*params.predelay_fb+p_src[i]);
+
+ echo_buffer[echo_buffer_pos]=in;
+
+ input_buffer[i]=in;
+
+ p_dst[i]=0; //take the chance and clear this
+
+ echo_buffer_pos++;
+ }
+
+ if (params.hpf>0) {
+ float hpaux=expf(-2.0*Math_PI*params.hpf*6000/params.mix_rate);
+ float hp_a1=(1.0+hpaux)/2.0;
+ float hp_a2=-(1.0+hpaux)/2.0;
+ float hp_b1=hpaux;
+
+ for (int i=0;i<p_frames;i++) {
+
+ float in=input_buffer[i];
+ input_buffer[i]=in*hp_a1+hpf_h1*hp_a2+hpf_h2*hp_b1;
+ hpf_h2=input_buffer[i];
+ hpf_h1=in;
+ }
+ }
+
+ for (int i=0;i<MAX_COMBS;i++) {
+
+ Comb &c=comb[i];
+
+ int size_limit=c.size-lrintf((float)c.extra_spread_frames*(1.0-params.extra_spread));
+ for (int j=0;j<p_frames;j++) {
+
+ if (c.pos>=size_limit) //reset this now just in case
+ c.pos=0;
+
+ float out=undenormalise(c.buffer[c.pos]*c.feedback);
+ out=out*(1.0-c.damp)+c.damp_h*c.damp; //lowpass
+ c.damp_h=out;
+ c.buffer[c.pos]=input_buffer[j]+out;
+ p_dst[j]+=out;
+ c.pos++;
+ }
+
+ }
+
+
+ static const float allpass_feedback=0.7;
+ /* this one works, but the other version is just nicer....
+ int ap_size_limit[MAX_ALLPASS];
+
+ for (int i=0;i<MAX_ALLPASS;i++) {
+
+ AllPass &a=allpass[i];
+ ap_size_limit[i]=a.size-lrintf((float)a.extra_spread_frames*(1.0-params.extra_spread));
+ }
+
+ for (int i=0;i<p_frames;i++) {
+
+ float sample=p_dst[i];
+ float aux,in;
+ float AllPass*ap;
+
+#define PROCESS_ALLPASS(m_ap) \
+ ap=&allpass[m_ap]; \
+ if (ap->pos>=ap_size_limit[m_ap]) \
+ ap->pos=0; \
+ aux=undenormalise(ap->buffer[ap->pos]); \
+ in=sample; \
+ sample=-in+aux; \
+ ap->pos++;
+
+
+ PROCESS_ALLPASS(0);
+ PROCESS_ALLPASS(1);
+ PROCESS_ALLPASS(2);
+ PROCESS_ALLPASS(3);
+
+ p_dst[i]=sample;
+ }
+ */
+
+ for (int i=0;i<MAX_ALLPASS;i++) {
+
+ AllPass &a=allpass[i];
+ int size_limit=a.size-lrintf((float)a.extra_spread_frames*(1.0-params.extra_spread));
+
+ for (int j=0;j<p_frames;j++) {
+
+ if (a.pos>=size_limit)
+ a.pos=0;
+
+ float aux=a.buffer[a.pos];
+ a.buffer[a.pos]=undenormalise(allpass_feedback*aux+p_dst[j]);
+ p_dst[j]=aux-allpass_feedback*a.buffer[a.pos];
+ a.pos++;
+
+ }
+ }
+
+ static const float wet_scale=0.6;
+
+ for (int i=0;i<p_frames;i++) {
+
+
+ p_dst[i]=p_dst[i]*params.wet*wet_scale+p_src[i]*params.dry;
+ }
+
+}
+
+
+void Reverb::set_room_size(float p_size) {
+
+ params.room_size=p_size;
+ update_parameters();
+
+}
+void Reverb::set_damp(float p_damp) {
+
+ params.damp=p_damp;
+ update_parameters();
+
+}
+void Reverb::set_wet(float p_wet) {
+
+ params.wet=p_wet;
+
+}
+
+void Reverb::set_dry(float p_dry) {
+
+ params.dry=p_dry;
+
+}
+
+void Reverb::set_predelay(float p_predelay) {
+
+ params.predelay=p_predelay;
+}
+void Reverb::set_predelay_feedback(float p_predelay_fb) {
+
+ params.predelay_fb=p_predelay_fb;
+
+}
+
+void Reverb::set_highpass(float p_frq) {
+
+ if (p_frq>1)
+ p_frq=1;
+ if (p_frq<0)
+ p_frq=0;
+ params.hpf=p_frq;
+}
+
+void Reverb::set_extra_spread(float p_spread) {
+
+ params.extra_spread=p_spread;
+
+}
+
+
+void Reverb::set_mix_rate(float p_mix_rate) {
+
+ params.mix_rate=p_mix_rate;
+ configure_buffers();
+}
+
+void Reverb::set_extra_spread_base(float p_sec) {
+
+ params.extra_spread_base=p_sec;
+ configure_buffers();
+}
+
+
+void Reverb::configure_buffers() {
+
+ clear_buffers(); //clear if necesary
+
+ for (int i=0;i<MAX_COMBS;i++) {
+
+ Comb &c=comb[i];
+
+
+ c.extra_spread_frames=lrint(params.extra_spread_base*params.mix_rate);
+
+ int len=lrint(comb_tunings[i]*params.mix_rate)+c.extra_spread_frames;
+ if (len<5)
+ len=5; //may this happen?
+
+ c.buffer = memnew_arr(float,len);
+ c.pos=0;
+ for (int j=0;j<len;j++)
+ c.buffer[j]=0;
+ c.size=len;
+
+ }
+
+ for (int i=0;i<MAX_ALLPASS;i++) {
+
+ AllPass &a=allpass[i];
+
+ a.extra_spread_frames=lrint(params.extra_spread_base*params.mix_rate);
+
+ int len=lrint(allpass_tunings[i]*params.mix_rate)+a.extra_spread_frames;
+ if (len<5)
+ len=5; //may this happen?
+
+ a.buffer = memnew_arr(float,len);
+ a.pos=0;
+ for (int j=0;j<len;j++)
+ a.buffer[j]=0;
+ a.size=len;
+ }
+
+ echo_buffer_size=(int)(((float)MAX_ECHO_MS/1000.0)*params.mix_rate+1.0);
+ echo_buffer = memnew_arr(float,echo_buffer_size);
+ for (int i=0;i<echo_buffer_size;i++) {
+
+ echo_buffer[i]=0;
+ }
+
+ echo_buffer_pos=0;
+}
+
+
+void Reverb::update_parameters() {
+
+ //more freeverb derived constants
+ static const float room_scale = 0.28f;
+ static const float room_offset = 0.7f;
+
+ for (int i=0;i<MAX_COMBS;i++) {
+
+ Comb &c=comb[i];
+ c.feedback=room_offset+params.room_size*room_scale;
+ if (c.feedback<room_offset)
+ c.feedback=room_offset;
+ else if (c.feedback>(room_offset+room_scale))
+ c.feedback=(room_offset+room_scale);
+
+ float auxdmp=params.damp/2.0+0.5; //only half the range (0.5 .. 1.0 is enough)
+ auxdmp*=auxdmp;
+
+ c.damp=expf(-2.0*Math_PI*auxdmp*10000/params.mix_rate); // 0 .. 10khz
+ }
+
+}
+
+void Reverb::clear_buffers() {
+
+ if (echo_buffer)
+ memdelete_arr(echo_buffer);
+
+ for (int i=0;i<MAX_COMBS;i++) {
+
+ if (comb[i].buffer)
+ memdelete_arr(comb[i].buffer);
+
+ comb[i].buffer=0;
+
+ }
+
+ for (int i=0;i<MAX_ALLPASS;i++) {
+
+ if (allpass[i].buffer)
+ memdelete_arr(allpass[i].buffer);
+
+ allpass[i].buffer=0;
+ }
+
+}
+
+Reverb::Reverb() {
+
+ params.room_size=0.8;
+ params.damp=0.5;
+ params.dry=1.0;
+ params.wet=0.0;
+ params.mix_rate=44100;
+ params.extra_spread_base=0;
+ params.extra_spread=1.0;
+ params.predelay=150;
+ params.predelay_fb=0.4;
+ params.hpf=0;
+ hpf_h1=0;
+ hpf_h2=0;
+
+
+ input_buffer=memnew_arr(float,INPUT_BUFFER_MAX_SIZE);
+ echo_buffer=0;
+
+ configure_buffers();
+ update_parameters();
+
+
+}
+
+
+Reverb::~Reverb() {
+
+ memdelete_arr(input_buffer);
+ clear_buffers();
+}
+
+