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.

506 lines
14KB

  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. float lp1, bp1;
  9. float hpIn;
  10. float lpIn;
  11. float mpIn;
  12. }FILTER_PARAM_STRUCT;
  13. #define L 0
  14. #define R 1
  15. #define DELAY_BUFF_LEN 0x80000
  16. #define MAC_DELAY_SECONDS 4.0f
  17. //-----------------------------------------------------
  18. // Module Definition
  19. //
  20. //-----------------------------------------------------
  21. struct PingPong : Module
  22. {
  23. enum ParamIds
  24. {
  25. PARAM_DELAYL,
  26. PARAM_DELAYR,
  27. PARAM_LEVEL_FB_LR,
  28. PARAM_LEVEL_FB_LL,
  29. PARAM_LEVEL_FB_RL,
  30. PARAM_LEVEL_FB_RR,
  31. PARAM_CUTOFF,
  32. PARAM_Q,
  33. PARAM_MIX,
  34. PARAM_FILTER_MODE,
  35. PARAM_REVERSE,
  36. nPARAMS
  37. };
  38. enum InputIds
  39. {
  40. INPUT_L,
  41. INPUT_R,
  42. INPUT_SYNC,
  43. INPUT_GNIP_TOGGLE,
  44. nINPUTS
  45. };
  46. enum OutputIds
  47. {
  48. OUT_L,
  49. OUT_R,
  50. nOUTPUTS
  51. };
  52. enum FILTER_TYPES
  53. {
  54. FILTER_OFF,
  55. FILTER_LP,
  56. FILTER_HP,
  57. FILTER_BP,
  58. FILTER_NT
  59. };
  60. bool m_bInitialized = false;
  61. CLog lg;
  62. FILTER_PARAM_STRUCT m_Filter[ 2 ];
  63. float m_fCutoff = 0.0;
  64. float m_LastOut[ 2 ] = {};
  65. float m_DelayBuffer[ 2 ][ DELAY_BUFF_LEN ];
  66. int m_DelayIn = 0;
  67. int m_DelayOut[ 2 ] = {0};
  68. SchmittTrigger m_SchmittReverse;
  69. bool m_bReverseState = false;
  70. // sync clock
  71. SchmittTrigger m_SchmittSync;
  72. int m_LastSyncCount = 0;
  73. int m_SyncCount = 0;
  74. int m_SyncTime = 0;
  75. // LAST
  76. int m_LastDelayKnob[ 2 ] = {};
  77. bool m_bWasSynced = false;
  78. MyLEDButton *m_pButtonReverse = NULL;
  79. // Contructor
  80. PingPong() : Module(nPARAMS, nINPUTS, nOUTPUTS, 0){}
  81. // Overrides
  82. void step() override;
  83. json_t* toJson() override;
  84. void fromJson(json_t *rootJ) override;
  85. void onRandomize() override;
  86. void onReset() override;
  87. void ChangeFilterCutoff( float cutfreq );
  88. float Filter( int ch, float in );
  89. };
  90. //-----------------------------------------------------
  91. // PingPong_Reverse
  92. //-----------------------------------------------------
  93. void PingPong_Reverse( void *pClass, int id, bool bOn )
  94. {
  95. float delay;
  96. PingPong *mymodule;
  97. mymodule = (PingPong*)pClass;
  98. mymodule->m_bReverseState = bOn;
  99. // recalc delay offsets when going back to forward mode
  100. if( !mymodule->m_bReverseState )
  101. {
  102. delay = mymodule->params[ PingPong::PARAM_DELAYL ].value * MAC_DELAY_SECONDS * engineGetSampleRate();
  103. mymodule->m_DelayOut[ L ] = ( mymodule->m_DelayIn - (int)delay ) & 0x7FFFF;
  104. delay = mymodule->params[ PingPong::PARAM_DELAYR ].value * MAC_DELAY_SECONDS * engineGetSampleRate();
  105. mymodule->m_DelayOut[ R ] = ( mymodule->m_DelayIn - (int)delay ) & 0x7FFFF;
  106. }
  107. }
  108. //-----------------------------------------------------
  109. // MyEQHi_Knob
  110. //-----------------------------------------------------
  111. struct MyCutoffKnob : Knob_Green1_40
  112. {
  113. PingPong *mymodule;
  114. void onChange( EventChange &e ) override
  115. {
  116. mymodule = (PingPong*)module;
  117. if( mymodule )
  118. {
  119. mymodule->ChangeFilterCutoff( value );
  120. }
  121. RoundKnob::onChange( e );
  122. }
  123. };
  124. //-----------------------------------------------------
  125. // Procedure: Widget
  126. //
  127. //-----------------------------------------------------
  128. #define Y_OFF_H 40
  129. #define X_OFF_W 40
  130. struct PingPong_Widget : ModuleWidget {
  131. PingPong_Widget( PingPong *module );
  132. };
  133. PingPong_Widget::PingPong_Widget( PingPong *module ) : ModuleWidget(module)
  134. {
  135. box.size = Vec( 15*8, 380);
  136. {
  137. SVGPanel *panel = new SVGPanel();
  138. panel->box.size = box.size;
  139. panel->setBackground(SVG::load(assetPlugin(plugin, "res/PingPong.svg")));
  140. addChild(panel);
  141. }
  142. //module->lg.Open("PingPong.txt");
  143. // sync clock
  144. addInput(Port::create<MyPortInSmall>( Vec( 10, 110 ), Port::INPUT, module, PingPong::INPUT_SYNC ) );
  145. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  146. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  147. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  148. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  149. // Filter/Res knobs
  150. addParam(ParamWidget::create<FilterSelectToggle>( Vec( 66, 55 ), module, PingPong::PARAM_FILTER_MODE, 0.0, 4.0, 0.0 ) );
  151. addParam(ParamWidget::create<MyCutoffKnob>( Vec( 23, 60 ), module, PingPong::PARAM_CUTOFF, 0.0, 1.0, 0.0 ) );
  152. addParam(ParamWidget::create<Knob_Purp1_20>( Vec( 73, 79 ), module, PingPong::PARAM_Q, 0.0, 1.0, 0.0 ) );
  153. // L Feedback
  154. addParam(ParamWidget::create<Knob_Red1_20>( Vec( 49, 110 ), module, PingPong::PARAM_LEVEL_FB_LL, 0.0, 1.0, 0.0 ) );
  155. // Left
  156. addInput(Port::create<MyPortInSmall>( Vec( 10, 154 ), Port::INPUT, module, PingPong::INPUT_L ) );
  157. addParam(ParamWidget::create<Knob_Yellow2_40>( Vec( 38, 143 ), module, PingPong::PARAM_DELAYL, 0.0, 1.0, 0.0 ) );
  158. addOutput(Port::create<MyPortOutSmall>( Vec( 90, 154 ), Port::OUTPUT, module, PingPong::OUT_L ) );
  159. // R to L level and L to R levels
  160. addParam(ParamWidget::create<Knob_Red1_20>( Vec( 9, 191 ), module, PingPong::PARAM_LEVEL_FB_RL, 0.0, 1.0, 0.0 ) );
  161. addParam(ParamWidget::create<Knob_Red1_20>( Vec( 9, 226 ), module, PingPong::PARAM_LEVEL_FB_LR, 0.0, 1.0, 0.0 ) );
  162. // mix knob
  163. addParam(ParamWidget::create<Knob_Blue2_40>( Vec( 77, 199 ), module, PingPong::PARAM_MIX, 0.0, 1.0, 0.0 ) );
  164. // Left
  165. addInput(Port::create<MyPortInSmall>( Vec( 10, 266 ), Port::INPUT, module, PingPong::INPUT_R ) );
  166. addParam(ParamWidget::create<Knob_Yellow2_40>( Vec( 38, 255 ), module, PingPong::PARAM_DELAYR, 0.0, 1.0, 0.0 ) );
  167. addOutput(Port::create<MyPortOutSmall>( Vec( 90, 266 ), Port::OUTPUT, module, PingPong::OUT_R ) );
  168. // R Feedback
  169. addParam(ParamWidget::create<Knob_Red1_20>( Vec( 49, 308 ), module, PingPong::PARAM_LEVEL_FB_RR, 0.0, 1.0, 0.0 ) );
  170. // reverse button
  171. addInput(Port::create<MyPortInSmall>( Vec( 3, 340 ), Port::INPUT, module, PingPong::INPUT_GNIP_TOGGLE ) );
  172. module->m_pButtonReverse = new MyLEDButton( 24, 343, 11, 11, 8.0, DWRGB( 180, 180, 180 ), DWRGB( 255, 255, 0 ), MyLEDButton::TYPE_SWITCH, 0, module, PingPong_Reverse );
  173. addChild( module->m_pButtonReverse );
  174. module->m_bInitialized = true;
  175. }
  176. //-----------------------------------------------------
  177. // Procedure: reset
  178. //
  179. //-----------------------------------------------------
  180. void PingPong::onReset()
  181. {
  182. if( !m_bInitialized )
  183. return;
  184. m_pButtonReverse->Set( false );
  185. m_bReverseState = false;
  186. }
  187. //-----------------------------------------------------
  188. // Procedure: randomize
  189. //
  190. //-----------------------------------------------------
  191. void PingPong::onRandomize()
  192. {
  193. }
  194. //-----------------------------------------------------
  195. // Procedure:
  196. //
  197. //-----------------------------------------------------
  198. json_t *PingPong::toJson()
  199. {
  200. json_t *rootJ = json_object();
  201. // reverse state
  202. json_object_set_new(rootJ, "ReverseState", json_boolean (m_bReverseState));
  203. return rootJ;
  204. }
  205. //-----------------------------------------------------
  206. // Procedure: fromJson
  207. //
  208. //-----------------------------------------------------
  209. void PingPong::fromJson(json_t *rootJ)
  210. {
  211. // reverse state
  212. json_t *revJ = json_object_get(rootJ, "ReverseState");
  213. if (revJ)
  214. m_bReverseState = json_is_true( revJ );
  215. m_pButtonReverse->Set( m_bReverseState );
  216. }
  217. //-----------------------------------------------------
  218. // Procedure: ChangeFilterCutoff
  219. //
  220. //-----------------------------------------------------
  221. void PingPong::ChangeFilterCutoff( float cutfreq )
  222. {
  223. float fx, fx2, fx3, fx5, fx7;
  224. // clamp at 1.0 and 20/samplerate
  225. cutfreq = fmax(cutfreq, 20 / engineGetSampleRate());
  226. cutfreq = fmin(cutfreq, 1.0);
  227. // calculate eq rez freq
  228. fx = 3.141592 * (cutfreq * 0.026315789473684210526315789473684) * 2 * 3.141592;
  229. fx2 = fx*fx;
  230. fx3 = fx2*fx;
  231. fx5 = fx3*fx2;
  232. fx7 = fx5*fx2;
  233. m_fCutoff = 2.0 * (fx
  234. - (fx3 * 0.16666666666666666666666666666667)
  235. + (fx5 * 0.0083333333333333333333333333333333)
  236. - (fx7 * 0.0001984126984126984126984126984127));
  237. }
  238. //-----------------------------------------------------
  239. // Procedure: Filter
  240. //
  241. //-----------------------------------------------------
  242. #define MULTI (0.33333333333333333333333333333333f)
  243. float PingPong::Filter( int ch, float in )
  244. {
  245. FILTER_PARAM_STRUCT *p;
  246. float rez, hp1, out = 0.0;
  247. float lowpass, highpass, bandpass;
  248. if( (int)params[ PARAM_FILTER_MODE ].value == 0 )
  249. return in;
  250. p = &m_Filter[ ch ];
  251. rez = 1.0 - params[ PARAM_Q ].value;
  252. in = in + 0.000000001;
  253. p->lp1 = p->lp1 + m_fCutoff * p->bp1;
  254. hp1 = in - p->lp1 - rez * p->bp1;
  255. p->bp1 = m_fCutoff * hp1 + p->bp1;
  256. lowpass = p->lp1;
  257. highpass = hp1;
  258. bandpass = p->bp1;
  259. p->lp1 = p->lp1 + m_fCutoff * p->bp1;
  260. hp1 = in - p->lp1 - rez * p->bp1;
  261. p->bp1 = m_fCutoff * hp1 + p->bp1;
  262. lowpass = lowpass + p->lp1;
  263. highpass = highpass + hp1;
  264. bandpass = bandpass + p->bp1;
  265. in = in - 0.000000001;
  266. p->lp1 = p->lp1 + m_fCutoff * p->bp1;
  267. hp1 = in - p->lp1 - rez * p->bp1;
  268. p->bp1 = m_fCutoff * hp1 + p->bp1;
  269. lowpass = (lowpass + p->lp1) * MULTI;
  270. highpass = (highpass + hp1) * MULTI;
  271. bandpass = (bandpass + p->bp1) * MULTI;
  272. switch( (int)params[ PARAM_FILTER_MODE ].value )
  273. {
  274. case FILTER_LP:
  275. out = lowpass;
  276. break;
  277. case FILTER_HP:
  278. out = highpass;
  279. break;
  280. case FILTER_BP:
  281. out = bandpass;
  282. break;
  283. case FILTER_NT:
  284. out = lowpass + highpass;
  285. break;
  286. default:
  287. break;
  288. }
  289. return out;
  290. }
  291. //-----------------------------------------------------
  292. // Procedure: step
  293. //
  294. //-----------------------------------------------------
  295. float syncQuant[ 10 ] = { 0.125, 0.25, 0.333, 0.375, 0.5, 0.625, 0.666, 0.750, 0.875, 1.0 };
  296. void PingPong::step()
  297. {
  298. float outL, outR, inL = 0.0, inR = 0.0, inOrigL = 0.0, inOrigR = 0.0, syncq = 0.0;
  299. bool bMono = false;
  300. int i, dR, dL;
  301. if( !m_bInitialized )
  302. return;
  303. dL = params[ PARAM_DELAYL ].value * MAC_DELAY_SECONDS * engineGetSampleRate();
  304. dR = params[ PARAM_DELAYR ].value * MAC_DELAY_SECONDS * engineGetSampleRate();
  305. if( m_SchmittReverse.process( inputs[ INPUT_GNIP_TOGGLE ].value ) )
  306. {
  307. if( m_pButtonReverse->m_bOn )
  308. m_pButtonReverse->Set( false );
  309. else
  310. m_pButtonReverse->Set( true );
  311. m_bReverseState = m_pButtonReverse->m_bOn;
  312. }
  313. // check right channel first for possible mono
  314. if( inputs[ INPUT_SYNC ].active )
  315. {
  316. m_SyncCount++;
  317. // sync'd delay
  318. if( m_SchmittSync.process( inputs[ INPUT_SYNC ].value ) )
  319. {
  320. if( !m_bWasSynced || ( (m_SyncTime / 10) != (m_SyncCount / 10) ) || ( m_LastDelayKnob[ L ] != dL ) || ( m_LastDelayKnob[ R ] != dR ) )
  321. {
  322. m_SyncTime = m_SyncCount;
  323. for( i = 0; i < 10; i++ )
  324. {
  325. if( params[ PARAM_DELAYL ].value <= syncQuant[ i ] )
  326. {
  327. syncq = syncQuant[ i ] * MAC_DELAY_SECONDS;
  328. break;
  329. }
  330. }
  331. m_DelayOut[ L ] = ( m_DelayIn - (int)(syncq * m_SyncTime) ) & 0x7FFFF;
  332. for( i = 0; i < 10; i++ )
  333. {
  334. if( params[ PARAM_DELAYR ].value <= syncQuant[ i ] )
  335. {
  336. syncq = syncQuant[ i ] * MAC_DELAY_SECONDS;
  337. break;
  338. }
  339. }
  340. m_DelayOut[ R ] = ( m_DelayIn - (int)(syncq * m_SyncTime) ) & 0x7FFFF;
  341. }
  342. m_SyncCount = 0;
  343. }
  344. m_bWasSynced = true;
  345. }
  346. else
  347. {
  348. // non sync'd delay
  349. if( m_bWasSynced || ( m_LastDelayKnob[ L ] != dL ) )
  350. m_DelayOut[ L ] = ( m_DelayIn - (int)dL ) & 0x7FFFF;
  351. if( m_bWasSynced || ( m_LastDelayKnob[ R ] != dR ) )
  352. m_DelayOut[ R ] = ( m_DelayIn - (int)dR ) & 0x7FFFF;
  353. m_bWasSynced = false;
  354. m_SyncCount = 0;
  355. }
  356. m_LastDelayKnob[ L ] = dL;
  357. m_LastDelayKnob[ R ] = dR;
  358. // check right channel first for possible mono
  359. if( inputs[ INPUT_R ].active )
  360. {
  361. inR = clamp( inputs[ INPUT_R ].value / AUDIO_MAX, -1.0f, 1.0f );
  362. inR = Filter( R, inR );
  363. inOrigR = inR;
  364. bMono = false;
  365. }
  366. else
  367. bMono = true;
  368. // left channel
  369. if( inputs[ INPUT_L ].active )
  370. {
  371. inL = clamp( inputs[ INPUT_L ].value / AUDIO_MAX, -1.0f, 1.0f );
  372. inL = Filter( L, inL );
  373. inOrigL = inL;
  374. if( bMono )
  375. {
  376. inOrigR = inL;
  377. inR = inL;
  378. }
  379. }
  380. m_DelayBuffer[ L ][ m_DelayIn ] = inL + ( m_LastOut[ L ] * params[ PARAM_LEVEL_FB_LL ].value ) + ( m_LastOut[ R ] * params[ PARAM_LEVEL_FB_RL ].value );
  381. m_DelayBuffer[ R ][ m_DelayIn ] = inR + ( m_LastOut[ R ] * params[ PARAM_LEVEL_FB_RR ].value ) + ( m_LastOut[ L ] * params[ PARAM_LEVEL_FB_LR ].value );
  382. m_DelayIn = ( ( m_DelayIn + 1 ) & 0x7FFFF );
  383. outL = m_DelayBuffer[ L ][ m_DelayOut[ L ] ];
  384. outR = m_DelayBuffer[ R ][ m_DelayOut[ R ] ];
  385. if( m_bReverseState )
  386. {
  387. m_DelayOut[ L ] = ( ( m_DelayOut[ L ] - 1 ) & 0x7FFFF );
  388. m_DelayOut[ R ] = ( ( m_DelayOut[ R ] - 1 ) & 0x7FFFF );
  389. }
  390. else
  391. {
  392. m_DelayOut[ L ] = ( ( m_DelayOut[ L ] + 1 ) & 0x7FFFF );
  393. m_DelayOut[ R ] = ( ( m_DelayOut[ R ] + 1 ) & 0x7FFFF );
  394. }
  395. m_LastOut[ L ] = outL;
  396. m_LastOut[ R ] = outR;
  397. // output
  398. outputs[ OUT_L ].value = clamp( ( inOrigL * ( 1.0f - params[ PARAM_MIX ].value ) ) + ( (outL * AUDIO_MAX) * params[ PARAM_MIX ].value ), -AUDIO_MAX, AUDIO_MAX );
  399. outputs[ OUT_R ].value = clamp( ( inOrigR * ( 1.0f - params[ PARAM_MIX ].value ) ) + ( (outR * AUDIO_MAX) * params[ PARAM_MIX ].value ), -AUDIO_MAX, AUDIO_MAX );
  400. }
  401. } // namespace rack_plugin_mscHack
  402. using namespace rack_plugin_mscHack;
  403. RACK_PLUGIN_MODEL_INIT(mscHack, PingPong) {
  404. Model *modelPingPong = Model::create<PingPong, PingPong_Widget>("mscHack", "PingPong_Widget", "DELAY Ping Pong", DELAY_TAG, PANNING_TAG);
  405. return modelPingPong;
  406. }