CrossoverEQ.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /*
  2. * CrossoverEQ.cpp - A native 4-band Crossover Equalizer
  3. * good for simulating tonestacks or simple peakless (flat-band) equalization
  4. *
  5. * Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
  6. * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
  7. *
  8. * This file is part of LMMS - https://lmms.io
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public
  12. * License as published by the Free Software Foundation; either
  13. * version 2 of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public
  21. * License along with this program (see COPYING); if not, write to the
  22. * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  23. * Boston, MA 02110-1301 USA.
  24. *
  25. */
  26. #include "CrossoverEQ.h"
  27. #include "lmms_math.h"
  28. #include "embed.h"
  29. #include "plugin_export.h"
  30. extern "C"
  31. {
  32. Plugin::Descriptor PLUGIN_EXPORT crossovereq_plugin_descriptor =
  33. {
  34. STRINGIFY( PLUGIN_NAME ),
  35. "Crossover Equalizer",
  36. QT_TRANSLATE_NOOP( "PluginBrowser", "A 4-band Crossover Equalizer" ),
  37. "Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>",
  38. 0x0100,
  39. Plugin::Effect,
  40. new PluginPixmapLoader( "logo" ),
  41. NULL,
  42. NULL
  43. };
  44. }
  45. CrossoverEQEffect::CrossoverEQEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ) :
  46. Effect( &crossovereq_plugin_descriptor, parent, key ),
  47. m_controls( this ),
  48. m_sampleRate( Engine::mixer()->processingSampleRate() ),
  49. m_lp1( m_sampleRate ),
  50. m_lp2( m_sampleRate ),
  51. m_lp3( m_sampleRate ),
  52. m_hp2( m_sampleRate ),
  53. m_hp3( m_sampleRate ),
  54. m_hp4( m_sampleRate ),
  55. m_needsUpdate( true )
  56. {
  57. m_tmp1 = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() );
  58. m_tmp2 = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() );
  59. m_work = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() );
  60. }
  61. CrossoverEQEffect::~CrossoverEQEffect()
  62. {
  63. MM_FREE( m_tmp1 );
  64. MM_FREE( m_tmp2 );
  65. MM_FREE( m_work );
  66. }
  67. void CrossoverEQEffect::sampleRateChanged()
  68. {
  69. m_sampleRate = Engine::mixer()->processingSampleRate();
  70. m_lp1.setSampleRate( m_sampleRate );
  71. m_lp2.setSampleRate( m_sampleRate );
  72. m_lp3.setSampleRate( m_sampleRate );
  73. m_hp2.setSampleRate( m_sampleRate );
  74. m_hp3.setSampleRate( m_sampleRate );
  75. m_hp4.setSampleRate( m_sampleRate );
  76. m_needsUpdate = true;
  77. }
  78. bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames )
  79. {
  80. if( !isEnabled() || !isRunning () )
  81. {
  82. return( false );
  83. }
  84. // filters update
  85. if( m_needsUpdate || m_controls.m_xover12.isValueChanged() )
  86. {
  87. m_lp1.setLowpass( m_controls.m_xover12.value() );
  88. m_hp2.setHighpass( m_controls.m_xover12.value() );
  89. }
  90. if( m_needsUpdate || m_controls.m_xover23.isValueChanged() )
  91. {
  92. m_lp2.setLowpass( m_controls.m_xover23.value() );
  93. m_hp3.setHighpass( m_controls.m_xover23.value() );
  94. }
  95. if( m_needsUpdate || m_controls.m_xover34.isValueChanged() )
  96. {
  97. m_lp3.setLowpass( m_controls.m_xover34.value() );
  98. m_hp4.setHighpass( m_controls.m_xover34.value() );
  99. }
  100. // gain values update
  101. if( m_needsUpdate || m_controls.m_gain1.isValueChanged() )
  102. {
  103. m_gain1 = dbfsToAmp( m_controls.m_gain1.value() );
  104. }
  105. if( m_needsUpdate || m_controls.m_gain2.isValueChanged() )
  106. {
  107. m_gain2 = dbfsToAmp( m_controls.m_gain2.value() );
  108. }
  109. if( m_needsUpdate || m_controls.m_gain3.isValueChanged() )
  110. {
  111. m_gain3 = dbfsToAmp( m_controls.m_gain3.value() );
  112. }
  113. if( m_needsUpdate || m_controls.m_gain4.isValueChanged() )
  114. {
  115. m_gain4 = dbfsToAmp( m_controls.m_gain4.value() );
  116. }
  117. // mute values update
  118. const bool mute1 = m_controls.m_mute1.value();
  119. const bool mute2 = m_controls.m_mute2.value();
  120. const bool mute3 = m_controls.m_mute3.value();
  121. const bool mute4 = m_controls.m_mute4.value();
  122. m_needsUpdate = false;
  123. memset( m_work, 0, sizeof( sampleFrame ) * frames );
  124. // run temp bands
  125. for( int f = 0; f < frames; ++f )
  126. {
  127. m_tmp1[f][0] = m_lp2.update( buf[f][0], 0 );
  128. m_tmp1[f][1] = m_lp2.update( buf[f][1], 1 );
  129. m_tmp2[f][0] = m_hp3.update( buf[f][0], 0 );
  130. m_tmp2[f][1] = m_hp3.update( buf[f][1], 1 );
  131. }
  132. // run band 1
  133. if( mute1 )
  134. {
  135. for( int f = 0; f < frames; ++f )
  136. {
  137. m_work[f][0] += m_lp1.update( m_tmp1[f][0], 0 ) * m_gain1;
  138. m_work[f][1] += m_lp1.update( m_tmp1[f][1], 1 ) * m_gain1;
  139. }
  140. }
  141. // run band 2
  142. if( mute2 )
  143. {
  144. for( int f = 0; f < frames; ++f )
  145. {
  146. m_work[f][0] += m_hp2.update( m_tmp1[f][0], 0 ) * m_gain2;
  147. m_work[f][1] += m_hp2.update( m_tmp1[f][1], 1 ) * m_gain2;
  148. }
  149. }
  150. // run band 3
  151. if( mute3 )
  152. {
  153. for( int f = 0; f < frames; ++f )
  154. {
  155. m_work[f][0] += m_lp3.update( m_tmp2[f][0], 0 ) * m_gain3;
  156. m_work[f][1] += m_lp3.update( m_tmp2[f][1], 1 ) * m_gain3;
  157. }
  158. }
  159. // run band 4
  160. if( mute4 )
  161. {
  162. for( int f = 0; f < frames; ++f )
  163. {
  164. m_work[f][0] += m_hp4.update( m_tmp2[f][0], 0 ) * m_gain4;
  165. m_work[f][1] += m_hp4.update( m_tmp2[f][1], 1 ) * m_gain4;
  166. }
  167. }
  168. const float d = dryLevel();
  169. const float w = wetLevel();
  170. double outSum = 0.0;
  171. for( int f = 0; f < frames; ++f )
  172. {
  173. buf[f][0] = d * buf[f][0] + w * m_work[f][0];
  174. buf[f][1] = d * buf[f][1] + w * m_work[f][1];
  175. outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1];
  176. }
  177. checkGate( outSum / frames );
  178. return isRunning();
  179. }
  180. void CrossoverEQEffect::clearFilterHistories()
  181. {
  182. m_lp1.clearHistory();
  183. m_lp2.clearHistory();
  184. m_lp3.clearHistory();
  185. m_hp2.clearHistory();
  186. m_hp3.clearHistory();
  187. m_hp4.clearHistory();
  188. }
  189. extern "C"
  190. {
  191. // necessary for getting instance out of shared lib
  192. PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* parent, void* data )
  193. {
  194. return new CrossoverEQEffect( parent, static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>( data ) );
  195. }
  196. }