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