|  |  | @@ -0,0 +1,667 @@ | 
		
	
		
			
			|  |  |  | #include "AudibleInstruments.hpp" | 
		
	
		
			
			|  |  |  | #include "dsp/digital.hpp" | 
		
	
		
			
			|  |  |  | #include "marbles/random/random_generator.h" | 
		
	
		
			
			|  |  |  | #include "marbles/random/random_stream.h" | 
		
	
		
			
			|  |  |  | #include "marbles/random/t_generator.h" | 
		
	
		
			
			|  |  |  | #include "marbles/random/x_y_generator.h" | 
		
	
		
			
			|  |  |  | #include "marbles/note_filter.h" | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static const int BLOCK_SIZE = 5; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static const marbles::Scale preset_scales[6] = { | 
		
	
		
			
			|  |  |  | // C major | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | 1.0f, | 
		
	
		
			
			|  |  |  | 12, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | { 0.0000f, 255 },  // C | 
		
	
		
			
			|  |  |  | { 0.0833f, 16 },   // C# | 
		
	
		
			
			|  |  |  | { 0.1667f, 96 },   // D | 
		
	
		
			
			|  |  |  | { 0.2500f, 24 },   // D# | 
		
	
		
			
			|  |  |  | { 0.3333f, 128 },  // E | 
		
	
		
			
			|  |  |  | { 0.4167f, 64 },   // F | 
		
	
		
			
			|  |  |  | { 0.5000f, 8 },    // F# | 
		
	
		
			
			|  |  |  | { 0.5833f, 192 },  // G | 
		
	
		
			
			|  |  |  | { 0.6667f, 16 },   // G# | 
		
	
		
			
			|  |  |  | { 0.7500f, 96 },   // A | 
		
	
		
			
			|  |  |  | { 0.8333f, 24 },   // A# | 
		
	
		
			
			|  |  |  | { 0.9167f, 128 },  // B | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // C minor | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | 1.0f, | 
		
	
		
			
			|  |  |  | 12, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | { 0.0000f, 255 },  // C | 
		
	
		
			
			|  |  |  | { 0.0833f, 16 },   // C# | 
		
	
		
			
			|  |  |  | { 0.1667f, 96 },   // D | 
		
	
		
			
			|  |  |  | { 0.2500f, 128 },  // Eb | 
		
	
		
			
			|  |  |  | { 0.3333f, 8 },    // E | 
		
	
		
			
			|  |  |  | { 0.4167f, 64 },   // F | 
		
	
		
			
			|  |  |  | { 0.5000f, 4 },    // F# | 
		
	
		
			
			|  |  |  | { 0.5833f, 192 },  // G | 
		
	
		
			
			|  |  |  | { 0.6667f, 16 },   // G# | 
		
	
		
			
			|  |  |  | { 0.7500f, 96 },   // A | 
		
	
		
			
			|  |  |  | { 0.8333f, 128 },  // Bb | 
		
	
		
			
			|  |  |  | { 0.9167f, 16 },   // B | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Pentatonic | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | 1.0f, | 
		
	
		
			
			|  |  |  | 12, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | { 0.0000f, 255 },  // C | 
		
	
		
			
			|  |  |  | { 0.0833f, 4 },    // C# | 
		
	
		
			
			|  |  |  | { 0.1667f, 96 },   // D | 
		
	
		
			
			|  |  |  | { 0.2500f, 4 },    // Eb | 
		
	
		
			
			|  |  |  | { 0.3333f, 4 },    // E | 
		
	
		
			
			|  |  |  | { 0.4167f, 140 },  // F | 
		
	
		
			
			|  |  |  | { 0.5000f, 4 },    // F# | 
		
	
		
			
			|  |  |  | { 0.5833f, 192 },  // G | 
		
	
		
			
			|  |  |  | { 0.6667f, 4 },    // G# | 
		
	
		
			
			|  |  |  | { 0.7500f, 96 },   // A | 
		
	
		
			
			|  |  |  | { 0.8333f, 4 },    // Bb | 
		
	
		
			
			|  |  |  | { 0.9167f, 4 },    // B | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Pelog | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | 1.0f, | 
		
	
		
			
			|  |  |  | 7, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | { 0.0000f, 255 },  // C | 
		
	
		
			
			|  |  |  | { 0.1275f, 128 },  // Db+ | 
		
	
		
			
			|  |  |  | { 0.2625f, 32 },  // Eb- | 
		
	
		
			
			|  |  |  | { 0.4600f, 8 },    // F#- | 
		
	
		
			
			|  |  |  | { 0.5883f, 192 },  // G | 
		
	
		
			
			|  |  |  | { 0.7067f, 64 },  // Ab | 
		
	
		
			
			|  |  |  | { 0.8817f, 16 },    // Bb+ | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Raag Bhairav That | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | 1.0f, | 
		
	
		
			
			|  |  |  | 12, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | { 0.0000f, 255 }, // ** Sa | 
		
	
		
			
			|  |  |  | { 0.0752f, 128 }, // ** Komal Re | 
		
	
		
			
			|  |  |  | { 0.1699f, 4 },   //    Re | 
		
	
		
			
			|  |  |  | { 0.2630f, 4 },   //    Komal Ga | 
		
	
		
			
			|  |  |  | { 0.3219f, 128 }, // ** Ga | 
		
	
		
			
			|  |  |  | { 0.4150f, 64 },  // ** Ma | 
		
	
		
			
			|  |  |  | { 0.4918f, 4 },   //    Tivre Ma | 
		
	
		
			
			|  |  |  | { 0.5850f, 192 }, // ** Pa | 
		
	
		
			
			|  |  |  | { 0.6601f, 64 },  // ** Komal Dha | 
		
	
		
			
			|  |  |  | { 0.7549f, 4 },   //    Dha | 
		
	
		
			
			|  |  |  | { 0.8479f, 4 },   //    Komal Ni | 
		
	
		
			
			|  |  |  | { 0.9069f, 64 },  // ** Ni | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Raag Shri | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | 1.0f, | 
		
	
		
			
			|  |  |  | 12, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | { 0.0000f, 255 }, // ** Sa | 
		
	
		
			
			|  |  |  | { 0.0752f, 4 },   //    Komal Re | 
		
	
		
			
			|  |  |  | { 0.1699f, 128 }, // ** Re | 
		
	
		
			
			|  |  |  | { 0.2630f, 64 },  // ** Komal Ga | 
		
	
		
			
			|  |  |  | { 0.3219f, 4 },   //    Ga | 
		
	
		
			
			|  |  |  | { 0.4150f, 128 }, // ** Ma | 
		
	
		
			
			|  |  |  | { 0.4918f, 4 },   //    Tivre Ma | 
		
	
		
			
			|  |  |  | { 0.5850f, 192 }, // ** Pa | 
		
	
		
			
			|  |  |  | { 0.6601f, 4 },   //    Komal Dha | 
		
	
		
			
			|  |  |  | { 0.7549f, 64 },  // ** Dha | 
		
	
		
			
			|  |  |  | { 0.8479f, 128 }, // ** Komal Ni | 
		
	
		
			
			|  |  |  | { 0.9069f, 4 },   //    Ni | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | struct Marbles : Module { | 
		
	
		
			
			|  |  |  | enum ParamIds { | 
		
	
		
			
			|  |  |  | T_DEJA_VU_PARAM, | 
		
	
		
			
			|  |  |  | X_DEJA_VU_PARAM, | 
		
	
		
			
			|  |  |  | DEJA_VU_PARAM, | 
		
	
		
			
			|  |  |  | T_RATE_PARAM, | 
		
	
		
			
			|  |  |  | X_SPREAD_PARAM, | 
		
	
		
			
			|  |  |  | T_MODE_PARAM, | 
		
	
		
			
			|  |  |  | X_MODE_PARAM, | 
		
	
		
			
			|  |  |  | DEJA_VU_LENGTH_PARAM, | 
		
	
		
			
			|  |  |  | T_BIAS_PARAM, | 
		
	
		
			
			|  |  |  | X_BIAS_PARAM, | 
		
	
		
			
			|  |  |  | T_RANGE_PARAM, | 
		
	
		
			
			|  |  |  | X_RANGE_PARAM, | 
		
	
		
			
			|  |  |  | EXTERNAL_PARAM, | 
		
	
		
			
			|  |  |  | T_JITTER_PARAM, | 
		
	
		
			
			|  |  |  | X_STEPS_PARAM, | 
		
	
		
			
			|  |  |  | NUM_PARAMS | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | enum InputIds { | 
		
	
		
			
			|  |  |  | T_BIAS_INPUT, | 
		
	
		
			
			|  |  |  | X_BIAS_INPUT, | 
		
	
		
			
			|  |  |  | T_CLOCK_INPUT, | 
		
	
		
			
			|  |  |  | T_RATE_INPUT, | 
		
	
		
			
			|  |  |  | T_JITTER_INPUT, | 
		
	
		
			
			|  |  |  | DEJA_VU_INPUT, | 
		
	
		
			
			|  |  |  | X_STEPS_INPUT, | 
		
	
		
			
			|  |  |  | X_SPREAD_INPUT, | 
		
	
		
			
			|  |  |  | X_CLOCK_INPUT, | 
		
	
		
			
			|  |  |  | NUM_INPUTS | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | enum OutputIds { | 
		
	
		
			
			|  |  |  | T1_OUTPUT, | 
		
	
		
			
			|  |  |  | T2_OUTPUT, | 
		
	
		
			
			|  |  |  | T3_OUTPUT, | 
		
	
		
			
			|  |  |  | Y_OUTPUT, | 
		
	
		
			
			|  |  |  | X1_OUTPUT, | 
		
	
		
			
			|  |  |  | X2_OUTPUT, | 
		
	
		
			
			|  |  |  | X3_OUTPUT, | 
		
	
		
			
			|  |  |  | NUM_OUTPUTS | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | enum LightIds { | 
		
	
		
			
			|  |  |  | T_DEJA_VU_LIGHT, | 
		
	
		
			
			|  |  |  | X_DEJA_VU_LIGHT, | 
		
	
		
			
			|  |  |  | ENUMS(T_MODE_LIGHTS, 2), | 
		
	
		
			
			|  |  |  | ENUMS(X_MODE_LIGHTS, 2), | 
		
	
		
			
			|  |  |  | ENUMS(T_RANGE_LIGHTS, 2), | 
		
	
		
			
			|  |  |  | ENUMS(X_RANGE_LIGHTS, 2), | 
		
	
		
			
			|  |  |  | EXTERNAL_LIGHT, | 
		
	
		
			
			|  |  |  | T1_LIGHT, | 
		
	
		
			
			|  |  |  | T2_LIGHT, | 
		
	
		
			
			|  |  |  | T3_LIGHT, | 
		
	
		
			
			|  |  |  | Y_LIGHT, | 
		
	
		
			
			|  |  |  | X1_LIGHT, | 
		
	
		
			
			|  |  |  | X2_LIGHT, | 
		
	
		
			
			|  |  |  | X3_LIGHT, | 
		
	
		
			
			|  |  |  | NUM_LIGHTS | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | marbles::RandomGenerator random_generator; | 
		
	
		
			
			|  |  |  | marbles::RandomStream random_stream; | 
		
	
		
			
			|  |  |  | marbles::TGenerator t_generator; | 
		
	
		
			
			|  |  |  | marbles::XYGenerator xy_generator; | 
		
	
		
			
			|  |  |  | marbles::NoteFilter note_filter; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // State | 
		
	
		
			
			|  |  |  | BooleanTrigger tDejaVuTrigger; | 
		
	
		
			
			|  |  |  | BooleanTrigger xDejaVuTrigger; | 
		
	
		
			
			|  |  |  | BooleanTrigger tModeTrigger; | 
		
	
		
			
			|  |  |  | BooleanTrigger xModeTrigger; | 
		
	
		
			
			|  |  |  | BooleanTrigger tRangeTrigger; | 
		
	
		
			
			|  |  |  | BooleanTrigger xRangeTrigger; | 
		
	
		
			
			|  |  |  | BooleanTrigger externalTrigger; | 
		
	
		
			
			|  |  |  | bool t_deja_vu; | 
		
	
		
			
			|  |  |  | bool x_deja_vu; | 
		
	
		
			
			|  |  |  | int t_mode; | 
		
	
		
			
			|  |  |  | int x_mode; | 
		
	
		
			
			|  |  |  | int t_range; | 
		
	
		
			
			|  |  |  | int x_range; | 
		
	
		
			
			|  |  |  | bool external; | 
		
	
		
			
			|  |  |  | int x_scale; | 
		
	
		
			
			|  |  |  | int y_divider_index; | 
		
	
		
			
			|  |  |  | int x_clock_source_internal; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Buffers | 
		
	
		
			
			|  |  |  | stmlib::GateFlags t_clocks[BLOCK_SIZE] = {}; | 
		
	
		
			
			|  |  |  | stmlib::GateFlags last_t_clock = 0; | 
		
	
		
			
			|  |  |  | stmlib::GateFlags xy_clocks[BLOCK_SIZE] = {}; | 
		
	
		
			
			|  |  |  | stmlib::GateFlags last_xy_clock = 0; | 
		
	
		
			
			|  |  |  | float ramp_master[BLOCK_SIZE] = {}; | 
		
	
		
			
			|  |  |  | float ramp_external[BLOCK_SIZE] = {}; | 
		
	
		
			
			|  |  |  | float ramp_slave[2][BLOCK_SIZE] = {}; | 
		
	
		
			
			|  |  |  | bool gates[BLOCK_SIZE * 2] = {}; | 
		
	
		
			
			|  |  |  | float voltages[BLOCK_SIZE * 4] = {}; | 
		
	
		
			
			|  |  |  | int blockIndex = 0; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | Marbles() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | 
		
	
		
			
			|  |  |  | random_generator.Init(1); | 
		
	
		
			
			|  |  |  | random_stream.Init(&random_generator); | 
		
	
		
			
			|  |  |  | note_filter.Init(); | 
		
	
		
			
			|  |  |  | onSampleRateChange(); | 
		
	
		
			
			|  |  |  | onReset(); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void onReset() override { | 
		
	
		
			
			|  |  |  | t_deja_vu = false; | 
		
	
		
			
			|  |  |  | x_deja_vu = false; | 
		
	
		
			
			|  |  |  | t_mode = 0; | 
		
	
		
			
			|  |  |  | x_mode = 0; | 
		
	
		
			
			|  |  |  | t_range = 1; | 
		
	
		
			
			|  |  |  | x_range = 1; | 
		
	
		
			
			|  |  |  | external = false; | 
		
	
		
			
			|  |  |  | x_scale = 0; | 
		
	
		
			
			|  |  |  | y_divider_index = 8; | 
		
	
		
			
			|  |  |  | x_clock_source_internal = 0; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void onRandomize() override { | 
		
	
		
			
			|  |  |  | t_mode = randomu32() % 3; | 
		
	
		
			
			|  |  |  | x_mode = randomu32() % 3; | 
		
	
		
			
			|  |  |  | t_range = randomu32() % 3; | 
		
	
		
			
			|  |  |  | x_range = randomu32() % 3; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void onSampleRateChange() override { | 
		
	
		
			
			|  |  |  | float sampleRate = engineGetSampleRate(); | 
		
	
		
			
			|  |  |  | t_generator.Init(&random_stream, sampleRate); | 
		
	
		
			
			|  |  |  | xy_generator.Init(&random_stream, sampleRate); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Set scales | 
		
	
		
			
			|  |  |  | for (int i = 0; i < 6; i++) { | 
		
	
		
			
			|  |  |  | xy_generator.LoadScale(i, preset_scales[i]); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_t *toJson() override { | 
		
	
		
			
			|  |  |  | json_t *rootJ = json_object(); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_object_set_new(rootJ, "t_deja_vu", json_boolean(t_deja_vu)); | 
		
	
		
			
			|  |  |  | json_object_set_new(rootJ, "x_deja_vu", json_boolean(x_deja_vu)); | 
		
	
		
			
			|  |  |  | json_object_set_new(rootJ, "t_mode", json_integer(t_mode)); | 
		
	
		
			
			|  |  |  | json_object_set_new(rootJ, "x_mode", json_integer(x_mode)); | 
		
	
		
			
			|  |  |  | json_object_set_new(rootJ, "t_range", json_integer(t_range)); | 
		
	
		
			
			|  |  |  | json_object_set_new(rootJ, "x_range", json_integer(x_range)); | 
		
	
		
			
			|  |  |  | json_object_set_new(rootJ, "external", json_boolean(external)); | 
		
	
		
			
			|  |  |  | json_object_set_new(rootJ, "x_scale", json_integer(x_scale)); | 
		
	
		
			
			|  |  |  | json_object_set_new(rootJ, "y_divider_index", json_integer(y_divider_index)); | 
		
	
		
			
			|  |  |  | json_object_set_new(rootJ, "x_clock_source_internal", json_integer(x_clock_source_internal)); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | return rootJ; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void fromJson(json_t *rootJ) override { | 
		
	
		
			
			|  |  |  | json_t *t_deja_vuJ = json_object_get(rootJ, "t_deja_vu"); | 
		
	
		
			
			|  |  |  | if (t_deja_vuJ) | 
		
	
		
			
			|  |  |  | t_deja_vu = json_boolean_value(t_deja_vuJ); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_t *x_deja_vuJ = json_object_get(rootJ, "x_deja_vu"); | 
		
	
		
			
			|  |  |  | if (x_deja_vuJ) | 
		
	
		
			
			|  |  |  | x_deja_vu = json_boolean_value(x_deja_vuJ); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_t *t_modeJ = json_object_get(rootJ, "t_mode"); | 
		
	
		
			
			|  |  |  | if (t_modeJ) | 
		
	
		
			
			|  |  |  | t_mode = json_integer_value(t_modeJ); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_t *x_modeJ = json_object_get(rootJ, "x_mode"); | 
		
	
		
			
			|  |  |  | if (x_modeJ) | 
		
	
		
			
			|  |  |  | x_mode = json_integer_value(x_modeJ); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_t *t_rangeJ = json_object_get(rootJ, "t_range"); | 
		
	
		
			
			|  |  |  | if (t_rangeJ) | 
		
	
		
			
			|  |  |  | t_range = json_integer_value(t_rangeJ); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_t *x_rangeJ = json_object_get(rootJ, "x_range"); | 
		
	
		
			
			|  |  |  | if (x_rangeJ) | 
		
	
		
			
			|  |  |  | x_range = json_integer_value(x_rangeJ); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_t *externalJ = json_object_get(rootJ, "external"); | 
		
	
		
			
			|  |  |  | if (externalJ) | 
		
	
		
			
			|  |  |  | external = json_boolean_value(externalJ); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_t *x_scaleJ = json_object_get(rootJ, "x_scale"); | 
		
	
		
			
			|  |  |  | if (x_scaleJ) | 
		
	
		
			
			|  |  |  | x_scale = json_integer_value(x_scaleJ); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_t *y_divider_indexJ = json_object_get(rootJ, "y_divider_index"); | 
		
	
		
			
			|  |  |  | if (y_divider_indexJ) | 
		
	
		
			
			|  |  |  | y_divider_index = json_integer_value(y_divider_indexJ); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_t *x_clock_source_internalJ = json_object_get(rootJ, "x_clock_source_internal"); | 
		
	
		
			
			|  |  |  | if (x_clock_source_internalJ) | 
		
	
		
			
			|  |  |  | x_clock_source_internal = json_integer_value(x_clock_source_internalJ); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void step() override { | 
		
	
		
			
			|  |  |  | // Buttons | 
		
	
		
			
			|  |  |  | if (tDejaVuTrigger.process(params[T_DEJA_VU_PARAM].value <= 0.f)) { | 
		
	
		
			
			|  |  |  | t_deja_vu = !t_deja_vu; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | if (xDejaVuTrigger.process(params[X_DEJA_VU_PARAM].value <= 0.f)) { | 
		
	
		
			
			|  |  |  | x_deja_vu = !x_deja_vu; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | if (tModeTrigger.process(params[T_MODE_PARAM].value <= 0.f)) { | 
		
	
		
			
			|  |  |  | t_mode = (t_mode + 1) % 3; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | if (xModeTrigger.process(params[X_MODE_PARAM].value <= 0.f)) { | 
		
	
		
			
			|  |  |  | x_mode = (x_mode + 1) % 3; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | if (tRangeTrigger.process(params[T_RANGE_PARAM].value <= 0.f)) { | 
		
	
		
			
			|  |  |  | t_range = (t_range + 1) % 3; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | if (xRangeTrigger.process(params[X_RANGE_PARAM].value <= 0.f)) { | 
		
	
		
			
			|  |  |  | x_range = (x_range + 1) % 3; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | if (externalTrigger.process(params[EXTERNAL_PARAM].value <= 0.f)) { | 
		
	
		
			
			|  |  |  | external = !external; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Clocks | 
		
	
		
			
			|  |  |  | bool t_gate = (inputs[T_CLOCK_INPUT].value >= 1.7f); | 
		
	
		
			
			|  |  |  | last_t_clock = stmlib::ExtractGateFlags(last_t_clock, t_gate); | 
		
	
		
			
			|  |  |  | t_clocks[blockIndex] = last_t_clock; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | bool x_gate = (inputs[X_CLOCK_INPUT].value >= 1.7f); | 
		
	
		
			
			|  |  |  | last_xy_clock = stmlib::ExtractGateFlags(last_xy_clock, x_gate); | 
		
	
		
			
			|  |  |  | xy_clocks[blockIndex] = last_xy_clock; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Process block | 
		
	
		
			
			|  |  |  | if (++blockIndex >= BLOCK_SIZE) { | 
		
	
		
			
			|  |  |  | blockIndex = 0; | 
		
	
		
			
			|  |  |  | stepBlock(); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Lights and outputs | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | lights[T_DEJA_VU_LIGHT].setBrightness(t_deja_vu); | 
		
	
		
			
			|  |  |  | lights[X_DEJA_VU_LIGHT].setBrightness(x_deja_vu); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | lights[T_MODE_LIGHTS + 0].setBrightness(t_mode == 0 || t_mode == 1); | 
		
	
		
			
			|  |  |  | lights[T_MODE_LIGHTS + 1].setBrightness(t_mode == 1 || t_mode == 2); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | lights[X_MODE_LIGHTS + 0].setBrightness(x_mode == 0 || x_mode == 1); | 
		
	
		
			
			|  |  |  | lights[X_MODE_LIGHTS + 1].setBrightness(x_mode == 1 || x_mode == 2); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | lights[T_RANGE_LIGHTS + 0].setBrightness(t_range == 0 || t_range == 1); | 
		
	
		
			
			|  |  |  | lights[T_RANGE_LIGHTS + 1].setBrightness(t_range == 1 || t_range == 2); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | lights[X_RANGE_LIGHTS + 0].setBrightness(x_range == 0 || x_range == 1); | 
		
	
		
			
			|  |  |  | lights[X_RANGE_LIGHTS + 1].setBrightness(x_range == 1 || x_range == 2); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | lights[EXTERNAL_LIGHT].setBrightness(external); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | outputs[T1_OUTPUT].value = gates[blockIndex*2 + 0] ? 10.f : 0.f; | 
		
	
		
			
			|  |  |  | lights[T1_LIGHT].setBrightnessSmooth(gates[blockIndex*2 + 0]); | 
		
	
		
			
			|  |  |  | outputs[T2_OUTPUT].value = (ramp_master[blockIndex] < 0.5f) ? 10.f : 0.f; | 
		
	
		
			
			|  |  |  | lights[T2_LIGHT].setBrightnessSmooth(ramp_master[blockIndex] < 0.5f); | 
		
	
		
			
			|  |  |  | outputs[T3_OUTPUT].value = gates[blockIndex*2 + 1] ? 10.f : 0.f; | 
		
	
		
			
			|  |  |  | lights[T3_LIGHT].setBrightnessSmooth(gates[blockIndex*2 + 1]); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | outputs[X1_OUTPUT].value = voltages[blockIndex*4 + 0]; | 
		
	
		
			
			|  |  |  | lights[X1_LIGHT].setBrightnessSmooth(voltages[blockIndex*4 + 0]); | 
		
	
		
			
			|  |  |  | outputs[X2_OUTPUT].value = voltages[blockIndex*4 + 1]; | 
		
	
		
			
			|  |  |  | lights[X2_LIGHT].setBrightnessSmooth(voltages[blockIndex*4 + 1]); | 
		
	
		
			
			|  |  |  | outputs[X3_OUTPUT].value = voltages[blockIndex*4 + 2]; | 
		
	
		
			
			|  |  |  | lights[X3_LIGHT].setBrightnessSmooth(voltages[blockIndex*4 + 2]); | 
		
	
		
			
			|  |  |  | outputs[Y_OUTPUT].value = voltages[blockIndex*4 + 3]; | 
		
	
		
			
			|  |  |  | lights[Y_LIGHT].setBrightnessSmooth(voltages[blockIndex*4 + 3]); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void stepBlock() { | 
		
	
		
			
			|  |  |  | // Ramps | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | marbles::Ramps ramps; | 
		
	
		
			
			|  |  |  | ramps.master = ramp_master; | 
		
	
		
			
			|  |  |  | ramps.external = ramp_external; | 
		
	
		
			
			|  |  |  | ramps.slave[0] = ramp_slave[0]; | 
		
	
		
			
			|  |  |  | ramps.slave[1] = ramp_slave[1]; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | float deja_vu = clamp(params[DEJA_VU_PARAM].value + inputs[DEJA_VU_INPUT].value / 5.f, 0.f, 1.f); | 
		
	
		
			
			|  |  |  | static const int loop_length[] = { | 
		
	
		
			
			|  |  |  | 1, 1, 1, 2, 2, | 
		
	
		
			
			|  |  |  | 2, 2, 2, 3, 3, | 
		
	
		
			
			|  |  |  | 3, 3, 4, 4, 4, | 
		
	
		
			
			|  |  |  | 4, 4, 5, 5, 6, | 
		
	
		
			
			|  |  |  | 6, 6, 7, 7, 8, | 
		
	
		
			
			|  |  |  | 8, 8, 10, 10, 12, | 
		
	
		
			
			|  |  |  | 12, 12, 14, 14, 16, | 
		
	
		
			
			|  |  |  | 16 | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | float deja_vu_length_index = params[DEJA_VU_LENGTH_PARAM].value * (LENGTHOF(loop_length) - 1); | 
		
	
		
			
			|  |  |  | int deja_vu_length = loop_length[(int) roundf(deja_vu_length_index)]; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Set up TGenerator | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | bool t_external_clock = inputs[T_CLOCK_INPUT].active; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | t_generator.set_model((marbles::TGeneratorModel) t_mode); | 
		
	
		
			
			|  |  |  | t_generator.set_range((marbles::TGeneratorRange) t_range); | 
		
	
		
			
			|  |  |  | float t_rate = 60.f * (params[T_RATE_PARAM].value + inputs[T_RATE_INPUT].value / 5.f); | 
		
	
		
			
			|  |  |  | t_generator.set_rate(t_rate); | 
		
	
		
			
			|  |  |  | float t_bias = clamp(params[T_BIAS_PARAM].value + inputs[T_BIAS_INPUT].value / 5.f, 0.f, 1.f); | 
		
	
		
			
			|  |  |  | t_generator.set_bias(t_bias); | 
		
	
		
			
			|  |  |  | float t_jitter = clamp(params[T_JITTER_PARAM].value + inputs[T_JITTER_INPUT].value / 5.f, 0.f, 1.f); | 
		
	
		
			
			|  |  |  | t_generator.set_jitter(t_jitter); | 
		
	
		
			
			|  |  |  | t_generator.set_deja_vu(t_deja_vu ? deja_vu : 0.f); | 
		
	
		
			
			|  |  |  | t_generator.set_length(deja_vu_length); | 
		
	
		
			
			|  |  |  | // TODO | 
		
	
		
			
			|  |  |  | t_generator.set_pulse_width_mean(0.f); | 
		
	
		
			
			|  |  |  | t_generator.set_pulse_width_std(0.f); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | t_generator.Process(t_external_clock, t_clocks, ramps, gates, BLOCK_SIZE); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | // Set up XYGenerator | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | marbles::ClockSource x_clock_source = (marbles::ClockSource) x_clock_source_internal; | 
		
	
		
			
			|  |  |  | if (inputs[X_CLOCK_INPUT].active) | 
		
	
		
			
			|  |  |  | x_clock_source = marbles::CLOCK_SOURCE_EXTERNAL; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | marbles::GroupSettings x; | 
		
	
		
			
			|  |  |  | x.control_mode = (marbles::ControlMode) x_mode; | 
		
	
		
			
			|  |  |  | x.voltage_range = (marbles::VoltageRange) x_range; | 
		
	
		
			
			|  |  |  | // TODO Fix the scaling | 
		
	
		
			
			|  |  |  | float note_cv = 0.5f * (params[X_SPREAD_PARAM].value + inputs[X_SPREAD_INPUT].value / 5.f); | 
		
	
		
			
			|  |  |  | float u = note_filter.Process(0.5f * (note_cv + 1.f)); | 
		
	
		
			
			|  |  |  | x.register_mode = external; | 
		
	
		
			
			|  |  |  | x.register_value = u; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | float x_spread = clamp(params[X_SPREAD_PARAM].value + inputs[X_SPREAD_INPUT].value / 5.f, 0.f, 1.f); | 
		
	
		
			
			|  |  |  | x.spread = x_spread; | 
		
	
		
			
			|  |  |  | float x_bias = clamp(params[X_BIAS_PARAM].value + inputs[X_BIAS_INPUT].value / 5.f, 0.f, 1.f); | 
		
	
		
			
			|  |  |  | x.bias = x_bias; | 
		
	
		
			
			|  |  |  | float x_steps = clamp(params[X_STEPS_PARAM].value + inputs[X_STEPS_INPUT].value / 5.f, 0.f, 1.f); | 
		
	
		
			
			|  |  |  | x.steps = x_steps; | 
		
	
		
			
			|  |  |  | x.deja_vu = x_deja_vu ? deja_vu : 0.f; | 
		
	
		
			
			|  |  |  | x.length = deja_vu_length; | 
		
	
		
			
			|  |  |  | x.ratio.p = 1; | 
		
	
		
			
			|  |  |  | x.ratio.q = 1; | 
		
	
		
			
			|  |  |  | x.scale_index = x_scale; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | marbles::GroupSettings y; | 
		
	
		
			
			|  |  |  | y.control_mode = marbles::CONTROL_MODE_IDENTICAL; | 
		
	
		
			
			|  |  |  | // TODO | 
		
	
		
			
			|  |  |  | y.voltage_range = (marbles::VoltageRange) x_range; | 
		
	
		
			
			|  |  |  | y.register_mode = false; | 
		
	
		
			
			|  |  |  | y.register_value = 0.0f; | 
		
	
		
			
			|  |  |  | // TODO | 
		
	
		
			
			|  |  |  | y.spread = x_spread; | 
		
	
		
			
			|  |  |  | y.bias = x_bias; | 
		
	
		
			
			|  |  |  | y.steps = x_steps; | 
		
	
		
			
			|  |  |  | y.deja_vu = 0.0f; | 
		
	
		
			
			|  |  |  | y.length = 1; | 
		
	
		
			
			|  |  |  | static const marbles::Ratio y_divider_ratios[] = { | 
		
	
		
			
			|  |  |  | { 1, 64 }, | 
		
	
		
			
			|  |  |  | { 1, 48 }, | 
		
	
		
			
			|  |  |  | { 1, 32 }, | 
		
	
		
			
			|  |  |  | { 1, 24 }, | 
		
	
		
			
			|  |  |  | { 1, 16 }, | 
		
	
		
			
			|  |  |  | { 1, 12 }, | 
		
	
		
			
			|  |  |  | { 1, 8 }, | 
		
	
		
			
			|  |  |  | { 1, 6 }, | 
		
	
		
			
			|  |  |  | { 1, 4 }, | 
		
	
		
			
			|  |  |  | { 1, 3 }, | 
		
	
		
			
			|  |  |  | { 1, 2 }, | 
		
	
		
			
			|  |  |  | { 1, 1 }, | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | y.ratio = y_divider_ratios[y_divider_index]; | 
		
	
		
			
			|  |  |  | y.scale_index = x_scale; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | xy_generator.Process(x_clock_source, x, y, xy_clocks, ramps, voltages, BLOCK_SIZE); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | template <typename BASE> | 
		
	
		
			
			|  |  |  | struct CKD6Light : BASE { | 
		
	
		
			
			|  |  |  | CKD6Light() { | 
		
	
		
			
			|  |  |  | this->box.size = Vec(22, 22); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | struct MarblesWidget : ModuleWidget { | 
		
	
		
			
			|  |  |  | MarblesWidget(Marbles *module) : ModuleWidget(module) { | 
		
	
		
			
			|  |  |  | setPanel(SVG::load(assetPlugin(plugin, "res/Marbles.svg"))); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | 
		
	
		
			
			|  |  |  | addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | 
		
	
		
			
			|  |  |  | addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | 
		
	
		
			
			|  |  |  | addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<CKD6>(mm2px(Vec(16.545, 17.794)), module, Marbles::T_DEJA_VU_PARAM, 0.0, 1.0, 0.0)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<CKD6>(mm2px(Vec(74.845, 17.794)), module, Marbles::X_DEJA_VU_PARAM, 0.0, 1.0, 0.0)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<Rogan2PSWhite>(mm2px(Vec(45.695, 22.244)), module, Marbles::DEJA_VU_PARAM, 0.0, 1.0, 0.5)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<Rogan3PSWhite>(mm2px(Vec(23.467, 35.264)), module, Marbles::T_RATE_PARAM, -1.0, 1.0, 0.0)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<Rogan3PSWhite>(mm2px(Vec(67.945, 35.243)), module, Marbles::X_SPREAD_PARAM, 0.0, 1.0, 0.5)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<TL1105>(mm2px(Vec(6.945, 38.794)), module, Marbles::T_MODE_PARAM, 0.0, 1.0, 0.0)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<TL1105>(mm2px(Vec(84.445, 38.793)), module, Marbles::X_MODE_PARAM, 0.0, 1.0, 0.0)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<Rogan2PSWhite>(mm2px(Vec(45.695, 51.144)), module, Marbles::DEJA_VU_LENGTH_PARAM, 0.0, 1.0, 0.0)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<Rogan2PSWhite>(mm2px(Vec(9.545, 58.394)), module, Marbles::T_BIAS_PARAM, 0.0, 1.0, 0.5)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<Rogan2PSWhite>(mm2px(Vec(81.844, 58.394)), module, Marbles::X_BIAS_PARAM, 0.0, 1.0, 0.5)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<TL1105>(mm2px(Vec(26.644, 59.694)), module, Marbles::T_RANGE_PARAM, 0.0, 1.0, 0.0)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<TL1105>(mm2px(Vec(64.744, 59.694)), module, Marbles::X_RANGE_PARAM, 0.0, 1.0, 0.0)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<TL1105>(mm2px(Vec(45.694, 67.294)), module, Marbles::EXTERNAL_PARAM, 0.0, 1.0, 0.0)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<Rogan2PSWhite>(mm2px(Vec(31.544, 73.694)), module, Marbles::T_JITTER_PARAM, 0.0, 1.0, 0.0)); | 
		
	
		
			
			|  |  |  | addParam(createParamCentered<Rogan2PSWhite>(mm2px(Vec(59.845, 73.694)), module, Marbles::X_STEPS_PARAM, 0.0, 1.0, 0.5)); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(9.545, 81.944)), module, Marbles::T_BIAS_INPUT)); | 
		
	
		
			
			|  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(81.844, 81.944)), module, Marbles::X_BIAS_INPUT)); | 
		
	
		
			
			|  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(9.545, 96.544)), module, Marbles::T_CLOCK_INPUT)); | 
		
	
		
			
			|  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(21.595, 96.544)), module, Marbles::T_RATE_INPUT)); | 
		
	
		
			
			|  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(33.644, 96.544)), module, Marbles::T_JITTER_INPUT)); | 
		
	
		
			
			|  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(45.695, 96.544)), module, Marbles::DEJA_VU_INPUT)); | 
		
	
		
			
			|  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(57.745, 96.544)), module, Marbles::X_STEPS_INPUT)); | 
		
	
		
			
			|  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(69.795, 96.544)), module, Marbles::X_SPREAD_INPUT)); | 
		
	
		
			
			|  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(81.844, 96.544)), module, Marbles::X_CLOCK_INPUT)); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(9.545, 111.144)), module, Marbles::T1_OUTPUT)); | 
		
	
		
			
			|  |  |  | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(21.595, 111.144)), module, Marbles::T2_OUTPUT)); | 
		
	
		
			
			|  |  |  | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(33.644, 111.144)), module, Marbles::T3_OUTPUT)); | 
		
	
		
			
			|  |  |  | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(45.695, 111.144)), module, Marbles::Y_OUTPUT)); | 
		
	
		
			
			|  |  |  | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(57.745, 111.144)), module, Marbles::X1_OUTPUT)); | 
		
	
		
			
			|  |  |  | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(69.795, 111.144)), module, Marbles::X2_OUTPUT)); | 
		
	
		
			
			|  |  |  | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(81.844, 111.144)), module, Marbles::X3_OUTPUT)); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<CKD6Light<GreenLight>>(mm2px(Vec(16.545, 17.794)), module, Marbles::T_DEJA_VU_LIGHT)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<CKD6Light<GreenLight>>(mm2px(Vec(74.845, 17.794)), module, Marbles::X_DEJA_VU_LIGHT)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(6.944, 29.894)), module, Marbles::T_MODE_LIGHTS)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(84.444, 29.894)), module, Marbles::X_MODE_LIGHTS)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(26.644, 53.994)), module, Marbles::T_RANGE_LIGHTS)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(64.744, 53.994)), module, Marbles::X_RANGE_LIGHTS)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(45.695, 76.194)), module, Marbles::EXTERNAL_LIGHT)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(6.044, 104.794)), module, Marbles::T1_LIGHT)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(18.094, 104.794)), module, Marbles::T2_LIGHT)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(30.145, 104.794)), module, Marbles::T3_LIGHT)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(42.194, 104.794)), module, Marbles::Y_LIGHT)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(54.244, 104.794)), module, Marbles::X1_LIGHT)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(66.294, 104.794)), module, Marbles::X2_LIGHT)); | 
		
	
		
			
			|  |  |  | addChild(createLightCentered<MediumLight<GreenLight>>(mm2px(Vec(78.344, 104.794)), module, Marbles::X3_LIGHT)); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void appendContextMenu(Menu *menu) override { | 
		
	
		
			
			|  |  |  | Marbles *module = dynamic_cast<Marbles*>(this->module); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | struct ScaleItem : MenuItem { | 
		
	
		
			
			|  |  |  | Marbles *module; | 
		
	
		
			
			|  |  |  | int scale; | 
		
	
		
			
			|  |  |  | void onAction(EventAction &e) override { | 
		
	
		
			
			|  |  |  | module->x_scale = scale; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | menu->addChild(MenuEntry::create()); | 
		
	
		
			
			|  |  |  | menu->addChild(MenuLabel::create("Scales")); | 
		
	
		
			
			|  |  |  | const std::string scaleLabels[] = { | 
		
	
		
			
			|  |  |  | "Major", | 
		
	
		
			
			|  |  |  | "Minor", | 
		
	
		
			
			|  |  |  | "Pentatonic", | 
		
	
		
			
			|  |  |  | "Pelog", | 
		
	
		
			
			|  |  |  | "Raag Bhairav That", | 
		
	
		
			
			|  |  |  | "Raag Shri", | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | for (int i = 0; i < (int) LENGTHOF(scaleLabels); i++) { | 
		
	
		
			
			|  |  |  | ScaleItem *item = MenuItem::create<ScaleItem>(scaleLabels[i], CHECKMARK(module->x_scale == i)); | 
		
	
		
			
			|  |  |  | item->module = module; | 
		
	
		
			
			|  |  |  | item->scale = i; | 
		
	
		
			
			|  |  |  | menu->addChild(item); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | struct XClockSourceInternal : MenuItem { | 
		
	
		
			
			|  |  |  | Marbles *module; | 
		
	
		
			
			|  |  |  | int source; | 
		
	
		
			
			|  |  |  | void onAction(EventAction &e) override { | 
		
	
		
			
			|  |  |  | module->x_clock_source_internal = source; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | menu->addChild(MenuEntry::create()); | 
		
	
		
			
			|  |  |  | menu->addChild(MenuLabel::create("Internal X clock source")); | 
		
	
		
			
			|  |  |  | const std::string sourceLabels[] = { | 
		
	
		
			
			|  |  |  | "T₁ → X₁, T₂ → X₂, T₃ → X₃", | 
		
	
		
			
			|  |  |  | "T₁ → X₁, X₂, X₃", | 
		
	
		
			
			|  |  |  | "T₂ → X₁, X₂, X₃", | 
		
	
		
			
			|  |  |  | "T₃ → X₁, X₂, X₃", | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | for (int i = 0; i < (int) LENGTHOF(sourceLabels); i++) { | 
		
	
		
			
			|  |  |  | XClockSourceInternal *item = MenuItem::create<XClockSourceInternal>(sourceLabels[i], CHECKMARK(module->x_clock_source_internal == i)); | 
		
	
		
			
			|  |  |  | item->module = module; | 
		
	
		
			
			|  |  |  | item->source = i; | 
		
	
		
			
			|  |  |  | menu->addChild(item); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | struct YDividerIndexItem : MenuItem { | 
		
	
		
			
			|  |  |  | Marbles *module; | 
		
	
		
			
			|  |  |  | int index; | 
		
	
		
			
			|  |  |  | void onAction(EventAction &e) override { | 
		
	
		
			
			|  |  |  | module->y_divider_index = index; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | struct YDividerItem : MenuItem { | 
		
	
		
			
			|  |  |  | Marbles *module; | 
		
	
		
			
			|  |  |  | Menu *createChildMenu() override { | 
		
	
		
			
			|  |  |  | Menu *menu = new Menu(); | 
		
	
		
			
			|  |  |  | const std::string yDividerRatioLabels[] = { | 
		
	
		
			
			|  |  |  | "1/64", | 
		
	
		
			
			|  |  |  | "1/48", | 
		
	
		
			
			|  |  |  | "1/32", | 
		
	
		
			
			|  |  |  | "1/24", | 
		
	
		
			
			|  |  |  | "1/16", | 
		
	
		
			
			|  |  |  | "1/12", | 
		
	
		
			
			|  |  |  | "1/8", | 
		
	
		
			
			|  |  |  | "1/6", | 
		
	
		
			
			|  |  |  | "1/4", | 
		
	
		
			
			|  |  |  | "1/3", | 
		
	
		
			
			|  |  |  | "1/2", | 
		
	
		
			
			|  |  |  | "1", | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | for (int i = 0; i < (int) LENGTHOF(yDividerRatioLabels); i++) { | 
		
	
		
			
			|  |  |  | YDividerIndexItem *item = MenuItem::create<YDividerIndexItem>(yDividerRatioLabels[i], CHECKMARK(module->y_divider_index == i)); | 
		
	
		
			
			|  |  |  | item->module = module; | 
		
	
		
			
			|  |  |  | item->index = i; | 
		
	
		
			
			|  |  |  | menu->addChild(item); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | return menu; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | menu->addChild(MenuEntry::create()); | 
		
	
		
			
			|  |  |  | YDividerItem *yDividerItem = MenuItem::create<YDividerItem>("Y divider ratio"); | 
		
	
		
			
			|  |  |  | yDividerItem->module = module; | 
		
	
		
			
			|  |  |  | menu->addChild(yDividerItem); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | Model *modelMarbles = createModel<Marbles, MarblesWidget>("Audible Instruments", "Marbles", "Random Sampler"); | 
		
	
		
			
			|  |  |  | 
 |