Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9564f42eb | |||
| bd2ac926b0 | |||
| a8c231df50 | |||
| 787bf50957 | |||
| 1fd0a56064 | |||
| 8a55e8e60c | |||
| 774980200c | |||
| 23a0cbee6f | |||
| 61e4106443 | |||
| 17a3a79c44 | |||
| 09b1a6707a | |||
| 0daca6f46b | |||
| 66ea7d41f6 | |||
| d5f8876ada | |||
| 57c3d2bc80 | |||
| cbed3f24a1 | |||
| 5c6a687471 | |||
| b857418795 | |||
| d29f2e8c39 | |||
| c515c1e07e | |||
| daa4fa9564 | |||
| de4f958b1e | |||
| 895b0d635c | |||
| 63ae3f694b | |||
| 83507f9945 | |||
| 5f8ace8de6 | |||
| 4e70e1458e | |||
| 630faa5a90 | |||
| 500c93a6fd | |||
| c71a162d86 | |||
| afaa87daec | |||
| e375586804 | |||
| d5933cd060 | |||
| 061ca37ad7 | |||
| 65516b2bff | |||
| a5d9b99098 | |||
| 3a24cb235b | |||
| 0c79b141fd | |||
| f4aa96104f | |||
| 946417b020 | |||
| baf6cdb4cc | |||
| 9d902924b5 | |||
| db4a1312a9 | |||
| 3ce93a08bf | |||
| 9d87c0748b | |||
| 63b977f937 |
+19
-4
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
|
||||||
# Initialise pico_sdk from installed location
|
# Initialise pico_sdk from installed location
|
||||||
# (note this can come from environment, CMake cache etc)
|
# (note this can come from environment, CMake cache etc)
|
||||||
@@ -25,7 +25,9 @@ pico_sdk_init()
|
|||||||
|
|
||||||
# Add executable. Default name is the project name, version 0.1
|
# Add executable. Default name is the project name, version 0.1
|
||||||
|
|
||||||
add_executable(sint-gauntlet main.cc vco.c mux.c)
|
file(GLOB_RECURSE DAISYSP_SOURCES "${CMAKE_CURRENT_LIST_DIR}/daisysp/**/*.cpp")
|
||||||
|
|
||||||
|
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_name(sint-gauntlet "sint-gauntlet")
|
||||||
pico_set_program_version(sint-gauntlet "0.1")
|
pico_set_program_version(sint-gauntlet "0.1")
|
||||||
@@ -40,19 +42,32 @@ target_link_libraries(sint-gauntlet
|
|||||||
# Add the standard include files to the build
|
# Add the standard include files to the build
|
||||||
target_include_directories(sint-gauntlet PRIVATE
|
target_include_directories(sint-gauntlet PRIVATE
|
||||||
${CMAKE_CURRENT_LIST_DIR}/daisysp
|
${CMAKE_CURRENT_LIST_DIR}/daisysp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/daisysp/Control
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/daisysp/Drums
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/daisysp/Dynamics
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/daisysp/Effects
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/daisysp/Filters
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/daisysp/Noise
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/daisysp/PhysicalModeling
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/daisysp/Sampling
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/daisysp/Synthesis
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/daisysp/Utility
|
||||||
${CMAKE_CURRENT_LIST_DIR}
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
|
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add any user requested libraries
|
# Add any user requested libraries
|
||||||
target_link_libraries(sint-gauntlet
|
target_link_libraries(sint-gauntlet
|
||||||
|
pico_stdlib
|
||||||
|
pico_cyw43_arch_none
|
||||||
hardware_pio
|
hardware_pio
|
||||||
hardware_interp
|
hardware_interp
|
||||||
hardware_timer
|
hardware_timer
|
||||||
hardware_clocks
|
hardware_clocks
|
||||||
|
hardware_sync
|
||||||
hardware_pwm
|
hardware_pwm
|
||||||
hardware_adc
|
hardware_adc
|
||||||
|
pico_multicore
|
||||||
)
|
)
|
||||||
|
|
||||||
pico_add_extra_outputs(sint-gauntlet)
|
pico_add_extra_outputs(sint-gauntlet)
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -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
|
||||||
|
|
||||||
@@ -1,18 +1,43 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define MUX0 26
|
||||||
|
#define MUX1 27
|
||||||
|
|
||||||
#define MUX_S0 2
|
#define MUX_S0 2
|
||||||
#define MUX_S1 3
|
#define MUX_S1 3
|
||||||
#define MUX_S2 4
|
#define MUX_S2 4
|
||||||
|
|
||||||
#define MUX0 26
|
#define BUTTON_BASE 6
|
||||||
#define MUX1 27
|
#define BUTTON_COUNT 4
|
||||||
|
|
||||||
#define VCO_BUTTON 6
|
#define AUDIO_OUT 0
|
||||||
#define QUANT_BUTTON 7
|
|
||||||
#define AMEN_BUTTON 8
|
|
||||||
|
|
||||||
#define AUDIO_OUTPUT 0
|
|
||||||
#define STATUS_LED 1
|
#define STATUS_LED 1
|
||||||
|
|
||||||
#define SAMPLE_RATE 44100.0f
|
#define SAMPLE_RATE 44100.0f
|
||||||
|
|
||||||
|
#define BPM_MIN 30.0f
|
||||||
|
#define BPM_MAX 300.0f
|
||||||
|
|
||||||
|
#define VCO_FREQ_MIN 40.0f
|
||||||
|
#define VCO_FREQ_MAX 3200.0f
|
||||||
|
#define VCO_VOLUME_MIN 0.0f
|
||||||
|
#define VCO_VOLUME_MAX 1.0f
|
||||||
|
|
||||||
|
#define FILTER_FREQ_MIN 20.0f
|
||||||
|
#define FILTER_FREQ_MAX 20000.0f
|
||||||
|
#define FILTER_RES_MIN 0.0f
|
||||||
|
#define FILTER_RES_MAX 1.0f
|
||||||
|
|
||||||
|
#define ENV_ATTACK_MIN 0.001f // 1ms
|
||||||
|
#define ENV_ATTACK_MAX 10.0f // 10s
|
||||||
|
#define ENV_RELEASE_MIN 0.001f // 1ms
|
||||||
|
#define ENV_RELEASE_MAX 10.0f // 10s
|
||||||
|
|
||||||
|
#define REVERB_AMOUNT_MIN 0.0f
|
||||||
|
#define REVERB_AMOUNT_MAX 1.0f
|
||||||
|
|
||||||
|
#define AMEN_BPM 85.0f
|
||||||
|
#define AMEN_BARS 4.0f
|
||||||
|
|
||||||
|
#define otherwise else
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
#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 <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdnoreturn.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
#include "hardware/pwm.h"
|
||||||
|
#include "hardware/adc.h"
|
||||||
|
#include "pico/multicore.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
|
||||||
|
#include "const.h"
|
||||||
|
#include "macro.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "synth.h"
|
||||||
|
#include "pwm.h"
|
||||||
|
|
||||||
|
input_t input;
|
||||||
|
state_t state;
|
||||||
|
|
||||||
|
void core0_init(void) {
|
||||||
|
// PWM bullshit
|
||||||
|
gpio_set_function(AUDIO_OUT, GPIO_FUNC_PWM);
|
||||||
|
|
||||||
|
uint slice = pwm_gpio_to_slice_num(AUDIO_OUT);
|
||||||
|
pwm_config cfg = pwm_get_default_config();
|
||||||
|
pwm_config_set_wrap(&cfg, 3400);
|
||||||
|
pwm_init(slice, &cfg, true);
|
||||||
|
|
||||||
|
pwm_clear_irq(slice);
|
||||||
|
pwm_set_irq_enabled(slice, true);
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
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_inputs(&input);
|
||||||
|
update_state(&state, &input);
|
||||||
|
sleep_ms(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();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#include "macro.h"
|
|
||||||
#include "pico/stdlib.h"
|
|
||||||
#include <hardware/gpio.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "const.h"
|
|
||||||
#include "hardware/pwm.h"
|
|
||||||
#include "hardware/adc.h"
|
|
||||||
#include "state.h"
|
|
||||||
#include "daisysp.h"
|
|
||||||
|
|
||||||
daisysp::Oscillator osc;
|
|
||||||
daisysp::Svf filter;
|
|
||||||
|
|
||||||
state_t state;
|
|
||||||
|
|
||||||
void init_all() {
|
|
||||||
stdio_init_all();
|
|
||||||
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};
|
|
||||||
|
|
||||||
gpio_set_function(AUDIO_OUTPUT, GPIO_FUNC_PWM);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint8_t i=0; i < ARRAY_LENGTH(adc_gpio); i++) {
|
|
||||||
adc_gpio_init(adc_gpio[i]);
|
|
||||||
adc_select_input(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noreturn)) int main() {
|
|
||||||
init_all();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#include "const.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
float read_value_from_mux() {
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_inputs() {
|
|
||||||
for(uint8_t i = 0; i < 2; i++) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include <hardware/pwm.h>
|
||||||
|
#include <pico/types.h>
|
||||||
|
|
||||||
|
#include "const.h"
|
||||||
|
#include "synth.h"
|
||||||
|
|
||||||
|
#include "pwm.h"
|
||||||
|
|
||||||
|
void pwm_isr(void) {
|
||||||
|
static uint slice = -1, chan = -1;
|
||||||
|
if (slice == -1) slice = pwm_gpio_to_slice_num(AUDIO_OUT);
|
||||||
|
if (chan == -1) chan = pwm_gpio_to_channel(AUDIO_OUT);
|
||||||
|
|
||||||
|
pwm_clear_irq(slice);
|
||||||
|
|
||||||
|
float sample = get_sample();
|
||||||
|
|
||||||
|
uint16_t level = (uint16_t)((sample + 1.0f) * 0.5f * 3400.0f);
|
||||||
|
pwm_set_chan_level(slice, chan, level);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,24 +1,48 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "input.h"
|
||||||
#include "vco.h"
|
#include "vco.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float clock_bpm;
|
float clock_bpm;
|
||||||
float vco_freq;
|
float vco_freq;
|
||||||
vco_mode_t vco_mode;
|
|
||||||
float vco_volume;
|
float vco_volume;
|
||||||
bool quant_enabled;
|
vco_mode_t vco_mode;
|
||||||
float filter_freq;
|
float filter_freq;
|
||||||
float filter_resonance;
|
float filter_resonance;
|
||||||
float env1_attack;
|
float env1_attack;
|
||||||
float env1_release;
|
float env1_decay;
|
||||||
float env2_attack;
|
float env2_attack;
|
||||||
float env2_release;
|
float env2_decay;
|
||||||
float reverb_amount;
|
float reverb_amount;
|
||||||
|
bool quant_enabled;
|
||||||
bool amen_enabled;
|
bool amen_enabled;
|
||||||
|
float beat_samples;
|
||||||
|
float playback_rate;
|
||||||
|
float clock_inc;
|
||||||
|
uint32_t amen_inc_fp;
|
||||||
} state_t;
|
} 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);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "daisysp.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "const.h"
|
||||||
|
#include "vco.h"
|
||||||
|
|
||||||
|
#include "synth.h"
|
||||||
|
|
||||||
|
using namespace daisysp;
|
||||||
|
|
||||||
|
static state_t* state = NULL;
|
||||||
|
|
||||||
|
Oscillator osc;
|
||||||
|
Svf filter;
|
||||||
|
AdEnv vco_env;
|
||||||
|
AdEnv filter_env;
|
||||||
|
|
||||||
|
float clock_phase = 0.0f;
|
||||||
|
bool clock_trig = false;
|
||||||
|
|
||||||
|
uint32_t amen_phase_fp = 0;
|
||||||
|
float amen_length = 0.0f;
|
||||||
|
|
||||||
|
static inline auto vco_mode_to_daisy(vco_mode_t mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case VCO_SQUARE: return daisysp::Oscillator::WAVE_SQUARE;
|
||||||
|
case VCO_TRIANGLE: return daisysp::Oscillator::WAVE_TRI;
|
||||||
|
case VCO_SAW: return daisysp::Oscillator::WAVE_SAW;
|
||||||
|
case VCO_SINE:
|
||||||
|
default: return daisysp::Oscillator::WAVE_SIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "amenbreak.h"
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
||||||
|
state = _state;
|
||||||
|
osc.Init(SAMPLE_RATE);
|
||||||
|
filter.Init(SAMPLE_RATE);
|
||||||
|
vco_env.Init(SAMPLE_RATE);
|
||||||
|
vco_env.SetMin(0.0f);
|
||||||
|
vco_env.SetMax(VCO_VOLUME_MAX);
|
||||||
|
filter_env.Init(SAMPLE_RATE);
|
||||||
|
filter_env.SetMin(0.0f);
|
||||||
|
filter_env.SetMax(FILTER_FREQ_MAX);
|
||||||
|
clock_phase = 0.0f;
|
||||||
|
clock_trig = false;
|
||||||
|
amen_phase_fp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float get_sample(void) {
|
||||||
|
if (state == NULL) return 0.0f;
|
||||||
|
|
||||||
|
bool clock_trig = false;
|
||||||
|
|
||||||
|
clock_phase += state->clock_inc;
|
||||||
|
if (clock_phase >= 1.0f) {
|
||||||
|
clock_phase -= 1.0f;
|
||||||
|
clock_trig = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clock_trig) {
|
||||||
|
vco_env.Trigger();
|
||||||
|
filter_env.Trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
osc.SetFreq(state->vco_freq);
|
||||||
|
osc.SetWaveform(vco_mode_to_daisy(state->vco_mode));
|
||||||
|
osc.SetAmp(1.0f);
|
||||||
|
float vco_out = osc.Process();
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
if (mix > 1.0f) return 1.0f;
|
||||||
|
if (mix < -1.0f) return -1.0f;
|
||||||
|
|
||||||
|
return mix;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
#define COMB0_SIZE 1103
|
||||||
|
#define COMB1_SIZE 1361
|
||||||
|
#define COMB2_SIZE 1499
|
||||||
|
#define COMB3_SIZE 1693
|
||||||
|
#define AP0_SIZE 131
|
||||||
|
#define AP1_SIZE 43
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void synth_init(state_t* state);
|
||||||
|
float get_sample(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#include "const.h"
|
|
||||||
#include "state.h"
|
|
||||||
|
|
||||||
#include "vco.h"
|
|
||||||
|
|
||||||
float phase = 0.0f;
|
|
||||||
float vco(void) {
|
|
||||||
float phase_inc = (state.vco_freq * 1760) / SAMPLE_RATE;
|
|
||||||
phase += phase_inc;
|
|
||||||
|
|
||||||
if (phase >= 1.0f) phase -= 1.0f;
|
|
||||||
|
|
||||||
return (phase * 2.0f) - 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user