You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

424 lines
12KB

  1. #include "mscHack.hpp"
  2. //#include "mscHack_Controls.hpp"
  3. #include "dsp/digital.hpp"
  4. //#include "CLog.h"
  5. namespace rack_plugin_mscHack {
  6. typedef struct
  7. {
  8. int state;
  9. float finc;
  10. int count;
  11. float fade;
  12. }COMP_STATE;
  13. //-----------------------------------------------------
  14. // Module Definition
  15. //
  16. //-----------------------------------------------------
  17. struct Compressor : Module
  18. {
  19. enum ParamIds
  20. {
  21. PARAM_INGAIN,
  22. PARAM_OUTGAIN,
  23. PARAM_THRESHOLD,
  24. PARAM_RATIO,
  25. PARAM_ATTACK,
  26. PARAM_RELEASE,
  27. PARAM_BYPASS,
  28. PARAM_SIDE_CHAIN,
  29. nPARAMS
  30. };
  31. enum InputIds
  32. {
  33. IN_AUDIOL,
  34. IN_AUDIOR,
  35. IN_SIDE_CHAIN,
  36. nINPUTS
  37. };
  38. enum OutputIds
  39. {
  40. OUT_AUDIOL,
  41. OUT_AUDIOR,
  42. nOUTPUTS
  43. };
  44. enum CompState
  45. {
  46. COMP_DONE,
  47. COMP_START,
  48. COMP_ATTACK,
  49. COMP_RELEASE,
  50. COMP_IDLE
  51. };
  52. bool m_bInitialized = false;
  53. CLog lg;
  54. LEDMeterWidget *m_pLEDMeterIn[ 2 ] = {0};
  55. CompressorLEDMeterWidget *m_pLEDMeterThreshold = NULL;
  56. CompressorLEDMeterWidget *m_pLEDMeterComp[ 2 ] = {0};
  57. LEDMeterWidget *m_pLEDMeterOut[ 2 ] = {0};
  58. bool m_bBypass = false;
  59. MyLEDButton *m_pButtonBypass = NULL;
  60. COMP_STATE m_CompL = {};
  61. COMP_STATE m_CompR = {};
  62. float m_fThreshold;
  63. // Contructor
  64. Compressor() : Module(nPARAMS, nINPUTS, nOUTPUTS, 0 ){}
  65. // Overrides
  66. void step() override;
  67. json_t* toJson() override;
  68. void fromJson(json_t *rootJ) override;
  69. void onReset() override;
  70. void onRandomize() override;
  71. bool ProcessCompState( COMP_STATE *pComp, bool bAboveThreshold );
  72. float Compress( float *pDetectInL, float *pDetectInR );
  73. };
  74. //-----------------------------------------------------
  75. // Compressor_Bypass
  76. //-----------------------------------------------------
  77. void Compressor_Bypass( void *pClass, int id, bool bOn )
  78. {
  79. Compressor *mymodule;
  80. mymodule = (Compressor*)pClass;
  81. mymodule->m_bBypass = bOn;
  82. }
  83. //-----------------------------------------------------
  84. // Procedure: Widget
  85. //
  86. //-----------------------------------------------------
  87. struct Compressor_Widget : ModuleWidget {
  88. Compressor_Widget( Compressor *module );
  89. };
  90. Compressor_Widget::Compressor_Widget( Compressor *module ) : ModuleWidget(module)
  91. {
  92. int x, y, y2;
  93. box.size = Vec( 15*8, 380);
  94. {
  95. SVGPanel *panel = new SVGPanel();
  96. panel->box.size = box.size;
  97. panel->setBackground(SVG::load(assetPlugin(plugin, "res/Compressor.svg")));
  98. addChild(panel);
  99. }
  100. //module->lg.Open("Compressor.txt");
  101. x = 10;
  102. y = 34;
  103. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  104. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  105. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  106. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  107. // bypass switch
  108. module->m_pButtonBypass = new MyLEDButton( x, y, 11, 11, 8.0, DWRGB( 180, 180, 180 ), DWRGB( 255, 0, 0 ), MyLEDButton::TYPE_SWITCH, 0, module, Compressor_Bypass );
  109. addChild( module->m_pButtonBypass );
  110. // audio inputs
  111. addInput(Port::create<MyPortInSmall>( Vec( x, y + 32 ), Port::INPUT, module, Compressor::IN_AUDIOL ) );
  112. addInput(Port::create<MyPortInSmall>( Vec( x, y + 78 ), Port::INPUT, module, Compressor::IN_AUDIOR ) );
  113. addInput(Port::create<MyPortInSmall>( Vec( x - 1, y + 210 ), Port::INPUT, module, Compressor::IN_SIDE_CHAIN ) );
  114. // LED meters
  115. module->m_pLEDMeterIn[ 0 ] = new LEDMeterWidget( x + 22, y + 25, 5, 3, 2, true );
  116. addChild( module->m_pLEDMeterIn[ 0 ] );
  117. module->m_pLEDMeterIn[ 1 ] = new LEDMeterWidget( x + 28, y + 25, 5, 3, 2, true );
  118. addChild( module->m_pLEDMeterIn[ 1 ] );
  119. module->m_pLEDMeterThreshold = new CompressorLEDMeterWidget( true, x + 39, y + 25, 5, 3, DWRGB( 245, 10, 174 ), DWRGB( 96, 4, 68 ) );
  120. addChild( module->m_pLEDMeterThreshold );
  121. module->m_pLEDMeterComp[ 0 ] = new CompressorLEDMeterWidget( true, x + 48, y + 25, 5, 3, DWRGB( 0, 128, 255 ), DWRGB( 0, 64, 128 ) );
  122. addChild( module->m_pLEDMeterComp[ 0 ] );
  123. module->m_pLEDMeterComp[ 1 ] = new CompressorLEDMeterWidget( true, x + 55, y + 25, 5, 3, DWRGB( 0, 128, 255 ), DWRGB( 0, 64, 128 ) );
  124. addChild( module->m_pLEDMeterComp[ 1 ] );
  125. module->m_pLEDMeterOut[ 0 ] = new LEDMeterWidget( x + 65, y + 25, 5, 3, 2, true );
  126. addChild( module->m_pLEDMeterOut[ 0 ] );
  127. module->m_pLEDMeterOut[ 1 ] = new LEDMeterWidget( x + 72, y + 25, 5, 3, 2, true );
  128. addChild( module->m_pLEDMeterOut[ 1 ] );
  129. // audio outputs
  130. addOutput(Port::create<MyPortOutSmall>( Vec( x + 83, y + 32 ), Port::OUTPUT, module, Compressor::OUT_AUDIOL ) );
  131. addOutput(Port::create<MyPortOutSmall>( Vec( x + 83, y + 78 ), Port::OUTPUT, module, Compressor::OUT_AUDIOR ) );
  132. // add param knobs
  133. y2 = y + 149;
  134. addParam(ParamWidget::create<Knob_Yellow1_26>( Vec( x + 11, y + 113 ), module, Compressor::PARAM_INGAIN, 0.0, 4.0, 1.0 ) );
  135. addParam(ParamWidget::create<Knob_Yellow1_26>( Vec( x + 62, y + 113 ), module, Compressor::PARAM_OUTGAIN, 0.0, 8.0, 1.0 ) );
  136. addParam(ParamWidget::create<Knob_Blue2_26>( Vec( x - 5, y2 + 20 ), module, Compressor::PARAM_SIDE_CHAIN, 0.0, 1.0, 0.0 ) );
  137. addParam(ParamWidget::create<Knob_Yellow1_26>( Vec( x + 39, y2 ), module, Compressor::PARAM_THRESHOLD, 0.0, 0.99, 0.0 ) ); y2 += 40;
  138. addParam(ParamWidget::create<Knob_Yellow1_26>( Vec( x + 39, y2 ), module, Compressor::PARAM_RATIO, 0.0, 2.0, 0.0 ) ); y2 += 40;
  139. addParam(ParamWidget::create<Knob_Yellow1_26>( Vec( x + 39, y2 ), module, Compressor::PARAM_ATTACK, 0.0, 1.0, 0.0 ) ); y2 += 40;
  140. addParam(ParamWidget::create<Knob_Yellow1_26>( Vec( x + 39, y2 ), module, Compressor::PARAM_RELEASE, 0.0, 1.0, 0.0 ) );
  141. //for( int i = 0; i < 15; i++ )
  142. //module->lg.f("level %d = %.3f\n", i, module->m_pLEDMeterThreshold->flevels[ i ] );
  143. module->m_bInitialized = true;
  144. }
  145. //-----------------------------------------------------
  146. // Procedure:
  147. //
  148. //-----------------------------------------------------
  149. json_t *Compressor::toJson()
  150. {
  151. json_t *rootJ = json_object();
  152. // reverse state
  153. json_object_set_new(rootJ, "m_bBypass", json_boolean (m_bBypass));
  154. return rootJ;
  155. }
  156. //-----------------------------------------------------
  157. // Procedure: fromJson
  158. //
  159. //-----------------------------------------------------
  160. void Compressor::fromJson(json_t *rootJ)
  161. {
  162. // reverse state
  163. json_t *revJ = json_object_get(rootJ, "m_bBypass");
  164. if (revJ)
  165. m_bBypass = json_is_true( revJ );
  166. m_pButtonBypass->Set( m_bBypass );
  167. }
  168. //-----------------------------------------------------
  169. // Procedure: reset
  170. //
  171. //-----------------------------------------------------
  172. void Compressor::onReset()
  173. {
  174. }
  175. //-----------------------------------------------------
  176. // Procedure: randomize
  177. //
  178. //-----------------------------------------------------
  179. void Compressor::onRandomize()
  180. {
  181. }
  182. //-----------------------------------------------------
  183. // Procedure: ProcessCompStatus
  184. //
  185. //-----------------------------------------------------
  186. #define MAX_ATT_TIME (0.5f) // 500ms
  187. #define MAX_REL_TIME (2.0f) // 2s
  188. bool Compressor::ProcessCompState( COMP_STATE *pComp, bool bAboveThreshold )
  189. {
  190. bool bCompressing = true;
  191. // restart compressor if it has finished
  192. if( bAboveThreshold && ( pComp->state == COMP_IDLE ) )
  193. {
  194. pComp->state = COMP_START;
  195. }
  196. // ready compressor for restart
  197. else if( !bAboveThreshold && ( pComp->state == COMP_DONE ) )
  198. {
  199. pComp->state = COMP_IDLE;
  200. }
  201. switch( pComp->state )
  202. {
  203. case COMP_START:
  204. pComp->count = 10 + (int)( MAX_ATT_TIME * engineGetSampleRate() * params[ PARAM_ATTACK ].value );
  205. pComp->state = COMP_ATTACK;
  206. pComp->finc = (1.0f - pComp->fade) / (float)pComp->count;
  207. break;
  208. case COMP_ATTACK:
  209. if( --pComp->count > 0 )
  210. {
  211. pComp->fade += pComp->finc;
  212. if( pComp->fade > 1.0f )
  213. pComp->fade = 1.0f;
  214. }
  215. else
  216. {
  217. pComp->count = 10 + (int)( MAX_REL_TIME * engineGetSampleRate() * params[ PARAM_RELEASE ].value );
  218. pComp->fade = 1.0f;
  219. pComp->finc = 1.0f / (float)pComp->count;
  220. pComp->state = COMP_RELEASE;
  221. }
  222. break;
  223. case COMP_RELEASE:
  224. if( --pComp->count > 0 )
  225. {
  226. pComp->fade -= pComp->finc;
  227. if( pComp->fade < 0.0f )
  228. pComp->fade = 0.0f;
  229. }
  230. else
  231. {
  232. pComp->fade = 0.0f;
  233. pComp->state = COMP_DONE;
  234. bCompressing = false;
  235. }
  236. break;
  237. case COMP_DONE:
  238. pComp->fade = 0.0f;
  239. bCompressing = false;
  240. break;
  241. case COMP_IDLE:
  242. pComp->fade = 0.0f;
  243. bCompressing = false;
  244. break;
  245. }
  246. return bCompressing;
  247. }
  248. //-----------------------------------------------------
  249. // Procedure: Compress
  250. //
  251. //-----------------------------------------------------
  252. float Compressor::Compress( float *pDetectInL, float *pDetectInR )
  253. {
  254. float rat, th, finL, finR, compL = 1.0f, compR = 1.0f;
  255. m_fThreshold = params[ PARAM_THRESHOLD ].value;
  256. th = 1.0f - m_fThreshold;
  257. rat = params[ PARAM_RATIO ].value;
  258. finL = fabs( *pDetectInL );
  259. if( ProcessCompState( &m_CompL, ( finL > th ) ) )
  260. compL = 1.0f - ( rat * m_CompL.fade );
  261. if( pDetectInR )
  262. {
  263. finR = fabs( *pDetectInR );
  264. if( ProcessCompState( &m_CompR, ( finR > th ) ) )
  265. compR = 1.0f - ( rat * m_CompR.fade );
  266. }
  267. else
  268. {
  269. m_CompR.state = COMP_IDLE;
  270. m_CompR.fade = 0.0;
  271. }
  272. return fmin( compL, compR );
  273. }
  274. //-----------------------------------------------------
  275. // Procedure: step
  276. //
  277. //-----------------------------------------------------
  278. void Compressor::step()
  279. {
  280. float outL, outR, diffL, diffR, fcomp, fside;
  281. if( !m_bInitialized )
  282. return;
  283. outL = inputs[ IN_AUDIOL ].normalize( 0.0 ) / AUDIO_MAX;
  284. outR = inputs[ IN_AUDIOR ].normalize( 0.0 ) / AUDIO_MAX;
  285. if( !m_bBypass )
  286. {
  287. outL = clamp( outL * params[ PARAM_INGAIN ].value, -1.0f, 1.0f );
  288. outR = clamp( outR * params[ PARAM_INGAIN ].value, -1.0f, 1.0f );
  289. }
  290. if( m_pLEDMeterIn[ 0 ] )
  291. m_pLEDMeterIn[ 0 ]->Process( outL );
  292. if( m_pLEDMeterIn[ 1 ] )
  293. m_pLEDMeterIn[ 1 ]->Process( outR );
  294. diffL = fabs( outL );
  295. diffR = fabs( outR );
  296. if( !m_bBypass )
  297. {
  298. // compress
  299. if( inputs[ IN_SIDE_CHAIN ].active )
  300. {
  301. fside = clamp( ( inputs[ IN_SIDE_CHAIN ].normalize( 0.0 ) / AUDIO_MAX ) * params[ PARAM_SIDE_CHAIN ].value, -1.0f, 1.0f );
  302. fcomp = Compress( &fside, NULL );
  303. }
  304. else
  305. {
  306. fcomp = Compress( &outL, &outR );
  307. }
  308. outL *= fcomp;
  309. outR *= fcomp;
  310. diffL -= fabs( outL );
  311. diffR -= fabs( outR );
  312. if( m_pLEDMeterComp[ 0 ] )
  313. m_pLEDMeterComp[ 0 ]->Process( diffL );
  314. if( m_pLEDMeterComp[ 1 ] )
  315. m_pLEDMeterComp[ 1 ]->Process( diffR );
  316. if( m_pLEDMeterThreshold )
  317. m_pLEDMeterThreshold->Process( m_fThreshold );
  318. outL = clamp( outL * params[ PARAM_OUTGAIN ].value, -1.0f, 1.0f );
  319. outR = clamp( outR * params[ PARAM_OUTGAIN ].value, -1.0f, 1.0f );
  320. }
  321. else
  322. {
  323. if( m_pLEDMeterComp[ 0 ] )
  324. m_pLEDMeterComp[ 0 ]->Process( 0 );
  325. if( m_pLEDMeterComp[ 1 ] )
  326. m_pLEDMeterComp[ 1 ]->Process( 0 );
  327. if( m_pLEDMeterThreshold )
  328. m_pLEDMeterThreshold->Process( 0 );
  329. }
  330. if( m_pLEDMeterOut[ 0 ] )
  331. m_pLEDMeterOut[ 0 ]->Process( outL );
  332. if( m_pLEDMeterOut[ 1 ] )
  333. m_pLEDMeterOut[ 1 ]->Process( outR );
  334. outputs[ OUT_AUDIOL ].value = outL * AUDIO_MAX;
  335. outputs[ OUT_AUDIOR ].value = outR * AUDIO_MAX;
  336. }
  337. } // namespace rack_plugin_mscHack
  338. using namespace rack_plugin_mscHack;
  339. RACK_PLUGIN_MODEL_INIT(mscHack, Compressor) {
  340. Model *modelCompressor = Model::create<Compressor, Compressor_Widget>( "mscHack", "Compressor1", "COMP Basic Compressor", DYNAMICS_TAG );
  341. return modelCompressor;
  342. }