diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cde0064 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.13) + +# 1. Generate the map for your Neovim clangd linter +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Include the SDK's CMake entry point +include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake) + +project(clock C CXX ASM) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +pico_sdk_init() + +include_directories(include) + +add_executable(clock + src/main.cpp + src/Gate.cpp +) + +# Enable USB output (useful for later printf debugging) +pico_enable_stdio_usb(clock 1) +pico_enable_stdio_uart(clock 0) + +# Pull in standard library and hardware abstraction +target_link_libraries(clock + pico_stdlib + hardware_gpio + hardware_i2c +) + +# Create the .uf2 file +pico_add_extra_outputs(clock) diff --git a/include/Gate.h b/include/Gate.h new file mode 100644 index 0000000..1dedb59 --- /dev/null +++ b/include/Gate.h @@ -0,0 +1,36 @@ +// Gate.h +#ifndef Gate_h +#define Gate_h + +#include +#include + + +class Gate { + private: + bool state; + int16_t cycle; + uint32_t dur; + uint32_t len; + uint8_t width; + uint8_t divideMode; + uint16_t div; + uint16_t modifier; + std::string divString; + uint8_t p; + + public: + Gate(uint8_t pin); + uint8_t pin; + + void turnOn(); + void turnOff(); + void setLen(uint32_t currentPeriod); + void setDiv(uint16_t newDiv, uint8_t divide = 1); + void setWidth(uint16_t newWidth); + void setP(uint16_t prob); + + bool getState(); +}; + +#endif diff --git a/include/globals.h b/include/globals.h new file mode 100644 index 0000000..c848cb8 --- /dev/null +++ b/include/globals.h @@ -0,0 +1,47 @@ +#ifndef GLOBALS_H +#define GLOBALS_H + +#include + +#endif // GLOBALS_H + +/* +TODO: + +PRE-DAC: +[x] Figure out multiplicative beats X2 X4 X8 X16 X32? +[ ] Swing/Phase (same thing) +[x] Probability +[ ] Humanization +[ ] Euclidian Rhythms + [ ] Steps - # of steps for a full pattern + [ ] Hits - how many hits across the steps, must be less than steps + [ ] Offset - move the starting point of the pattern +[ ] Logic (NO | AND | OR | XOR) +[ ] Mute +[ ] Save +[ ] Load + +POST-DAC: +[ ] Different Wave Forms +[ ] Different Voltage levels +[ ] v/oct? + +100BPM +4800BPM + +POSSIBLE DIVISIONS: +1: x48 +16: x32 +32: x16 +40: x8 +44: x4 +46: x2 +--- +48*1: 1 ** THIS NEEDS TO BE PASSED IN AS DIVIDE MODE +48*2 = 96: /2 +48*4 = 192: /4 +48*8 = /8 +48*16 = /16 + +*/ diff --git a/src/Gate.cpp b/src/Gate.cpp new file mode 100644 index 0000000..432ac49 --- /dev/null +++ b/src/Gate.cpp @@ -0,0 +1,86 @@ +// Gate.cpp +#include "pico/stdlib.h" +#include "Gate.h" +#include "globals.h" +#include +#include + +Gate::Gate(uint8_t pin) { + this->pin = pin; + state = 0; + + divideMode = 1; // 1 divison | 0 multiplication + modifier = 1; // divide mode modifier (4x, /32, etc) + div = 1; // cycles needed before a pulse based on divide mode and modifier + cycle = 0; // how many cycles have passed since last pulse + divString = ""; // string for screen .. probably does not belong here + + dur = 0; // how long pulse is on + width = 50; // pulse width + len = 0; // max len a pulse can be on, as determined by width + + p = 100; // probability of a pulse +} + +bool Gate::getState() { + return state; +} + +void Gate::setLen(uint32_t currentPeriod) { + len = (uint32_t)((double)currentPeriod * (width / 100.0) / 1000.0); +} + +void Gate::setDiv(uint16_t modifier, uint8_t divide) { + if (divide == 1) { + div = ppqn * modifier; + divString = "/" + std::to_string(modifier); + } else { + div = ppqn / modifier; + divString = "x" + std::to_string(modifier); + } + divideMode = divide; + this->modifier = modifier; +}; + +void Gate::setWidth(uint16_t newWidth) { + width = newWidth; + if (divideMode == 1) { + len = (uint32_t)((double)(minute / BPM) * (width / 100.0) / 1000.0); + } else { + len = (uint32_t)((double)(minute / BPM / modifier) * (width / 100.0) / 1000.0); + } +}; + +void Gate::setP(uint16_t prob) { + this->p = prob; +} + +void Gate::turnOn() { + cycle += 1; + uint8_t pRes = 1; + + if (cycle == div) { + if (p < 100) { + uint32_t r = (rand() % 100) + 1; + if (r > p) { + pRes = 0; + } + } + + if (pRes == 1) { + state = 1; + digitalWrite(pin, state); + dur = millis(); + } + cycle = 0; + }; +} + +void Gate::turnOff() { + if (state == 1 && millis() - dur >= len) { + state = 0; + digitalWrite(pin, state); + dur = 0; + }; +} + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..f6f1451 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,96 @@ +#include +#include "hardware/structs/rosc.h" +#include "pico/stdlib.h" +#include "pico/time.h" +#include + +#include "globals.h" +#include "Gate.h" + +static constexpr uint8_t OUT_1_PIN = 0; +static constexpr uint8_t OUT_2_PIN = 2; +static constexpr uint8_t OUT_3_PIN = 4; +static constexpr uint8_t OUT_4_PIN = 6; +static constexpr uint8_t OUT_5_PIN = 8; +static constexpr uint8_t OUT_6_PIN = 10; +static constexpr uint8_t OUT_7_PIN = 12; +static constexpr uint8_t OUT_8_PIN = 14; + +static constexpr uint8_t SCREEN_SCL_PIN = 18; +static constexpr uint8_t SCREEN_SDA_PIN = 19; + +static constexpr uint8_t ENCODER_CLK_PIN = 20; +static constexpr uint8_t ENCODER_DT_PIN = 21; +static constexpr uint8_t ENCODER_SW_PIN = 22; + +volatile uint8_t PLAY = 1; +volatile uint8_t BPM = 100; + +static constexpr uint32_t MINUTE_US = 60000000; +static constexpr uint8_t PPQN = 96; +volatile uint32_t period_us = 0; + +struct repeating_timer bpm_timer; + +volatile bool beatToggle = false; + +Gate out1(OUT_1_PIN); +Gate out2(OUT_2_PIN); +Gate out3(OUT_3_PIN); +Gate out4(OUT_4_PIN); +Gate out5(OUT_5_PIN); +Gate out6(OUT_6_PIN); +Gate out7(OUT_7_PIN); +Gate out8(OUT_8_PIN); + + +bool timer_callback(struct repeating_timer *t) { + if (PLAY == 1) { + beatToggle = true; + } + return true; +} + + +void init_timer(uint32_t period_us) { + cancel_repeating_timer(&bpm_timer); + add_repeating_timer_us(-(int64_t)period_us, timer_callback, NULL, &bpm_timer); +} + + +void update_period() { + period_us = (uint32_t)(MINUTE_US / (uint32_t)BPM / PPQN); + init_timer(period_us); +} + + +int main() { + stdio_init_all(); + + srand(rosc_hw->randombit); + + gpio_init(out1.pin); + gpio_init(out2.pin); + gpio_init(out3.pin); + gpio_init(out4.pin); + gpio_init(out5.pin); + gpio_init(out6.pin); + gpio_init(out7.pin); + gpio_init(out8.pin); + + gpio_set_dir(out1.pin, GPIO_OUT); + gpio_set_dir(out2.pin, GPIO_OUT); + gpio_set_dir(out3.pin, GPIO_OUT); + gpio_set_dir(out4.pin, GPIO_OUT); + gpio_set_dir(out5.pin, GPIO_OUT); + gpio_set_dir(out6.pin, GPIO_OUT); + gpio_set_dir(out7.pin, GPIO_OUT); + gpio_set_dir(out8.pin, GPIO_OUT); + + update_period(); + + while (true) { + + } + +}