1 // ------------------------------------------------------------------------
2 // audiofx_reverb.cpp: Reverb effect
3 // Copyright (C) 2000 Stefan Fendt
4 // Copyright (C) 2000,2003,2008 Kai Vehmanen (C++ version)
5 //
6 // Attributes:
7 //     eca-style-version: 3 (see Ecasound Programmer's Guide)
8 //
9 // This program is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 // USA
23 //
24 // ------------------------------------------------------------------------
25 // History:
26 //
27 // 2003-01-19 Kai Vehmanen
28 //     - Added param hint information.
29 // 2002-12-04 Hans-Georg Fischer
30 //     - Fixed a bug in initializing the delay line, which cause
31 //       unwanted audible noise at start of processing.
32 // 2000-06-06 Kai Vehmanen
33 //     - Initial version. Based on Stefan M. Fendt's reverb
34 //       code.
35 // ------------------------------------------------------------------------
36 
37 #include <cstdlib>
38 
39 #include "sample-ops_impl.h"
40 #include "samplebuffer_iterators.h"
41 #include "sample-specs.h"
42 #include "audiofx_reverb.h"
43 
ADVANCED_REVERB(parameter_t roomsize,parameter_t feedback_percent,parameter_t wet_percent)44 ADVANCED_REVERB::ADVANCED_REVERB (parameter_t roomsize,
45 				  parameter_t feedback_percent,
46 				  parameter_t wet_percent)
47 {
48   set_parameter(1, roomsize);
49   set_parameter(2, feedback_percent);
50   set_parameter(3, wet_percent);
51 }
52 
parameter_description(int param,struct PARAM_DESCRIPTION * pd) const53 void ADVANCED_REVERB::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const
54 {
55   switch (param) {
56   case 1:
57     pd->default_value = 10.0f;
58     pd->description = get_parameter_name(param);
59     pd->bounded_above = false;
60     // pd->upper_bound = 0.0f;
61     pd->bounded_below = true;
62     pd->lower_bound = 0.0f;
63     pd->toggled = false;
64     pd->integer = false;
65     pd->logarithmic = false;
66     pd->output = false;
67     break;
68   case 2:
69     pd->default_value = 50.0f;
70     pd->description = get_parameter_name(param);
71     pd->bounded_above = true;
72     pd->upper_bound = 100.0f;
73     pd->bounded_below = true;
74     pd->lower_bound = 0.0f;
75     pd->toggled = false;
76     pd->integer = false;
77     pd->logarithmic = false;
78     pd->output = false;
79     break;
80   case 3:
81     pd->default_value = 50.0f;
82     pd->description = get_parameter_name(param);
83     pd->bounded_above = true;
84     pd->upper_bound = 100.0f;
85     pd->bounded_below = true;
86     pd->lower_bound = 0.0f;
87     pd->toggled = false;
88     pd->integer = false;
89     pd->logarithmic = false;
90     pd->output = false;
91     break;
92   default: {}
93   }
94 }
95 
get_parameter(int param) const96 CHAIN_OPERATOR::parameter_t ADVANCED_REVERB::get_parameter(int param) const
97 {
98   switch (param) {
99   case 1:
100     return roomsize_rep;
101   case 2:
102     return feedback_rep * 100.0;
103   case 3:
104     return wet_rep * 100.0;
105   }
106   return 0.0;
107 }
108 
set_parameter(int param,CHAIN_OPERATOR::parameter_t value)109 void ADVANCED_REVERB::set_parameter(int param, CHAIN_OPERATOR::parameter_t value)
110 {
111   switch (param) {
112   case 1:
113     roomsize_rep = value;
114     break;
115 
116   case 2:
117     if (value == 0)
118       feedback_rep = 0.001;
119     else
120       feedback_rep = value / 100.0;
121     break;
122 
123   case 3:
124     wet_rep = value / 100.0;
125     break;
126   }
127   if (param == 1 || param == 2) {
128     std::vector<CHANNEL_DATA>::iterator p = cdata.begin();
129     while(p != cdata.end()) {
130       p->oldvalue=0.0;
131       p->lpvalue=0.0;
132       p->dpos[0] = static_cast<long int>(roomsize_rep * samples_per_second() / 333);
133       p->mul[0] = 0.035;
134       p->bufferpos_rep = 0;
135       for(int i = 1; i < 64; i++) {
136 	p->dpos[i] = p->dpos[i-1] + (rand() & 511);
137 	p->mul[i] = p->mul[i-1] * (1 - 1 / feedback_rep / 1000);
138       }
139       ++p;
140     }
141   }
142 }
143 
init(SAMPLE_BUFFER * insample)144 void ADVANCED_REVERB::init(SAMPLE_BUFFER *insample)
145 {
146   i_channels.init(insample);
147   cdata.resize(insample->number_of_channels());
148   std::vector<CHANNEL_DATA>::iterator p = cdata.begin();
149   while(p != cdata.end()) {
150     p->oldvalue=0.0;
151     p->lpvalue=0.0;
152     p->dpos[0] = static_cast<long int>(roomsize_rep * samples_per_second() / 333);
153     p->mul[0] = 0.035;
154     p->bufferpos_rep = 0;
155     for(size_t i = 0; i < p->buffer.size(); i++)
156       p->buffer[i] = 0.0f;
157     for(int i = 1; i < 64; i++) {
158       p->dpos[i] = p->dpos[i-1] + (rand() & 511);
159       p->mul[i] = p->mul[i-1] * (1 - 1 / feedback_rep / 1000);
160     }
161     ++p;
162   }
163 }
164 
process(void)165 void ADVANCED_REVERB::process(void)
166 {
167   i_channels.begin();
168   while(!i_channels.end()) {
169     int ch = i_channels.channel();
170 
171     cdata[ch].bufferpos_rep++;
172     cdata[ch].bufferpos_rep &= 65535;
173 
174     double old_value = cdata[ch].oldvalue;
175     cdata[ch].buffer[cdata[ch].bufferpos_rep] =
176       ecaops_flush_to_zero(*i_channels.current() + old_value);
177 
178     old_value = 0.0;
179     for(int i = 0; i < 64; i++) {
180       old_value +=
181 	static_cast<float>(cdata[ch].buffer[(cdata[ch].bufferpos_rep - cdata[ch].dpos[i]) & 65535] * cdata[ch].mul[i]);
182     }
183 
184     /**
185      * This is just a very simple high-pass-filter to remove offsets
186      * which can accour during calculation of the echos
187      */
188     cdata[ch].lpvalue =
189       ecaops_flush_to_zero(cdata[ch].lpvalue * 0.99 + old_value * 0.01);
190     old_value = old_value - cdata[ch].lpvalue;
191 
192     /**
193      * This is a simple lowpass to make the apearence of the reverb
194      * more realistic... (Walls do not reflect high frequencies very
195      * well at all...)
196      */
197     cdata[ch].oldvalue =
198       ecaops_flush_to_zero(cdata[ch].oldvalue * 0.75 + old_value * 0.25);
199 
200     *i_channels.current() = cdata[ch].oldvalue * wet_rep + *i_channels.current() * (1 - wet_rep);
201     i_channels.next();
202   }
203 }
204