1 /* Calf DSP plugin pack 2 * Equalization related plugins 3 * 4 * Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General 17 * Public License along with this program; if not, write to the 18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02111-1307, USA. 20 */ 21 #ifndef CALF_MODULES_FILTER_H 22 #define CALF_MODULES_FILTER_H 23 24 #include <assert.h> 25 #include <limits.h> 26 #include "biquad.h" 27 #include "inertia.h" 28 #include "audio_fx.h" 29 #include "giface.h" 30 #include "metadata.h" 31 #include "plugin_tools.h" 32 #include "loudness.h" 33 #include "analyzer.h" 34 #include "bypass.h" 35 #include "orfanidis_eq.h" 36 37 namespace calf_plugins { 38 39 /********************************************************************** 40 * EQUALIZER N BAND by Markus Schmidt and Krzysztof Foltman 41 **********************************************************************/ 42 43 template<class BaseClass, bool has_lphp> 44 class equalizerNband_audio_module: public audio_module<BaseClass>, public frequency_response_line_graph { 45 public: 46 typedef audio_module<BaseClass> AM; 47 using AM::ins; 48 using AM::outs; 49 using AM::params; 50 using AM::in_count; 51 using AM::out_count; 52 using AM::param_count; 53 using AM::PeakBands; 54 private: 55 analyzer _analyzer; 56 enum { graph_param_count = BaseClass::last_graph_param - BaseClass::first_graph_param + 1, params_per_band = AM::param_p2_active - AM::param_p1_active }; 57 float hp_mode_old, hp_freq_old, hp_q_old; 58 float lp_mode_old, lp_freq_old, lp_q_old; 59 float ls_level_old, ls_freq_old, ls_q_old; 60 float hs_level_old, hs_freq_old, hs_q_old; 61 int indiv_old; 62 bool analyzer_old; 63 float p_level_old[PeakBands], p_freq_old[PeakBands], p_q_old[PeakBands]; 64 mutable float old_params_for_graph[graph_param_count]; 65 vumeters meters; 66 CalfEqMode hp_mode, lp_mode; 67 dsp::biquad_d2 hp[3][2], lp[3][2]; 68 dsp::biquad_d2 lsL, lsR, hsL, hsR; 69 dsp::biquad_d2 pL[PeakBands], pR[PeakBands]; 70 dsp::bypass bypass; 71 int keep_gliding; 72 mutable int last_peak; 73 inline void process_hplp(float &left, float &right); 74 public: 75 typedef std::complex<double> cfloat; 76 uint32_t srate; 77 bool is_active; 78 mutable volatile int last_generation, last_calculated_generation; 79 equalizerNband_audio_module(); 80 void activate(); 81 void deactivate(); 82 83 void params_changed(); 84 bool get_gridline(int index, int subindex, int phase, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const; 85 bool get_graph(int index, int subindex, int phase, float *data, int points, cairo_iface *context, int *mode) const; 86 bool get_layers(int index, int generation, unsigned int &layers) const; 87 float freq_gain(int index, double freq) const; 88 std::string get_crosshair_label(int x, int y, int sx, int sy, float q, int dB, int name, int note, int cents) const; 89 set_sample_rate(uint32_t sr)90 void set_sample_rate(uint32_t sr) 91 { 92 srate = sr; 93 _analyzer.set_sample_rate(sr); 94 int meter[] = {AM::param_meter_inL, AM::param_meter_inR, AM::param_meter_outL, AM::param_meter_outR}; 95 int clip[] = {AM::param_clip_inL, AM::param_clip_inR, AM::param_clip_outL, AM::param_clip_outR}; 96 meters.init(params, meter, clip, 4, sr); 97 } 98 uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask); 99 }; 100 101 typedef equalizerNband_audio_module<equalizer5band_metadata, false> equalizer5band_audio_module; 102 typedef equalizerNband_audio_module<equalizer8band_metadata, true> equalizer8band_audio_module; 103 typedef equalizerNband_audio_module<equalizer12band_metadata, true> equalizer12band_audio_module; 104 105 /********************************************************************** 106 * EQUALIZER 30 BAND 107 **********************************************************************/ 108 109 class equalizer30band_audio_module: public audio_module<equalizer30band_metadata> { 110 OrfanidisEq::Conversions conv; 111 OrfanidisEq::FrequencyGrid fg; 112 std::vector<OrfanidisEq::Eq*> eq_arrL; 113 std::vector<OrfanidisEq::Eq*> eq_arrR; 114 115 OrfanidisEq::filter_type flt_type; 116 OrfanidisEq::filter_type flt_type_old; 117 118 dsp::switcher<OrfanidisEq::filter_type> swL; 119 dsp::switcher<OrfanidisEq::filter_type> swR; 120 121 public: 122 uint32_t srate; 123 bool is_active; 124 dsp::bypass bypass; 125 dsp::bypass eq_switch; 126 vumeters meters; 127 equalizer30band_audio_module(); 128 ~equalizer30band_audio_module(); 129 130 void activate(); 131 void deactivate(); 132 void params_changed(); 133 void set_sample_rate(uint32_t sr); 134 uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask); 135 }; 136 137 /********************************************************************** 138 * FILTER MODULE by Krzysztof Foltman 139 **********************************************************************/ 140 141 template<typename FilterClass, typename Metadata> 142 class filter_module_with_inertia: public audio_module<Metadata>, public FilterClass, 143 public frequency_response_line_graph 144 { 145 using FilterClass::calculate_filter; 146 using FilterClass::freq_gain; 147 public: 148 /// These are pointers to the ins, outs, params arrays in the main class 149 typedef filter_module_with_inertia inertia_filter_module; 150 using audio_module<Metadata>::ins; 151 using audio_module<Metadata>::outs; 152 using audio_module<Metadata>::params; 153 154 dsp::inertia<dsp::exponential_ramp> inertia_cutoff, inertia_resonance, inertia_gain; 155 dsp::once_per_n timer; 156 bool is_active; 157 mutable volatile int last_generation, last_calculated_generation; 158 159 dsp::bypass bypass; 160 vumeters meters; 161 filter_module_with_inertia()162 filter_module_with_inertia() 163 : inertia_cutoff(dsp::exponential_ramp(128), 20) 164 , inertia_resonance(dsp::exponential_ramp(128), 20) 165 , inertia_gain(dsp::exponential_ramp(128), 1.0) 166 , timer(128) 167 , is_active(false) 168 , last_generation(-1) 169 , last_calculated_generation(-2) 170 {} 171 calculate_filter()172 void calculate_filter() 173 { 174 float freq = inertia_cutoff.get_last(); 175 // printf("freq=%g inr.cnt=%d timer.left=%d\n", freq, inertia_cutoff.count, timer.left); 176 // XXXKF this is resonance of a single stage, obviously for three stages, resonant gain will be different 177 float q = inertia_resonance.get_last(); 178 int mode = dsp::fastf2i_drm(*params[Metadata::par_mode]); 179 // printf("freq = %f q = %f mode = %d\n", freq, q, mode); 180 181 int inertia = dsp::fastf2i_drm(*params[Metadata::par_inertia]); 182 if (inertia != inertia_cutoff.ramp.length()) { 183 inertia_cutoff.ramp.set_length(inertia); 184 inertia_resonance.ramp.set_length(inertia); 185 inertia_gain.ramp.set_length(inertia); 186 } 187 188 FilterClass::calculate_filter(freq, q, mode, inertia_gain.get_last()); 189 } 190 params_changed()191 virtual void params_changed() 192 { 193 calculate_filter(); 194 } 195 on_timer()196 void on_timer() 197 { 198 int gen = last_generation; 199 inertia_cutoff.step(); 200 inertia_resonance.step(); 201 inertia_gain.step(); 202 calculate_filter(); 203 last_calculated_generation = gen; 204 } 205 activate()206 void activate() 207 { 208 params_changed(); 209 FilterClass::filter_activate(); 210 timer = dsp::once_per_n(FilterClass::srate / 1000); 211 timer.start(); 212 is_active = true; 213 } 214 set_sample_rate(uint32_t sr)215 void set_sample_rate(uint32_t sr) 216 { 217 FilterClass::srate = sr; 218 int meter[] = {Metadata::param_meter_inL, Metadata::param_meter_inR, Metadata::param_meter_outL, Metadata::param_meter_outR}; 219 int clip[] = {Metadata::param_clip_inL, Metadata::param_clip_inR, Metadata::param_clip_outL, Metadata::param_clip_outR}; 220 meters.init(params, meter, clip, 4, sr); 221 } 222 223 deactivate()224 void deactivate() 225 { 226 is_active = false; 227 } 228 process(uint32_t offset,uint32_t numsamples,uint32_t inputs_mask,uint32_t outputs_mask)229 uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) { 230 // printf("sr=%d cutoff=%f res=%f mode=%f\n", FilterClass::srate, *params[Metadata::par_cutoff], *params[Metadata::par_resonance], *params[Metadata::par_mode]); 231 uint32_t ostate = 0; 232 uint32_t orig_offset = offset, orig_numsamples = numsamples; 233 bool bypassed = bypass.update(*params[Metadata::param_bypass] > 0.5f, numsamples); 234 if (bypassed) { 235 float values[] = {0,0,0,0}; 236 for (uint32_t i = offset; i < offset + numsamples; i++) { 237 outs[0][i] = ins[0][i]; 238 outs[1][i] = ins[1][i]; 239 //float values[] = {ins[0][i],ins[1][i],outs[0][i],outs[1][i]}; 240 meters.process(values); 241 ostate = -1; 242 } 243 } else { 244 numsamples += offset; 245 while(offset < numsamples) { 246 uint32_t numnow = numsamples - offset; 247 // if inertia's inactive, we can calculate the whole buffer at once 248 if (inertia_cutoff.active() || inertia_resonance.active() || inertia_gain.active()) 249 numnow = timer.get(numnow); 250 if (outputs_mask & 1) { 251 ostate |= FilterClass::process_channel(0, ins[0] + offset, outs[0] + offset, numnow, inputs_mask & 1, *params[Metadata::param_level_in], *params[Metadata::param_level_out]); 252 } 253 if (outputs_mask & 2) { 254 ostate |= FilterClass::process_channel(1, ins[1] + offset, outs[1] + offset, numnow, inputs_mask & 2, *params[Metadata::param_level_in], *params[Metadata::param_level_out]); 255 } 256 if (timer.elapsed()) { 257 on_timer(); 258 } 259 for (uint32_t i = offset; i < offset + numnow; i++) { 260 float values[] = {ins[0][i] * *params[Metadata::param_level_in], ins[1][i] * *params[Metadata::param_level_in], outs[0][i], outs[1][i]}; 261 meters.process(values); 262 } 263 offset += numnow; 264 } 265 bypass.crossfade(ins, outs, 2, orig_offset, orig_numsamples); 266 } 267 meters.fall(orig_numsamples); 268 return ostate; 269 } freq_gain(int index,double freq)270 float freq_gain(int index, double freq) const { 271 return FilterClass::freq_gain(index, (float)freq, (float)FilterClass::srate); 272 } 273 }; 274 275 /********************************************************************** 276 * FILTER by Krzysztof Foltman 277 **********************************************************************/ 278 279 class filter_audio_module: 280 public filter_module_with_inertia<dsp::biquad_filter_module, filter_metadata> 281 { 282 mutable float old_cutoff, old_resonance, old_mode; 283 public: filter_audio_module()284 filter_audio_module() 285 { 286 last_generation = 0; 287 old_mode = old_resonance = old_cutoff = -1; 288 redraw_graph = true; 289 } params_changed()290 void params_changed() 291 { 292 inertia_cutoff.set_inertia(*params[par_cutoff]); 293 inertia_resonance.set_inertia(*params[par_resonance]); 294 inertia_filter_module::params_changed(); 295 redraw_graph = true; 296 } 297 }; 298 299 /********************************************************************** 300 * FILTERKLAVIER by Hans Baier 301 **********************************************************************/ 302 303 class filterclavier_audio_module: 304 public filter_module_with_inertia<dsp::biquad_filter_module, filterclavier_metadata> 305 { 306 using audio_module<filterclavier_metadata>::ins; 307 using audio_module<filterclavier_metadata>::outs; 308 using audio_module<filterclavier_metadata>::params; 309 310 const float min_gain; 311 const float max_gain; 312 313 int last_note; 314 int last_velocity; 315 public: 316 filterclavier_audio_module(); 317 void params_changed(); 318 void activate(); 319 void set_sample_rate(uint32_t sr); 320 void deactivate(); 321 322 /// MIDI control 323 virtual void note_on(int channel, int note, int vel); 324 virtual void note_off(int channel, int note, int vel); 325 private: 326 void adjust_gain_according_to_filter_mode(int velocity); 327 }; 328 329 /********************************************************************** 330 * EMPHASIS by Damien Zammit 331 **********************************************************************/ 332 333 class emphasis_audio_module: public audio_module<emphasis_metadata>, public frequency_response_line_graph { 334 public: 335 dsp::riaacurve riaacurvL, riaacurvR; 336 dsp::bypass bypass; 337 int mode, type, bypass_; 338 typedef std::complex<double> cfloat; 339 uint32_t srate; 340 bool is_active; 341 vumeters meters; 342 emphasis_audio_module(); 343 void activate(); 344 void deactivate(); 345 void params_changed(); set_sample_rate(uint32_t sr)346 void set_sample_rate(uint32_t sr) 347 { 348 srate = sr; 349 int meter[] = {param_meter_inL, param_meter_inR, param_meter_outL, param_meter_outR}; 350 int clip[] = {param_clip_inL, param_clip_inR, param_clip_outL, param_clip_outR}; 351 meters.init(params, meter, clip, 4, sr); 352 } freq_gain(int index,double freq)353 virtual float freq_gain(int index, double freq) const { 354 return riaacurvL.freq_gain(freq, (float)srate); 355 } 356 virtual bool get_graph(int index, int subindex, int phase, float *data, int points, cairo_iface *context, int *mode) const; 357 virtual bool get_gridline(int index, int subindex, int phase, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const; 358 uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask); 359 }; 360 361 /********************************************************************** 362 * CROSSOVER MODULES by Markus Schmidt 363 **********************************************************************/ 364 365 template<class XoverBaseClass> 366 class xover_audio_module: public audio_module<XoverBaseClass>, public frequency_response_line_graph { 367 public: 368 typedef audio_module<XoverBaseClass> AM; 369 using AM::ins; 370 using AM::outs; 371 using AM::params; 372 using AM::in_count; 373 using AM::out_count; 374 using AM::param_count; 375 using AM::bands; 376 using AM::channels; 377 enum { params_per_band = AM::param_level2 - AM::param_level1 }; 378 uint32_t srate; 379 bool is_active; 380 float * buffer; 381 float in[channels]; 382 unsigned int pos; 383 unsigned int buffer_size; 384 int last_peak; sign(float x)385 static inline float sign(float x) { 386 if(x < 0) return -1.f; 387 if(x > 0) return 1.f; 388 return 0.f; 389 } 390 vumeters meters; 391 dsp::crossover crossover; 392 xover_audio_module(); 393 ~xover_audio_module(); 394 void activate(); 395 void deactivate(); 396 void params_changed(); 397 void set_sample_rate(uint32_t sr); 398 uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask); 399 bool get_graph(int index, int subindex, int phase, float *data, int points, cairo_iface *context, int *mode) const; 400 bool get_layers(int index, int generation, unsigned int &layers) const; 401 }; 402 403 typedef xover_audio_module<xover2_metadata> xover2_audio_module; 404 typedef xover_audio_module<xover3_metadata> xover3_audio_module; 405 typedef xover_audio_module<xover4_metadata> xover4_audio_module; 406 407 /********************************************************************** 408 * VOCODER by Markus Schmidt & Christian Holschuh 409 **********************************************************************/ 410 411 class vocoder_audio_module: public audio_module<vocoder_metadata>, public frequency_response_line_graph { 412 public: 413 int bands, bands_old, order, hiq_old; 414 float order_old, lower_old, upper_old, tilt_old; 415 float q_old[32]; 416 float bandfreq[32]; 417 uint32_t srate; 418 bool is_active; 419 static const int maxorder = 8; 420 dsp::biquad_d2 detector[2][maxorder][32], modulator[2][maxorder][32]; 421 dsp::bypass bypass; 422 double env_mods[2][32]; 423 vumeters meters; 424 analyzer _analyzer; 425 double attack, release, fcoeff, log2_; 426 vocoder_audio_module(); 427 void activate(); 428 void deactivate(); 429 void params_changed(); 430 int get_solo() const; set_sample_rate(uint32_t sr)431 void set_sample_rate(uint32_t sr) 432 { 433 srate = sr; 434 _analyzer.set_sample_rate(sr); 435 int meter[] = {param_carrier_inL, param_carrier_inR, param_mod_inL, param_mod_inR, param_outL, param_outR}; 436 int clip[] = {param_carrier_clip_inL, param_carrier_clip_inR, param_mod_clip_inL, param_mod_clip_inR, param_clip_outL, param_clip_outR}; 437 meters.init(params, meter, clip, 6, sr); 438 } 439 virtual bool get_graph(int index, int subindex, int phase, float *data, int points, cairo_iface *context, int *mode) const; 440 virtual bool get_layers(int index, int generation, unsigned int &layers) const; 441 uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask); 442 }; 443 444 /********************************************************************** 445 * ENVELOPE FILTER by Markus Schmidt 446 **********************************************************************/ 447 448 class envelopefilter_audio_module: public audio_module<envelopefilter_metadata>, public dsp::biquad_filter_module, 449 public frequency_response_line_graph 450 { 451 using dsp::biquad_filter_module::freq_gain; 452 public: 453 uint32_t srate; 454 bool is_active; 455 456 dsp::bypass bypass; 457 vumeters meters; 458 459 float envelope, attack, release, envelope_old, attack_old, release_old, q_old; 460 float gain, gain_old, upper, upper_old, lower, lower_old; 461 float coefa, coefb, coefz; 462 int mode, mode_old; 463 envelopefilter_audio_module()464 envelopefilter_audio_module() 465 { 466 envelope = envelope_old = 0; 467 lower = 10; lower_old = 0; 468 upper = 10; upper_old = 0; 469 gain = 1; gain_old = 0; 470 attack = release = attack_old = release_old = -1; 471 mode = mode_old = q_old = 0; 472 coefa = coefb = 0; 473 coefz = 2; 474 } 475 activate()476 void activate() 477 { 478 params_changed(); 479 filter_activate(); 480 is_active = true; 481 } 482 deactivate()483 void deactivate() 484 { 485 is_active = false; 486 } 487 set_sample_rate(uint32_t sr)488 void set_sample_rate(uint32_t sr) 489 { 490 srate = sr; 491 dsp::biquad_filter_module::srate = sr; 492 int meter[] = {param_meter_inL, param_meter_inR, param_meter_outL, param_meter_outR}; 493 int clip[] = {param_clip_inL, param_clip_inR, param_clip_outL, param_clip_outR}; 494 meters.init(params, meter, clip, 4, sr); 495 } 496 params_changed()497 void params_changed() 498 { 499 if (*params[param_attack] != attack_old) { 500 attack_old = *params[param_attack]; 501 attack = exp(log(0.01)/( attack_old * srate * 0.001)); 502 } 503 if (*params[param_release] != release_old) { 504 release_old = *params[param_release]; 505 release = exp(log(0.01)/( release_old * srate * 0.001)); 506 } 507 if (*params[param_mode] != mode_old) { 508 mode = dsp::fastf2i_drm(*params[param_mode]); 509 mode_old = *params[param_mode]; 510 calc_filter(); 511 } 512 if (*params[param_q] != q_old) { 513 q_old = *params[param_q]; 514 calc_filter(); 515 } 516 if (*params[param_upper] != upper_old) { 517 upper = *params[param_upper]; 518 upper_old = *params[param_upper]; 519 calc_coef(); 520 calc_filter(); 521 } 522 if (*params[param_lower] != lower_old) { 523 lower = *params[param_lower]; 524 lower_old = *params[param_lower]; 525 calc_coef(); 526 calc_filter(); 527 } 528 if (*params[param_gain] != gain_old) { 529 gain = *params[param_gain]; 530 gain_old = *params[param_gain]; 531 calc_filter(); 532 } 533 } 534 process(uint32_t offset,uint32_t numsamples,uint32_t inputs_mask,uint32_t outputs_mask)535 uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) 536 { 537 bool bypassed = bypass.update(*params[param_bypass] > 0.5f, numsamples); 538 uint32_t end = numsamples + offset; 539 while(offset < end) { 540 float D; 541 if (*params[param_sidechain] > 0.5) 542 D = std::max(fabs(ins[2][offset]), fabs(ins[3][offset])) * *params[param_gain]; 543 else 544 D = std::max(fabs(ins[0][offset]), fabs(ins[1][offset])) * *params[param_gain]; 545 546 // advance envelope 547 envelope = std::min(1.f, (D > envelope ? attack : release) * (envelope - D) + D); 548 if (envelope != envelope_old) { 549 envelope_old = envelope; 550 redraw_graph = true; 551 dsp::biquad_filter_module::calculate_filter(get_freq(envelope), *params[param_q], mode, 1.0); 552 } 553 554 if(bypassed) { 555 outs[0][offset] = ins[0][offset]; 556 outs[1][offset] = ins[1][offset]; 557 float values[] = {0, 0, 0, 0}; 558 meters.process(values); 559 } else { 560 const float inL = ins[0][offset] * *params[param_level_in]; 561 const float inR = ins[1][offset] * *params[param_level_in]; 562 float outL = outs[0][offset]; 563 float outR = outs[1][offset]; 564 565 // process filters 566 dsp::biquad_filter_module::process_channel(0, &inL, &outL, 1, inputs_mask & 1); 567 dsp::biquad_filter_module::process_channel(1, &inR, &outR, 1, inputs_mask & 2); 568 569 // mix and out level 570 outs[0][offset] = (outL * *params[param_mix] + inL * (*params[param_mix] * -1 + 1)) * *params[param_level_out]; 571 outs[1][offset] = (outR * *params[param_mix] + inR * (*params[param_mix] * -1 + 1)) * *params[param_level_out]; 572 573 // meters 574 float values[] = {inL, inR, outs[0][offset], outs[1][offset]}; 575 meters.process(values); 576 } 577 // step on 578 offset += 1; 579 } 580 if (bypassed) 581 bypass.crossfade(ins, outs, 2, offset - numsamples, numsamples); 582 meters.fall(numsamples); 583 return outputs_mask; 584 } 585 freq_gain(int index,double freq)586 float freq_gain(int index, double freq) const { 587 return dsp::biquad_filter_module::freq_gain(index, (float)freq, (float)srate); 588 } 589 calc_filter()590 void calc_filter () 591 { 592 redraw_graph = true; 593 dsp::biquad_filter_module::calculate_filter(get_freq(envelope), *params[param_q], mode, 1.0); 594 } 595 calc_coef()596 void calc_coef () 597 { 598 coefa = log10(upper) - log10(lower); 599 coefb = log10(lower); 600 } 601 get_freq(float envelope)602 float get_freq(float envelope) const { 603 float diff = upper - lower; 604 float env = pow(envelope, pow(2, *params[param_response] * -2)); 605 float freq = pow(10, coefa * env + coefb); 606 if (diff < 0) 607 return std::max(upper, std::min(lower, freq)); 608 return std::min(upper, std::max(lower, freq)); 609 } 610 611 }; 612 613 }; 614 615 #endif 616