Compare commits

...

6 Commits

Author SHA1 Message Date
PoliEcho e9564f42eb Turn on onboard led on power on
(causes the synth to work for some reason)
2026-06-05 16:12:42 +02:00
Tom-on bd2ac926b0 Maybe works? 2026-04-30 09:32:14 +02:00
Tom-on a8c231df50 Added input processing to state 2026-04-27 14:16:28 +02:00
Tom-on 787bf50957 Added state processing stage 2026-04-27 12:11:41 +02:00
Tom-on 1fd0a56064 Combined mux.c and buttons.c to input.c 2026-04-26 17:04:37 +02:00
Tom-on 8a55e8e60c Added build script 2026-04-26 16:26:35 +02:00
15 changed files with 299 additions and 247 deletions
+4 -1
View File
@@ -27,7 +27,7 @@ pico_sdk_init()
file(GLOB_RECURSE DAISYSP_SOURCES "${CMAKE_CURRENT_LIST_DIR}/daisysp/**/*.cpp")
add_executable(sint-gauntlet ${DAISYSP_SOURCES} main.c buttons.c mux.c pwm.c synth.cc)
add_executable(sint-gauntlet ${DAISYSP_SOURCES} main.c input.c state.c pwm.c synth.cc)
pico_set_program_name(sint-gauntlet "sint-gauntlet")
pico_set_program_version(sint-gauntlet "0.1")
@@ -58,10 +58,13 @@ target_include_directories(sint-gauntlet PRIVATE
# Add any user requested libraries
target_link_libraries(sint-gauntlet
pico_stdlib
pico_cyw43_arch_none
hardware_pio
hardware_interp
hardware_timer
hardware_clocks
hardware_sync
hardware_pwm
hardware_adc
pico_multicore
Executable
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
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-part1" /mnt
sudo cp $TARGET /mnt
sync
-24
View File
@@ -1,24 +0,0 @@
#include "const.h"
#include "state.h"
#include "vco.h"
#include <pico/types.h>
#include "hardware/gpio.h"
void check_button_change(uint pin, bool* btn_state) {
static bool btn_prev[32] = { false };
bool btn_now = gpio_get(pin);
if (btn_now && !btn_prev[pin]) *btn_state = !*btn_state;
btn_prev[pin] = btn_now;
}
void update_buttons() {
check_button_change(QUANT_BUTTON, &state.quant_enabled);
check_button_change(AMEN_BUTTON, &state.amen_enabled);
bool vco_change;
check_button_change(VCO_BUTTON, &vco_change);
if (vco_change) {
if (state.vco_mode == VCO_SAW) state.vco_mode = VCO_SINE;
otherwise state.vco_mode++;
}
}
-5
View File
@@ -1,5 +0,0 @@
#pragma once
#include <pico/types.h>
void update_buttons();
+5 -6
View File
@@ -1,15 +1,14 @@
#pragma once
#define MUX0 26
#define MUX1 27
#define MUX_S0 2
#define MUX_S1 3
#define MUX_S2 4
#define MUX0 26
#define MUX1 27
#define VCO_BUTTON 6
#define QUANT_BUTTON 7
#define AMEN_BUTTON 8
#define BUTTON_BASE 6
#define BUTTON_COUNT 4
#define AUDIO_OUT 0
#define STATUS_LED 1
+48
View File
@@ -0,0 +1,48 @@
#include <hardware/adc.h>
#include <hardware/gpio.h>
#include <pico/stdlib.h>
#include <pico/time.h>
#include <sys/types.h>
#include <stdint.h>
#include <math.h>
#include "const.h"
#include "input.h"
bool is_toggle[BUTTON_COUNT] = { false };
bool btn_prev[BUTTON_COUNT] = { false };
void set_mux_addr(uint8_t addr) {
gpio_put(MUX_S0, addr & 1);
gpio_put(MUX_S1, (addr >> 1) & 1);
gpio_put(MUX_S2, (addr >> 2) & 1);
}
void update_inputs(input_t* input) {
// Pots
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;
}
}
// Buttons
for (uint8_t i = 0; i < BUTTON_COUNT; i++) {
bool btn_curr = gpio_get(BUTTON_BASE + i);
if (btn_curr == true && btn_prev[i] == false) {
if (is_toggle[i]) input->buttons[i] = !input->buttons[i];
}
if (!is_toggle[i]) input->buttons[i] = btn_curr;
btn_prev[i] = btn_curr;
}
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <sys/types.h>
typedef struct {
float pots[16];
bool buttons[4];
} input_t;
extern bool is_toggle[4];
static inline void set_toggle_button(uint8_t index, bool toggle) { is_toggle[index] = toggle; }
void update_inputs(input_t* input);
+90 -42
View File
@@ -1,8 +1,13 @@
#include "pico/cyw43_arch.h"
#include <arm_acle.h>
#include <hardware/irq.h>
#include <hardware/sync.h>
#include <pico/stdio.h>
#include <pico/time.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdio.h>
#include <stdnoreturn.h>
#include <sys/types.h>
#include "hardware/gpio.h"
#include "hardware/pwm.h"
@@ -10,44 +15,17 @@
#include "pico/multicore.h"
#include "pico/stdlib.h"
#include "buttons.h"
#include "synth.h"
#include "const.h"
#include "macro.h"
#include "input.h"
#include "state.h"
#include "synth.h"
#include "pwm.h"
#include "mux.h"
input_t input;
state_t state;
void init_all() {
stdio_init_all();
sleep_ms(2000);
puts("Starting PIN initialization");
const uint8_t out_gpio[] = {MUX_S0,MUX_S1,MUX_S2, STATUS_LED};
const uint8_t in_gpio[] = {VCO_BUTTON,QUANT_BUTTON,AMEN_BUTTON};
const uint8_t adc_gpio[] = {MUX0,MUX1};
for (uint8_t i=0; i < ARRAY_LENGTH(out_gpio); i++) {
gpio_init(out_gpio[i]);
gpio_set_dir(out_gpio[i], true);
}
for (uint8_t i=0; i < ARRAY_LENGTH(in_gpio); i++) {
gpio_init(in_gpio[i]);
gpio_set_dir(in_gpio[i], false);
}
adc_init();
for (uint8_t i=0; i < ARRAY_LENGTH(adc_gpio); i++) {
adc_gpio_init(adc_gpio[i]);
adc_select_input(i);
}
// Synth
synth_init();
void core0_init(void) {
// PWM bullshit
gpio_set_function(AUDIO_OUT, GPIO_FUNC_PWM);
@@ -61,22 +39,92 @@ void init_all() {
irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_isr);
irq_set_enabled(PWM_IRQ_WRAP, true);
puts("PWM Initialized.");
// Synth
synth_init(&state);
puts("Synth Initialized.");
}
__attribute__((noreturn))
void core1_main(void) {
void core1_init(void) {
// GPIO
const uint8_t out_gpio[] = { MUX_S0, MUX_S1, MUX_S2, STATUS_LED };
const uint8_t adc_gpio[] = {MUX0,MUX1};
for (uint8_t i = 0; i < ARRAY_LENGTH(out_gpio); i++) {
gpio_init(out_gpio[i]);
gpio_set_dir(out_gpio[i], true);
}
for (uint8_t i = 0; i < BUTTON_COUNT; i++) {
gpio_init(BUTTON_BASE + i);
gpio_set_dir(BUTTON_BASE + 1, false);
}
adc_init();
adc_gpio_init(MUX0);
adc_gpio_init(MUX1);
puts("GPIO Initialized.");
// Input
set_toggle_button(0, true);
set_toggle_button(1, false);
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_buttons();
update_inputs();
printf("Sample: %f\n", state.dbg_sample);
update_inputs(&input);
update_state(&state, &input);
sleep_ms(1);
}
}
__attribute__((noreturn))
int main() {
init_all();
multicore_launch_core1(core1_main);
while (1);
noreturn void core0_loop(void) {
while (1) __wfi();
}
int main() {
stdio_init_all();
if (cyw43_arch_init())
return -1;
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); // sign of life
core1_init();
core0_init();
multicore_launch_core1(core1_loop);
core0_loop();
}
-25
View File
@@ -1,25 +0,0 @@
#include "const.h"
#include "state.h"
#include <hardware/adc.h>
#include <pico/time.h>
#include <stdint.h>
#include <hardware/gpio.h>
void set_mux_addr(uint8_t addr) {
gpio_put(MUX_S0, addr & 1);
gpio_put(MUX_S1, (addr >> 1) & 1);
gpio_put(MUX_S2, (addr >> 2) & 1);
}
void update_inputs() {
for(uint8_t i = 0; i < 2; i++) {
adc_select_input(i);
for(uint8_t j = 0; j < 8;j++) {
set_mux_addr(j);
sleep_ms(1); // let multiplexor multiplex
state.array[i*8 + j]= adc_read()/4096.0f;
}
}
}
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include <stdint.h>
void set_mux_addr(uint8_t addr);
void update_inputs();
-2
View File
@@ -2,7 +2,6 @@
#include <pico/types.h>
#include "const.h"
#include "state.h"
#include "synth.h"
#include "pwm.h"
@@ -15,7 +14,6 @@ void pwm_isr(void) {
pwm_clear_irq(slice);
float sample = get_sample();
state.dbg_sample = sample;
uint16_t level = (uint16_t)((sample + 1.0f) * 0.5f * 3400.0f);
pwm_set_chan_level(slice, chan, level);
+45
View File
@@ -0,0 +1,45 @@
#include "const.h"
#include "input.h"
#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->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]) {
state->vco_mode = (vco_mode_t)((state->vco_mode + 1) % 4);
pressed = true;
} else pressed = false;
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);
}
+36 -19
View File
@@ -1,31 +1,48 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
#include <math.h>
#include "input.h"
#include "vco.h"
typedef struct {
union {
struct {
float clock_bpm;
float vco_freq;
float vco_volume;
float filter_freq;
float filter_resonance;
float env1_attack;
float env1_release;
float env2_attack;
float env2_release;
float reverb_amount;
};
float array[16];
};
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_decay;
float env2_attack;
float env2_decay;
float reverb_amount;
bool quant_enabled;
bool amen_enabled;
float dbg_sample;
float beat_samples;
float playback_rate;
float clock_inc;
uint32_t amen_inc_fp;
} state_t;
extern state_t state;
static inline float map_linear(float v, float min, float max) {
return min + v * (max - min);
}
static inline float map_exponential(float v, float min, float max) {
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);
+39 -114
View File
@@ -1,32 +1,25 @@
#include <math.h>
#include <stdint.h>
#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 = NULL;
Oscillator osc;
Svf filter;
AdEnv vco_env;
AdEnv filter_env;
DelayLine<float, COMB0_SIZE> comb0;
DelayLine<float, COMB1_SIZE> comb1;
DelayLine<float, COMB2_SIZE> comb2;
DelayLine<float, COMB3_SIZE> comb3;
DelayLine<float, AP0_SIZE> ap0;
DelayLine<float, AP1_SIZE> 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) {
@@ -39,104 +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);
float amenbreak() {
amen_phase_fp += state->amen_inc_fp;
amen_phase += playback_rate;
if (amen_phase >= amen_length) amen_phase -= amen_length;
return amen_out;
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(void) {
void synth_init(state_t* _state) {
state = _state;
osc.Init(SAMPLE_RATE);
osc.SetWaveform(vco_mode_to_daisy(VCO_SAW));
osc.SetFreq(440.0f);
osc.SetAmp(2.0f);
filter.Init(SAMPLE_RATE);
filter.SetFreq(2000.0f);
filter.SetRes(0.0f);
vco_env.Init(SAMPLE_RATE);
vco_env.SetTime(ADENV_SEG_ATTACK, 0.01f);
vco_env.SetTime(ADENV_SEG_DECAY, 0.8f);
vco_env.SetMin(0.0f);
vco_env.SetMax(1.0f);
vco_env.SetMax(VCO_VOLUME_MAX);
filter_env.Init(SAMPLE_RATE);
filter_env.SetTime(ADENV_SEG_ATTACK, 0.01f);
filter_env.SetTime(ADENV_SEG_DECAY, 0.5f);
filter_env.SetMin(0.0f);
filter_env.SetMax(1.0f);
state.clock_bpm = 0.2f;
filter_env.SetMax(FILTER_FREQ_MAX);
clock_phase = 0.0f;
clock_trig = false;
amen_phase = 0.0f;
state.amen_enabled = true;
state.reverb_amount = 1.0;
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;
@@ -147,41 +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);
osc.SetWaveform(vco_mode_to_daisy(state.vco_mode));
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);
float mod_cutoff = base_cutoff + filter_env_out * (FILTER_FREQ_MAX - FILTER_FREQ_MIN);
mod_cutoff = fclamp(mod_cutoff, FILTER_FREQ_MIN, FILTER_FREQ_MAX);
float cutoff = state->filter_freq + filter_env_out;
if (cutoff > 16000.0f) cutoff = 16000.0f;
filter.SetFreq(mod_cutoff);
filter.SetRes(state.filter_resonance);
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 = vca_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;
}
+3 -1
View File
@@ -1,5 +1,7 @@
#pragma once
#include "state.h"
#define COMB0_SIZE 1103
#define COMB1_SIZE 1361
#define COMB2_SIZE 1499
@@ -11,7 +13,7 @@
extern "C" {
#endif
void synth_init(void);
void synth_init(state_t* state);
float get_sample(void);
#ifdef __cplusplus