diff --git a/CMakeLists.txt b/CMakeLists.txt index 08025af..fda7353 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ target_link_libraries(sint-gauntlet hardware_interp hardware_timer hardware_clocks + hardware_sync hardware_pwm hardware_adc pico_multicore diff --git a/build.sh b/build.sh index 3739ed5..783e039 100755 --- a/build.sh +++ b/build.sh @@ -1,12 +1,13 @@ #!/bin/bash -DISK_ID=usb-RPI_RP2350_6F361FE334DD320F-0 +DISK_ID=usb-RPI_RP2350_DD85828F2371B53B-0:0 +#DISK_ID=usb-RPI_RP2350_6F361FE334DD320F-0:0 TARGET=sint-gauntlet.uf2 cd ./build cmake .. make -j -sudo mount "/dev/disk/by-id/$DISK_ID:0-part1" /mnt +sudo mount "/dev/disk/by-id/$DISK_ID-part1" /mnt sudo cp $TARGET /mnt sync diff --git a/input.c b/input.c index 15ddd84..d4ea154 100644 --- a/input.c +++ b/input.c @@ -21,12 +21,12 @@ void set_mux_addr(uint8_t addr) { void update_inputs(input_t* input) { // Pots - for (uint8_t j = 0; j < 2; j++) { - adc_select_input(j); - for (uint8_t i = 0; i < 8; i++) { - set_mux_addr(i); - sleep_us(50); // Let multiplexers multiplex - float old_val = input->buttons[i + j * 8]; + for (uint8_t i = 0; i < 8; i++) { + set_mux_addr(i); + sleep_us(50); // Let multiplexers multiplex + for (uint8_t j = 0; j < 2; j++) { + adc_select_input(j); + float old_val = input->pots[i + j * 8]; float new_val = (float)adc_read() / 4096.0f; if (fabs(new_val - old_val) >= 0.01f) input->pots[i + j * 8] = new_val; } @@ -43,7 +43,6 @@ void update_inputs(input_t* input) { if (!is_toggle[i]) input->buttons[i] = btn_curr; btn_prev[i] = btn_curr; - sleep_ms(1); // Why don't you bounce on this dihh } } diff --git a/main.c b/main.c index 1265472..12a41a0 100644 --- a/main.c +++ b/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -70,28 +71,52 @@ void core1_init(void) { set_toggle_button(2, true); } +void debug_print(void) { + printf("\x1b[H\x1b[J"); + printf("0: %f, 1: %f, 2: %f, 3: %f, 4: %f, 5: %f, 6: %f, 7: %f\n", + input.pots[0], input.pots[1], input.pots[2], input.pots[3], + input.pots[4], input.pots[5], input.pots[6], input.pots[7]); + printf( + "vco_freq: %f\n" + "vco_volume: %f\n" + "filter_freq: %f\n" + "filter_resonance: %f\n" + "clock_bpm: %f\n" + "env1_attack: %f\n" + "env1_decay: %f\n" + "env2_attack: %f\n" + "env2_decay: %f\n" + "reverb_amount: %f\n", + state.vco_freq, + state.vco_volume, + state.filter_freq, + state.filter_resonance, + state.clock_bpm, + state.env1_attack, + state.env1_decay, + state.env2_attack, + state.env2_decay, + state.reverb_amount + ); +} + noreturn void core1_loop(void) { while (1) { update_inputs(&input); update_state(&state, &input); - printf("%d %f\n", input.buttons[0], input.pots[0]); - printf("%d\n", input.buttons[1]); - printf("%d\n", input.buttons[2]); - printf("%d\n", input.buttons[3]); sleep_ms(1); } } noreturn void core0_loop(void) { - while (1) { - - } + while (1) __wfi(); } int main() { stdio_init_all(); - core0_init(); + core1_init(); + core0_init(); multicore_launch_core1(core1_loop); core0_loop(); diff --git a/state.c b/state.c index 5458fa5..9ba06df 100644 --- a/state.c +++ b/state.c @@ -3,17 +3,24 @@ #include "state.h" +float quantize(float freq) { + if (freq <= 0.0f) return 0.0f; + float midi = 69.0f + 12.0f * log2f(freq / 440.0f); + return 440.0f * powf(2.0f, (roundf(midi) - 69.0f) / 12.0f); +} + + void update_state(state_t* state, input_t* input) { - state->clock_bpm = map_linear(input->pots[0], BPM_MIN, BPM_MAX); - state->vco_freq = map_exponential(input->pots[1], VCO_FREQ_MIN, VCO_FREQ_MAX); - state->vco_volume = map_exponential(input->pots[2], VCO_VOLUME_MIN, VCO_VOLUME_MAX); - state->filter_freq = map_exponential(input->pots[3], FILTER_FREQ_MIN, FILTER_FREQ_MAX); - state->filter_resonance = map_linear(input->pots[4], FILTER_RES_MIN, FILTER_RES_MAX); - state->env1_attack = map_linear(input->pots[5], ENV_ATTACK_MIN, ENV_ATTACK_MAX); - state->env1_release = map_linear(input->pots[6], ENV_RELEASE_MIN, ENV_RELEASE_MAX); - state->env2_attack = map_linear(input->pots[7], ENV_ATTACK_MIN, ENV_ATTACK_MAX); - state->env2_release = map_linear(input->pots[8], ENV_RELEASE_MIN, ENV_RELEASE_MAX); - state->reverb_amount = map_linear(input->pots[9], REVERB_AMOUNT_MIN, REVERB_AMOUNT_MAX); + state->vco_freq = map_exponential(input->pots[0], VCO_FREQ_MIN, VCO_FREQ_MAX); + state->vco_volume = map_exponential(input->pots[1], VCO_VOLUME_MIN, VCO_VOLUME_MAX); + state->filter_freq = map_exponential(input->pots[2], FILTER_FREQ_MIN, FILTER_FREQ_MAX); + state->filter_resonance = map_linear(input->pots[3], FILTER_RES_MIN, FILTER_RES_MAX); + state->clock_bpm = map_linear(input->pots[4], BPM_MIN, BPM_MAX); + state->env1_attack = map_linear(input->pots[5], ENV_ATTACK_MIN, ENV_ATTACK_MAX); + state->env1_decay = map_linear(input->pots[6], ENV_RELEASE_MIN, ENV_RELEASE_MAX); + state->env2_attack = map_linear(input->pots[7], ENV_ATTACK_MIN, ENV_ATTACK_MAX); + state->env2_decay = map_linear(input->pots[8], ENV_RELEASE_MIN, ENV_RELEASE_MAX); + state->reverb_amount = map_linear(input->pots[9], REVERB_AMOUNT_MIN, REVERB_AMOUNT_MAX); static bool pressed = false; if (!pressed && input->buttons[0]) { @@ -21,7 +28,18 @@ void update_state(state_t* state, input_t* input) { pressed = true; } else pressed = false; - state->quant_enabled = input->buttons[1]; - state->amen_enabled = input->buttons[2]; + + if (input->buttons[1]) { + if (state->vco_freq <= 0.0f) state->vco_freq = 0.0f; + else state->vco_freq = quantize(state->vco_freq); + } + + // state->amen_enabled = input->buttons[2]; + state->amen_enabled = true; + + state->beat_samples = SAMPLE_RATE * 60.0f / state->clock_bpm; + state->playback_rate = state->clock_bpm / AMEN_BPM; + state->clock_inc = state->clock_bpm / 60.0f / SAMPLE_RATE; + state->amen_inc_fp = (uint32_t)(state->playback_rate * 65536.0f); } diff --git a/state.h b/state.h index 39558a5..649711a 100644 --- a/state.h +++ b/state.h @@ -9,22 +9,22 @@ typedef struct { float clock_bpm; - float vco_freq; float vco_volume; vco_mode_t vco_mode; - float filter_freq; float filter_resonance; - float env1_attack; - float env1_release; + float env1_decay; float env2_attack; - float env2_release; - + float env2_decay; float reverb_amount; bool quant_enabled; bool amen_enabled; + float beat_samples; + float playback_rate; + float clock_inc; + uint32_t amen_inc_fp; } state_t; static inline float map_linear(float v, float min, float max) { @@ -32,12 +32,17 @@ static inline float map_linear(float v, float min, float max) { } static inline float map_exponential(float v, float min, float max) { - return min + powf(max / min, v); + if (min == 0.0f) min = 1e-6f; + return min * powf(max / min, v); } static inline float map_squared(float v, float min, float max) { return min + (v * v) * (max - min); } +static inline float max_constant(float _0, float val, float _1) { + return val; +} + void update_state(state_t* state, input_t* input); diff --git a/synth.cc b/synth.cc index 36a66ef..281235d 100644 --- a/synth.cc +++ b/synth.cc @@ -1,34 +1,25 @@ -#include #include #include "daisysp.h" #include "state.h" #include "const.h" #include "vco.h" -#include "dsp.h" #include "synth.h" using namespace daisysp; -static state_t* state; +static state_t* state = NULL; Oscillator osc; Svf filter; AdEnv vco_env; AdEnv filter_env; -DelayLine comb0; -DelayLine comb1; -DelayLine comb2; -DelayLine comb3; -DelayLine ap0; -DelayLine ap1; - float clock_phase = 0.0f; bool clock_trig = false; -float amen_phase = 0.0f; +uint32_t amen_phase_fp = 0; float amen_length = 0.0f; static inline auto vco_mode_to_daisy(vco_mode_t mode) { @@ -41,86 +32,45 @@ static inline auto vco_mode_to_daisy(vco_mode_t mode) { } } -static inline float pot_to_freq(float v, float min, float max) { return min * powf(max / min, v); } - -static inline float pot_to_time(float v, float min, float max) { return min * powf(max / min, v); } - -static inline float quantize(float freq, float temp) { - float midi = temp * log2f(freq / 440.0f) + 69.0f; - float note = roundf(midi); - return 440.0f * powf(2.0f, (note - 69.0f) / temp); -} - -static inline float low_pass(float in, float* z, float coeff) { - *z += coeff * (in - *z); - return *z; -} - -float reverb(float in, float amount) { - static float damp0 = 0.0f, damp1 = 0.0f, damp2 = 0.0f, damp3 = 0.0f; - float feedback = 0.84f; - float damp = 0.3f; - - float c0 = comb0.Read(); low_pass(in + c0 * feedback, &damp0, damp); comb0.Write(damp0); - float c1 = comb1.Read(); low_pass(in + c1 * feedback, &damp1, damp); comb1.Write(damp1); - float c2 = comb2.Read(); low_pass(in + c2 * feedback, &damp2, damp); comb2.Write(damp2); - float c3 = comb3.Read(); low_pass(in + c3 * feedback, &damp3, damp); comb3.Write(damp3); - - float wet = (c0 + c1 + c2 + c3) * 0.25f; - - const float g = 0.7f; - float a0r = ap0.Read(); - float a0in = wet + (-g * a0r); - ap0.Write(a0in); - wet = a0r + g * a0in; - - float a1r = ap1.Read(); - float a1in = wet + (-g * a1r); - ap1.Write(a1in); - wet = a1r + g * a1in; - - return in + amount * (wet - in); -} - #include "amenbreak.h" -float amenbreak(float beat_samples, float playback_rate) { - amen_length = beat_samples * 4.0f * AMEN_BARS; - uint32_t idx0 = (uint32_t)amen_phase % amen_sample_count; - uint32_t idx1 = (idx0 + 1) % amen_sample_count; - float frac = amen_phase - floorf(amen_phase); - float s0 = amen_samples[idx0] * (1.0f / 32768.0f); - float s1 = amen_samples[idx1] * (1.0f / 32768.0f); - float amen_out = s0 + frac * (s1 - s0); - - amen_phase += playback_rate; - if (amen_phase >= amen_length) amen_phase -= amen_length; - return amen_out; +float amenbreak() { + amen_phase_fp += state->amen_inc_fp; + + uint32_t idx0 = (amen_phase_fp >> 16) % amen_sample_count; + uint32_t idx1 = (idx0 + 1) % amen_sample_count; + + float frac = (float)(amen_phase_fp & 0xFFFF) * 0.0000152588f; + + const float INV_INT16 = 0.00003051757f; + float s0 = amen_samples[idx0] * INV_INT16; + float s1 = amen_samples[idx1] * INV_INT16; + + return s0 + frac * (s1 - s0); } -void synth_init(state_t* state) { +void synth_init(state_t* _state) { + state = _state; osc.Init(SAMPLE_RATE); filter.Init(SAMPLE_RATE); vco_env.Init(SAMPLE_RATE); - vco_env.SetMin(0); - vco_env.SetMax(FILTER_FREQ_MAX); + vco_env.SetMin(0.0f); + vco_env.SetMax(VCO_VOLUME_MAX); filter_env.Init(SAMPLE_RATE); - filter_env.SetMin(0); + filter_env.SetMin(0.0f); filter_env.SetMax(FILTER_FREQ_MAX); clock_phase = 0.0f; clock_trig = false; - amen_phase = 0.0f; + amen_phase_fp = 0; } float get_sample(void) { - float bpm = BPM_MIN + state->clock_bpm * (BPM_MAX - BPM_MIN); - float beat_samples = SAMPLE_RATE * 60.0f / bpm; - float playback_rate = bpm / AMEN_BPM; + if (state == NULL) return 0.0f; - float clock_inc = bpm / 60.0f / SAMPLE_RATE; - clock_phase += clock_inc; - clock_trig = false; + bool clock_trig = false; + + clock_phase += state->clock_inc; if (clock_phase >= 1.0f) { clock_phase -= 1.0f; clock_trig = true; @@ -131,38 +81,32 @@ float get_sample(void) { filter_env.Trigger(); } - vco_env.SetTime(ADENV_SEG_ATTACK, pot_to_time(state->env1_attack, ENV_ATTACK_MIN, ENV_ATTACK_MAX)); - vco_env.SetTime(ADENV_SEG_DECAY, pot_to_time(state->env1_release, ENV_RELEASE_MIN, ENV_RELEASE_MAX)); - filter_env.SetTime(ADENV_SEG_ATTACK, pot_to_time(state->env2_attack, ENV_ATTACK_MIN, ENV_ATTACK_MAX)); - filter_env.SetTime(ADENV_SEG_DECAY, pot_to_time(state->env2_release, ENV_RELEASE_MIN, ENV_RELEASE_MAX)); - + vco_env.SetTime(ADENV_SEG_ATTACK, state->env1_attack); + vco_env.SetTime(ADENV_SEG_DECAY, state->env1_decay); + filter_env.SetTime(ADENV_SEG_ATTACK, state->env2_attack); + filter_env.SetTime(ADENV_SEG_DECAY, state->env2_decay); + float vco_env_out = vco_env.Process(); float filter_env_out = filter_env.Process(); - float vco_freq = pot_to_freq(state->vco_freq, VCO_FREQ_MIN, VCO_FREQ_MAX); - if (state->quant_enabled) vco_freq = quantize(vco_freq, 12.0f); - - osc.SetFreq(vco_freq); - static int n = VCO_SINE; - osc.SetWaveform(vco_mode_to_daisy((vco_mode_t)(n++ % 4))); + osc.SetFreq(state->vco_freq); + osc.SetWaveform(vco_mode_to_daisy(state->vco_mode)); osc.SetAmp(1.0f); float vco_out = osc.Process(); - float base_cutoff = pot_to_freq(state->filter_freq, FILTER_FREQ_MIN, FILTER_FREQ_MAX); - filter.SetFreq(base_cutoff + filter_env_out); + float cutoff = state->filter_freq + filter_env_out; + if (cutoff > 16000.0f) cutoff = 16000.0f; + + filter.SetFreq(cutoff); filter.SetRes(state->filter_resonance); filter.Process(vco_out); - float filtered = filter.Low(); - float vca_out = filtered * vco_env_out * state->vco_volume; + float vca_out = filter.Low() * vco_env_out * state->vco_volume; + float amen_out = state->amen_enabled ? amenbreak() : 0.0f; + float mix = vca_out + amen_out; - float reverb_out = reverb(vca_out, state->reverb_amount); - - float amen_out = 0.0f; - if (state->amen_enabled) amen_out = amenbreak(beat_samples, playback_rate); - - float mix = reverb_out + amen_out; - mix = fclamp(mix, -1.0f, 1.0f); + if (mix > 1.0f) return 1.0f; + if (mix < -1.0f) return -1.0f; return mix; }