| 
				
				
				
				 | 
			
			 | 
			@@ -0,0 +1,697 @@ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include <string> | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include <chrono> | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include "dsp/digital.hpp" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include "dsp/samplerate.hpp" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include "dsp/ringbuffer.hpp" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include "peaks/processors.h" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include "AudibleInstruments.hpp" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			enum SwitchIndex { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SWITCH_TWIN_MODE, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SWITCH_FUNCTION, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SWITCH_GATE_TRIG_1, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SWITCH_GATE_TRIG_2 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			enum EditMode { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				EDIT_MODE_TWIN, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				EDIT_MODE_SPLIT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				EDIT_MODE_FIRST, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				EDIT_MODE_SECOND, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				EDIT_MODE_LAST | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			enum Function { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				FUNCTION_ENVELOPE, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				FUNCTION_LFO, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				FUNCTION_TAP_LFO, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				FUNCTION_DRUM_GENERATOR, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				FUNCTION_MINI_SEQUENCER, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				FUNCTION_PULSE_SHAPER, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				FUNCTION_PULSE_RANDOMIZER, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				FUNCTION_FM_DRUM_GENERATOR, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				FUNCTION_LAST, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				FUNCTION_FIRST_ALTERNATE_FUNCTION = FUNCTION_MINI_SEQUENCER | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			struct Settings { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t edit_mode; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t function[2]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t pot_value[8]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				bool snap_mode; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			static const size_t kNumBlocks = 2; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			static const size_t kNumChannels = 2; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			static const size_t kBlockSize = 4; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			static const int32_t kLongPressDuration = 600; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			static const uint8_t kNumAdcChannels = 4; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			static const uint16_t kAdcThresholdUnlocked = 1 << (16 - 10);  // 10 bits | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			static const uint16_t kAdcThresholdLocked = 1 << (16 - 8);  // 8 bits | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			struct Peaks : Module { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				enum ParamIds { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					KNOB_1_PARAM, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					KNOB_2_PARAM, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					KNOB_3_PARAM, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					KNOB_4_PARAM, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					BUTTON_1_PARAM, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					BUTTON_2_PARAM, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					TRIG_1_PARAM, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					TRIG_2_PARAM, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					NUM_PARAMS | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				enum InputIds { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					GATE_1_INPUT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					GATE_2_INPUT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					NUM_INPUTS | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				enum OutputIds { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					OUT_1_OUTPUT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					OUT_2_OUTPUT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					NUM_OUTPUTS | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				enum LightIds { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					TRIG_1_LIGHT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					TRIG_2_LIGHT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					TWIN_MODE_LIGHT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					FUNC_1_LIGHT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					FUNC_2_LIGHT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					FUNC_3_LIGHT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					FUNC_4_LIGHT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					NUM_LIGHTS | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				static const peaks::ProcessorFunction function_table_[FUNCTION_LAST][2]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				EditMode edit_mode_ = EDIT_MODE_TWIN; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				Function function_[2] = {FUNCTION_ENVELOPE, FUNCTION_ENVELOPE}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				Settings settings_; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t pot_value_[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				bool snap_mode_ = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				bool snapped_[4] = {false, false, false, false}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				int32_t adc_lp_[kNumAdcChannels] = {0, 0, 0, 0}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				int32_t adc_value_[kNumAdcChannels] = {0, 0, 0, 0}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				int32_t adc_threshold_[kNumAdcChannels] = {0, 0, 0, 0}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				long long press_time_[2] = {0, 0}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				peaks::Processors processors[2]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				int16_t output[kBlockSize]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				int16_t brightness[kNumChannels] = {0, 0}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SchmittTrigger switches_[2]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				peaks::GateFlags gate_flags[2] = {0, 0}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SampleRateConverter<2> outputSrc; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				DoubleRingBuffer<Frame<2>, 256> outputBuffer; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				bool initNumberStation = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				struct Block { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					peaks::GateFlags input[kNumChannels][kBlockSize]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					uint16_t output[kNumChannels][kBlockSize]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				struct Slice { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					Block* block; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					size_t frame_index; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				Block block_[kNumBlocks]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				size_t io_frame_ = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				size_t io_block_ = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				size_t render_block_ = kNumBlocks / 2; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				Peaks() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					settings_.edit_mode = EDIT_MODE_TWIN; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					settings_.function[0] = FUNCTION_ENVELOPE; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					settings_.function[1] = FUNCTION_ENVELOPE; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					settings_.snap_mode = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					std::fill(&settings_.pot_value[0], &settings_.pot_value[8], 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					memset(&processors[0], 0, sizeof(processors[0])); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					memset(&processors[1], 0, sizeof(processors[1])); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[0].Init(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[1].Init(1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void onReset() override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					init(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void init() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					std::fill(&pot_value_[0], &pot_value_[8], 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					std::fill(&press_time_[0], &press_time_[1], 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					std::fill(&brightness[0], &brightness[1], 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					std::fill(&adc_lp_[0], &adc_lp_[kNumAdcChannels], 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					std::fill(&adc_value_[0], &adc_value_[kNumAdcChannels], 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					std::fill(&adc_threshold_[0], &adc_threshold_[kNumAdcChannels], 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					std::fill(&snapped_[0], &snapped_[kNumAdcChannels], false); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					edit_mode_ = static_cast<EditMode>(settings_.edit_mode); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					function_[0] = static_cast<Function>(settings_.function[0]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					function_[1] = static_cast<Function>(settings_.function[1]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					std::copy(&settings_.pot_value[0], &settings_.pot_value[8], &pot_value_[0]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (edit_mode_ == EDIT_MODE_FIRST || edit_mode_ == EDIT_MODE_SECOND) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						lockPots(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						for (uint8_t i = 0; i < 4; ++i) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							processors[0].set_parameter( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							  i, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							  static_cast<uint16_t>(pot_value_[i]) << 8); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							processors[1].set_parameter( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							  i, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							  static_cast<uint16_t>(pot_value_[i + 4]) << 8); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					snap_mode_ = settings_.snap_mode; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					changeControlMode(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					setFunction(0, function_[0]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					setFunction(1, function_[1]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				json_t* toJson() override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					saveState(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t* rootJ = json_object(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_object_set_new(rootJ, "edit_mode", json_integer((int)settings_.edit_mode)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_object_set_new(rootJ, "fcn_channel_1", json_integer((int)settings_.function[0])); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_object_set_new(rootJ, "fcn_channel_2", json_integer((int)settings_.function[1])); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t* potValuesJ = json_array(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					for (int p : pot_value_) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						json_t* pJ = json_integer(p); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						json_array_append_new(potValuesJ, pJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_object_set_new(rootJ, "pot_values", potValuesJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_object_set_new(rootJ, "snap_mode", json_boolean(settings_.snap_mode)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return rootJ; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void fromJson(json_t* rootJ) override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t* editModeJ = json_object_get(rootJ, "edit_mode"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (editModeJ) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						settings_.edit_mode = static_cast<EditMode>(json_integer_value(editModeJ)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t* fcnChannel1J = json_object_get(rootJ, "fcn_channel_1"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (fcnChannel1J) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						settings_.function[0] = static_cast<Function>(json_integer_value(fcnChannel1J)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t* fcnChannel2J = json_object_get(rootJ, "fcn_channel_2"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (fcnChannel2J) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						settings_.function[1] = static_cast<Function>(json_integer_value(fcnChannel2J)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t* snapModeJ = json_object_get(rootJ, "snap_mode"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (snapModeJ) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						settings_.snap_mode = json_boolean_value(snapModeJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t* potValuesJ = json_object_get(rootJ, "pot_values"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					size_t potValueId; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t* pJ; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_array_foreach(potValuesJ, potValueId, pJ) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (potValueId < sizeof(pot_value_) / sizeof(pot_value_)[0]) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							settings_.pot_value[potValueId] = json_integer_value(pJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Update module internal state from settings. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					init(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void step() override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					poll(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					pollPots(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Initialize "secret" number station mode. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (initNumberStation) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						processors[0].set_function(peaks::PROCESSOR_FUNCTION_NUMBER_STATION); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						processors[1].set_function(peaks::PROCESSOR_FUNCTION_NUMBER_STATION); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						initNumberStation = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (outputBuffer.empty()) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						while (render_block_ != io_block_) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							process(&block_[render_block_], kBlockSize); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							render_block_ = (render_block_ + 1) % kNumBlocks; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						uint32_t external_gate_inputs = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						external_gate_inputs |= (inputs[GATE_1_INPUT].value ? 1 : 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						external_gate_inputs |= (inputs[GATE_2_INPUT].value ? 2 : 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						uint32_t buttons = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						buttons |= (params[TRIG_1_PARAM].value ? 1 : 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						buttons |= (params[TRIG_2_PARAM].value ? 2 : 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						uint32_t gate_inputs = external_gate_inputs | buttons; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// Prepare sample rate conversion. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// Peaks is sampling at 48kHZ. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						outputSrc.setRates(48000, engineGetSampleRate()); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						int inLen = kBlockSize; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						int outLen = outputBuffer.capacity(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						Frame<2> f[kBlockSize]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// Process an entire block of data from the IOBuffer. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						for (size_t k = 0; k < kBlockSize; ++k) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							Slice slice = NextSlice(1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							for (size_t i = 0; i < kNumChannels; ++i) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								gate_flags[i] = peaks::ExtractGateFlags( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								                  gate_flags[i], | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								                  gate_inputs & (1 << i)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								f[k].samples[i] = slice.block->output[i][slice.frame_index]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							// A hack to make channel 1 aware of what's going on in channel 2. Used to | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							// reset the sequencer. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							slice.block->input[0][slice.frame_index] = gate_flags[0] | (gate_flags[1] << 4) | (buttons & 8 ? peaks::GATE_FLAG_FROM_BUTTON : 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							slice.block->input[1][slice.frame_index] = gate_flags[1] | (buttons & 2 ? peaks::GATE_FLAG_FROM_BUTTON : 0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						outputSrc.process(f, &inLen, outputBuffer.endData(), &outLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						outputBuffer.endIncr(outLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Update outputs. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (!outputBuffer.empty()) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						Frame<2> f = outputBuffer.shift(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// Peaks manual says output spec is 0..8V for envelopes and 10Vpp for audio/CV. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// TODO Check the output values against an actual device. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						outputs[OUT_1_OUTPUT].value = rescale(static_cast<float>(f.samples[0]), 0.0f, 65535.f, -8.0f, 8.0f); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						outputs[OUT_2_OUTPUT].value = rescale(static_cast<float>(f.samples[1]), 0.0f, 65535.f, -8.0f, 8.0f); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				inline Slice NextSlice(size_t size) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					Slice s; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					s.block = &block_[io_block_]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					s.frame_index = io_frame_; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					io_frame_ += size; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (io_frame_ >= kBlockSize) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						io_frame_ -= kBlockSize; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						io_block_ = (io_block_ + 1) % kNumBlocks; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return s; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				inline Function function() const { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return edit_mode_ == EDIT_MODE_SECOND ? function_[1] : function_[0]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				inline void set_led_brightness(int channel, int16_t value) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					brightness[channel] = value; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				inline void process(Block* block, size_t size) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					for (size_t i = 0; i < kNumChannels; ++i) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						processors[i].Process(block->input[i], output, size); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						set_led_brightness(i, output[0]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						for (size_t j = 0; j < size; ++j) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							// From calibration_data.h, shifting signed to unsigned values. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							int32_t shifted_value = 32767 + static_cast<int32_t>(output[j]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							CONSTRAIN(shifted_value, 0, 65535); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							block->output[i][j] = static_cast<uint16_t>(shifted_value); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void changeControlMode(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void setFunction(uint8_t index, Function f); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void onPotChanged(uint16_t id, uint16_t value); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void onSwitchReleased(uint16_t id, uint16_t data); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void saveState(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void lockPots(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void poll(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void pollPots(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void refreshLeds(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				long long getSystemTimeMs(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			const peaks::ProcessorFunction Peaks::function_table_[FUNCTION_LAST][2] = { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				{ peaks::PROCESSOR_FUNCTION_ENVELOPE, peaks::PROCESSOR_FUNCTION_ENVELOPE }, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				{ peaks::PROCESSOR_FUNCTION_LFO, peaks::PROCESSOR_FUNCTION_LFO }, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				{ peaks::PROCESSOR_FUNCTION_TAP_LFO, peaks::PROCESSOR_FUNCTION_TAP_LFO }, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				{ peaks::PROCESSOR_FUNCTION_BASS_DRUM, peaks::PROCESSOR_FUNCTION_SNARE_DRUM }, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				{ peaks::PROCESSOR_FUNCTION_MINI_SEQUENCER, peaks::PROCESSOR_FUNCTION_MINI_SEQUENCER }, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				{ peaks::PROCESSOR_FUNCTION_PULSE_SHAPER, peaks::PROCESSOR_FUNCTION_PULSE_SHAPER }, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				{ peaks::PROCESSOR_FUNCTION_PULSE_RANDOMIZER, peaks::PROCESSOR_FUNCTION_PULSE_RANDOMIZER }, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				{ peaks::PROCESSOR_FUNCTION_FM_DRUM, peaks::PROCESSOR_FUNCTION_FM_DRUM }, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void Peaks::changeControlMode() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint16_t parameters[4]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				for (int i = 0; i < 4; ++i) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					parameters[i] = adc_value_[i]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (edit_mode_ == EDIT_MODE_SPLIT) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[0].CopyParameters(¶meters[0], 2); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[1].CopyParameters(¶meters[2], 2); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[0].set_control_mode(peaks::CONTROL_MODE_HALF); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[1].set_control_mode(peaks::CONTROL_MODE_HALF); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				else if (edit_mode_ == EDIT_MODE_TWIN) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[0].CopyParameters(¶meters[0], 4); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[1].CopyParameters(¶meters[0], 4); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[0].set_control_mode(peaks::CONTROL_MODE_FULL); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[1].set_control_mode(peaks::CONTROL_MODE_FULL); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[0].set_control_mode(peaks::CONTROL_MODE_FULL); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[1].set_control_mode(peaks::CONTROL_MODE_FULL); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void Peaks::setFunction(uint8_t index, Function f) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (edit_mode_ == EDIT_MODE_SPLIT || edit_mode_ == EDIT_MODE_TWIN) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					function_[0] = function_[1] = f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[0].set_function(function_table_[f][0]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[1].set_function(function_table_[f][1]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					function_[index] = f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					processors[index].set_function(function_table_[f][index]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void Peaks::onSwitchReleased(uint16_t id, uint16_t data) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				switch (id) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					case SWITCH_TWIN_MODE: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (data > kLongPressDuration) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							edit_mode_ = static_cast<EditMode>( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							               (edit_mode_ + EDIT_MODE_FIRST) % EDIT_MODE_LAST); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							function_[0] = function_[1]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							processors[0].set_function(function_table_[function_[0]][0]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							processors[1].set_function(function_table_[function_[0]][1]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							lockPots(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (edit_mode_ <= EDIT_MODE_SPLIT) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								edit_mode_ = static_cast<EditMode>(EDIT_MODE_SPLIT - edit_mode_); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								edit_mode_ = static_cast<EditMode>(EDIT_MODE_SECOND - (edit_mode_ & 1)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								lockPots(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						changeControlMode(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						saveState(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					case SWITCH_FUNCTION: { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						Function f = function(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (data > kLongPressDuration) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							f = static_cast<Function>((f + FUNCTION_FIRST_ALTERNATE_FUNCTION) % FUNCTION_LAST); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (f <= FUNCTION_DRUM_GENERATOR) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								f = static_cast<Function>((f + 1) & 3); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								f = static_cast<Function>(((f + 1) & 3) + FUNCTION_FIRST_ALTERNATE_FUNCTION); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						setFunction(edit_mode_ - EDIT_MODE_FIRST, f); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						saveState(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					case SWITCH_GATE_TRIG_1: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// no-op | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					case SWITCH_GATE_TRIG_2: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// no-op | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void Peaks::lockPots() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				std::fill( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				  &adc_threshold_[0], | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				  &adc_threshold_[kNumAdcChannels], | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				  kAdcThresholdLocked); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				std::fill(&snapped_[0], &snapped_[kNumAdcChannels], false); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void Peaks::pollPots() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				for (uint8_t i = 0; i < kNumAdcChannels; ++i) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					adc_lp_[i] = (int32_t(params[KNOB_1_PARAM + i].value) + adc_lp_[i] * 7) >> 3; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					int32_t value = adc_lp_[i]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					int32_t current_value = adc_value_[i]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (value >= current_value + adc_threshold_[i] || | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    value <= current_value - adc_threshold_[i] || | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    !adc_threshold_[i]) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						onPotChanged(i, value); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						adc_value_[i] = value; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						adc_threshold_[i] = kAdcThresholdUnlocked; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void Peaks::onPotChanged(uint16_t id, uint16_t value) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				switch (edit_mode_) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					case EDIT_MODE_TWIN: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						processors[0].set_parameter(id, value); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						processors[1].set_parameter(id, value); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						pot_value_[id] = value >> 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					case EDIT_MODE_SPLIT: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (id < 2) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							processors[0].set_parameter(id, value); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							processors[1].set_parameter(id - 2, value); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						pot_value_[id] = value >> 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					case EDIT_MODE_FIRST: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					case EDIT_MODE_SECOND: { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						uint8_t index = id + (edit_mode_ - EDIT_MODE_FIRST) * 4; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						peaks::Processors* p = &processors[edit_mode_ - EDIT_MODE_FIRST]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						int16_t delta = static_cast<int16_t>(pot_value_[index]) - \ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						                static_cast<int16_t>(value >> 8); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (delta < 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							delta = -delta; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (!snap_mode_ || snapped_[id] || delta <= 2) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							p->set_parameter(id, value); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							pot_value_[index] = value >> 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							snapped_[id] = true; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					case EDIT_MODE_LAST: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			long long Peaks::getSystemTimeMs() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				return std::chrono::duration_cast<std::chrono::milliseconds>( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				         std::chrono::steady_clock::now().time_since_epoch() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				       ).count(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void Peaks::poll() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				for (uint8_t i = 0; i < 2; ++i) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (switches_[i].process(params[BUTTON_1_PARAM + i].value)) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						press_time_[i] = getSystemTimeMs(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (switches_[i].isHigh() && press_time_[i] != 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						int32_t pressed_time = getSystemTimeMs() - press_time_[i]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (pressed_time > kLongPressDuration) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							onSwitchReleased(SWITCH_TWIN_MODE + i, pressed_time); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							press_time_[i] = 0;  // Inhibit next release event | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (!switches_[i].isHigh() && press_time_[i] != 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						int32_t delta = getSystemTimeMs() - press_time_[i] + 1; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						onSwitchReleased(SWITCH_TWIN_MODE + i, delta); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						press_time_[i] = 0; // Not in original code! | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				refreshLeds(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void Peaks::saveState() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				settings_.edit_mode = edit_mode_; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				settings_.function[0] = function_[0]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				settings_.function[1] = function_[1]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				std::copy(&pot_value_[0], &pot_value_[8], &settings_.pot_value[0]); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				settings_.snap_mode = snap_mode_; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void Peaks::refreshLeds() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t flash = (getSystemTimeMs() >> 7) & 7; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				switch (edit_mode_) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					case EDIT_MODE_FIRST: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						lights[TWIN_MODE_LIGHT].value = (flash == 1) ? 1.0f : 0.0f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					case EDIT_MODE_SECOND: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						lights[TWIN_MODE_LIGHT].value = (flash == 1 || flash == 3) ? 1.0f : 0.0f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					default: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						lights[TWIN_MODE_LIGHT].value = (edit_mode_ & 1) ? 1.0f : 0.0f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if ((getSystemTimeMs() & 256) && function() >= FUNCTION_FIRST_ALTERNATE_FUNCTION) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					for (size_t i = 0; i < 4; ++i) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						lights[FUNC_1_LIGHT + i].value = 0.0f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					for (size_t i = 0; i < 4; ++i) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						lights[FUNC_1_LIGHT + i].value = ((function() & 3) == i) ? 1.0f : 0.0f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t b[2]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				for (uint8_t i = 0; i < 2; ++i) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					switch (function_[i]) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						case FUNCTION_DRUM_GENERATOR: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						case FUNCTION_FM_DRUM_GENERATOR: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							b[i] = (int16_t) abs(brightness[i]) >> 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							b[i] = b[i] >= 255 ? 255 : b[i]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						case FUNCTION_LFO: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						case FUNCTION_TAP_LFO: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						case FUNCTION_MINI_SEQUENCER: { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							int32_t brightnessVal = int32_t(brightness[i]) * 409 >> 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							brightnessVal += 32768; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							brightnessVal >>= 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							CONSTRAIN(brightnessVal, 0, 255); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							b[i] = brightnessVal; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						default: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							b[i] = brightness[i] >> 7; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (processors[0].function() == peaks::PROCESSOR_FUNCTION_NUMBER_STATION) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					uint8_t pattern = processors[0].number_station().digit() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					                  ^ processors[1].number_station().digit(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					for (size_t i = 0; i < 4; ++i) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						lights[FUNC_1_LIGHT + i].value = (pattern & 1) ? 1.0f : 0.0f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						pattern = pattern >> 1; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					b[0] = processors[0].number_station().gate() ? 255 : 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					b[1] = processors[1].number_station().gate() ? 255 : 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				lights[TRIG_1_LIGHT].value = rescale(static_cast<float>(b[0]), 0.0f, 255.0f, 0.0f, 1.0f); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				lights[TRIG_2_LIGHT].value = rescale(static_cast<float>(b[1]), 0.0f, 255.0f, 0.0f, 1.0f); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			struct PeaksWidget : ModuleWidget { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				PeaksWidget(Peaks* module) : ModuleWidget(module) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					setPanel(SVG::load(assetPlugin(plugin, "res/Peaks.svg"))); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addChild(Widget::create<ScrewSilver>(Vec(15, 0))); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addChild(Widget::create<ScrewSilver>(Vec(15, 365))); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addParam(ParamWidget::create<TL1105>(Vec(8.5, 52), module, Peaks::BUTTON_1_PARAM, 0.0f, 1.0f, 0.0f)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(11.88, 74), module, Peaks::TWIN_MODE_LIGHT)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addParam(ParamWidget::create<TL1105>(Vec(8.5, 89), module, Peaks::BUTTON_2_PARAM, 0.0f, 1.0f, 0.0f)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(11.88, 111), module, Peaks::FUNC_1_LIGHT)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(11.88, 126.75), module, Peaks::FUNC_2_LIGHT)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(11.88, 142.5), module, Peaks::FUNC_3_LIGHT)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(11.88, 158), module, Peaks::FUNC_4_LIGHT)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addParam(ParamWidget::create<Rogan1PSWhite>(Vec(61, 51), module, Peaks::KNOB_1_PARAM, 0.0f, 65535.0f, 16384.0f)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addParam(ParamWidget::create<Rogan1PSWhite>(Vec(61, 115), module, Peaks::KNOB_2_PARAM, 0.0f, 65535.0f, 16384.0f)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addParam(ParamWidget::create<Rogan1PSWhite>(Vec(61, 179), module, Peaks::KNOB_3_PARAM, 0.0f, 65535.0f, 32678.0f)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addParam(ParamWidget::create<Rogan1PSWhite>(Vec(61, 244), module, Peaks::KNOB_4_PARAM, 0.0f, 65535.0f, 32678.0f)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addParam(ParamWidget::create<LEDBezel>(Vec(11, 188), module, Peaks::TRIG_1_PARAM, 0.0f, 1.0f, 0.0f)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addParam(ParamWidget::create<LEDBezel>(Vec(11, 273), module, Peaks::TRIG_2_PARAM, 0.0f, 1.0f, 0.0f)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addChild(ModuleLightWidget::create<LEDBezelLight<GreenLight>>(Vec(11, 188).plus(mm2px(Vec(0.75, 0.75))), module, Peaks::TRIG_1_LIGHT)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addChild(ModuleLightWidget::create<LEDBezelLight<GreenLight>>(Vec(11, 273).plus(mm2px(Vec(0.75, 0.75))), module, Peaks::TRIG_2_LIGHT)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addInput(Port::create<PJ301MPort>(Vec(10, 230), Port::INPUT, module, Peaks::GATE_1_INPUT)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addInput(Port::create<PJ301MPort>(Vec(10, 315), Port::INPUT, module, Peaks::GATE_2_INPUT)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addOutput(Port::create<PJ301MPort>(Vec(53, 315), Port::OUTPUT, module, Peaks::OUT_1_OUTPUT)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addOutput(Port::create<PJ301MPort>(Vec(86, 315), Port::OUTPUT, module, Peaks::OUT_2_OUTPUT)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				Menu* createContextMenu() override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					Menu* menu = ModuleWidget::createContextMenu(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					Peaks* peaks = dynamic_cast<Peaks*>(this->module); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					struct SnapModeItem : MenuItem { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						Peaks* peaks; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						void onAction(EventAction& e) override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							peaks->snap_mode_ = !peaks->snap_mode_; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						void step() override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							rightText = (peaks->snap_mode_) ? "✔" : ""; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							MenuItem::step(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					struct NumberStationItem : MenuItem { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						Peaks* peaks; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						void onAction(EventAction& e) override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							peaks->initNumberStation = true; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						void step() override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							rightText = (peaks->processors[0].function() == peaks::PROCESSOR_FUNCTION_NUMBER_STATION) ? "✔" : ""; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							MenuItem::step(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					menu->addChild(construct<MenuLabel>()); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					menu->addChild(construct<SnapModeItem>(&SnapModeItem::text, "Snap Mode", &SnapModeItem::peaks, peaks)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					menu->addChild(construct<MenuLabel>()); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Secret Modes")); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					menu->addChild(construct<NumberStationItem>(&NumberStationItem::text, "Number Station", &NumberStationItem::peaks, peaks)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return menu; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			Model* modelPeaks = Model::create<Peaks, PeaksWidget>("Audible Instruments", "Peaks", "Percussive Synthesizer", UTILITY_TAG, LFO_TAG, DRUM_TAG); |