diff --git a/firmware/generic/src/data/images.cpp b/firmware/generic/src/data/images.cpp deleted file mode 100644 index 9f89ab6..0000000 --- a/firmware/generic/src/data/images.cpp +++ /dev/null @@ -1,7 +0,0 @@ -extern "C" -{ -#include "gd32vf103.h" -} - - -const uint8_t img1[176] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 50, 8, 102, 66, 38, 15, 115, 1, 66, 1, 34, 1, 130, 0, 70, 0, 32, 0, 131, 0, 72, 0, 30, 0, 131, 0, 74, 0, 28, 0, 255, 116, 0, 76, 0, 26, 0, 255, 255, 112, 24, 9, 24, 45, 11, 120, 0, 33, 0, 69, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 146, 0, 239, 68, 255, 255, 130, 0, 76, 0, 26, 0, 104, 0, 210, 0, 55, 0, 74, 0, 28, 0, 74, 0, 28, 0, 27, 0, 72, 0, 30, 0, 72, 0, 30, 0, 27, 0, 70, 0, 32, 0, 70, 0, 32, 0, 27, 1, 66, 1, 34, 1, 66, 1, 34, 1, 27, 66, 38, 2, 25, 8, 25, 2, 38, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252}; diff --git a/firmware/generic/src/drivers/adc.cpp b/firmware/generic/src/drivers/adc.cpp index 0abe2bc..535896e 100644 --- a/firmware/generic/src/drivers/adc.cpp +++ b/firmware/generic/src/drivers/adc.cpp @@ -1,17 +1,18 @@ #include "adc.h" extern "C" { -#include "gd32vf103.h" + #include "drivers/SysTick.h" + #include "gd32vf103.h" } // anUpdate (downshifted oversampling) -uint8_t anCount = 0; // counter for oversampling (new value after 255, @44,4KHz => 173Hz, suitable for potentiometers) -uint32_t anTotal[MAX_AN]; // the running total -int anAvg[MAX_AN]; // analog average -uint16_t anRaw[8]; // raw sampled values stored by DMA +//uint8_t anCount = 0; // counter for oversampling (new value after 255, @44,4KHz => 173Hz, suitable for potentiometers) +//uint32_t anTotal[MAX_AN]; // the running total +//int anAvg[MAX_AN]; // analog average +//uint16_t anRaw[8]; // raw sampled values stored by DMA // analog average (downshifted oversampling) -void adc::update(uint8_t ch) +void adc::updateAVG(uint8_t ch) { if (anCount < 255) { @@ -25,4 +26,50 @@ void adc::update(uint8_t ch) anAvg[ch] = anTotal[ch] >> 8; // inputs and pot 3 is not inverted anTotal[ch] = 0; } +} + +void adc::update() +{ + // Running average is done after DAC since it will add jitter due to uneven number of cycles + anCount++; + updateAVG(0); // ADC0 + updateAVG(1); // ADC1 + updateAVG(2); // pot0 (not inverted anymore) + updateAVG(3); // pot1 (not inverted anymore) + updateAVG(4); // pot2 + + // Start ADC sampling (using DMA) + adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL); // start ADC: DMA => adc_value[] +} + +void adc::init(SysTick &stk) { + /* reset ADC */ + adc_deinit(ADC0); + /* ADC mode config */ + adc_mode_config(ADC_MODE_FREE); // ADC0 and ADC1 work independently + /* ADC data alignment config */ + adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT); + /* Configure word length */ + adc_resolution_config(ADC0, ADC_RESOLUTION_12B); + /* ADC channel length config */ + adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 5); + adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_0, ADC_SAMPLETIME_13POINT5); // ADC left 41.4+12.5=54 cycles =5uS *5=20us => 50.1Hz + adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_3, ADC_SAMPLETIME_13POINT5); // ADC right + adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_6, ADC_SAMPLETIME_13POINT5); // pot0 (UR) + adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_7, ADC_SAMPLETIME_13POINT5); // pot1 (LR) + adc_regular_channel_config(ADC0, 4, ADC_CHANNEL_8, ADC_SAMPLETIME_13POINT5); // pot2 (LL) + /* ADC trigger config */ + adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_NONE); // software trigger + /* ADC external trigger enable */ + adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE); + /* ADC discontinuous mode */ + adc_discontinuous_mode_config(ADC0, ADC_REGULAR_CHANNEL, 5); // should it be 4? + /* enable ADC interface */ + adc_enable(ADC0); + stk.delay(1); + /* ADC calibration and reset calibration */ + adc_calibration_enable(ADC0); + stk.delay(1); + /* ADC DMA function enable */ + adc_dma_mode_enable(ADC0); } \ No newline at end of file diff --git a/firmware/generic/src/drivers/adc.h b/firmware/generic/src/drivers/adc.h index 4bca932..7020b69 100644 --- a/firmware/generic/src/drivers/adc.h +++ b/firmware/generic/src/drivers/adc.h @@ -2,7 +2,8 @@ #define __adc_h extern "C" { -#include + #include "drivers/SysTick.h" + #include } #define MAX_AN 5 // number of analog filters @@ -11,12 +12,14 @@ class adc { public: adc(){}; + void init(SysTick &stk) ; // initialise ADC uint16_t anRaw[8]; // raw sampled values stored by DMA - int anAvg[MAX_AN]; // analog average - uint8_t anCount; // counter for oversampling (new value after 255, @44,4KHz => 173Hz, suitable for potentiometers) - void update(uint8_t ch); + int anAvg[MAX_AN]; // analog average + uint8_t anCount; // counter for oversampling (new value after 255, @44,4KHz => 173Hz, suitable for potentiometers) + void update() ; // update ADC private: uint32_t anTotal[MAX_AN]; // the running total + void updateAVG(uint8_t ch) ; }; #endif \ No newline at end of file diff --git a/firmware/generic/src/drivers/dac.cpp b/firmware/generic/src/drivers/dac.cpp new file mode 100644 index 0000000..053affc --- /dev/null +++ b/firmware/generic/src/drivers/dac.cpp @@ -0,0 +1,29 @@ +#include "dac.h" +extern "C" +{ + #include +} + +void dac::dac_out(uint16_t d0, uint16_t d1) +{ + dac_data_set(DAC0, DAC_ALIGN_12B_R, 4095 - d0); // inverted due to inverting op amp + dac_data_set(DAC1, DAC_ALIGN_12B_R, 4095 - d1); // inverted due to inverting op amp + dac_software_trigger_enable(DAC0); + dac_software_trigger_enable(DAC1); +} + +void dac::init(void) +{ + dac_deinit(); + dac_trigger_source_config(DAC0, DAC_TRIGGER_SOFTWARE); + dac_trigger_enable(DAC0); + dac_wave_mode_config(DAC0, DAC_WAVE_DISABLE); + dac_output_buffer_enable(DAC0); + dac_enable(DAC0); + + dac_trigger_source_config(DAC1, DAC_TRIGGER_SOFTWARE); + dac_trigger_enable(DAC1); + dac_wave_mode_config(DAC1, DAC_WAVE_DISABLE); + dac_output_buffer_enable(DAC1); + dac_enable(DAC1); +} \ No newline at end of file diff --git a/firmware/generic/src/drivers/dac.h b/firmware/generic/src/drivers/dac.h new file mode 100644 index 0000000..63bd08a --- /dev/null +++ b/firmware/generic/src/drivers/dac.h @@ -0,0 +1,14 @@ +#ifndef __dac_h +#define __dac_h +extern "C" +{ + #include + #include "drivers/display.h" +} + +class dac { + public: + void init(void) ; + void dac_out(uint16_t d0, uint16_t d1) ; +}; +#endif \ No newline at end of file diff --git a/firmware/generic/src/drivers/display.cpp b/firmware/generic/src/drivers/display.cpp index a9bfb3c..58d17cf 100644 --- a/firmware/generic/src/drivers/display.cpp +++ b/firmware/generic/src/drivers/display.cpp @@ -2,9 +2,12 @@ #include "display.h" #include "spi.h" #include "SysTick.h" +#include "res/font.h" extern "C" { -#include "gd32vf103.h" + #include "gd32vf103.h" + + //extern "C" const uint8_t font[1520]; } extern SysTick stk; /* @@ -29,7 +32,7 @@ extern SysTick stk; RST PB1 CS PB2 */ -void display::begin(spi *SPI, const uint8_t *font) +void display::begin(spi *SPI) { this->SPI = SPI; // remember the SPI interface @@ -193,12 +196,11 @@ void display::putChar(char ch, uint8_t x, uint8_t y, uint16_t txtFg, uint16_t tx if ((' ' <= ch) && (ch <= '~')) // 7bit ASCII { openAperture(x, y, x + 8 - 1, y + 16 - 1); - ch += 4; - const uint8_t *charFont = _font + (uint8_t)ch * 16; - for (uint8_t y = 0; y < 16; ++y) - { - for (uint8_t x = 0, c = charFont[y]; x < 8; ++x, c >>= 1) - { + ch -= 32; // why this offset ... + const uint8_t *charFont = &font[(uint8_t)ch * 16]; + + for (uint8_t y = 0; y < 16; ++y) { + for (uint8_t x = 0, c = charFont[y]; x < 8; ++x, c >>= 1) { uint16_t rgb = (c & 0x01) ? txtFg : txtBg; writeData16(rgb); } @@ -231,6 +233,7 @@ void display::putImage(uint16_t x, uint16_t y, uint16_t width, uint16_t height, } } } + void display::drawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t Colour) { drawLine(x, y, x + w, y, Colour); @@ -238,6 +241,7 @@ void display::drawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint drawLine(x + w, y, x + w, y + h, Colour); drawLine(x, y + h, x + w, y + h, Colour); } + void display::fillRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t Colour) { openAperture(x, y, x + width - 1, y + height - 1); @@ -249,6 +253,7 @@ void display::fillRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t hei } } } + void display::drawCircle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t Colour) { // Reference : https://en.wikipedia.org/wiki/Midpoint_circle_algorithm @@ -292,6 +297,7 @@ void display::drawCircle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t Col } } } + void display::fillCircle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t Colour) { // Reference : https://en.wikipedia.org/wiki/Midpoint_circle_algorithm @@ -333,6 +339,7 @@ void display::fillCircle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t Col } } } + void display::drawLineLowSlope(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t Colour) { // Reference : https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm @@ -386,6 +393,7 @@ void display::drawLineHighSlope(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t D = D + 2 * dx; } } + void display::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t Colour) { // Reference : https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm @@ -457,34 +465,42 @@ void display::RSLow() { // drive D/C pin low gpio_bit_reset(GPIOA, GPIO_PIN_11); } + void display::RSHigh() { // drive D/C pin high gpio_bit_set(GPIOA, GPIO_PIN_11); } + void display::RSTLow() { // Drive reset low gpio_bit_reset(GPIOA, GPIO_PIN_8); } + void display::RSTHigh() { // Drive reset high gpio_bit_set(GPIOA, GPIO_PIN_8); } + void display::CSLow() { // Drive chip select low // gpio_bit_reset(GPIOB,GPIO_PIN_2); } + void display::CSHigh() { // Drive chip select high // gpio_bit_set(GPIOB,GPIO_PIN_2); } + void display::BLKLow() { // Drive backlight low (off) gpio_bit_reset(GPIOB, GPIO_PIN_10); } + void display::BLKHigh() { // Drive backlight high (on) gpio_bit_set(GPIOB, GPIO_PIN_10); } + void display::writeCommand(uint8_t Cmd) { RSLow(); @@ -492,6 +508,7 @@ void display::writeCommand(uint8_t Cmd) SPI->writeData8(Cmd); CSHigh(); } + void display::writeData8(uint8_t Data) { RSHigh(); @@ -499,6 +516,7 @@ void display::writeData8(uint8_t Data) SPI->writeData8(Data); CSHigh(); } + void display::writeData16(uint16_t Data) { RSHigh(); @@ -506,6 +524,7 @@ void display::writeData16(uint16_t Data) SPI->writeData16(Data); CSHigh(); } + void display::exchangeXY() { writeCommand(0x36); @@ -520,6 +539,7 @@ void display::exchangeXY() writeData8(0x00); } } + uint32_t display::getWidth() { if (!XYSwapped) @@ -531,6 +551,7 @@ uint32_t display::getWidth() return SCREEN_HEIGHT; } } + uint32_t display::getHeight() { if (!XYSwapped) @@ -543,8 +564,7 @@ uint32_t display::getHeight() } } -void display::showSprite(const uint8_t *rle_data, uint16_t size) -{ +void display::showSprite(const uint8_t *rle_data, uint16_t size) { const uint8_t *__ip, *__il, *__rd; __ip = (rle_data); __il = __ip + (size)*2; diff --git a/firmware/generic/src/drivers/display.h b/firmware/generic/src/drivers/display.h index 0242612..a0dcc98 100644 --- a/firmware/generic/src/drivers/display.h +++ b/firmware/generic/src/drivers/display.h @@ -9,11 +9,13 @@ // Format of colour value: //#define RGBToWord( R, G, B) ( ((G&0xf8) << (11-3)) | ((R&0xfc) << (5-2)) | ((B&0xf8)>>3) ) #define RGBToWord( R, G, B) ( ((B&0xf8) << (11-3)) | ((G&0xfc) << (5-2)) | ((R&0xf8)>>3) ) + class display { public: display(){}; - void begin(spi *SPI, const uint8_t *font); + //void begin(spi *SPI, const uint8_t *font); + void begin(spi *SPI) ; void showImg(const uint8_t *img, uint16_t size, uint8_t x, uint8_t y, uint16_t width, uint16_t height, uint16_t color); void putChar(char ch, uint8_t x, uint8_t y, uint16_t txtFg, uint16_t txtBg); void putStr(const char *str, uint8_t x, uint8_t y, uint16_t txtFg, uint16_t txtBg); @@ -53,6 +55,6 @@ class display } spi *SPI; int XYSwapped; - const uint8_t *_font ; // ' ' .. '~' + //const uint8_t *font ; // ' ' .. '~' }; #endif \ No newline at end of file diff --git a/firmware/generic/src/drivers/led.h b/firmware/generic/src/drivers/led.h deleted file mode 100644 index e313dd2..0000000 --- a/firmware/generic/src/drivers/led.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef __led_h -#define __led_h -extern "C" { - #include -} -class led { -public: - led() {}; - void begin() { - /* enable the led clock */ - rcu_periph_clock_enable(RCU_GPIOC); - rcu_periph_clock_enable(RCU_GPIOA); - - /* configure led GPIO port */ - gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13); - gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1); - gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2); - - GPIO_BOP(GPIOC) = GPIO_PIN_13; - GPIO_BOP(GPIOA) = GPIO_PIN_1; - GPIO_BOP(GPIOA) = GPIO_PIN_2; - } - /*! - Turn on the red part of the RGB LED - by clearing PC13 - */ - void red_on() - { - GPIO_BC(GPIOC) = GPIO_PIN_13; - } - /*! - Turn off the red part of the RGB LED - by setting PC13 - */ - void red_off() - { - GPIO_BOP(GPIOC) = GPIO_PIN_13; - } - /*! - Turn on the blue part of the RGB LED - by clearing PA1 - */ - void green_on() - { - GPIO_BC(GPIOA) = GPIO_PIN_1; - } - /*! - Turn off the green part of the RGB LED - by setting PA1 - */ - void green_off() - { - GPIO_BOP(GPIOA) = GPIO_PIN_1; - } - /*! - Turn on the blue part of the RGB LED - by clearing PA2 - */ - void blue_on() - { - GPIO_BC(GPIOA) = GPIO_PIN_2; - } - /*! - Turn off the blue part of the RGB LED - by setting PA2 - */ - void blue_off() - { - GPIO_BOP(GPIOA) = GPIO_PIN_2; - } - -}; -#endif \ No newline at end of file diff --git a/firmware/generic/src/lib/menu.cpp b/firmware/generic/src/lib/menu.cpp new file mode 100644 index 0000000..27b4bc7 --- /dev/null +++ b/firmware/generic/src/lib/menu.cpp @@ -0,0 +1,94 @@ +#include +#include "menu.h" +#include "res/modes.h" +#include "res/colors.h" +#include "res/images.h" +#include "drivers/display.h" + +void menu::prev() { + if (selectMode == mainSelect ) { + if ( mainMode > 1 ) { + mainMode -- ; + } + } else { + if ( subMode > 0 ) { + subMode -- ; + } + } + needUpdate = 1 ; +} + +void menu::next() { + if (selectMode == mainSelect ) { + if ( mainMode < maxMode ) { + mainMode ++ ; + } + } else { + if ( subMode < subMenuMax-1 ) { + subMode ++ ; + } + } + needUpdate = 1 ; +} + +void menu::switchMode() { + if (selectMode == mainSelect ) { + selectMode = subSelect ; + + } else { + selectMode = mainSelect ; + subMode = 0 ; + } + needUpdate = 1 ; +} + +void menu::drawSkel(display &Display) { + // clean all screen + Display.fillRectangle(0, 0, 240, 240, blackColor ); + + // show logo + Display.showImg(logoImage, imgSize, 0, 0, 240, 60, subModeColor); + + // show frame + Display.drawRectangle(0, 86, 239, 79, whiteColor ); + +} + +void menu::drawPotsName(display &Display) { + + char str[16] ; // to handle pot value formating + // --------------------------- Line 1 -------------------------------- + // POT UR + sprintf(str, "%s : ",potsName[0]); + Display.putStr(str, 130, 180, potUpperRightColor, blackColor); + + // --------------------------- Line 3 -------------------------------- + // POT LL + sprintf(str, "%s : ",potsName[1]); + Display.putStr(str, 0 , 200, potLowerLeftColor, blackColor); + + // POT LR + sprintf(str, "%s : ",potsName[2]); + Display.putStr(str, 130, 200, potLowerRightColor, blackColor); + +} + +void menu::drawSubmode(display &Display) { + + char str[17] ; // to handle pot value formating + + Display.fillRectangle(0, 60, 240, 20, blackColor); + sprintf(str,"%16s",subMenuTxt[subMode]) ; + Display.putStr(str, 80, 60, subModeColor, blackColor); + + if (selectMode == subSelect ) { + sprintf(str,"Sub") ; + Display.putStr(str, 1, 60, main1Color, blackColor); + //Display.showImg(arrowLeftImage, sizeof(arrowLeftImage), 0, 60, 16, 16, main1Color); + } else { + sprintf(str,"Main") ; + Display.putStr(str, 1, 60, main1Color, blackColor); + } + + needUpdate = 0 ; // menu have been updated +} diff --git a/firmware/generic/src/lib/menu.h b/firmware/generic/src/lib/menu.h new file mode 100644 index 0000000..dc2bb33 --- /dev/null +++ b/firmware/generic/src/lib/menu.h @@ -0,0 +1,33 @@ +#ifndef __menu_h +#define __menu_h +extern "C" +{ + + #include "drivers/display.h" +} + +#define mainSelect 0 +#define subSelect 1 + +class menu { + public: + uint8_t selectMode = subSelect ; + uint8_t mainMode ; + uint8_t subMode ; + uint8_t subMenuMax ; + uint8_t needUpdate = 1; + uint16_t subModeColor ; + const char ** subMenuTxt ; + const char ** potsName ; + const uint8_t * logoImage ; + uint16_t imgSize = 0 ; + + void switchMode() ; + void prev() ; + void next() ; + + void drawSkel(display &Display) ; + void drawPotsName(display &Display) ; + void drawSubmode(display &Display) ; +}; +#endif \ No newline at end of file diff --git a/firmware/generic/src/drivers/scope.cpp b/firmware/generic/src/lib/scope.cpp similarity index 95% rename from firmware/generic/src/drivers/scope.cpp rename to firmware/generic/src/lib/scope.cpp index 5605c0f..d536aae 100644 --- a/firmware/generic/src/drivers/scope.cpp +++ b/firmware/generic/src/lib/scope.cpp @@ -1,9 +1,10 @@ #include "scope.h" #include "drivers/display.h" + scope::scope(uint16_t Colour) { this->Colour = Colour; - offset = 76; // add space above scope + offset = 96 ; // add space above scope scale = 1; BufferInputIndex = 0; for (uint32_t i = 0; i < SCOPE_BUFFER_SIZE; i++) @@ -44,3 +45,5 @@ void scope::insert(uint16_t value) Buffer[BufferInputIndex] = value; BufferInputIndex = (BufferInputIndex + 1) % SCOPE_BUFFER_SIZE; } + + diff --git a/firmware/generic/src/drivers/scope.h b/firmware/generic/src/lib/scope.h similarity index 100% rename from firmware/generic/src/drivers/scope.h rename to firmware/generic/src/lib/scope.h diff --git a/firmware/generic/src/main.cpp b/firmware/generic/src/main.cpp index 7ace768..98ad39b 100644 --- a/firmware/generic/src/main.cpp +++ b/firmware/generic/src/main.cpp @@ -1,22 +1,20 @@ -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| - // *********************************************************************************** // LEET modular - a versatile, easy to build, affordable and powerful eurorack module! // *********************************************************************************** // todo: // * integrate midi to cv code -// * clean up code // * implement s&h // * implement vca, attenuverter etc // * transparent image for dual color intro // * rainbow intro? // * why is one ouput affecting the other in calibration manual cv? (variations in AD or vcc -> pots?) -// * move generated contents (python scripts) to external files (with .h) // * improve potentiometer calibration // * design and print dual attenuated output (with improved clip) // done: +// * move generated contents (python scripts) to external files (with .h) +// * clean up code // * implement vcf and a few different filters // * implement primitive delay // * add dead zone for pot controling ammount of modulation @@ -44,135 +42,112 @@ // * update spi and display to support 240x240 tft with st7789 driver // * verify ADC resolution // * optimize display update (remove flickering) - // note: calibration values for unconnected input (potentiometer calibration): 3238 // target 2473 +//===================================================================================================== +// Config Section +//===================================================================================================== + +// Which PBC // #define pcb_version 0 // the first 3dpcb version #define pcb_version 1 // the first pcb version, marked rev 1 (menu and P2,P3 direction changed) +// pathThru IN1 -> OUT1 to easily duplicate CV in +#define CVOut 0 + +// Default mode at startup, see the list in modes.h +#define defaultMain modeVCA +#define defaultSub mode_VCA + +#define version "0.9.0" +//===================================================================================================== +// End Config Section +//===================================================================================================== + +/* potentiometer and I/O mapping + R P2 + P4 P3 + C0 C1 + D0 D1 +*/ + +// Encode wiring +#if (pcb_version == 0) + #define cw 'W' + #define cc 'C' +#else + #define cw 'C' + #define cc 'W' +#endif + #include -extern "C" -{ + #include "drivers/SysTick.h" -#include "drivers/led.h" #include "drivers/display.h" #include "drivers/spi.h" #include "drivers/timer.h" #include "drivers/encoder.h" #include "drivers/adc.h" -#include "drivers/scope.h" -#include "module_vco.h" -#include "module_noise.h" -#include "module_vcf.h" -#include "module_adsr.h" -#include "module_delay.h" -#include "gd32vf103.h" - extern "C" const uint8_t font[1520]; - extern "C" const float freq[2113]; +#include "drivers/dac.h" + + +#include "res/modes.h" +#include "res/colors.h" +#include "res/images.h" + +#include "modules/module_vco.h" +#include "modules/module_noise.h" +#include "modules/module_vcf.h" +#include "modules/module_env.h" +#include "modules/module_delay.h" +#include "modules/module_vca.h" +#include "modules/module_lfo.h" +#include "modules/module_cal.h" + +#include "lib/menu.h" +#include "lib/scope.h" + +extern "C" { + #include "gd32vf103.h" } -timer T; -led LED; +timer Timer; SysTick stk; display Display; spi SPI; -encoder enc; +encoder Encoder; adc ADC; +dac DACS ; +menu Menu; + module_vco vco; module_noise noise; module_vcf vcf; -module_adsr adsr; +module_env env; module_delay delay; -scope ch0(RGBToWord(0xff, 0x00, 0xff)); -scope ch1(RGBToWord(0x00, 0xff, 0xff)); -scope ch0old(RGBToWord(0x00, 0x00, 0x00)); -scope ch1old(RGBToWord(0x00, 0x00, 0x00)); - -// menu structure generated by python script: -const char *menuTxt[] = {"OSC", "< back", "sine am", "sine add", "sine fm", "square am", "square add", "square fm", "square pw", "square 2x", "square 3x", "triangle am", "triangle add", "triangle fm", "triangle saw", "triangle 2x", "triangle 3x", "quantize", "cvout", "Noise", "LFO", "< back", "LFO sine", "LFO square", "LFO triangle", "VCF", "< back", "2 pole v1", "2 pole v2", "4 pole", "test", "VCA", "SnH", "Envelope", " 127)) -// uint8_t moduleMode = mode_triangle_saw; // function of module selected by menu (fixed menu positions > 127)) -// uint8_t moduleMode = mode_ADSR; // function of module selected by menu (fixed menu positions > 127)) -// uint8_t moduleMode = mode_4_pole; // function of module selected by menu (fixed menu positions > 127)) -// uint8_t moduleMode = mode_Delay; // function of module selected by menu (fixed menu positions > 127)) + 2441, 2492, 2577, 2697, 2748, 2577, 2697, 2748}; */ + + volatile int dispPos = 0; // counter for scope column (update screen when dispPos == 240) -uint8_t menuPos = 0; // current menu position (< 127) -uint8_t menuUpd = 1; // signals if menu was updated (=>redraw display) -float phaseC = 0; // phaseCount | stepsize within one period (0-65535). frequenzy = xStep * 44100 / (2*65536) -uint8_t quantized = 0; // quantize CV input of module? -uint8_t cvOut = 0; // use dac0 as cv for vco? uint8_t scopeTrig = 0; // flag to init scope sampling +uint8_t cvOut = CVOut ; // use dac0 as cv for vco? -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| void rcu_config(void) { @@ -223,93 +198,6 @@ void dma_config(void) dma_channel_enable(DMA0, DMA_CH0); } -void initADC() -{ - /* reset ADC */ - adc_deinit(ADC0); - /* ADC mode config */ - adc_mode_config(ADC_MODE_FREE); // ADC0 and ADC1 work independently - /* ADC data alignment config */ - adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT); - /* Configure word length */ - adc_resolution_config(ADC0, ADC_RESOLUTION_12B); - /* ADC channel length config */ - adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 5); - adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_0, ADC_SAMPLETIME_13POINT5); // ADC left 41.4+12.5=54 cycles =5uS *5=20us => 50.1Hz - adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_3, ADC_SAMPLETIME_13POINT5); // ADC right - adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_6, ADC_SAMPLETIME_13POINT5); // pot0 (UR) - adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_7, ADC_SAMPLETIME_13POINT5); // pot1 (LR) - adc_regular_channel_config(ADC0, 4, ADC_CHANNEL_8, ADC_SAMPLETIME_13POINT5); // pot2 (LL) - /* ADC trigger config */ - adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_NONE); // software trigger - /* ADC external trigger enable */ - adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE); - /* ADC discontinuous mode */ - adc_discontinuous_mode_config(ADC0, ADC_REGULAR_CHANNEL, 5); // should it be 4? - /* enable ADC interface */ - adc_enable(ADC0); - stk.delay(1); - /* ADC calibration and reset calibration */ - adc_calibration_enable(ADC0); - stk.delay(1); - /* ADC DMA function enable */ - adc_dma_mode_enable(ADC0); -} - -void initDAC(void) -{ - dac_deinit(); - dac_trigger_source_config(DAC0, DAC_TRIGGER_SOFTWARE); - dac_trigger_enable(DAC0); - dac_wave_mode_config(DAC0, DAC_WAVE_DISABLE); - dac_output_buffer_enable(DAC0); - dac_enable(DAC0); - - dac_trigger_source_config(DAC1, DAC_TRIGGER_SOFTWARE); - dac_trigger_enable(DAC1); - dac_wave_mode_config(DAC1, DAC_WAVE_DISABLE); - dac_output_buffer_enable(DAC1); - dac_enable(DAC1); -} - -void initUART() -{ - /* enable USART clock */ - rcu_periph_clock_enable(RCU_USART0); - /* connect port to USARTx_Tx */ - gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); - /* USART configure */ - usart_deinit(USART0); - usart_baudrate_set(USART0, 9600U); - usart_word_length_set(USART0, USART_WL_8BIT); - usart_stop_bit_set(USART0, USART_STB_1BIT); - usart_parity_config(USART0, USART_PM_NONE); - usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); - usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); - usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); - usart_enable(USART0); -} - -float ad2phase(uint16_t in) -{ - float xTmp; - if (in < 1472) // below -3V - xTmp = 24.135547645748918; - else if (in > 3584) // above 8V - xTmp = 49429.601578493784; - else - { - if (quantized == 0) - xTmp = freq[in - 1472]; - else - { - uint16_t q = (in - 1472 + 8) >> 4; // add 16/2 to quantize between notes - xTmp = freq[q << 4]; - } - } - return (xTmp); -} - void addScope(uint16_t c0, uint16_t c1, uint8_t trig) { if (dispPos < 240) @@ -323,596 +211,567 @@ void addScope(uint16_t c0, uint16_t c1, uint8_t trig) } } -void dac_out(uint16_t d0, uint16_t d1) -{ - dac_data_set(DAC0, DAC_ALIGN_12B_R, 4095 - d0); // inverted due to inverting op amp - dac_data_set(DAC1, DAC_ALIGN_12B_R, 4095 - d1); // inverted due to inverting op amp - dac_software_trigger_enable(DAC0); - dac_software_trigger_enable(DAC1); -} - -int16_t fold(int16_t tmp) -{ - do - { - if (tmp < 1088) - tmp = (1088 * 2 - tmp); - if (tmp > 3008) - tmp = (3008 * 2 - tmp); - } while (tmp < 1088 || tmp > 3008); - return (tmp); -} - -uint16_t deadzone(uint16_t tmp) -{ - if (tmp < 255) // within? - return (0); // ..yes - return zero - else // .. no - return (tmp - 255); // return value - deadzone -} - -float getPhase() -{ - uint8_t p_note; - uint16_t p_octave; - p_note = deadzone(ADC.anAvg[2]) >> 4; // add deadzone, one octave is 192 AD steps (1V). (4096-1024/16)=192 - p_octave = (ADC.anAvg[4] >> 9) * 192; // 4096/512 => 8 octaves - phaseC = ad2phase(ADC.anAvg[0] + p_octave - (192 << 1) + p_note); // convert CV to logaritmic scale - return (phaseC); -} - -/* potentiometer and I/O mapping - R P2 - P4 P3 - C0 C1 - D0 D1 -*/ - -void OnTimer() +void OnTimer() { + // this is the main loop running @44K + // it's handling audio processing + // things should be kept simple and fast here ! -#if (pcb_version == 1) - ADC.anRaw[2] = 4096 - ADC.anRaw[2]; // pot inverted in this pcb version - ADC.anRaw[3] = 4096 - ADC.anRaw[3]; // pot inverted in this pcb version -#endif + #if (pcb_version == 1) + ADC.anRaw[2] = 4096 - ADC.anRaw[2]; // pot inverted in this pcb version + ADC.anRaw[3] = 4096 - ADC.anRaw[3]; // pot inverted in this pcb version + #endif // static float x; - static uint8_t songPos; // position in song - static uint16_t tCount; // temp for song - static uint8_t prevMode; // + //static uint8_t songPos; // position in song + //static uint16_t tCount; // temp for song + int16_t tmp = 0; uint32_t cv2; - if (moduleMode == mode_quantize) - { - quantized ^= 1; + + // Handle some config options + // Quantize + /* if (moduleMode == mode_quantize) { + vco.quantized ^= 1; moduleMode = prevMode; } - if (moduleMode == mode_cvout) - { + // CV OUT if enable IN0 -> OUT0 to chain modules + if (moduleMode == mode_cvout) { cvOut ^= 1; moduleMode = prevMode; - } + } */ - //-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| - switch (moduleMode) - { - case mode_sine_am: // sine (ampitude modulation) - cv2 = ((ADC.anRaw[1] << 2) * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude - tmp = 2048 + ((int(vco.sine(getPhase()) - 2048) * cv2) >> 12); // get level from position - tmp = fold(tmp); // fold if outside range - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_sine_add: // sine (sum: in1 is added to the oscillator) - cv2 = ((ADC.anRaw[1] << 2) * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude - tmp = (int(vco.sine(getPhase()) + cv2) >> 1); // get level from position - tmp = fold(tmp); // fold if outside range - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_sine_fm: // sine (frequency modulation) - cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 12); // 12+12-13=11 (0-2048). pot2 (bottom right) sets level for CV2 that modulates the frequency - tmp = int(vco.sine(getPhase() + cv2 - 1024)); // get level from position (1088-3008 => -5V to +5V) - tmp = fold(tmp); // fold if outside range - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_square_am: // square (amplitude modulation) - cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude - tmp = 2048 + ((int(vco.square(getPhase(), 32768) - 2048) * cv2) >> 12); // get level from position - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_square_add: // square (sum: in1 is added to the oscillator) - cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude - tmp = (int(vco.square(getPhase(), 32768) + cv2) >> 1); // get level from position - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_square_fm: // square (frequency modulation) - cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 12); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the frequency - tmp = vco.square(getPhase() + cv2, 32768); // get level from position (50% dyuty cycle) - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_square_pw: // square (pulsewidth modulation) - cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 8); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the pulse width - tmp = vco.square(getPhase(), cv2); // get level from position (50% dyuty cycle) - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_square_2x: // 2 slightly detuned square oscillators - cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 18); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the pulse width - tmp = vco.square2x(getPhase(), cv2); // get level from position (50% dyuty cycle) - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_square_3x: // 3 slightly detuned square oscillators - cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 17); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the pulse width - tmp = vco.square3x(getPhase(), cv2); // get level from position (50% dyuty cycle) - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_triangle_am: // triangle (amplitude modulation) - cv2 = ((ADC.anRaw[1] << 2) * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude - tmp = 2048 + ((int(vco.triangle(getPhase(), 32768) - 2048) * cv2) >> 12); // get level from position - tmp = fold(tmp); // fold if outside range - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_triangle_add: // triangle (sum: in1 is added to the oscillator) - cv2 = ((ADC.anRaw[1] << 2) * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude - tmp = (int(vco.triangle(getPhase(), 32768) + cv2) >> 1); // get level from position - tmp = fold(tmp); // fold if outside range - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_triangle_fm: // triangle (frequency modulation) - cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 8); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the frequency - tmp = vco.triangle(getPhase() + cv2, 32768); // get level from position (50% dyuty cycle) - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_triangle_saw: // triangle (shape modulation) - cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 8); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the pulse width - if (cv2 > 32767) // prevent wrap around - cv2 = 32767; // .. - cv2 = cv2 * 2; // double value to get full range without modulation signal (pot only) - tmp = vco.triangle(getPhase(), cv2); // get level from position (50% dyuty cycle) - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_triangle_2x: // 2 slightly detuned triangle oscillators - cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 18); // 0-256. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude - tmp = vco.triangle2x(getPhase(), cv2); // get level from position - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_triangle_3x: // 3 slightly detuned triangle oscillators - cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 17); // 0-256. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude - tmp = vco.triangle3x(getPhase(), cv2); // get level from position - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_Noise: // white noise generator (PRNG) - cv2 = (ADC.anRaw[0] * ADC.anAvg[4] >> 13); // pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude - tmp = noise.prng(cv2); // get level from position - dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) - addScope(ADC.anRaw[1], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_LFO_sine: // LFO sine - cv2 = ((ADC.anRaw[1] << 2) * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude - tmp = 2048 + ((int(vco.sine(getPhase() / 512) - 2048) * cv2) >> 12); // get level from position - tmp = fold(tmp); // fold if outside range - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[1], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_LFO_square: // LFO square - tmp = vco.square(getPhase() / 512, ADC.anAvg[3] << 4); // get level from position - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[1], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_LFO_triangle: // LFO triangle - tmp = vco.triangle(getPhase() / 512, ADC.anAvg[3] << 4); // get level from position - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[1], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_4_pole: // VCF (Voltage Controled Filter) - tmp = vcf.r4pole1(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, cutoff, resonance - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[0], tmp, vcf.phaseTrig); // store levels in scope buffer - break; - - case mode_2_pole_v1: // VCF (Voltage Controled Filter) - tmp = vcf.r2pole1(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, cutoff, resonance - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[0], tmp, vcf.phaseTrig); // store levels in scope buffer - break; - - case mode_2_pole_v2: // VCF (Voltage Controled Filter) - tmp = vcf.r2pole2(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, cutoff, resonance - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[0], tmp, vcf.phaseTrig); // store levels in scope buffer - break; - - case mode_test: // VCF (Voltage Controled Filter) - tmp = vcf.test(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, cutoff, resonance - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[0], tmp, vcf.phaseTrig); // store levels in scope buffer - break; - - case mode_VCA: // VCA (Voltage Controled Amplifier) - cv2 = ((ADC.anRaw[0] * ADC.anRaw[1] * ADC.anAvg[3]) >> 21); // pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude - cv2 += (ADC.anAvg[2] >> 1) - 2048; // add offset - dac_out(cv2, cv2); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[0], cv2, 1); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_SnH: // SnH (Sample and Hold) - tmp = vcf.test(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, cutoff, resonance - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[0], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_ADSR: // adsr - tmp = adsr.int_adsr(ADC.anRaw[0], ADC.anAvg[2] << 1, ADC.anAvg[4] << 1, ADC.anAvg[3] << 2, 0); // trig, A,D,R,!S - cv2 = 2048 + (((tmp - 2048) * (ADC.anRaw[1] - 2048)) >> 9); // - dac_out(tmp, cv2); // output level on DAC0 & DAC1 - break; - - case mode_ADR: // adsr - tmp = adsr.int_adsr(ADC.anRaw[0], ADC.anAvg[2] << 1, ADC.anAvg[4] << 1, ADC.anAvg[3] << 2, 1); // trig, A,D,R,!S - cv2 = 2048 + (((tmp - 2048) * (ADC.anRaw[1] - 2048)) >> 9); // - dac_out(tmp, cv2); // output level on DAC0 & DAC1 - break; - - case mode_Delay: // Delay - tmp = delay.delay(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, mix, length - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[0], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) - break; - - case mode_CV_Play: - tCount++; - if (tCount < (4095 - ADC.anAvg[2]) * 5) // genereate trig signal (50% duty) - cv2 = 3008; // 5V trig signal (> 3.3V threshold) - else // no trig - cv2 = 2048; // 0V - if (tCount > (4095 - ADC.anAvg[2]) * 10) // playback speed / tempo - { - tCount = 0; - songPos++; - if (songPos >= 48) - songPos = 0; - } - tmp = song[songPos]; // add pot + quantization? - dac_out(tmp, cv2); // output level on DAC0 & DAC1 - phaseC = 0; // DC => 0Hz - addScope(tmp, cv2, tCount ^ 1); // store levels in scope buffer - break; - - case mode_out_minus10: // callibrate -10V - dac_out(128, 128); // 3840*(-10+10)/20+128 = 128 - phaseC = 0; // DC => 0Hz - addScope(ADC.anRaw[1], tmp, 1); // store levels in scope buffer - break; - - case mode_out_0V: // callibrate 0V - dac_out(2048, 2048); // 3840*(0+10)/20+128 = 2048 - phaseC = 0; // DC => 0Hz - addScope(ADC.anRaw[1], tmp, 1); // store levels in scope buffer - break; - - case mode_out_3V3: // callibrate 3V3 - dac_out(2682, 2682); // 3840*(3.3+10)/20+128 = 2682 - phaseC = 0; // DC => 0Hz - addScope(ADC.anRaw[1], tmp, 1); // store levels in scope buffer - break; - - case mode_out_10V: // callibrate 10V - dac_out(3968, 3968); // 3840*(10+10)/20+128 = 3968 - phaseC = 0; // DC => 0Hz - addScope(ADC.anRaw[1], tmp, 1); // store levels in scope buffer - break; - - case mode_in1_to_out1: // calibrate input (connect out to in and match triangle waveform) - phaseC = 450; // - tmp = vco.triangle(phaseC, ADC.anAvg[3] << 4); // triangle waveform (get level from position) - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[0] * 2 - 2048, tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer - break; - - case mode_in2_to_out1: // calibrate input (connect out to in and match triangle waveform) - phaseC = 450; // - tmp = vco.triangle(phaseC, ADC.anAvg[3] << 4); // triangle waveform (get level from position) - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[1] * 2 - 2048, tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer - break; - - case mode_pass_thru: // pass thru to meassure noise and crosstalk - dac_out(ADC.anRaw[0], ADC.anRaw[1]); // output ADC0 to DAC0 & ACD1 to DAC1 - addScope(ADC.anRaw[0] * 2 - 2048, ADC.anRaw[1] * 2 - 2048, 1); // store levels in scope buffer - break; - - case mode_square_A4: // used to measure frequency accuracy - phaseC = 1299; // 440Hz - A4 concert pitch (midi 69) Should be 2611.2 @ 108MHz -> 44.4KHz - tmp = vco.square(phaseC, 32768); // square waveform (get level from position) - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anRaw[0], tmp, vco.phaseTrig); // store levels in scope buffer - break; - - case mode_manual_sine: - tmp = (ADC.anAvg[2] >> 1) + 1472; // 1472->3520 (not full range:1472->3584) - phaseC = ad2phase(tmp); // convert CV to logaritmic scale - tmp = vco.sine(phaseC); // get level from position - dac_out(tmp, tmp); // output level on DAC0 & DAC1 - addScope(ADC.anAvg[2], tmp, vco.phaseTrig); // store levels in scope buffer - break; - - case mode_manual_CV: // pot 3 controls cv1, pot2 controls cv2 - dac_out(ADC.anAvg[4], ADC.anAvg[3]); // 4095*(10+10)/20 = 4095 - phaseC = 0; // DC => 0Hz - addScope(ADC.anAvg[4], ADC.anAvg[3], 1); // store levels in scope buffer - break; - } - prevMode = moduleMode; - - // LED.green_off(); - - // Running average is done after DAC since it will add jitter due to uneven number of cycles - ADC.anCount++; - ADC.update(0); // ADC0 - ADC.update(1); // ADC1 - ADC.update(2); // pot0 (not inverted anymore) - ADC.update(3); // pot1 (not inverted anymore) - ADC.update(4); // pot2 + // here are the core of the things + switch (Menu.mainMode) { + // -------------------------- VCO ------------------------------------ + case modeVCO: + switch (Menu.subMode) { + case mode_sine_am: // sine (ampitude modulation) + tmp = vco.processSineAM(ADC.anAvg[0],ADC.anRaw[1],ADC.anAvg[3],ADC.anAvg[4],ADC.anAvg[2]) ; + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_sine_add: // sine (sum: in1 is added to the oscillator) + tmp = vco.processSineADD(ADC.anAvg[0],ADC.anRaw[1],ADC.anAvg[3],ADC.anAvg[4],ADC.anAvg[2]) ; + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_sine_fm: // sine (frequency modulation) + tmp = vco.processSineFM(ADC.anAvg[0],ADC.anRaw[1],ADC.anAvg[3],ADC.anAvg[4],ADC.anAvg[2]) ; + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_square_am: // square (amplitude modulation) + cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude + tmp = 2048 + ((int(vco.square(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]), 32768) - 2048) * cv2) >> 12); // get level from position + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_square_add: // square (sum: in1 is added to the oscillator) + cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude + tmp = (int(vco.square(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]), 32768) + cv2) >> 1); // get level from position + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_square_fm: // square (frequency modulation) + cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 12); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the frequency + tmp = vco.square(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]) + cv2, 32768); // get level from position (50% dyuty cycle) + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_square_pw: // square (pulsewidth modulation) + cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 8); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the pulse width + tmp = vco.square(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]), cv2); // get level from position (50% dyuty cycle) + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_square_2x: // 2 slightly detuned square oscillators + cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 18); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the pulse width + tmp = vco.square2x(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]), cv2); // get level from position (50% dyuty cycle) + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_square_3x: // 3 slightly detuned square oscillators + cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 17); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the pulse width + tmp = vco.square3x(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]), cv2); // get level from position (50% dyuty cycle) + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_triangle_am: // triangle (amplitude modulation) + cv2 = ((ADC.anRaw[1] << 2) * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude + tmp = 2048 + ((int(vco.triangle(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]), 32768) - 2048) * cv2) >> 12); // get level from position + tmp = vco.fold(tmp); // fold if outside range + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_triangle_add: // triangle (sum: in1 is added to the oscillator) + cv2 = ((ADC.anRaw[1] << 2) * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude + tmp = (int(vco.triangle(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]), 32768) + cv2) >> 1); // get level from position + tmp = vco.fold(tmp); // fold if outside range + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_triangle_fm: // triangle (frequency modulation) + cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 8); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the frequency + tmp = vco.triangle(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]) + cv2, 32768); // get level from position (50% dyuty cycle) + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_triangle_saw: // triangle (shape modulation) + cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 8); // 12+12-8=16 (0-65535). pot2 (bottom right) sets level for CV2 that modulates the pulse width + if (cv2 > 32767) // prevent wrap around + cv2 = 32767; // .. + cv2 = cv2 * 2; // double value to get full range without modulation signal (pot only) + tmp = vco.triangle(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]), cv2); // get level from position (50% dyuty cycle) + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_triangle_2x: // 2 slightly detuned triangle oscillators + cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 18); // 0-256. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude + tmp = vco.triangle2x(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]), cv2); // get level from position + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_triangle_3x: // 3 slightly detuned triangle oscillators + cv2 = (ADC.anRaw[1] * ADC.anAvg[3] >> 17); // 0-256. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude + tmp = vco.triangle3x(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]), cv2); // get level from position + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + } ; break ; + + // -------------------------- Noise ---------------------------------- + case modeNoise: // white noise generator (PRNG) + tmp = noise.process(ADC.anRaw[0],ADC.anAvg[4]) ; // sample, amp + DACS.dac_out(cvOut ? ADC.anRaw[0] : tmp, tmp); // output level on DAC1 & DAC0 if ! cvOut (else CV out on DAC0) + addScope(ADC.anRaw[1], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + // -------------------------- LFO ------------------------------------ + case modeLFO: + switch(Menu.subMode) { + case mode_LFO_sine: // LFO sine + cv2 = ((ADC.anRaw[1] << 2) * ADC.anAvg[3] >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude + tmp = 2048 + ((int(vco.sine(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]) / 512) - 2048) * cv2) >> 12); // get level from position + tmp = vco.fold(tmp); // fold if outside range + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[1], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_LFO_square: // LFO square + tmp = vco.square(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]) / 512, ADC.anAvg[3] << 4); // get level from position + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[1], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + case mode_LFO_triangle: // LFO triangle + tmp = vco.triangle(vco.getPhase(ADC.anAvg[0],ADC.anAvg[4],ADC.anAvg[2]) / 512, ADC.anAvg[3] << 4); // get level from position + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[1], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) + break; + + } ; break ; + + // -------------------------- VCF ------------------------------------ + case modeVCF: + switch(Menu.subMode) { + case mode_4_pole: // VCF (Voltage Controled Filter) + tmp = vcf.r4pole1(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, cutoff, resonance + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[0], tmp, vcf.phaseTrig); // store levels in scope buffer + break; + + case mode_2_pole_v1: // VCF (Voltage Controled Filter) + tmp = vcf.r2pole1(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, cutoff, resonance + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[0], tmp, vcf.phaseTrig); // store levels in scope buffer + break; + + case mode_2_pole_v2: // VCF (Voltage Controled Filter) + tmp = vcf.r2pole2(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, cutoff, resonance + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[0], tmp, vcf.phaseTrig); // store levels in scope buffer + break; + + case mode_test: // VCF (Voltage Controled Filter) + tmp = vcf.test(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, cutoff, resonance + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[0], tmp, vcf.phaseTrig); // store levels in scope buffer + break; + + } ; break ; + + // -------------------------- VCA ------------------------------------ + case modeVCA: // VCA (Voltage Controled Amplifier) + tmp = vca.process(ADC.anRaw[0],ADC.anAvg[1],ADC.anAvg[3],ADC.anAvg[2]) ; // sample, env, amp, offset + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[0], tmp , 1); // store levels in scope buffer + break; - // Start ADC sampling (using DMA) - adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL); // start ADC: DMA => adc_value[] + // -------------------------- Env ------------------------------------ + case modeEnv: + switch (Menu.subMode) { + case mode_ADSR: // adsr + tmp = env.int_adsr(ADC.anRaw[0], ADC.anAvg[2] << 1, ADC.anAvg[4] << 1, ADC.anAvg[3] << 2, 0); // trig, A,D,R,!S + cv2 = 2048 + (((tmp - 2048) * (ADC.anRaw[1] - 2048)) >> 9); // + DACS.dac_out(tmp, cv2); // output level on DAC0 & DAC1 + break; + + case mode_ADR: // adsr + tmp = env.int_adsr(ADC.anRaw[0], ADC.anAvg[2] << 1, ADC.anAvg[4] << 1, ADC.anAvg[3] << 2, 1); // trig, A,D,R,!S + cv2 = 2048 + (((tmp - 2048) * (ADC.anRaw[1] - 2048)) >> 9); // + DACS.dac_out(tmp, cv2); // output level on DAC0 & DAC1 + break; + + } ; break ; + + // -------------------------- Delay ---------------------------------- + case modeDelay: // Delay + tmp = delay.delay(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, mix, length + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[0], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) + break; - // handle menu from rotation encoder + // -------------------------- Calibration ---------------------------- + case modeCalibrate: + switch (Menu.subMode) { + case mode_out_minus10: // callibrate -10V + DACS.dac_out(128, 128); // 3840*(-10+10)/20+128 = 128 + vco.phaseC = 0; // DC => 0Hz + addScope(ADC.anRaw[1], tmp, 1); // store levels in scope buffer + break; + + case mode_out_0V: // callibrate 0V + DACS.dac_out(2048, 2048); // 3840*(0+10)/20+128 = 2048 + vco.phaseC = 0; // DC => 0Hz + addScope(ADC.anRaw[1], tmp, 1); // store levels in scope buffer + break; + + case mode_out_3V3: // callibrate 3V3 + DACS.dac_out(2682, 2682); // 3840*(3.3+10)/20+128 = 2682 + vco.phaseC = 0; // DC => 0Hz + addScope(ADC.anRaw[1], tmp, 1); // store levels in scope buffer + break; + + case mode_out_10V: // callibrate 10V + DACS.dac_out(3968, 3968); // 3840*(10+10)/20+128 = 3968 + vco.phaseC = 0; // DC => 0Hz + addScope(ADC.anRaw[1], tmp, 1); // store levels in scope buffer + break; + + case mode_in1_to_out1: // calibrate input (connect out to in and match triangle waveform) + vco.phaseC = 450; // + tmp = vco.triangle(vco.phaseC, ADC.anAvg[3] << 4); // triangle waveform (get level from position) + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[0] * 2 - 2048, tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer + break; + + case mode_in2_to_out1: // calibrate input (connect out to in and match triangle waveform) + vco.phaseC = 450; // + tmp = vco.triangle(vco.phaseC, ADC.anAvg[3] << 4); // triangle waveform (get level from position) + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[1] * 2 - 2048, tmp * 2 - 2048, vco.phaseTrig); // store levels in scope buffer + break; + + case mode_pass_thru: // pass thru to meassure noise and crosstalk + DACS.dac_out(ADC.anRaw[0], ADC.anRaw[1]); // output ADC0 to DAC0 & ACD1 to DAC1 + addScope(ADC.anRaw[0] * 2 - 2048, ADC.anRaw[1] * 2 - 2048, 1); // store levels in scope buffer + break; + + case mode_square_A4: // used to measure frequency accuracy + vco.phaseC = 1299; // 440Hz - A4 concert pitch (midi 69) Should be 2611.2 @ 108MHz -> 44.4KHz + tmp = vco.square(vco.phaseC, 32768); // square waveform (get level from position) + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[0], tmp, vco.phaseTrig); // store levels in scope buffer + break; + + case mode_manual_sine: + tmp = (ADC.anAvg[2] >> 1) + 1472; // 1472->3520 (not full range:1472->3584) + vco.phaseC = vco.ad2phase(tmp); // convert CV to logaritmic scale + tmp = vco.sine(vco.phaseC); // get level from position + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anAvg[2], tmp, vco.phaseTrig); // store levels in scope buffer + break; + + case mode_manual_CV: // pot 3 controls cv1, pot2 controls cv2 + DACS.dac_out(ADC.anAvg[4], ADC.anAvg[3]); // 4095*(10+10)/20 = 4095 + vco.phaseC = 0; // DC => 0Hz + addScope(ADC.anAvg[4], ADC.anAvg[3], 1); // store levels in scope buffer + break; + + } ; break ; + + /* case mode_CV_Play: + tCount++; + if (tCount < (4095 - ADC.anAvg[2]) * 5) // genereate trig signal (50% duty) + cv2 = 3008; // 5V trig signal (> 3.3V threshold) + else // no trig + cv2 = 2048; // 0V + if (tCount > (4095 - ADC.anAvg[2]) * 10) // playback speed / tempo + { + tCount = 0; + songPos++; + if (songPos >= 48) + songPos = 0; + } + tmp = song[songPos]; // add pot + quantization? + DACS.dac_out(tmp, cv2); // output level on DAC0 & DAC1 + vco.phaseC = 0; // DC => 0Hz + addScope(tmp, cv2, tCount ^ 1); // store levels in scope buffer + break; */ /* case mode_CV_Play: + tCount++; + if (tCount < (4095 - ADC.anAvg[2]) * 5) // genereate trig signal (50% duty) + cv2 = 3008; // 5V trig signal (> 3.3V threshold) + else // no trig + cv2 = 2048; // 0V + if (tCount > (4095 - ADC.anAvg[2]) * 10) // playback speed / tempo + { + tCount = 0; + songPos++; + if (songPos >= 48) + songPos = 0; + } + tmp = song[songPos]; // add pot + quantization? + DACS.dac_out(tmp, cv2); // output level on DAC0 & DAC1 + vco.phaseC = 0; // DC => 0Hz + addScope(tmp, cv2, tCount ^ 1); // store levels in scope buffer + break; */ + + /* case mode_SnH: // SnH (Sample and Hold) + tmp = vcf.test(ADC.anRaw[0], ADC.anAvg[4], ADC.anAvg[3]); // sample, cutoff, resonance + DACS.dac_out(tmp, tmp); // output level on DAC0 & DAC1 + addScope(ADC.anRaw[0], tmp * 2 - 2048, 1); // store levels in scope buffer (amplifies oscillator output 2x) + break; + */ + } ; - #if (pcb_version == 0) - #define cw 'W' - #define cc 'C' - #else - #define cw 'C' - #define cc 'W' - #endif + // update ADC averages and pull new values + ADC.update() ; + + // TODO: check if it can't be moved to the main loop to keep audio loop from jitters + char encoderValue = Encoder.get() ; + switch (encoderValue) { + case cw : Menu.prev() ; break ; + case cc : Menu.next() ; break ; + case 'S': Menu.switchMode() ; break ; + }; - switch (enc.get()) - { - case 0: - LED.red_off(); - LED.blue_off(); - break; - case cw: // test clockwise (next menu) - LED.red_on(); - menuPos = menuNxt[menuPos]; - menuUpd = 1; - break; - case cc: // test counter clockwise (previous menu) - LED.blue_on(); - menuPos = menuPre[menuPos]; - menuUpd = 1; - break; - case 'S': // test encoder click (menu select) - if (menuSel[menuPos] < 128) - { - menuPos = menuSel[menuPos]; - } - else - moduleMode = menuSel[menuPos]; - menuUpd = 1; // for main loop (update display) - break; - } } -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| -int main() -{ + +int main() { + + uint8_t oldMainMode = 0 ; + uint8_t needScope = 1 ; + char str[16] ; + + Menu.mainMode = defaultMain ; // main mode VCO / VCA / VCF / .... + Menu.subMode = defaultSub ; // sub mode : sine_am / triangle_fm / etc .... + SPI.begin(); - LED.begin(); stk.begin(1000); rcu_config(); gpio_config(); dma_config(); - initADC(); - initDAC(); - // initUART(); - Display.begin(&SPI, font); - Display.showImg(img_1337, sizeof(img_1337), 0, 50, 240, 86, RGBToWord(0x00, 0xff, 0xff)); - Display.showImg(img_modular, sizeof(img_modular), 0, 150, 240, 32, RGBToWord(0xff, 0x00, 0xff)); + + ADC.init(stk); + DACS.init(); + + Display.begin(&SPI); + + // Welome screen + sprintf(str,version) ; + Display.showImg(img_1337, sizeof(img_1337), 0, 50, 240, 86, welcomeUpperColor); + Display.showImg(img_modular, sizeof(img_modular), 0, 150, 240, 32, welcomeLowerColor); + Display.putStr(str,80,220,welcomeUpperColor,blackColor); stk.delay(2000); - Display.fillRectangle(0, 0, 240, 240, RGBToWord(0x0, 0x0, 0x0)); - Display.drawRectangle(0, 70, 239, 79, RGBToWord(0xff, 0xff, 0xff)); - T.begin(); - T.attach(OnTimer); - uint8_t oldMode = 0; - while (1) - { - if (moduleMode != oldMode) // update display icon - { - switch (moduleMode) - { - case mode_sine_am: - case mode_sine_add: - case mode_sine_fm: - case mode_square_am: - case mode_square_add: - case mode_square_fm: - case mode_square_pw: - case mode_triangle_am: - case mode_triangle_add: - case mode_triangle_fm: - case mode_triangle_saw: - case mode_triangle_2x: - case mode_triangle_3x: - Display.showImg(img_vco, sizeof(img_vco), 0, 0, 240, 60, RGBToWord(0x00, 0xff, 0xff)); - break; - case mode_Noise: - noise.init(Display); - break; - case mode_LFO_sine: - case mode_LFO_square: - case mode_LFO_triangle: - Display.showImg(img_lfo, sizeof(img_lfo), 0, 0, 240, 60, RGBToWord(0x00, 0xff, 0xff)); - break; - case mode_4_pole: - case mode_2_pole_v1: - case mode_2_pole_v2: - case mode_test: - Display.showImg(img_vcf, sizeof(img_vcf), 0, 0, 240, 60, RGBToWord(0x00, 0xff, 0xff)); - break; - case mode_ADSR: - adsr.init(Display); - adsr.draw(Display, ADC.anRaw[0], ADC.anAvg[2], ADC.anAvg[4], ADC.anAvg[3], 0); - case mode_ADR: - adsr.init(Display); - adsr.draw(Display, ADC.anRaw[0], ADC.anAvg[2], ADC.anAvg[4], ADC.anAvg[3], 1); - break; - case mode_VCA: - Display.showImg(img_vca, sizeof(img_vca), 0, 0, 240, 60, RGBToWord(0x00, 0xff, 0xff)); - break; - case mode_SnH: - Display.showImg(img_snh, sizeof(img_snh), 0, 0, 240, 60, RGBToWord(0x00, 0xff, 0xff)); - break; - case mode_Delay: - Display.showImg(img_dly, sizeof(img_dly), 0, 0, 240, 60, RGBToWord(0x00, 0xff, 0xff)); - break; - case mode_CV_Play: - Display.showImg(img_play, sizeof(img_play), 0, 0, 240, 60, RGBToWord(0x00, 0xff, 0xff)); - break; - case mode_out_minus10: - case mode_out_0V: - case mode_out_3V3: - case mode_out_10V: - case mode_in1_to_out1: - case mode_in2_to_out1: - case mode_manual_sine: - case mode_square_A4: - Display.showImg(img_cal, sizeof(img_cal), 0, 0, 240, 60, RGBToWord(0xff, 0x00, 0xff)); - break; - default: - Display.fillRectangle(0, 0, 240, 60, RGBToWord(0x0, 0x0, 0x0)); - break; - } - oldMode = moduleMode; - } - switch (moduleMode) - { - case mode_ADSR: - adsr.draw(Display, ADC.anRaw[0], ADC.anAvg[2], ADC.anAvg[4], ADC.anAvg[3], 0); // display, gate, A, D, R, !S - break; - case mode_ADR: - adsr.draw(Display, ADC.anRaw[0], ADC.anAvg[2], ADC.anAvg[4], ADC.anAvg[3], 1); // display, gate, A, D, R, !S - break; - } + //clean screen + Display.fillRectangle(0, 0, 240, 240, blackColor ); + + // create and attach main timer loop @44K + // this loop is timer driven and handle audio part + Timer.begin(); + Timer.attach(OnTimer); + + // endless loop which handle screen + while (1) { + if (Menu.mainMode != oldMainMode) { + switch (Menu.mainMode) { + case modeVCO: + needScope = 1 ; + Menu.logoImage = vcoImage ; + Menu.imgSize = sizeof(vcoImage) ; + Menu.subModeColor = vcoColor ; + Menu.subMenuTxt = vco.subMenuText ; + Menu.subMenuMax = vcoSubMenuMax ; + Menu.potsName = vco.potsName ; + break ; + case modeVCA: + needScope = 1 ; + Menu.logoImage = vcaImage ; + Menu.imgSize = sizeof(vcaImage) ; + Menu.subModeColor = vcaColor ; + Menu.subMenuTxt = vca.subMenuText ; + Menu.subMenuMax = vcaSubMenuMax ; + Menu.potsName = vca.potsName ; + break ; + case modeVCF: + needScope = 1 ; + Menu.logoImage = vcfImage ; + Menu.imgSize = sizeof(vcfImage) ; + Menu.subModeColor = vcfColor ; + Menu.subMenuTxt = vcf.subMenuText ; + Menu.subMenuMax = vcfSubMenuMax ; + Menu.potsName = vcf.potsName ; + break ; + case modeLFO: + needScope = 1 ; + Menu.logoImage = lfoImage ; + Menu.imgSize = sizeof(lfoImage) ; + Menu.subModeColor = lfoColor ; + Menu.subMenuTxt = lfo.subMenuText ; + Menu.subMenuMax = lfoSubMenuMax ; + Menu.potsName = lfo.potsName ; + break ; + case modeEnv: + needScope = 0 ; + Menu.logoImage = envImage ; + Menu.imgSize = sizeof(envImage) ; + Menu.subModeColor = envColor ; + Menu.subMenuTxt = env.subMenuText ; + Menu.subMenuMax = envSubMenuMax ; + Menu.potsName = env.potsName ; + break ; + case modeDelay: + needScope = 1 ; + Menu.logoImage = delayImage ; + Menu.imgSize = sizeof(delayImage) ; + Menu.subModeColor = delayColor ; + Menu.subMenuTxt = delay.subMenuText ; + Menu.subMenuMax = delaySubMenuMax ; + Menu.potsName = delay.potsName ; + break ; + case modeNoise: + needScope = 1 ; + Menu.logoImage = noiseImage ; + Menu.imgSize = sizeof(noiseImage) ; + Menu.subModeColor = noiseColor ; + Menu.subMenuTxt = noise.subMenuText ; + Menu.subMenuMax = noiseSubMenuMax ; + Menu.potsName = noise.potsName ; + break ; + case modeCalibrate: + needScope = 1 ; + Menu.logoImage = calImage ; + Menu.imgSize = sizeof(calImage) ; + Menu.subModeColor = calColor ; + Menu.subMenuTxt = cal.subMenuText ; + Menu.subMenuMax = calSubMenuMax ; + Menu.potsName = cal.potsName ; + break ; + } - if (dispPos >= 240) - { - // show scope - for (uint32_t i = 1; i < SCOPE_BUFFER_SIZE - 1; i++) - { - ch0old.draw1(Display, i); // clear old pixel - ch0.draw1(Display, i); // write new pixel - ch0old.write(i, ch0.read(i)); // save old position - ch1old.draw1(Display, i); - ch1.draw1(Display, i); - ch1old.write(i, ch1.read(i)); - } - dispPos = 0; + Menu.drawSkel(Display) ; // draw the basic mode skel + Menu.drawPotsName(Display) ; // draw pots Name + oldMainMode = Menu.mainMode; // update old mode - // show AD values - char str[11]; - sprintf(str, "%d", ADC.anAvg[0]); - Display.fillRectangle(130, 160, 40, 16, RGBToWord(0x00, 0x00, 0x00)); - Display.putStr(str, 130 - 9, 160, RGBToWord(0x00, 0x00, 0xff), RGBToWord(0x00, 0x00, 0x00)); - sprintf(str, "%d", ADC.anAvg[1]); - Display.fillRectangle(190, 160, 40, 16, RGBToWord(0x00, 0x00, 0x00)); - Display.putStr(str, 190 - 9, 160, RGBToWord(0x00, 0x00, 0xff), RGBToWord(0x00, 0x00, 0x00)); - if (!quantized || !cvOut) - Display.fillRectangle(190, 180, 40, 16, RGBToWord(0x00, 0x00, 0x00)); - if (quantized) - { - sprintf(str, "Q"); - Display.putStr(str, 190 - 9, 180, RGBToWord(0xff, 0x00, 0x00), RGBToWord(0x00, 0x00, 0x00)); - } - if (cvOut) - { - sprintf(str, "C"); - Display.putStr(str, 210 - 9, 180, RGBToWord(0xff, 0x00, 0xff), RGBToWord(0x00, 0x00, 0x00)); - } - // show pot values - sprintf(str, "%d", ADC.anAvg[2]); - Display.fillRectangle(130, 180, 40, 16, RGBToWord(0x00, 0x00, 0x00)); - Display.putStr(str, 130 - 9, 180, RGBToWord(0x00, 0xff, 0x00), RGBToWord(0x00, 0x00, 0x00)); - sprintf(str, "%d", ADC.anAvg[3]); - Display.fillRectangle(130, 200, 40, 16, RGBToWord(0x00, 0x00, 0x00)); - Display.putStr(str, 130 - 9, 200, RGBToWord(0x00, 0xff, 0x00), RGBToWord(0x00, 0x00, 0x00)); - sprintf(str, "%d", ADC.anAvg[4]); - Display.fillRectangle(130, 220, 40, 16, RGBToWord(0x00, 0x00, 0x00)); - Display.putStr(str, 130 - 9, 220, RGBToWord(0x00, 0xff, 0x00), RGBToWord(0x00, 0x00, 0x00)); - uint16_t freqency = int(phaseC * 44400 / (2 * 65536)); - if (freqency < 1000) - sprintf(str, "%dHz", freqency); - else - sprintf(str, "%d", freqency); - Display.fillRectangle(190, 200, 50, 16, RGBToWord(0x00, 0x00, 0x00)); - Display.putStr(str, 190 - 9, 200, RGBToWord(0xff, 0xff, 0xff), RGBToWord(0x00, 0x00, 0x00)); - sprintf(str, "%d V", (4095 - ADC.anAvg[2]) * 10 / 2048 - 10); - Display.fillRectangle(190, 220, 50, 16, RGBToWord(0x00, 0x00, 0x00)); - Display.putStr(str, 190 - 9, 220, RGBToWord(0xff, 0xff, 0xff), RGBToWord(0x00, 0x00, 0x00)); } - // show menu - if (menuUpd != 0) - { - char str[11]; - Display.fillRectangle(18, 160, 110, 20, RGBToWord(0x00, 0x00, 0x00)); - if (menuPos != menuPre[menuPos]) - Display.putStr(menuTxt[menuPre[menuPos]], 10, 160, RGBToWord(0xff, 0xff, 0xff), RGBToWord(0x00, 0x00, 0x00)); + // redraw submenu if needed + if (Menu.needUpdate == 1) Menu.drawSubmode(Display) ; + + // show AD(S)R + if (Menu.mainMode == modeEnv) { + switch (Menu.subMode) { + case mode_ADSR: + env.draw(Display, ADC.anRaw[0], ADC.anAvg[2], ADC.anAvg[4], ADC.anAvg[3], 0); // display, gate, A, D, R, !S + break; + case mode_ADR: + env.draw(Display, ADC.anRaw[0], ADC.anAvg[2], ADC.anAvg[4], ADC.anAvg[3], 1); // display, gate, A, D, R, !S + break; + } + } - sprintf(str, ">"); - Display.putStr(str, 0, 180, RGBToWord(0x00, 0xff, 0xff), RGBToWord(0x00, 0x00, 0x00)); + if (dispPos >= 240 ) { - Display.fillRectangle(18, 180, 110, 20, RGBToWord(0x00, 0x00, 0x00)); - Display.putStr(menuTxt[menuPos], 10, 180, RGBToWord(0x00, 0xff, 0xff), RGBToWord(0x00, 0x00, 0x00)); - if (menuPos != menuNxt[menuPos]) - { - Display.fillRectangle(18, 200, 110, 40, RGBToWord(0x00, 0x00, 0x00)); - Display.putStr(menuTxt[menuNxt[menuPos]], 10, 200, RGBToWord(0xff, 0xff, 0xff), RGBToWord(0x00, 0x00, 0x00)); - if (menuNxt[menuPos] != menuNxt[menuNxt[menuPos]]) - { - Display.fillRectangle(18, 220, 110, 20, RGBToWord(0x00, 0x00, 0x00)); - Display.putStr(menuTxt[menuNxt[menuNxt[menuPos]]], 10, 220, RGBToWord(0xff, 0xff, 0xff), RGBToWord(0x00, 0x00, 0x00)); + // show scope + if (needScope == 1 ) { + //scope.showScope(Display) ; + for (uint32_t i = 1; i < SCOPE_BUFFER_SIZE - 1; i++) { + + ch0old.draw1(Display, i); // clear old pixel + ch0.draw1(Display, i); // write new pixel + ch0old.write(i, ch0.read(i)); // save old position + + ch1old.draw1(Display, i); + ch1.draw1(Display, i); + ch1old.write(i, ch1.read(i)); } + dispPos = 0; } - else - { - Display.fillRectangle(18, 200, 110, 40, RGBToWord(0x00, 0x00, 0x00)); // end of menu, clear display + + } + + // show AD values + // --------------------------- Line 1 -------------------------------- + // Freq only for VCO + if (Menu.mainMode == modeVCO) { + uint16_t freqency = int(vco.phaseC * 44400 / (2 * 65536)); + if (freqency < 1000) + sprintf(str, "Freq : %dHz", freqency); + else + sprintf(str, "Freq : %d", freqency); + Display.fillRectangle(64, 180, 72, 16, blackColor); + Display.putStr(str, 0, 180, whiteColor, blackColor); + } else { + Display.fillRectangle(0, 180, 64, 16, blackColor); } - menuUpd = 0; + + // POT UR + sprintf(str, "%d",ADC.anAvg[2]); + Display.fillRectangle(130+64, 180, 40, 16, blackColor); + Display.putStr(str, 130+64, 180, potUpperRightColor, blackColor); + + // --------------------------- Line 2 -------------------------------- + // POT LL + sprintf(str, "%d",ADC.anAvg[4]); + Display.fillRectangle(64,200, 64, 16, blackColor); + Display.putStr(str, 64 , 200, potLowerLeftColor, blackColor); + + // POT LR + sprintf(str, "%d",ADC.anAvg[3]); + Display.fillRectangle(130+64, 200, 88, 16, blackColor); + Display.putStr(str, 130+64, 200, potLowerRightColor, blackColor); + + // --------------------------- Line 3 -------------------------------- + // IN 1 + sprintf(str, "In 0 : %d", ADC.anRaw[0]); + //sprintf(str, "In 1 : %d V", (4095 - ADC.anAvg[0]) * 10 / 2048 - 10); + Display.fillRectangle(64, 220, 64, 16, blackColor); + Display.putStr(str, 0, 220, in1Color, blackColor); + + // IN 2 + sprintf(str, "In 1 : %d", ADC.anRaw[1]); + //sprintf(str, "In 2 : %d V", (4095 - ADC.anAvg[1]) * 10 / 2048 - 10); + Display.fillRectangle(130+64, 220, 64, 16, blackColor); + Display.putStr(str, 130 , 220, in2Color, blackColor); + + } - } + } -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| \ No newline at end of file diff --git a/firmware/generic/src/module_adsr.h b/firmware/generic/src/module_adsr.h deleted file mode 100644 index d1c71b6..0000000 --- a/firmware/generic/src/module_adsr.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef __module_adsr_h -#define __module_adsr_h -extern "C" -{ -#include "drivers/display.h" -} - -class module_adsr -{ -public: - module_adsr(){}; - uint16_t int_adsr(uint16_t gate, uint16_t pot_a, uint16_t pot_d, uint16_t pot_r, uint8_t noSustain); - void draw(display &disp, uint16_t gate, uint16_t pot_a, uint16_t pot_d, uint16_t pot_r, uint8_t noSustain); - void init(display &disp); - -private: - uint16_t tCount; - uint8_t adsrMode; - uint8_t oldGate; - uint8_t xorGate; - int8_t gateCount; - uint16_t a, d, s, r; -}; -#endif \ No newline at end of file diff --git a/firmware/generic/src/module_delay.h b/firmware/generic/src/module_delay.h deleted file mode 100644 index 77d7581..0000000 --- a/firmware/generic/src/module_delay.h +++ /dev/null @@ -1,20 +0,0 @@ -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| -#ifndef __module_delay_h -#define __module_delay_h -extern "C" -{ -#include -} - -class module_delay -{ -public: - module_delay(){}; - uint16_t delay(uint16_t sample, uint16_t mix, uint16_t length); - -private: - uint16_t anHistory[4096]; // old readings from the analog input. Uses 8192 bytes of RAM... - uint16_t anPos; // index of the current reading -}; -#endif -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| \ No newline at end of file diff --git a/firmware/generic/src/module_noise.cpp b/firmware/generic/src/module_noise.cpp deleted file mode 100644 index 9bb5096..0000000 --- a/firmware/generic/src/module_noise.cpp +++ /dev/null @@ -1,37 +0,0 @@ -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| -#include "module_noise.h" -#include "drivers/display.h" - -// compressed image generated with python script (used by init) -const uint8_t img_rnd[175] = {255, 255, 255, 255, 255, 255, 255, 255, 132, 59, 15, 59, 9, 65, 25, 1, 59, 0, 12, 1, 59, 0, 74, 0, 23, 0, 62, 0, 10, 0, 62, 0, 74, 1, 20, 0, 64, 0, 8, 0, 64, 0, 95, 0, 66, 0, 6, 0, 66, 0, 74, 0, 239, 0, 16, 0, 68, 0, 4, 0, 68, 0, 255, 255, 255, 255, 43, 48, 26, 48, 26, 48, 255, 255, 255, 233, 48, 255, 255, 255, 255, 189, 0, 255, 222, 0, 237, 0, 237, 0, 237, 0, 183, 41, 255, 224, 0, 11, 0, 255, 211, 0, 11, 0, 255, 79, 48, 82, 0, 11, 0, 255, 211, 0, 11, 0, 245, 0, 219, 0, 11, 0, 74, 0, 74, 0, 93, 0, 219, 0, 11, 0, 72, 0, 74, 0, 95, 0, 64, 0, 74, 0, 77, 0, 11, 0, 5, 1, 61, 0, 74, 0, 100, 1, 56, 2, 73, 1, 20, 10, 48, 12, 9, 3, 48, 3, 11, 64, 255, 255, 255, 255, 255, 255, 165}; -#define logoColor RGBToWord(0x00, 0xff, 0xff) -//#define black RGBToWord(0x00, 0x00, 0x00) - -/* potentiometer and I/O mapping - R P2 - P4 P3 - C0 C1 - D0 D1 -*/ - -// simple pseudo random noise generator -uint16_t module_noise::prng(uint16_t volume) -{ - LowBit = Rnd & 1; - Rnd >>= 1; - Rnd ^= LowBit ? 0x80000057ul : 0ul; - if (LowBit) - return (2048+volume); // 2048 = 0V - else - return (2048-volume); -} - -void module_noise::init(display &disp) -{ - Rnd = 4294967294; - // show logo - disp.showImg(img_rnd, sizeof(img_rnd), 0, 0, 240, 60, logoColor); -} - - -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| \ No newline at end of file diff --git a/firmware/generic/src/module_noise.h b/firmware/generic/src/module_noise.h deleted file mode 100644 index b9c4cd7..0000000 --- a/firmware/generic/src/module_noise.h +++ /dev/null @@ -1,18 +0,0 @@ -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| -#ifndef __module_noise_h -#define __module_noise_h -#include "drivers/display.h" - -class module_noise -{ -public: - module_noise(){}; - uint16_t prng(uint16_t volume); - void init(display &disp); - -private: - uint32_t Rnd; // random seed - uint8_t LowBit; // noise out -}; -#endif -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| \ No newline at end of file diff --git a/firmware/generic/src/module_vcf.h b/firmware/generic/src/module_vcf.h deleted file mode 100644 index 6b915d6..0000000 --- a/firmware/generic/src/module_vcf.h +++ /dev/null @@ -1,26 +0,0 @@ -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| -#ifndef __module_vcf_h -#define __module_vcf_h -extern "C" -{ -#include -} - -class module_vcf -{ -public: - module_vcf(){}; - uint8_t phaseTrig; - int16_t filterValue; - int16_t filterOut; - uint16_t r4pole1(uint16_t sample, uint16_t cutoff, uint16_t resonance); - uint16_t r2pole1(uint16_t sample, uint16_t cutoff, uint16_t resonance); - uint16_t r2pole2(uint16_t sample, uint16_t cutoff, uint16_t resonance); - uint16_t test(uint16_t sample, uint16_t cutoff, uint16_t resonance); - -private: - static float in1, in2, in3, in4, buf1, buf2, buf3, buf4, fb, in, cut, q; - static int16_t oldOut; -}; -#endif -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| \ No newline at end of file diff --git a/firmware/generic/src/module_vco.h b/firmware/generic/src/module_vco.h deleted file mode 100644 index c901b36..0000000 --- a/firmware/generic/src/module_vco.h +++ /dev/null @@ -1,31 +0,0 @@ -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| -#ifndef __module_vco_h -#define __module_vco_h -extern "C" -{ -#include -} - -class module_vco -{ -public: - module_vco(){}; - uint8_t phaseTrig; - float sine(float xTmp); - // float sine3x(float xTmp, uint16_t dist); - uint16_t square(float xTmp, uint16_t pw); - uint16_t square2x(float xTmp, uint16_t dist); - uint16_t square3x(float xTmp, uint16_t dist); - uint16_t triangle(float xTmp, uint16_t shape); - uint16_t triangle2x(float xTmp, uint16_t dist); - uint16_t triangle3x(float xTmp, uint16_t dist); - // uint16_t int_saw(uint16_t x); - // uint16_t int_r_saw(uint16_t x); - -private: - float phase, phase1, phase2, phase3; - float oldPhase; - float sin(float x); -}; -#endif -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| \ No newline at end of file diff --git a/firmware/generic/src/modules/module_cal.cpp b/firmware/generic/src/modules/module_cal.cpp new file mode 100644 index 0000000..dda8af8 --- /dev/null +++ b/firmware/generic/src/modules/module_cal.cpp @@ -0,0 +1 @@ +# include "module_cal.h" diff --git a/firmware/generic/src/modules/module_cal.h b/firmware/generic/src/modules/module_cal.h new file mode 100644 index 0000000..2f6fb63 --- /dev/null +++ b/firmware/generic/src/modules/module_cal.h @@ -0,0 +1,28 @@ +#ifndef __module_cal_h +#define __module_cal_h + +#include "gd32vf103.h" + +// sub modes +#define mode_out_minus10 0 +#define mode_out_0V 1 +#define mode_out_3V3 2 +#define mode_out_10V 3 +#define mode_in1_to_out1 4 +#define mode_in2_to_out1 5 +#define mode_pass_thru 6 +#define mode_square_A4 7 +#define mode_manual_sine 8 +#define mode_manual_CV 9 + +#define calColor main2Color +#define calSubMenuMax 10 +const uint8_t calImage[480] = {255,255,255,255,255,255,255,24,13,161,5,3,0,49,2,13,3,25,13,21,11,80,2,4,0,2,0,47,2,20,2,21,0,125,2,6,0,2,0,47,0,26,1,18,0,35,0,88,0,12,0,46,1,97,0,75,0,9,0,48,1,49,0,15,0,118,0,2,0,44,0,32,0,138,1,16,0,44,0,51,0,36,0,81,0,14,0,47,0,51,0,49,0,67,1,14,0,2,0,4,0,37,0,17,4,148,0,3,0,14,0,43,0,14,3,4,2,8,0,15,0,117,0,16,0,62,0,11,1,62,0,88,0,5,0,2,0,43,0,12,1,14,1,20,0,20,0,28,0,131,0,12,0,18,0,18,0,134,1,2,0,56,0,20,0,1,0,147,2,3,0,43,0,35,1,14,0,10,1,102,0,13,1,64,0,80,0,73,0,4,0,16,0,43,0,11,0,39,0,10,0,40,0,60,0,1,0,30,0,86,0,10,0,114,1,10,0,124,0,87,0,8,1,11,0,44,0,11,0,38,0,10,0,29,0,77,0,2,0,167,0,83,0,97,0,10,0,186,0,37,0,117,0,3,0,15,0,109,0,5,0,23,0,77,0,114,0,10,0,43,0,62,0,0,0,16,0,46,0,165,0,4,0,31,0,82,0,10,0,19,0,83,0,21,0,96,0,115,0,1,0,131,9,22,0,93,0,96,0,57,0,255,41,0,2,0,18,0,62,0,21,1,8,0,119,0,35,0,33,0,33,0,9,0,46,0,92,0,64,0,17,1,48,0,20,0,59,0,102,1,11,0,117,0,29,1,57,1,11,1,134,0,26,0,1,0,42,0,14,2,4,3,7,0,5,0,11,15,19,0,11,20,76,0,54,4,17,0,11,0,68,0,37,0,75,0,51,0,137,0,86,0,120,0,22,0,5,2,4,0,38,0,31,0,16,0,29,0,102,0,53,0,35,0,30,0,17,0,31,0,36,0,37,0,41,0,28,0,3,0,11,0,108,0,25,1,7,1,43,1,24,1,16,0,138,7,47,1,19,2,5,0,209,2,13,2,8,12,21,11,5,32,127,13,255,255,255,255,255,29}; + +class module_cal +{ + public: + const char *subMenuText[10] = {"out -10V", "out 0V", "out 3V3", "out 10V", "in1 to out1", "in2 to out1", "pass thru", "square 440Hz", "manual sine", "manual CV"} ; + const char *potsName[3] = {" "," "," "} ; +}; +#endif \ No newline at end of file diff --git a/firmware/generic/src/module_delay.cpp b/firmware/generic/src/modules/module_delay.cpp similarity index 84% rename from firmware/generic/src/module_delay.cpp rename to firmware/generic/src/modules/module_delay.cpp index 9459280..074fba7 100644 --- a/firmware/generic/src/module_delay.cpp +++ b/firmware/generic/src/modules/module_delay.cpp @@ -1,4 +1,3 @@ -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| #include "module_delay.h" extern "C" { @@ -14,7 +13,6 @@ extern "C" uint16_t module_delay::delay(uint16_t sample, uint16_t mix, uint16_t length) { - static uint16_t anHistory[4096]; // old readings from the analog input. Uses 8192 bytes of RAM... static uint16_t anPos = 0; // index of the current reading uint16_t old; // @@ -28,4 +26,3 @@ uint16_t module_delay::delay(uint16_t sample, uint16_t mix, uint16_t length) anPos++; // advance to the next position in the array: return (tmp); // return (mix) } -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| \ No newline at end of file diff --git a/firmware/generic/src/modules/module_delay.h b/firmware/generic/src/modules/module_delay.h new file mode 100644 index 0000000..a82aed7 --- /dev/null +++ b/firmware/generic/src/modules/module_delay.h @@ -0,0 +1,24 @@ +#ifndef __module_delay_h +#define __module_delay_h + +#include "gd32vf103.h" + +// sub modes +#define mode_Delay 0 + +#define delayColor RGBToWord(0xff, 0x0a, 0xE2) //ff0ae2 +#define delaySubMenuMax 1 +const uint8_t delayImage[877] = {255,255,255,255,255,255,255,255,225,23,26,11,22,12,17,13,132,3,87,0,12,0,61,0,6,0,36,0,27,1,19,0,63,0,12,0,34,0,6,0,26,0,67,1,29,0,22,0,11,0,27,0,62,1,21,0,54,0,80,0,11,0,36,1,6,0,15,0,15,0,63,0,52,0,24,0,35,0,6,0,13,0,0,1,12,0,0,0,5,0,37,0,34,0,13,0,49,0,10,0,12,0,14,0,23,0,5,0,1,1,8,1,25,0,57,0,24,0,48,0,12,0,15,0,6,0,6,0,6,0,0,0,5,0,2,1,4,1,2,0,5,1,6,1,5,0,33,6,70,0,34,0,25,0,6,0,15,0,3,4,3,0,5,0,7,0,7,0,6,0,32,3,14,0,63,0,7,0,11,0,18,0,14,1,5,0,0,0,6,0,10,0,5,0,9,0,28,0,22,0,25,0,59,0,11,0,27,0,15,0,0,0,6,2,3,3,5,0,1,0,5,0,7,0,7,0,24,0,11,1,12,0,21,0,27,0,17,0,33,1,6,0,7,0,6,0,0,1,7,3,7,1,1,0,7,0,5,0,7,0,118,0,17,0,29,1,5,0,0,0,6,0,1,0,21,0,6,0,8,0,5,1,40,0,81,0,11,0,22,1,14,0,0,0,6,0,1,2,12,2,1,0,6,0,0,0,5,0,116,0,14,0,11,0,33,0,8,0,6,0,3,12,3,0,6,0,0,0,7,0,5,0,14,0,27,0,11,0,7,0,54,0,0,0,11,0,26,0,5,0,0,0,5,0,1,0,6,0,17,1,6,0,8,0,8,0,25,0,49,0,30,0,24,0,26,0,7,0,0,0,5,0,1,0,6,1,13,1,7,0,1,0,5,0,0,0,6,0,122,0,41,0,15,0,1,0,7,3,6,2,8,0,1,0,5,0,0,0,6,0,135,0,28,0,0,0,5,0,0,0,9,1,9,6,9,1,1,0,5,0,8,0,15,0,41,0,6,0,44,0,21,0,30,0,9,0,5,0,3,1,22,1,2,0,5,0,1,0,5,0,0,0,26,0,49,0,54,0,33,0,5,1,0,0,5,1,3,0,19,1,3,0,5,0,1,0,5,0,135,0,35,0,8,0,6,0,3,3,12,2,3,1,5,0,11,0,113,0,17,0,34,1,7,0,1,1,5,1,5,3,4,3,5,0,6,0,1,1,5,0,0,0,15,0,29,0,18,0,104,0,6,0,2,0,6,0,8,4,7,1,6,0,9,0,81,0,35,0,15,0,37,0,0,1,5,0,2,0,6,2,16,1,6,1,1,1,5,0,1,0,28,0,29,0,71,0,39,0,8,1,1,1,7,5,4,5,7,0,2,0,9,0,47,0,83,0,43,1,10,1,11,4,11,1,10,1,0,0,119,0,11,0,42,0,9,1,3,1,24,1,3,1,5,0,1,0,18,0,28,0,11,0,6,0,107,0,1,1,6,1,3,2,18,2,3,1,6,0,1,0,31,0,49,0,96,0,2,0,7,0,5,5,6,5,5,0,6,1,1,0,49,0,11,0,58,0,58,0,2,1,6,1,9,6,8,2,6,0,2,0,49,0,84,0,47,0,3,0,7,1,21,1,8,0,2,0,21,0,26,0,12,0,7,0,112,1,2,1,7,3,14,2,8,1,2,0,34,0,13,0,12,0,20,0,102,0,3,1,9,14,9,1,3,0,49,0,74,0,64,0,4,2,28,2,3,1,48,1,13,0,72,0,53,1,5,1,24,1,4,1,25,0,19,3,14,0,9,0,11,20,86,1,5,3,16,3,5,0,40,7,17,0,43,0,88,1,7,2,9,3,6,2,66,0,62,0,72,2,7,9,8,1,68,0,75,0,63,2,20,2,68,1,144,4,10,4,35,0,33,0,14,0,31,0,102,10,73,1,234,2,68,0,163,2,83,0,124,26,23,32,17,11,255,255,255,255,209}; + +class module_delay +{ + public: + const char *subMenuText[1] = {"Delay"} ; + const char *potsName[3] = {" ","Mix ","Leng"} ; + uint16_t delay(uint16_t sample, uint16_t mix, uint16_t length); + + private: + uint16_t anHistory[4096]; // old readings from the analog input. Uses 8192 bytes of RAM... + uint16_t anPos; // index of the current reading +}; +#endif \ No newline at end of file diff --git a/firmware/generic/src/module_adsr.cpp b/firmware/generic/src/modules/module_env.cpp similarity index 62% rename from firmware/generic/src/module_adsr.cpp rename to firmware/generic/src/modules/module_env.cpp index 101aef7..9a9ff55 100644 --- a/firmware/generic/src/module_adsr.cpp +++ b/firmware/generic/src/modules/module_env.cpp @@ -1,23 +1,15 @@ -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| -#include "module_adsr.h" -//#include + +#include "module_env.h" #include #include "drivers/display.h" -// compressed image generated with python script (used by init) -const uint8_t img_env[196] = {255, 255, 255, 255, 255, 255, 255, 255, 126, 65, 15, 59, 15, 4, 48, 10, 82, 0, 12, 1, 59, 0, 12, 1, 52, 0, 94, 0, 10, 0, 62, 0, 10, 0, 151, 0, 8, 0, 64, 0, 8, 0, 66, 0, 85, 0, 6, 0, 66, 0, 6, 0, 55, 0, 255, 83, 0, 4, 0, 68, 0, 4, 0, 67, 0, 226, 0, 255, 234, 0, 226, 0, 41, 59, 15, 48, 255, 55, 0, 10, 0, 255, 255, 195, 0, 10, 0, 31, 59, 255, 255, 103, 0, 10, 0, 255, 255, 195, 0, 10, 0, 255, 255, 195, 0, 10, 0, 255, 255, 4, 59, 130, 0, 10, 0, 255, 222, 0, 226, 0, 255, 33, 59, 91, 37, 10, 0, 255, 255, 207, 0, 103, 0, 74, 0, 156, 0, 74, 0, 62, 0, 105, 0, 74, 0, 154, 0, 74, 0, 62, 0, 97, 0, 8, 0, 64, 0, 8, 0, 52, 0, 97, 0, 10, 1, 61, 0, 10, 0, 50, 0, 96, 1, 13, 1, 56, 2, 12, 1, 46, 1, 32, 64, 17, 3, 48, 3, 17, 46, 255, 255, 255, 255, 255, 255, 177}; - -#define black RGBToWord(0x00, 0x00, 0x00) -#define aColor RGBToWord(0xff, 0x00, 0xff) -#define dColor RGBToWord(0xff, 0xff, 0x00) -#define sColor RGBToWord(0xaa, 0xaa, 0xaa) -#define rColor RGBToWord(0x00, 0x55, 0xff) -#define logoColor RGBToWord(0x00, 0xff, 0xff) +#include "res/colors.h" // Generate envelope (ADSR / Attack Decay Sustain Release) waveform. // All transitions are linear, but could be exponential by altering functions below. // Operates between 0V to +5V (2048 - 3008) // Sustain level is fixed to 50% (2.5V or 2528) since there are only three potentiometers avaliable -uint16_t module_adsr::int_adsr(uint16_t gate, uint16_t pot_a, uint16_t pot_d, uint16_t pot_r, uint8_t noSustain) + +uint16_t module_env::int_adsr(uint16_t gate, uint16_t pot_a, uint16_t pot_d, uint16_t pot_r, uint8_t noSustain) { uint16_t out = 2048; // out level (2048 = 0V) @@ -86,10 +78,10 @@ uint16_t module_adsr::int_adsr(uint16_t gate, uint16_t pot_a, uint16_t pot_d, ui return (out); } -void module_adsr::draw(display &disp, uint16_t gate, uint16_t pot_a, uint16_t pot_d, uint16_t pot_r, uint8_t noSustain) +void module_env::draw(display &disp, uint16_t gate, uint16_t pot_a, uint16_t pot_d, uint16_t pot_r, uint8_t noSustain) { - uint16_t yOffset = 142; + uint16_t yOffset = 160; uint16_t height = 64; uint16_t sh = height >> 1; uint8_t radius = 4; @@ -103,28 +95,28 @@ void module_adsr::draw(display &disp, uint16_t gate, uint16_t pot_a, uint16_t po disp.fillRectangle(220, 80, 10, 10, RGBToWord(0xff, 0xff, 0x00)); else disp.fillRectangle(220, 80, 10, 10, RGBToWord(0x00, 0xff, 0x00)); - disp.putPixel(220, 80, black); - disp.putPixel(220, 89, black); - disp.putPixel(229, 80, black); - disp.putPixel(229, 89, black); + disp.putPixel(220, 80, blackColor); + disp.putPixel(220, 89, blackColor); + disp.putPixel(229, 80, blackColor); + disp.putPixel(229, 89, blackColor); } if (pot_a >> scale != a || pot_d >> scale != d || pot_r >> scale != r) { // delete previous envelope (lines + circles) - disp.drawLine(xOffset, yOffset, xOffset + a, yOffset - height, black); // A - disp.fillCircle(xOffset + a, yOffset - height, radius, black); + disp.drawLine(xOffset, yOffset, xOffset + a, yOffset - height, blackColor); // A + disp.fillCircle(xOffset + a, yOffset - height, radius, blackColor); if (a + d < 238 - xOffset) { - disp.drawLine(xOffset + a, yOffset - height, xOffset + a + d, yOffset - sh, black); // D - disp.fillCircle(xOffset + a + d, yOffset - sh, radius, black); + disp.drawLine(xOffset + a, yOffset - height, xOffset + a + d, yOffset - sh, blackColor); // D + disp.fillCircle(xOffset + a + d, yOffset - sh, radius, blackColor); } if (a + d + s < 238 - xOffset) - disp.drawLine(xOffset + a + d, yOffset - sh, xOffset + a + d + s, yOffset - sh, black); // S + disp.drawLine(xOffset + a + d, yOffset - sh, xOffset + a + d + s, yOffset - sh, blackColor); // S if (a + d + s + r < 238 - xOffset) { - disp.drawLine(xOffset + a + d + s, yOffset - sh, xOffset + a + d + s + r, yOffset, black); // R - disp.fillCircle(xOffset + a + d + s + r, yOffset, radius, black); + disp.drawLine(xOffset + a + d + s, yOffset - sh, xOffset + a + d + s + r, yOffset, blackColor); // R + disp.fillCircle(xOffset + a + d + s + r, yOffset, radius, blackColor); } a = pot_a >> scale; @@ -147,11 +139,11 @@ void module_adsr::draw(display &disp, uint16_t gate, uint16_t pot_a, uint16_t po if (a + d + s + r < 238 - xOffset) disp.drawLine(xOffset + a + d + s, yOffset - sh, xOffset + a + d + s + r, yOffset, rColor); // R - disp.fillCircle(xOffset + a, yOffset - height, radius - 1, black); - disp.fillCircle(xOffset + a + d + s + r, yOffset, radius - 1, black); + disp.fillCircle(xOffset + a, yOffset - height, radius - 1, blackColor); + disp.fillCircle(xOffset + a + d + s + r, yOffset, radius - 1, blackColor); if (a + d < 238 - xOffset) { - disp.fillCircle(xOffset + a + d, yOffset - sh, radius - 1, black); + disp.fillCircle(xOffset + a + d, yOffset - sh, radius - 1, blackColor); disp.drawCircle(xOffset + a + d, yOffset - sh, radius, dColor); } if (a + d + s + r < 238 - xOffset) @@ -161,31 +153,21 @@ void module_adsr::draw(display &disp, uint16_t gate, uint16_t pot_a, uint16_t po } // show pot values - char str[11]; + /* char str[11]; sprintf(str, "A: %d", pot_a); - disp.fillRectangle(130, 160, 70, 16, black); - disp.putStr(str, 130 - 9, 160, aColor, black); + disp.fillRectangle(130, 160, 70, 16, blackColor); + disp.putStr(str, 130 - 9, 160, aColor, blackColor); sprintf(str, "D: %d", pot_d); - disp.fillRectangle(130, 180, 70, 16, black); - disp.putStr(str, 130 - 9, 180, dColor, black); + disp.fillRectangle(130, 180, 70, 16, blackColor); + disp.putStr(str, 130 - 9, 180, dColor, blackColor); sprintf(str, "S: 50%%"); - disp.fillRectangle(130, 200, 70, 16, black); - disp.putStr(str, 130 - 9, 200, sColor, black); + disp.fillRectangle(130, 200, 70, 16, blackColor); + disp.putStr(str, 130 - 9, 200, sColor, blackColor); sprintf(str, "R: %d", pot_r); - disp.fillRectangle(130, 220, 70, 16, black); - disp.putStr(str, 130 - 9, 220, rColor, black); + disp.fillRectangle(130, 220, 70, 16, blackColor); + disp.putStr(str, 130 - 9, 220, rColor, blackColor); */ } if (gate < 2682) // 3V3 trig level - disp.fillRectangle(220, 80, 10, 10, black); + disp.fillRectangle(220, 80, 10, 10, blackColor); } - -void module_adsr::init(display &disp) -{ - // show logo - disp.showImg(img_env, sizeof(img_env), 0, 0, 240, 60, logoColor); - // clear background - disp.fillRectangle(0, 60, 240, 240 - 60, black); -} - -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| \ No newline at end of file diff --git a/firmware/generic/src/modules/module_env.h b/firmware/generic/src/modules/module_env.h new file mode 100644 index 0000000..a050400 --- /dev/null +++ b/firmware/generic/src/modules/module_env.h @@ -0,0 +1,32 @@ +#ifndef __module_env_h +#define __module_env_h + +#include "gd32vf103.h" +#include "drivers/display.h" + +// sub modes +#define mode_ADSR 0 +#define mode_ADR 1 + +#define envColor RGBToWord(0x00, 0xff, 0x00) +#define envSubMenuMax 2 +const uint8_t envImage[439] = {255,255,255,255,255,255,255,255,150,5,63,32,8,13,15,10,5,11,20,12,31,2,170,0,31,0,29,1,71,0,40,0,13,0,63,0,40,1,106,0,37,0,47,0,11,0,26,1,3,5,148,0,5,0,69,0,3,1,129,0,60,0,11,0,25,0,3,0,72,0,40,0,116,0,3,0,133,0,58,0,11,0,24,0,3,0,107,0,37,0,58,0,29,0,10,0,75,20,27,0,20,0,34,0,37,0,2,0,14,0,59,0,40,0,61,0,12,0,11,0,28,0,139,0,93,0,1,0,14,0,73,0,58,0,19,0,23,0,11,0,24,0,1,0,18,0,141,0,45,0,24,0,23,0,121,0,51,0,40,0,17,0,59,0,40,0,86,0,51,0,67,0,39,1,10,0,4,0,43,0,61,0,2,0,66,19,50,0,44,0,25,0,1,0,24,0,84,0,34,0,47,0,10,0,25,0,1,0,21,0,3,29,22,0,40,0,12,0,38,0,10,0,6,0,65,0,32,0,74,0,73,0,54,1,110,0,10,0,1,0,41,0,38,0,29,2,133,0,42,0,29,0,29,25,23,0,30,0,8,0,15,0,10,0,42,0,10,0,255,149,0,17,0,40,0,10,0,30,0,56,0,34,19,27,0,21,0,70,0,62,0,117,0,13,0,10,0,112,0,40,0,18,0,47,0,0,0,10,0,125,0,39,0,103,0,61,0,100,0,27,0,10,0,92,0,83,0,90,0,82,0,40,0,80,0,94,0,1,0,27,0,39,0,10,0,56,0,216,0,118,0,1,0,80,0,15,0,37,0,101,0,12,0,11,20,7,0,179,0,48,0,87,0,32,0,66,0,1,1,63,0,13,0,51,0,102,0,2,2,90,0,141,0,4,1,74,0,49,0,34,3,66,1,9,0,31,0,7,0,186,0,61,0,16,0,31,0,14,0,109,3,87,0,34,0,255,104,32,8,10,18,10,21,13,255,255,255,255,209}; + +class module_env +{ + public: + const char *subMenuText[2] = {"ADSR","ADR"} ; + const char *potsName[3] = {"Atck","Dcay","Rels"} ; + + uint16_t int_adsr(uint16_t gate, uint16_t pot_a, uint16_t pot_d, uint16_t pot_r, uint8_t noSustain); + void draw(display &disp, uint16_t gate, uint16_t pot_a, uint16_t pot_d, uint16_t pot_r, uint8_t noSustain); + + private: + uint16_t tCount; + uint8_t adsrMode; + uint8_t oldGate; + uint8_t xorGate; + int8_t gateCount; + uint16_t a, d, s, r; +}; +#endif \ No newline at end of file diff --git a/firmware/generic/src/modules/module_lfo.cpp b/firmware/generic/src/modules/module_lfo.cpp new file mode 100644 index 0000000..9b4fe3f --- /dev/null +++ b/firmware/generic/src/modules/module_lfo.cpp @@ -0,0 +1,5 @@ +#include "module_lfo.h" +#include "drivers/display.h" +#include "res/colors.h" + + diff --git a/firmware/generic/src/modules/module_lfo.h b/firmware/generic/src/modules/module_lfo.h new file mode 100644 index 0000000..39bd919 --- /dev/null +++ b/firmware/generic/src/modules/module_lfo.h @@ -0,0 +1,21 @@ +#ifndef __module_lfo_h +#define __module_lfo_h + +#include "gd32vf103.h" + +// sub modes +#define mode_LFO_sine 0 +#define mode_LFO_square 1 +#define mode_LFO_triangle 2 + +#define lfoColor RGBToWord(0xF0, 0xE8, 0x02) // Yellow +#define lfoSubMenuMax 3 +const uint8_t lfoImage[326] = {255,255,255,255,255,255,255,255,78,13,74,15,41,11,27,32,16,2,13,2,70,0,15,0,112,0,13,2,19,1,126,0,38,0,44,1,24,0,137,0,71,0,27,0,207,1,29,0,71,6,126,0,32,0,69,0,50,0,38,0,31,0,7,0,34,0,132,0,67,0,15,4,15,0,199,0,13,2,4,1,186,20,5,0,13,0,9,0,13,0,117,0,38,0,51,1,11,0,143,0,38,0,25,0,11,0,14,0,208,0,42,0,207,0,16,0,206,0,11,0,147,0,38,0,123,10,75,0,38,0,23,0,11,0,84,0,114,18,92,0,163,0,73,0,73,0,38,0,35,0,87,0,84,0,153,4,138,0,99,0,155,0,33,0,113,0,38,0,163,0,45,0,98,0,92,5,98,0,4,0,133,0,78,18,52,0,225,0,87,0,10,0,25,0,38,0,160,0,8,0,38,0,38,0,70,0,77,8,138,0,205,0,11,0,17,0,11,0,114,0,38,0,210,0,38,0,56,0,11,0,208,0,14,0,209,0,12,0,12,0,12,0,115,0,11,20,5,0,54,0,9,1,12,0,58,0,89,0,17,0,28,0,13,2,3,2,13,0,60,6,149,3,15,0,201,0,34,0,203,0,32,0,119,0,31,0,5,0,44,0,30,0,58,0,15,0,95,0,33,0,27,1,60,15,132,0,24,1,212,1,20,1,125,32,6,11,38,2,13,3,221,13,255,255,255,230}; + +class module_lfo +{ + public: + const char *subMenuText[3] = { "Sine", "Square", "Triangle" } ; + const char *potsName[3] = {"Note","Octv","Levl"} ; +}; +#endif \ No newline at end of file diff --git a/firmware/generic/src/modules/module_noise.cpp b/firmware/generic/src/modules/module_noise.cpp new file mode 100644 index 0000000..4dbe0dd --- /dev/null +++ b/firmware/generic/src/modules/module_noise.cpp @@ -0,0 +1,27 @@ + +#include "module_noise.h" +#include "drivers/display.h" + +// constructor +module_noise::module_noise(void) { + this-> Rnd = 4294967294; +} + +// simple pseudo random noise generator +uint16_t module_noise::prng(uint16_t volume) { + LowBit = this->Rnd & 1; + this->Rnd >>= 1; + this->Rnd ^= LowBit ? 0x80000057ul : 0ul; + if (LowBit) + return (2048+volume); // 2048 = 0V + else + return (2048-volume); +} + +// main sample processing +int16_t module_noise::process(uint16_t in1, int potLL) +{ + uint32_t cv1; + cv1 = (in1 * potLL >> 13); // pot2 (bottom right) sets level for CV1 that attenuates or amplifies amplitude + return prng(cv1); +} \ No newline at end of file diff --git a/firmware/generic/src/modules/module_noise.h b/firmware/generic/src/modules/module_noise.h new file mode 100644 index 0000000..600eee4 --- /dev/null +++ b/firmware/generic/src/modules/module_noise.h @@ -0,0 +1,28 @@ +#ifndef __module_noise_h +#define __module_noise_h + +#include "gd32vf103.h" + +// sub modes +#define mode_Noise 0 + +#define noiseColor RGBToWord(0x00, 0xff, 0xff) +#define noiseSubMenuMax 1 +const uint8_t noiseImage[471] = {255,255,255,255,255,255,255,255,145,5,23,15,26,24,22,13,15,10,10,23,41,0,5,0,20,1,15,0,50,3,94,3,36,0,7,0,63,0,28,1,15,0,13,0,35,0,27,1,140,0,44,0,51,1,139,0,54,0,42,0,31,0,7,0,19,0,16,0,58,0,28,0,68,0,31,7,21,16,24,0,46,0,50,0,34,0,138,0,28,0,69,0,39,17,55,7,57,0,32,6,57,0,81,2,43,0,20,0,28,3,14,0,99,0,22,0,22,0,50,0,22,0,126,0,55,0,41,0,11,1,12,0,36,0,138,0,99,17,131,0,36,0,181,0,97,7,21,16,22,0,10,0,34,0,50,0,27,0,11,0,34,0,19,0,16,0,46,0,11,0,19,1,10,0,4,0,32,0,55,0,93,0,63,0,172,0,12,0,34,0,94,0,7,0,59,0,19,2,23,0,12,0,36,0,41,0,26,0,5,0,20,1,15,0,33,7,15,0,20,0,51,0,58,5,23,15,57,0,25,0,10,0,1,0,194,1,52,0,90,17,39,0,32,0,12,0,15,0,10,0,21,0,29,0,48,0,89,1,235,1,26,0,17,0,32,0,29,0,161,0,21,0,39,0,49,0,239,17,38,0,10,6,11,0,15,0,18,0,30,0,28,0,11,0,154,0,51,0,59,7,21,17,37,0,11,0,55,0,38,0,11,0,28,0,7,0,19,0,105,0,57,0,109,0,10,0,7,0,11,0,12,0,50,0,26,0,12,0,155,0,10,0,39,0,13,0,12,0,30,0,7,0,19,0,57,0,11,0,89,0,46,7,21,17,89,0,15,0,33,1,13,0,97,0,22,0,11,0,9,0,50,0,19,3,14,0,43,16,49,0,24,0,73,7,17,0,43,0,16,0,61,0,32,0,13,0,62,0,139,0,48,0,46,0,127,0,47,0,58,1,101,0,10,0,27,0,5,0,50,0,33,0,48,0,16,0,63,0,29,0,16,0,54,1,50,16,78,0,45,0,39,2,135,0,96,2,109,11,17,12,4,10,18,10,10,26,255,255,255,255,203}; + +class module_noise +{ + public: + module_noise(void); + + const char *subMenuText[2] = {"white noise"} ; + const char *potsName[3] = {" ","Levl"," "} ; // UR , LL , LR + + int16_t process(uint16_t in1, int potLL) ; + + private: + uint16_t prng(uint16_t volume); + uint32_t Rnd; // random seed + uint8_t LowBit; // noise out +}; +#endif \ No newline at end of file diff --git a/firmware/generic/src/modules/module_vca.cpp b/firmware/generic/src/modules/module_vca.cpp new file mode 100644 index 0000000..23c5573 --- /dev/null +++ b/firmware/generic/src/modules/module_vca.cpp @@ -0,0 +1,18 @@ +#include "module_vca.h" + + +int16_t module_vca::process(uint16_t sample, int env, int envAmp, int offset) { + //int16_t tmp ; + if (env < 2048 ) env = 2048 ; // lock to 0V <->5V + if (env > 3008 ) env = 3008 ; // lock to 0V <->5V + //tmp = ((ADC.anRaw[1]-2048) * ADC.anAvg[3]) >> 10 ; // env (2048 <-> 3008 ; 0V <-> 5V ) * attenuation 0 <-> 4096 + //tmp = (sample-2048) * (env-2048) >> 10 + return (( (sample-2048) * (env-2048) ) >> 10) + 2048 ; + + + +} + + + // cv2 = ((sample * env * envAmp) >> 21); // pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude + // cv2 += (offset >> 1) - 2048; \ No newline at end of file diff --git a/firmware/generic/src/modules/module_vca.h b/firmware/generic/src/modules/module_vca.h new file mode 100644 index 0000000..2660d49 --- /dev/null +++ b/firmware/generic/src/modules/module_vca.h @@ -0,0 +1,23 @@ +#ifndef __module_vca_h +#define __module_vca_h + +#include "gd32vf103.h" + +// sub modes +#define mode_VCA 0 + +#define vcaColor RGBToWord(0xFC, 0x56, 0x06) // red~orange +#define vcaSubMenuMax 1 + +const uint8_t vcaImage[457] = {255,255,255,255,255,255,172,3,18,2,120,13,82,1,75,11,20,12,15,2,13,3,25,13,40,1,85,0,31,0,12,2,20,2,21,0,56,1,103,0,24,0,26,1,18,0,59,1,100,0,11,0,10,1,111,2,66,0,52,1,49,0,15,0,48,1,94,0,11,0,8,0,32,0,86,1,114,0,51,0,70,1,89,0,11,0,7,0,51,0,73,1,99,0,7,0,17,4,193,0,19,0,14,3,4,2,8,0,15,0,79,1,66,0,12,0,11,0,22,0,11,1,106,1,99,0,12,1,14,1,20,0,20,0,62,1,50,0,23,0,11,0,6,0,12,0,18,0,18,0,87,1,85,0,20,0,20,0,1,0,105,1,70,0,19,0,35,1,14,0,10,1,79,2,79,0,20,0,136,1,64,0,19,0,11,0,39,0,10,0,86,1,74,0,60,0,10,0,89,1,59,0,10,0,88,0,76,1,36,0,10,0,6,0,20,0,11,0,38,0,10,0,28,18,46,25,42,0,225,0,73,0,10,0,163,0,21,0,37,0,42,18,45,26,28,0,10,0,73,0,5,0,86,1,129,0,10,0,92,1,56,0,10,0,10,0,153,1,132,0,10,0,19,0,68,1,44,0,13,0,10,0,60,0,100,1,58,0,0,0,10,0,74,9,77,0,136,0,98,1,62,0,10,0,159,1,104,0,21,1,8,0,95,1,77,0,14,0,33,0,9,0,94,1,78,0,29,0,17,1,48,0,115,0,68,1,11,0,175,0,31,1,11,1,105,1,101,0,14,2,4,3,7,0,5,0,11,15,61,1,83,0,37,4,17,0,11,0,75,1,84,0,20,0,51,0,74,1,148,0,85,1,87,0,22,0,31,0,16,0,29,0,41,0,114,0,35,0,30,0,51,1,73,0,14,0,25,0,28,0,3,0,11,0,69,1,90,0,27,1,24,1,16,0,68,1,123,1,19,2,5,0,79,1,80,13,32,2,13,2,8,12,21,11,32,0,132,13,87,3,19,2,255,255,255,255,129}; + +class module_vca +{ + public: + + const char *subMenuText[1] = {"vca"} ; + const char *potsName[3] = {"Offt","Gain","Env "} ; + + int16_t process(uint16_t sample, int env, int envEmp, int offset) ; +}; +#endif diff --git a/firmware/generic/src/module_vcf.cpp b/firmware/generic/src/modules/module_vcf.cpp similarity index 91% rename from firmware/generic/src/module_vcf.cpp rename to firmware/generic/src/modules/module_vcf.cpp index dd58b9c..27fa750 100644 --- a/firmware/generic/src/module_vcf.cpp +++ b/firmware/generic/src/modules/module_vcf.cpp @@ -1,20 +1,15 @@ -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| + #include "module_vcf.h" -extern "C" -{ -#include "gd32vf103.h" -} +#include "drivers/display.h" +#include "res/colors.h" +#include -/* potentiometer and I/O mapping - R P2 - P4 P3 - C0 C1 - D0 D1 -*/ +extern "C" { + #include "gd32vf103.h" +} // 4 pole ladder resonant low pass filter (Karlsen fast ladder) -uint16_t module_vcf::r4pole1(uint16_t sample, uint16_t cutoff, uint16_t resonance) -{ +uint16_t module_vcf::r4pole1(uint16_t sample, uint16_t cutoff, uint16_t resonance) { static int16_t oldOut; if (sample > 2048 && oldOut < 2048) phaseTrig = 1; @@ -46,8 +41,7 @@ uint16_t module_vcf::r4pole1(uint16_t sample, uint16_t cutoff, uint16_t resonanc } // 2-pole resonant low pass filter (Paul Kellett) -uint16_t module_vcf::r2pole1(uint16_t sample, uint16_t cutoff, uint16_t resonance) -{ +uint16_t module_vcf::r2pole1(uint16_t sample, uint16_t cutoff, uint16_t resonance) { static int16_t oldOut; if (sample > 2048 && oldOut < 2048) phaseTrig = 1; @@ -76,8 +70,7 @@ uint16_t module_vcf::r2pole1(uint16_t sample, uint16_t cutoff, uint16_t resonanc } // 2-pole resonant low pass filter (BASS) -uint16_t module_vcf::r2pole2(uint16_t sample, uint16_t cutoff, uint16_t resonance) -{ +uint16_t module_vcf::r2pole2(uint16_t sample, uint16_t cutoff, uint16_t resonance) { // F = LPF (cutoff) // Q = RESONANCE // q = SCALED_RESONANCE @@ -110,8 +103,7 @@ uint16_t module_vcf::r2pole2(uint16_t sample, uint16_t cutoff, uint16_t resonanc } // test for filter development -uint16_t module_vcf::test(uint16_t sample, uint16_t cutoff, uint16_t resonance) -{ +uint16_t module_vcf::test(uint16_t sample, uint16_t cutoff, uint16_t resonance) { static int16_t oldOut; if (sample > 2048 && oldOut < 2048) phaseTrig = 1; @@ -135,8 +127,7 @@ uint16_t module_vcf::test(uint16_t sample, uint16_t cutoff, uint16_t resonance) /* // MOOG 4pole 24 db resonant low pass (Stilson/Smith & Timo Tossavainen) // ***doesnt work*** - takes too long to calculate at 44kHz ;( -uint16_t module_vcf::moog24db(uint16_t sample, uint16_t cutoff, uint16_t resonance) -{ +uint16_t module_vcf::moog24db(uint16_t sample, uint16_t cutoff, uint16_t resonance) { static int16_t oldOut; if (sample > 2048 && oldOut < 2048) @@ -172,8 +163,7 @@ uint16_t module_vcf::moog24db(uint16_t sample, uint16_t cutoff, uint16_t resonan /* // integer implementation of 2pole filter, without feedback division. fast, but not so good... -uint16_t module_vcf::r2pole3(uint16_t sample, uint16_t cutoff, uint16_t resonance) -{ +uint16_t module_vcf::r2pole3(uint16_t sample, uint16_t cutoff, uint16_t resonance) { static int16_t filterValue; static int16_t filterOut; static int16_t out; @@ -197,6 +187,7 @@ uint16_t module_vcf::r2pole3(uint16_t sample, uint16_t cutoff, uint16_t resonanc return (out); } */ + /* // Running average analog filter ***works bad, too steep (exponential by nature)***: static uint16_t anHistory[4096]; // old readings from the analog input. Uses 8192 bytes of RAM... @@ -229,6 +220,4 @@ anPos++; // advance to the next position in the array: anAvg = anTotal / newLength; // calculate the average: oldLength = newLength; // return (anAvg); // return average value -*/ - -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| \ No newline at end of file +*/ \ No newline at end of file diff --git a/firmware/generic/src/modules/module_vcf.h b/firmware/generic/src/modules/module_vcf.h new file mode 100644 index 0000000..e06508d --- /dev/null +++ b/firmware/generic/src/modules/module_vcf.h @@ -0,0 +1,32 @@ +#ifndef __module_vcf_h +#define __module_vcf_h + +#include "gd32vf103.h" + +// sub modes +#define mode_2_pole_v1 0 +#define mode_2_pole_v2 1 +#define mode_4_pole 2 +#define mode_test 3 + +#define vcfColor RGBToWord(0x61, 0xff, 0x79) // light green +const uint8_t vcfImage[530] = {255,255,255,239,0,237,0,0,0,235,0,2,0,255,221,0,1,0,116,13,101,0,57,11,20,12,15,2,13,3,13,32,121,0,31,0,12,2,20,2,42,0,64,0,75,0,24,0,26,1,7,0,96,0,0,0,73,0,11,0,10,1,134,0,2,0,41,0,52,1,213,0,11,0,8,0,32,0,96,0,5,0,1,0,95,0,41,0,31,0,68,0,69,0,11,0,7,0,130,0,96,0,7,0,17,4,113,0,78,0,19,0,14,3,4,2,8,0,19,20,43,0,20,0,57,0,12,0,11,0,22,0,11,1,14,0,75,0,0,1,112,0,12,1,14,1,24,0,62,0,3,0,14,0,48,0,23,0,11,0,6,0,12,0,18,0,86,0,107,0,20,0,20,0,1,0,85,0,91,0,19,0,35,1,82,0,1,0,0,0,31,2,67,0,20,0,32,0,81,0,27,0,2,0,53,0,19,0,11,0,45,0,60,0,1,0,104,0,67,18,79,0,57,0,10,0,178,1,24,0,10,0,6,0,20,0,11,0,33,0,72,0,1,0,43,0,1,0,55,0,177,0,46,0,100,0,28,5,16,0,34,0,4,0,53,0,21,0,94,0,5,0,4,0,1,0,41,0,2,0,4,10,27,0,10,0,57,0,58,0,2,1,2,4,40,0,6,0,0,0,1,1,10,0,160,0,1,0,8,0,34,0,5,0,20,0,25,0,10,0,10,0,77,0,26,0,1,0,3,1,4,1,45,0,2,1,1,10,110,18,37,4,14,0,14,0,9,0,3,0,5,1,21,0,13,0,10,0,118,2,45,0,10,0,42,0,0,0,10,0,59,0,84,0,30,2,130,0,152,0,10,0,255,11,0,21,1,97,0,21,0,1,0,60,0,14,0,33,0,9,0,91,0,82,0,29,0,17,1,22,0,71,0,68,0,68,1,100,1,1,0,82,0,31,1,11,1,122,0,5,0,78,0,14,2,4,3,7,0,6,0,85,0,2,1,18,1,61,0,37,4,31,0,74,2,13,0,68,0,20,0,151,0,3,0,225,0,7,0,1,0,58,0,22,0,31,0,119,1,84,0,38,0,97,0,1,0,54,0,14,0,25,0,28,0,19,0,159,0,27,1,24,1,113,0,97,1,19,2,110,0,2,0,53,13,32,2,13,2,13,11,88,2,104,13,255,255,255,255,8}; +#define vcfSubMenuMax 4 + +class module_vcf +{ + public: + const char *subMenuText[4] = { "2 poles v1", "2 poles v2", "4 poles", "test" } ; + const char *potsName[3] = {" ","Cutf","Reso"} ; + uint8_t phaseTrig; + int16_t filterValue; + int16_t filterOut; + uint16_t r4pole1(uint16_t sample, uint16_t cutoff, uint16_t resonance); + uint16_t r2pole1(uint16_t sample, uint16_t cutoff, uint16_t resonance); + uint16_t r2pole2(uint16_t sample, uint16_t cutoff, uint16_t resonance); + uint16_t test(uint16_t sample, uint16_t cutoff, uint16_t resonance); + private: + static float in1, in2, in3, in4, buf1, buf2, buf3, buf4, fb, in, cut, q; + static int16_t oldOut; +}; +#endif diff --git a/firmware/generic/src/module_vco.cpp b/firmware/generic/src/modules/module_vco.cpp similarity index 57% rename from firmware/generic/src/module_vco.cpp rename to firmware/generic/src/modules/module_vco.cpp index 20a64cf..92ab164 100644 --- a/firmware/generic/src/module_vco.cpp +++ b/firmware/generic/src/modules/module_vco.cpp @@ -1,12 +1,51 @@ -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| + #include "module_vco.h" -extern "C" +#include "res/colors.h" +#include "res/images.h" +#include "drivers/display.h" +#include + +extern "C" { + #include "gd32vf103.h" + extern "C" const float freq[2113]; +} + +//Sine Amplitude modulation +int16_t module_vco::processSineAM(uint16_t note, uint16_t amplitude, int amplitudeLevel, int octave, int fine) +{ + int16_t tmp = 0; + uint32_t cv2; + + cv2 = ((amplitude << 2) * amplitudeLevel >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude + tmp = 2048 + ((int(sine(getPhase(note,octave,fine)) - 2048) * cv2) >> 12); // get level from position + return fold(tmp); // fold if outside range +} + +//Sine Mix +int16_t module_vco::processSineADD(uint16_t note, uint16_t sample, int sampleLevel, int octave, int fine) { -#include "gd32vf103.h" + int16_t tmp = 0; + uint32_t cv2; + + cv2 = ((sample << 2) * sampleLevel >> 11); // 0-8192. pot2 (bottom right) sets level for CV2 that attenuates or amplifies amplitude + tmp = (int(sine(getPhase(note,octave,fine)) + cv2) >> 1); // get level from position + return fold(tmp); // fold if outside range } -float module_vco::sine(float xTmp) +//Sine FM +int16_t module_vco::processSineFM(uint16_t note, uint16_t frequency, int frequencyLevel, int octave, int fine) { + int16_t tmp = 0; + uint32_t cv2; + + cv2 = (frequency * frequencyLevel >> 12); // 12+12-13=11 (0-2048). pot2 (bottom right) sets level for CV2 that modulates the frequency + tmp = int(sine(getPhase(note,octave,fine) + cv2 - 1024)); // get level from position (1088-3008 => -5V to +5V) + return fold(tmp); // fold if outside range + +} + +// sine +float module_vco::sine(float xTmp) { if (xTmp > 0) { phase += xTmp; @@ -23,10 +62,8 @@ float module_vco::sine(float xTmp) return (sin(phase)); } - // square - pw (16 bit) controls pulse width / duty cycle -uint16_t module_vco::square(float xTmp, uint16_t pw) -{ +uint16_t module_vco::square(float xTmp, uint16_t pw) { phase += xTmp; if (phase > 65535) { @@ -42,9 +79,9 @@ uint16_t module_vco::square(float xTmp, uint16_t pw) else return (3008); // 3008 = +5V } + // square - pw (16 bit) controls pulse width / duty cycle -uint16_t module_vco::square2x(float xTmp, uint16_t dist) -{ +uint16_t module_vco::square2x(float xTmp, uint16_t dist) { int16_t out1, out2, out3; uint16_t pw = 32768; // 50%pw / 2 phase2 += xTmp; @@ -64,9 +101,9 @@ uint16_t module_vco::square2x(float xTmp, uint16_t dist) else out3 = 480; // 3008 = +5V return(2048+out1+out3); } + // square - pw (16 bit) controls pulse width / duty cycle -uint16_t module_vco::square3x(float xTmp, uint16_t dist) -{ +uint16_t module_vco::square3x(float xTmp, uint16_t dist) { int16_t out1, out2, out3; uint16_t pw = 32768; // 50%pw / 2 phase2 += xTmp; @@ -88,8 +125,7 @@ uint16_t module_vco::square3x(float xTmp, uint16_t dist) } // triangle - shape (16 bit) morphs between reverse saw, triangle and saw (could use bresenham to avoid division...) -uint16_t module_vco::triangle(float xTmp, uint16_t shape) -{ +uint16_t module_vco::triangle(float xTmp, uint16_t shape) { phase += xTmp; if (phase > 65535) { @@ -109,8 +145,7 @@ uint16_t module_vco::triangle(float xTmp, uint16_t shape) // triangle - shape (16 bit) morphs between reverse saw, triangle and saw (could use bresenham to avoid division...) -uint16_t module_vco::triangle2x(float xTmp, uint16_t dist) -{ +uint16_t module_vco::triangle2x(float xTmp, uint16_t dist) { uint16_t out1,out2,out3; //uint16_t shape = 32768; // triangle shape (not saw) uint16_t shape = 65000; // saw shape @@ -138,8 +173,7 @@ uint16_t module_vco::triangle2x(float xTmp, uint16_t dist) } // triangle - shape (16 bit) morphs between reverse saw, triangle and saw (could use bresenham to avoid division...) -uint16_t module_vco::triangle3x(float xTmp, uint16_t dist) -{ +uint16_t module_vco::triangle3x(float xTmp, uint16_t dist) { uint16_t out1,out2,out3; //uint16_t shape = 32768; // triangle shape (not saw) uint16_t shape = 65000; // saw shape @@ -166,26 +200,14 @@ uint16_t module_vco::triangle3x(float xTmp, uint16_t dist) return(((out1+out3)>>1)+out2>>1); } -/* -uint16_t module_vco::int_saw(uint16_t x) -{ - uint32_t tmp = 4095 - (x >> 4); // (16 bit -> 12 bit) - return (tmp); -} -uint16_t module_vco::int_r_saw(uint16_t x) -{ - uint32_t tmp = x >> 4; // (16 bit -> 12 bit) - return (tmp); -} -*/ -float module_vco::sin(float x) -{ -#define PI 3.1415926535897932384626433832795028841971693993751058209749445923078164062 -#define r360 2 * PI -#define r180 PI -#define r90 PI / 2 -#define PIR PI / 32768 // 65536/2 +float module_vco::sin(float x) { + #define PI 3.1415926535897932384626433832795028841971693993751058209749445923078164062 + #define r360 2 * PI + #define r180 PI + #define r90 PI / 2 + #define PIR PI / 32768 // 65536/2 + x = x * PIR; // convert uint16_t to rad // x = x * PI / 180; // convert degrees to rad int sign = 1; @@ -202,5 +224,58 @@ float module_vco::sin(float x) float y = sign * x * (x2 * (x2 * (42 - x2) - 840) + 5040) / 5040; y = y * 960 + 2048; // convert from +/-1 to 1088 - 3008 (-5V to +5V) return (y); +}; + +int16_t module_vco::fold(int16_t tmp) { + do + { + if (tmp < 1088) + tmp = (1088 * 2 - tmp); + if (tmp > 3008) + tmp = (3008 * 2 - tmp); + } while (tmp < 1088 || tmp > 3008); + return (tmp); +}; + +uint16_t module_vco::deadzone(uint16_t tmp) { + if (tmp < 255) // within? + return (0); // ..yes - return zero + else // .. no + return (tmp - 255); // return value - deadzone +}; + +float module_vco::ad2phase(uint16_t in) { + float xTmp; + if (in < 1472) // below -3V + xTmp = 24.135547645748918; + else if (in > 3584) // above 8V + xTmp = 49429.601578493784; + else + { + if (quantized == 0) + xTmp = freq[in - 1472]; + else + { + uint16_t q = (in - 1472 + 8) >> 4; // add 16/2 to quantize between notes + xTmp = freq[q << 4]; + } + } + return (xTmp); } -//-----10|-------20|-------30|-------40|-------50|-------60|-------70|-------80| \ No newline at end of file + +float module_vco::getPhase(int cv, int octave, int note ) { + uint8_t p_note; + uint16_t p_octave; + p_note = deadzone(note) >> 4; // add deadzone, one octave is 192 AD steps (1V). (4096-1024/16)=192 + p_octave = (octave >> 9) * 192; // 4096/512 => 8 octaves + phaseC = ad2phase(cv + p_octave - (192 << 1) + p_note); // convert CV to logaritmic scale + return (phaseC); +} + +/* potentiometer and I/O mapping + R P2 + P4 P3 + C0 C1 + D0 D1 +*/ + diff --git a/firmware/generic/src/modules/module_vco.h b/firmware/generic/src/modules/module_vco.h new file mode 100644 index 0000000..723e8f8 --- /dev/null +++ b/firmware/generic/src/modules/module_vco.h @@ -0,0 +1,59 @@ + +#ifndef __module_vco_h +#define __module_vco_h + +#include "gd32vf103.h" + +// sub modes +#define mode_sine_am 0 +#define mode_sine_add 1 +#define mode_sine_fm 2 +#define mode_square_am 3 +#define mode_square_add 4 +#define mode_square_fm 5 +#define mode_square_pw 6 +#define mode_square_2x 7 +#define mode_square_3x 8 +#define mode_triangle_am 9 +#define mode_triangle_add 10 +#define mode_triangle_fm 11 +#define mode_triangle_saw 12 +#define mode_triangle_2x 13 +#define mode_triangle_3x 14 + +#define vcoColor RGBToWord(0x0E, 0x11, 0xC7) // dark blue +#define vcoSubMenuMax 15 +const uint8_t vcoImage[693] = {255,255,255,255,255,255,255,255,45,13,30,13,30,4,32,4,41,11,20,12,15,2,13,3,23,2,13,2,26,0,4,1,29,0,4,1,51,0,31,0,12,2,20,2,17,2,19,1,23,0,7,0,27,0,7,0,70,0,24,0,26,1,13,1,24,0,21,0,9,0,25,0,9,0,68,0,11,0,10,1,42,0,27,0,19,0,36,0,49,0,52,1,42,1,29,0,23,2,4,0,29,1,4,0,66,0,11,0,8,0,32,0,9,0,32,0,16,0,7,0,33,0,1,0,93,0,43,0,34,0,20,0,8,0,21,0,3,0,3,0,3,0,64,0,11,0,7,0,43,0,15,4,15,0,13,0,9,0,119,0,7,0,17,4,20,0,13,2,4,1,33,0,31,0,3,0,5,0,3,0,62,0,19,0,14,3,4,2,8,0,6,0,13,0,9,0,13,0,24,0,3,0,85,0,12,0,11,0,22,0,11,1,27,1,11,0,25,0,3,0,133,0,12,1,14,1,11,0,11,0,14,0,62,0,3,0,7,0,3,0,34,0,23,0,11,0,6,0,12,0,18,0,9,0,42,0,24,0,3,0,109,0,20,0,20,0,1,0,18,0,16,0,22,0,3,0,111,0,19,0,35,1,5,0,11,0,79,0,3,0,9,0,3,0,69,0,20,0,203,0,19,0,11,0,30,0,11,0,58,0,3,0,106,0,108,0,3,0,110,0,10,0,65,0,80,0,3,0,32,2,15,0,10,0,6,0,20,0,11,0,30,0,73,0,36,0,3,0,13,0,2,0,46,0,133,0,90,0,127,0,36,0,83,0,21,0,43,0,33,0,8,0,1,0,52,0,55,0,10,0,113,1,33,0,23,0,11,0,3,0,135,0,40,0,3,0,87,0,10,0,10,0,43,0,255,30,0,45,0,3,0,15,0,3,0,32,0,13,0,10,0,90,0,42,0,52,0,3,0,28,0,0,0,10,0,140,0,201,0,45,0,3,0,17,0,49,0,10,0,91,0,49,0,36,0,7,0,3,0,70,0,21,1,6,0,11,0,17,0,11,0,32,0,98,0,14,0,33,0,100,0,3,0,24,0,53,0,29,0,17,1,40,0,11,0,39,0,31,0,10,0,3,0,19,0,68,1,25,0,14,0,48,0,9,0,84,0,31,1,11,1,14,0,12,0,12,0,12,0,41,0,8,0,26,0,3,0,65,0,14,2,4,3,7,0,22,0,9,1,12,0,37,0,4,0,1,0,27,0,4,0,1,0,4,0,40,0,37,4,21,0,13,2,3,2,13,0,45,1,4,0,23,0,4,1,46,0,20,0,61,3,15,0,40,0,49,0,108,0,34,0,42,0,9,0,25,0,9,0,40,0,22,0,31,0,11,0,32,0,44,0,7,0,27,0,7,0,66,0,44,0,30,0,46,0,4,1,29,1,4,0,25,0,14,0,25,0,28,0,14,0,27,1,48,4,33,4,41,0,27,1,24,1,16,0,24,1,167,1,19,2,19,1,20,1,124,13,32,2,13,2,24,2,13,3,176,13,30,13,255,255,255,218}; + +class module_vco +{ + public: + const char *subMenuText[15] = { "sine am", "sine add", "sine fm","square am", "square add", "square fm", "square pw", "square 2x", "square 3x","triangle am", "triangle add", "triangle fm", "triangle saw", "triangle 2x","triangle 3x"} ; + const char *potsName[3] = {"Note","Octv","Levl"} ; + + uint8_t phaseTrig; + float phaseC = 0; // phaseCount | stepsize within one period (0-65535). frequenzy = xStep * 44100 / (2*65536) + uint8_t quantized = 0; // quantize CV input of module? + + int16_t processSineAM(uint16_t note, uint16_t amplitude, int amplitudeLevel, int octave, int fine) ; + int16_t processSineADD(uint16_t note, uint16_t amplitude, int amplitudeLevel, int octave, int fine) ; + int16_t processSineFM(uint16_t note, uint16_t amplitude, int amplitudeLevel, int octave, int fine) ; + + uint16_t square(float xTmp, uint16_t pw); + uint16_t square2x(float xTmp, uint16_t dist); + uint16_t square3x(float xTmp, uint16_t dist); + uint16_t triangle(float xTmp, uint16_t shape); + uint16_t triangle2x(float xTmp, uint16_t dist); + uint16_t triangle3x(float xTmp, uint16_t dist); + + int16_t fold(int16_t tmp) ; + float ad2phase(uint16_t in) ; + float sine(float xTmp); + float getPhase(int cv, int octave, int note) ; + private: + float phase, phase1, phase2, phase3; + float oldPhase; + float sin(float x); + uint16_t deadzone(uint16_t tmp) ; +}; +#endif diff --git a/firmware/generic/src/res/colors.h b/firmware/generic/src/res/colors.h new file mode 100644 index 0000000..c1c9c55 --- /dev/null +++ b/firmware/generic/src/res/colors.h @@ -0,0 +1,38 @@ +// generic colors +#define blackColor RGBToWord(0x00, 0x00, 0x00) +#define whiteColor RGBToWord(0xff, 0xff, 0xff) +#define darkGrey RGBToWord(0xaa, 0xaa, 0xaa) +#define lightBlueColor RGBToWord(0x00, 0xff, 0xff) +#define purpleColor RGBToWord(0xff, 0x00, 0xff) + +// theme colors +#define main1Color lightBlueColor +#define main2Color purpleColor +#define disableColor darkGrey + +//welcome image colors +#define welcomeUpperColor main1Color +#define welcomeLowerColor main2Color + +// I/O colors +#define potUpperRightColor RGBToWord(0xff, 0x33, 0x99) // Pink +#define potLowerLeftColor RGBToWord(0xff, 0xcc, 0x33) // Yellow +#define potLowerRightColor RGBToWord(0x00, 0x99, 0xff) // Navy Blue +#define in1Color RGBToWord(0x00, 0xff, 0x00) // Green +#define in2Color RGBToWord(0x00, 0xff, 0x00) // Green +#define out1Color whiteColor +#define out2Color whiteColor + +// scopes Colors +#define chan0Color main1Color +#define chan1Color main2Color + +// ADSR Colors (match Pot colors) +#define aColor potUpperRightColor +#define dColor potLowerLeftColor +#define sColor disableColor +#define rColor potLowerRightColor + +// modules main colors +#define calColor main2Color // +#define SnHColor RGBToWord(0x00, 0xff, 0xff) // diff --git a/firmware/generic/src/data/font.cpp b/firmware/generic/src/res/font.h similarity index 98% rename from firmware/generic/src/data/font.cpp rename to firmware/generic/src/res/font.h index cc35ec4..625b1c5 100644 --- a/firmware/generic/src/data/font.cpp +++ b/firmware/generic/src/res/font.h @@ -1,8 +1,7 @@ extern "C" { -#include "gd32vf103.h" - extern const uint8_t font[1520] ; -} + #include "gd32vf103.h" +} /* Font copied from https://github.com/sipeed/Longan_GD32VF_examples.git @@ -20,6 +19,11 @@ extern "C" limitations under the License. */ +// more fonts to check +// https://lexus2k.github.io/ssd1306/group___l_c_d___f_o_n_t_s.html + + + const uint8_t font[1520]={ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x18,0x18,0x00,0x00, diff --git a/firmware/generic/src/data/frequency.cpp b/firmware/generic/src/res/frequency.cpp similarity index 100% rename from firmware/generic/src/data/frequency.cpp rename to firmware/generic/src/res/frequency.cpp diff --git a/firmware/generic/src/res/images.h b/firmware/generic/src/res/images.h new file mode 100644 index 0000000..cc4f2a0 --- /dev/null +++ b/firmware/generic/src/res/images.h @@ -0,0 +1,12 @@ +#include "gd32vf103.h" + +//welcome images +const uint8_t img_1337[465] = {24, 9, 25, 9, 7, 9, 7, 9, 7, 9, 7, 9, 7, 9, 7, 9, 7, 9, 7, 9, 255, 32, 1, 9, 1, 21, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 255, 255, 255, 255, 255, 255, 255, 255, 165, 1, 9, 1, 21, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 255, 32, 9, 25, 9, 7, 9, 7, 9, 7, 9, 7, 9, 7, 9, 7, 9, 7, 9, 7, 9, 255, 255, 255, 4, 9, 61, 9, 7, 9, 7, 9, 43, 9, 255, 50, 1, 9, 1, 57, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 39, 1, 9, 1, 255, 255, 255, 255, 255, 255, 255, 255, 183, 1, 9, 1, 57, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 39, 1, 9, 1, 255, 50, 9, 61, 9, 7, 9, 7, 9, 43, 9, 255, 255, 255, 22, 9, 43, 9, 7, 9, 7, 9, 7, 9, 7, 9, 25, 9, 255, 50, 1, 9, 1, 39, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 21, 1, 9, 1, 255, 255, 255, 255, 255, 255, 255, 255, 183, 1, 9, 1, 39, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 21, 1, 9, 1, 255, 50, 9, 43, 9, 7, 9, 7, 9, 7, 9, 7, 9, 25, 9, 255, 255, 255, 22, 9, 61, 9, 7, 9, 61, 9, 255, 50, 1, 9, 1, 57, 1, 9, 1, 3, 1, 9, 1, 57, 1, 9, 1, 255, 255, 255, 255, 255, 255, 255, 255, 183, 1, 9, 1, 57, 1, 9, 1, 3, 1, 9, 1, 57, 1, 9, 1, 255, 50, 9, 61, 9, 7, 9, 61, 9, 255, 255, 255, 22, 9, 7, 9, 7, 9, 7, 9, 7, 9, 7, 9, 7, 9, 7, 9, 25, 9, 255, 50, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 21, 1, 9, 1, 255, 255, 255, 255, 255, 255, 255, 255, 183, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 3, 1, 9, 1, 21, 1, 9, 1, 255, 25}; +const uint8_t img_modular[105] = {22, 7, 10, 6, 255, 255, 255, 255, 161, 2, 3, 3, 57, 6, 35, 10, 255, 255, 255, 74, 3, 21, 14, 38, 7, 6, 7, 30, 14, 9, 18, 170, 3, 255, 255, 255, 78, 2, 3, 3, 13, 3, 3, 6, 3, 3, 9, 10, 67, 10, 3, 3, 13, 6, 3, 3, 255, 255, 255, 70, 7, 67, 10, 94, 3, 46, 3, 118, 7, 255, 255, 255, 142, 3, 3, 6, 255, 255, 255, 79, 6, 13, 3, 32, 6, 13, 3, 6, 3, 13, 6, 116, 3, 14, 3, 12, 7, 13, 3, 45, 3, 255, 255, 255, 26}; + +// arrow +const uint8_t arrowLeftImage[21] = {50,0,14,3,11,5,9,8,6,9,5,9,5,8,6,5,9,3,11,0,59}; + +//module images +//const uint8_t img_snh[182] = {255, 255, 255, 255, 255, 255, 255, 255, 132, 59, 15, 21, 25, 10, 48, 10, 42, 1, 59, 0, 12, 1, 20, 0, 138, 0, 62, 0, 10, 0, 161, 0, 64, 0, 8, 0, 22, 0, 137, 0, 66, 0, 6, 0, 255, 7, 0, 137, 0, 68, 0, 4, 0, 255, 7, 0, 244, 10, 221, 0, 255, 135, 59, 15, 11, 255, 255, 255, 255, 91, 54, 20, 18, 10, 7, 15, 48, 115, 0, 239, 0, 239, 0, 170, 0, 67, 0, 255, 155, 0, 67, 0, 255, 155, 0, 239, 0, 239, 1, 239, 53, 26, 18, 10, 7, 15, 48, 255, 255, 255, 244, 59, 26, 18, 255, 255, 255, 88, 0, 74, 0, 232, 0, 58, 0, 68, 0, 39, 0, 74, 0, 230, 0, 60, 0, 66, 0, 41, 0, 64, 0, 8, 0, 52, 0, 64, 0, 43, 0, 62, 0, 10, 0, 52, 0, 62, 0, 45, 1, 57, 2, 12, 1, 51, 2, 56, 2, 48, 57, 17, 34, 19, 3, 48, 3, 255, 255, 255, 255, 255, 255, 188}; +//const uint8_t img_play[110] = {255, 255, 255, 255, 255, 255, 255, 255, 136, 3, 234, 0, 3, 0, 232, 0, 5, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 239, 0, 255, 255, 208, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 237, 0, 230, 0, 5, 0, 232, 0, 3, 0, 234, 3, 255, 255, 255, 255, 255, 255, 255, 113}; diff --git a/firmware/generic/src/res/modes.h b/firmware/generic/src/res/modes.h new file mode 100644 index 0000000..8182dc1 --- /dev/null +++ b/firmware/generic/src/res/modes.h @@ -0,0 +1,17 @@ +// main mode definition + +#define modeVCO 1 +#define modeVCA 2 +#define modeVCF 3 +#define modeLFO 4 +#define modeEnv 5 +#define modeDelay 6 +#define modeNoise 7 +#define modeCalibrate 8 +#define maxMode 8 + +//#define mode_quantize 0 +//#define mode_cvout 1 + +//#define mode_SnH 0 +//#define mode_CV_Play 0 \ No newline at end of file diff --git a/firmware/tools/arrow_left.png b/firmware/tools/arrow_left.png new file mode 100644 index 0000000..58207be Binary files /dev/null and b/firmware/tools/arrow_left.png differ diff --git a/firmware/tools/in-cal.png b/firmware/tools/in-cal.png new file mode 100644 index 0000000..2ffc34a Binary files /dev/null and b/firmware/tools/in-cal.png differ diff --git a/firmware/tools/in-dly.png b/firmware/tools/in-dly.png new file mode 100644 index 0000000..f946920 Binary files /dev/null and b/firmware/tools/in-dly.png differ diff --git a/firmware/tools/in-env.png b/firmware/tools/in-env.png new file mode 100644 index 0000000..a4e1828 Binary files /dev/null and b/firmware/tools/in-env.png differ diff --git a/firmware/tools/in-lfo.png b/firmware/tools/in-lfo.png new file mode 100644 index 0000000..17d1c28 Binary files /dev/null and b/firmware/tools/in-lfo.png differ diff --git a/firmware/tools/in-rnd.png b/firmware/tools/in-rnd.png new file mode 100644 index 0000000..694a5fa Binary files /dev/null and b/firmware/tools/in-rnd.png differ diff --git a/firmware/tools/in-vca.png b/firmware/tools/in-vca.png new file mode 100644 index 0000000..bf2c0b9 Binary files /dev/null and b/firmware/tools/in-vca.png differ diff --git a/firmware/tools/in-vcf.png b/firmware/tools/in-vcf.png new file mode 100644 index 0000000..10f0e93 Binary files /dev/null and b/firmware/tools/in-vcf.png differ diff --git a/firmware/tools/in-vco.png b/firmware/tools/in-vco.png new file mode 100644 index 0000000..d43e646 Binary files /dev/null and b/firmware/tools/in-vco.png differ diff --git a/firmware/tools/in.png b/firmware/tools/in.png index 03b8c28..2ffc34a 100644 Binary files a/firmware/tools/in.png and b/firmware/tools/in.png differ diff --git a/firmware/tools/in.xcf b/firmware/tools/in.xcf new file mode 100644 index 0000000..c82dac9 Binary files /dev/null and b/firmware/tools/in.xcf differ diff --git a/firmware/tools/menu.txt b/firmware/tools/menu.txt index 6727416..4bc2f02 100644 --- a/firmware/tools/menu.txt +++ b/firmware/tools/menu.txt @@ -1,4 +1,4 @@ -OSC +VCO < back sine am|128 sine add|129 @@ -17,26 +17,26 @@ OSC triangle 3x|142 quantize|143 cvout|144 -Noise|145 -LFO - < back - LFO sine|146 - LFO square|147 - LFO triangle|148 +VCA|160 VCF < back 2 pole v1|150 2 pole v2|151 4 pole|152 test|153 -VCA|160 -SnH|161 -Envelope +Env