diff --git a/click_module.ino b/click_module.ino index ed56b92..72cd25b 100644 --- a/click_module.ino +++ b/click_module.ino @@ -73,26 +73,26 @@ void Click_Process(float *left, float *right) { if (seq_bar == 0) { - /* create highest pitch sound for the first beat of the first bar */ + /* create highest pitch sound for the first beat of the first bar */ *left += ((float)((seq_counter & 16) / 16)) * 0.1f; *right += ((float)((seq_counter & 16) / 16)) * 0.1f; } else { - /* generate middle pitched sound for the first beat of all other bars */ + /* generate middle pitched sound for the first beat of all other bars */ *left += ((float)((seq_counter & 32) / 32)) * 0.1f; *right += ((float)((seq_counter & 32) / 32)) * 0.1f; } } else if ((seq_pos % 4) == 0) { - /* generate sound with lowest pitch for 2, 3, 4 */ + /* generate sound with lowest pitch for 2, 3, 4 */ *left += ((float)((seq_counter & 64) / 64)) * 0.1f; *right += ((float)((seq_counter & 64) / 64)) * 0.1f; } } - /* only if module is active the loop should be reset when jumping to first beat in first bar */ + /* only if module is active the loop should be reset when jumping to first beat in first bar */ if (seq_tempo) { if ((seq_pos == 0) && (seq_counter == 0) && (seq_bar == 0)) diff --git a/config.h b/config.h new file mode 100644 index 0000000..8a4b998 --- /dev/null +++ b/config.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 Marcel Licence + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Dieses Programm ist Freie Software: Sie können es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * veröffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch + * OHNE JEDE GEWÄHR,; sogar ohne die implizite + * Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License für weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/** + * @file config.h + * @author Marcel Licence + * @date 02.01.2021 + * + * @brief This file contains the project configuration + * + * All definitions are visible in the entire project + * + * Put all your project settings here (defines, numbers, etc.) + * configurations which are requiring knowledge of types etc. + * shall be placed in z_config.ino (will be included at the end) + */ + + +#ifndef CONFIG_H_ +#define CONFIG_H_ + + +#ifdef __CDT_PARSER__ +#include +#endif + +/* + * include the board configuration + * there you will find the most hardware depending pin settings + */ +#define BOARD_ESP32_AUDIO_KIT_AC101 + +#include /* requires https://github.com/marcel-licence/ML_SynthTools */ + +/* our samplerate */ +#define SAMPLE_RATE 44100 + +/* on board led */ +#define LED_PIN 19 + + +#endif /* CONFIG_H_ */ + diff --git a/es8388.ino b/es8388.ino index 51c14c1..345ec79 100644 --- a/es8388.ino +++ b/es8388.ino @@ -1,6 +1,4 @@ /* - * The GNU GENERAL PUBLIC LICENSE (GNU GPLv3) - * * Copyright (c) 2021 Marcel Licence * * This program is free software: you can redistribute it and/or modify @@ -30,17 +28,23 @@ * Programm erhalten haben. Wenn nicht, siehe . */ -/* - * es8388.ino +/** + * @file es8388.ino + * @author Marcel Licence + * @date 22.08.2021 * - * Created on: 22.08.2021 - * Author: Marcel Licence + * @brief This module is used to initialize the ES8388 + * + * @see ESP32 Audio Kit AC101 codec failure - Get synthesizer projects working based on ES8388 - https://youtu.be/8UB3fYPjqSk + * @see https://github.com/espressif/esp-adf/blob/master/components/audio_hal/driver/es8388/es8388.c */ + #ifdef __CDT_PARSER__ #include #endif + #ifdef ES8388_ENABLED /* * http://www.everest-semi.com/pdf/ES8388%20DS.pdf @@ -171,16 +175,6 @@ void es8388_read_all() } } -#define ES8388_PIN_SDA 18 -#define ES8388_PIN_SCL 23 - -#define ES8388_PIN_MCLK 0 -#define ES8388_PIN_SCLK 5 -#define ES8388_PIN_LRCK 25 -#define ES8388_PIN_DIN 26 -#define ES8388_PIN_DOUT 35 - - void ES8388_SetADCVOL(uint8_t unused, float vol) { #ifdef STATUS_ENABLED diff --git a/esp32_audio_kit_module.ino b/esp32_audio_kit_module.ino index d6c80c2..25fd6a9 100644 --- a/esp32_audio_kit_module.ino +++ b/esp32_audio_kit_module.ino @@ -1,6 +1,4 @@ /* - * The GNU GENERAL PUBLIC LICENSE (GNU GPLv3) - * * Copyright (c) 2021 Marcel Licence * * This program is free software: you can redistribute it and/or modify @@ -30,52 +28,99 @@ * Programm erhalten haben. Wenn nicht, siehe . */ -/* - * this file contains the module controlling the tempo of the looper - * it counts always 4 bars - * min tempo will be calculated depending on the max loop length +/** + * @file esp32_audio_kit_module.ino + * @author Marcel Licence + * @date 12.10.2021 * - * Author: Marcel Licence + * @brief This file contains basic stuff to work with the ESP32 Audio Kit V2.2 module + * + * @see ESP32 Audio Kit AC101 codec failure - Get synthesizer projects working based on ES8388 - https://youtu.be/8UB3fYPjqSk + * @see Instructions: http://myosuploads3.banggood.com/products/20210306/20210306011116instruction.pdf + * @see Schematic: https://docs.ai-thinker.com/_media/esp32-audio-kit_v2.2_sch.pdf */ + +#ifdef __CDT_PARSER__ +#include +#endif + + +#ifdef ESP32_AUDIO_KIT + +#ifdef AC101_ENABLED #include "AC101.h" /* only compatible with forked repo: https://github.com/marcel-licence/AC101 */ +#endif -/* AC101 pins */ -#define IIS_SCLK 27 -#define IIS_LCLK 26 -#define IIS_DSIN 25 -#define IIS_DSOUT 35 -#define IIC_CLK 32 -#define IIC_DATA 33 +//#define BUTTON_DEBUG_MSG -#define GPIO_PA_EN GPIO_NUM_21 -#define GPIO_SEL_PA_EN GPIO_SEL_21 -#define PIN_KEY_2 (13) -#define PIN_PLAY (23) // KEY 4 -#define PIN_VOL_UP (18) // KEY 5 -#define PIN_VOL_DOWN (5) // KEY 6 +#define PIN_LED4 (22) +#define PIN_LED5 (19) + + +#ifdef AUDIO_KIT_BUTTON_DIGITAL +/* + * when not modified and R66-R70 are placed on the board + */ +#define PIN_KEY_1 (36) +#define PIN_KEY_2 (13) +#define PIN_KEY_3 (19) +#define PIN_KEY_4 (23) +#define PIN_KEY_5 (18) +#define PIN_KEY_6 (5) + +#define PIN_PLAY PIN_KEY_4 +#define PIN_VOL_UP PIN_KEY_5 +#define PIN_VOL_DOWN PIN_KEY_6 +#endif + +#ifdef AUDIO_KIT_BUTTON_ANALOG +/* + * modification required: + * - remove R66-R70 + * - insert R60-R64 (0 Ohm or solder bridge) + * - insert R55-R59 using 1.8kOhm (recommended but other values might be possible with tweaking the code) + */ +#ifndef PIN_KEY_ANALOG +#define PIN_KEY_ANALOG (36) +#endif + +#define KEY_SETTLE_VAL 9 /* use higher value if anaog buton detection is unstable */ + +uint32_t keyMin[7] = {4095 - 32, 0, 462 - 32, 925 - 32, 1283 - 32, 1570 - 32, 1800 - 32}; +uint32_t keyMax[7] = {4095 + 32, 0 + 32, 525 + 32, 1006 + 32, 1374 + 32, 1570 + 32, 1800 + 32 }; +#endif #define OUTPUT_PIN 0 -#define MCLK_CH 0 -#define PWM_BIT 1 +#define MCLK_CH 0 +#define PWM_BIT 1 +#ifdef AC101_ENABLED static AC101 ac; +#endif /* actually only supporting 16 bit */ #define SAMPLE_SIZE_16BIT //#define SAMPLE_SIZE_24BIT //#define SAMPLE_SIZE_32BIT -#define SAMPLE_RATE 44100 -#define CHANNEL_COUNT 2 -#define WORD_SIZE 16 -#define I2S1CLK (512*SAMPLE_RATE) -#define BCLK (SAMPLE_RATE*CHANNEL_COUNT*WORD_SIZE) -#define LRCK (SAMPLE_RATE*CHANNEL_COUNT) +#ifndef SAMPLE_RATE +#define SAMPLE_RATE 44100 +#endif +#define CHANNEL_COUNT 2 +#define WORD_SIZE 16 +#define I2S1CLK (512*SAMPLE_RATE) +#define BCLK (SAMPLE_RATE*CHANNEL_COUNT*WORD_SIZE) +#define LRCK (SAMPLE_RATE*CHANNEL_COUNT) + +typedef void(*audioKitButtonCb)(uint8_t, uint8_t); +extern audioKitButtonCb audioKitButtonCallback; + +#ifdef AC101_ENABLED /* * this function could be used to set up the masterclock * it is not necessary to use the ac101 @@ -96,7 +141,7 @@ void ac101_mclk_setup() void ac101_setup() { Serial.printf("Connect to AC101 codec... "); - while (not ac.begin(IIC_DATA, IIC_CLK)) + while (not ac.begin(AC101_PIN_SDA, AC101_PIN_SCL)) { Serial.printf("Failed!\n"); delay(1000); @@ -112,7 +157,8 @@ void ac101_setup() #if (SAMPLE_RATE==44100)&&(defined(SAMPLE_SIZE_16BIT)) ac.SetI2sSampleRate(AC101::SAMPLE_RATE_44100); /* - * BCLK: 44100 * 2 * 16 = + * BCLK: 44100 * 2 * 16 = 1411200 Hz + * SYSCLK: 512 * fs = 512* 44100 = 22579200 Hz * * I2S1CLK/BCLK1 -> 512 * 44100 / 44100*2*16 * BCLK1/LRCK -> 44100*2*16 / 44100 Obacht ... ein clock cycle goes high and low @@ -138,21 +184,32 @@ void ac101_setup() #endif // Enable amplifier +#if 0 /* amplifier only required when speakers attached? */ pinMode(GPIO_PA_EN, OUTPUT); digitalWrite(GPIO_PA_EN, HIGH); +#endif } +#endif /* #ifdef AC101_ENABLED */ /* * pullup required to enable reading the buttons (buttons will connect them to ground if pressed) */ void button_setup() { +#ifdef AUDIO_KIT_BUTTON_DIGITAL // Configure keys on ESP32 Audio Kit board pinMode(PIN_PLAY, INPUT_PULLUP); pinMode(PIN_VOL_UP, INPUT_PULLUP); pinMode(PIN_VOL_DOWN, INPUT_PULLUP); +#endif +#ifdef AUDIO_KIT_BUTTON_ANALOG_OLD + adcAttachPin(PIN_KEY_ANALOG); + analogReadResolution(10); + analogSetAttenuation(ADC_11db); +#endif } +#ifdef AC101_ENABLED /* * selects the microphone as audio source * handle with care: mic is very sensitive and might cause feedback using amp!!! @@ -169,6 +226,7 @@ void ac101_setSourceLine(void) { ac.SetLineSource(); } +#endif /* #ifdef AC101_ENABLED */ /* * very bad implementation checking the button state @@ -176,16 +234,94 @@ void ac101_setSourceLine(void) */ void button_loop() { +#ifdef AUDIO_KIT_BUTTON_DIGITAL if (digitalRead(PIN_PLAY) == LOW) { Serial.println("PIN_PLAY pressed"); + if (buttonMapping.key4_pressed != NULL) + { + buttonMapping.key4_pressed(); + } } if (digitalRead(PIN_VOL_UP) == LOW) { Serial.println("PIN_VOL_UP pressed"); + if (buttonMapping.key5_pressed != NULL) + { + buttonMapping.key5_pressed(); + } } if (digitalRead(PIN_VOL_DOWN) == LOW) { Serial.println("PIN_VOL_DOWN pressed"); + if (buttonMapping.key6_pressed != NULL) + { + buttonMapping.key6_pressed(); + } } +#endif +#ifdef AUDIO_KIT_BUTTON_ANALOG + static uint32_t lastKeyAD = 0xFFFF; + static uint32_t keyAD = 0; + static uint8_t pressedKey = 0; + static uint8_t newPressedKey = 0; + static uint8_t pressedKeyLast = 0; + static uint8_t keySettle = 0; + + keyAD = analogRead(PIN_KEY_ANALOG); + if (keyAD != lastKeyAD) + { + //Serial.printf("keyAd: %d\n", keyAD); + lastKeyAD = keyAD; + + //pressedKey = 0; + for (int i = 0; i < 7; i ++) + { + if ((keyAD >= keyMin[i]) && (keyAD < keyMax[i])) + { + newPressedKey = i; + } + } + if (newPressedKey != pressedKey) + { + keySettle = KEY_SETTLE_VAL; + pressedKey = newPressedKey; + } + } + + if (keySettle > 0) + { + keySettle--; + if (keySettle == 0) + { + if (pressedKey != pressedKeyLast) + { + if (pressedKeyLast > 0) + { +#ifdef BUTTON_DEBUG_MSG + Serial.printf("Key %d up\n", pressedKeyLast); +#endif + if (audioKitButtonCallback != NULL) + { + audioKitButtonCallback(pressedKeyLast - 1, 0); + } + } + if (pressedKey > 0) + { +#ifdef BUTTON_DEBUG_MSG + Serial.printf("Key %d down\n", pressedKey); +#endif + if (audioKitButtonCallback != NULL) + { + audioKitButtonCallback(pressedKey - 1, 1); + } + } + pressedKeyLast = pressedKey; + } + } + } +#endif } + +#endif + diff --git a/esp32_multitrack_looper.ino b/esp32_multitrack_looper.ino index 3a4b665..e8afa7b 100644 --- a/esp32_multitrack_looper.ino +++ b/esp32_multitrack_looper.ino @@ -1,6 +1,4 @@ /* - * The GNU GENERAL PUBLIC LICENSE (GNU GPLv3) - * * Copyright (c) 2021 Marcel Licence * * This program is free software: you can redistribute it and/or modify @@ -38,12 +36,28 @@ * Author: Marcel Licence */ +#ifdef __CDT_PARSER__ +#include +#endif + +/* + * + /$$$$$$$$ /$$ /$$ /$$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$ /$$ /$$ /$$ /$$ +| $$_____/ | $$ | $$ | $$__ $$ /$$__ $$| $$__ $$ /$$__ $$| $$$ /$$$| $$| $$ +| $$ /$$$$$$$ /$$$$$$ | $$$$$$$ | $$ /$$$$$$ | $$ \ $$| $$ \__/| $$ \ $$| $$ \ $$| $$$$ /$$$$| $$| $$ +| $$$$$ | $$__ $$ |____ $$| $$__ $$| $$ /$$__ $$ | $$$$$$$/| $$$$$$ | $$$$$$$/| $$$$$$$$| $$ $$/$$ $$| $$| $$ +| $$__/ | $$ \ $$ /$$$$$$$| $$ \ $$| $$| $$$$$$$$ | $$____/ \____ $$| $$__ $$| $$__ $$| $$ $$$| $$|__/|__/ +| $$ | $$ | $$ /$$__ $$| $$ | $$| $$| $$_____/ | $$ /$$ \ $$| $$ \ $$| $$ | $$| $$\ $ | $$ +| $$$$$$$$| $$ | $$| $$$$$$$| $$$$$$$/| $$| $$$$$$$ | $$ | $$$$$$/| $$ | $$| $$ | $$| $$ \/ | $$ /$$ /$$ +|________/|__/ |__/ \_______/|_______/ |__/ \_______/ |__/ \______/ |__/ |__/|__/ |__/|__/ |__/|__/|__/ + + + */ +#include "config.h" + #include #include -/* on board led */ -#define LED_PIN 19 - /* * Chip is ESP32D0WDQ5 (revision 1) @@ -58,11 +72,6 @@ * */ -/* our samplerate */ -#define SAMPLE_RATE 44100 - -/* this is used to add a task to core 0 */ -TaskHandle_t Core0TaskHnd ; /* to avoid the high click when turning on the microphone */ static float click_supp_gain = 0.0f; @@ -83,19 +92,25 @@ void setup() click_supp_gain = 0.0f; +#ifdef BLINK_LED_PIN Blink_Setup(); - +#endif Status_Setup(); +#ifdef ESP32_AUDIO_KIT #ifdef ES8388_ENABLED ES8388_Setup(); #else ac101_setup(); + /* using mic as default source */ + ac101_setSourceMic(); +#endif #endif setup_i2s(); - +#ifdef ESP32_AUDIO_KIT button_setup(); +#endif /* * setup midi module / rx port @@ -108,8 +123,10 @@ void setup() WiFi.mode(WIFI_OFF); #endif +#ifndef ESP8266 btStop(); // esp_wifi_deinit(); +#endif Delay_Init(); Delay_Reset(); @@ -128,21 +145,53 @@ void setup() Serial.printf("Total PSRAM: %d\n", ESP.getPsramSize()); Serial.printf("Free PSRAM: %d\n", ESP.getFreePsram()); - /* we need a second task for the terminal output */ - xTaskCreatePinnedToCore(CoreTask0, "terminalTask", 8000, NULL, 999, &Core0TaskHnd, 0); +#ifdef ESP32 + Core0TaskInit(); +#else +#error only supported by ESP32 platform +#endif } -void CoreTask0(void *parameter) +#ifdef ESP32 +/* + * Core 0 + */ +/* this is used to add a task to core 0 */ +TaskHandle_t Core0TaskHnd ; + +inline +void Core0TaskInit() { + /* we need a second task for the terminal output */ + xTaskCreatePinnedToCore(Core0Task, "CoreTask0", 8000, NULL, 999, &Core0TaskHnd, 0); +} + +inline +void Core0TaskSetup() +{ + +} + +inline +void Core0TaskLoop() +{ + Status_Process(); +} + +void Core0Task(void *parameter) +{ + Core0TaskSetup(); + while (true) { - Status_Process(); + Core0TaskLoop(); /* this seems necessary to trigger the watchdog */ delay(1); yield(); } } +#endif float main_gain = 1.0f; @@ -232,13 +281,17 @@ void MTLooper_ToggleSource(uint8_t channel, float value) { case acSrcLine: click_supp_gain = 0.0f; +#ifdef AC101_ENABLED ac101_setSourceMic(); +#endif selSource = acSrcMic; Status_TestMsg("Input: Microphone"); break; case acSrcMic: click_supp_gain = 0.0f; +#ifdef AC101_ENABLED ac101_setSourceLine(); +#endif selSource = acSrcLine; Status_TestMsg("Input: LineIn"); break; @@ -315,13 +368,10 @@ inline void audio_task() */ void loop_1Hz(void) { - static uint32_t cycl = ESP.getCycleCount(); - static uint32_t lastCycl; - - lastCycl = cycl; - button_loop(); +#ifdef BLINK_LED_PIN Blink_Process(); +#endif } static uint32_t midi_pre_scaler = 0; diff --git a/i2s_interface.ino b/i2s_interface.ino index f4a47e1..7c589b4 100644 --- a/i2s_interface.ino +++ b/i2s_interface.ino @@ -132,12 +132,10 @@ bool i2s_write_stereo_samples(float *fl_sample, float *fr_sample) } sampleDataU; #endif - sampleDataU.ch[0] = int16_t(*fl_sample * 16383.0f); sampleDataU.ch[1] = int16_t(*fr_sample * 16383.0f); static size_t bytes_written = 0; - static size_t bytes_read = 0; i2s_write(i2s_num, (const char *)&sampleDataU.sample, 4, &bytes_written, portMAX_DELAY); @@ -153,10 +151,8 @@ bool i2s_write_stereo_samples(float *fl_sample, float *fr_sample) void i2s_read_stereo_samples(float *fl_sample, float *fr_sample) { - static size_t bytes_written = 0; static size_t bytes_read = 0; - static union { uint32_t sample; diff --git a/loop_module.ino b/loop_module.ino index e2ea383..a27c928 100644 --- a/loop_module.ino +++ b/loop_module.ino @@ -37,9 +37,9 @@ */ -/* - * MAX_LOOP can only increased by decreasing TRACK_CNT (track count) - * +/* + * MAX_LOOP can only increased by decreasing TRACK_CNT (track count) + * * for example using only 2 tracks will allow MAX_LOOP to be set to 1026492 * all PSRAM memory will be used * if values are set to big the startup of the firmware will fail @@ -76,7 +76,7 @@ void Loop_init(void) if (loopLine[n] == NULL) { - /* when you see the message the count of TRACK_CNT is too high or MAX_LOOP is too big */ + /* when you see the message the count of TRACK_CNT is too high or MAX_LOOP is too big */ Serial.printf("not enough memory!\n"); return; } @@ -197,7 +197,7 @@ void Loop_Process(float *signal_l, float *signal_r) for (int n = 0; n < TRACK_CNT; n++) { - /* stop erasing when final position has been reached */ + /* stop erasing when final position has been reached */ if (loopErase[n] == loopIn) { loopErase[n] = 0xFFFFFFFFUL; diff --git a/midi_interface.ino b/midi_interface.ino index 3a8d3ff..f36c718 100644 --- a/midi_interface.ino +++ b/midi_interface.ino @@ -1,6 +1,4 @@ /* - * The GNU GENERAL PUBLIC LICENSE (GNU GPLv3) - * * Copyright (c) 2021 Marcel Licence * * This program is free software: you can redistribute it and/or modify @@ -30,26 +28,83 @@ * Programm erhalten haben. Wenn nicht, siehe . */ -/* - * a simple implementation to use midi +/** + * @file midi_interface.ino + * @author Marcel Licence + * @date 04.10.2021 * - * Author: Marcel Licence + * @brief This file contains an implementation of a simple MIDI interpreter to parse incoming messages + * + * MIDI_DUMP_Serial1_TO_SERIAL <- when active received data will be output as hex on serial(1) + * MIDI_SERIAL1_BAUDRATE <- use define to override baud-rate for MIDI, otherwise default of 31250 will be used + * + * @see https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message */ +#ifdef __CDT_PARSER__ +#include +#endif + + /* * look for midi interface using 1N136 * to convert the MIDI din signal to * a uart compatible signal */ -#if 0 -#define RXD2 16 /* U2RRXD */ -#define TXD2 17 -#else -#define RXD2 22 -#define TXD2 21 + +#ifndef MIDI_SERIAL1_BAUDRATE +#define MIDI_SERIAL1_BAUDRATE 31250 #endif +#ifndef MIDI_SERIAL2_BAUDRATE +#define MIDI_SERIAL2_BAUDRATE 31250 +#endif + +/* use define to dump midi data */ +//#define MIDI_DUMP_SERIAL2_TO_SERIAL + + +#if (defined MIDI_RX_PIN) || (defined MIDI_RECV_FROM_SERIAL) +#define MIDI_PORT_ACTIVE +#endif + +#ifdef MIDI_RX1_PIN +#define MIDI_PORT1_ACTIVE +#endif + +#ifdef MIDI_RX2_PIN +#define MIDI_PORT2_ACTIVE +#endif + +struct midi_port_s +{ + Stream *serial; /* this can be software or hardware serial */ + uint32_t inMsgWd ; + uint8_t inMsg[3]; + uint8_t inMsgIndex ; +}; + +#ifdef ARDUINO_DAISY_SEED +HardwareSerial Serial2(USART1); +#endif + +#ifdef ARDUINO_GENERIC_F407VGTX +HardwareSerial Serial2(USART3); /* PB11 */ +#endif + + +#ifdef MIDI_PORT_ACTIVE +struct midi_port_s MidiPort; +#endif + +#ifdef MIDI_PORT1_ACTIVE +struct midi_port_s MidiPort1; +#endif + +#ifdef MIDI_PORT2_ACTIVE +struct midi_port_s MidiPort2; +#endif /* * structure is used to build the mapping table @@ -60,98 +115,65 @@ struct midiControllerMapping uint8_t data1; const char *desc; void(*callback_mid)(uint8_t ch, uint8_t data1, uint8_t data2); +#ifdef MIDI_FMT_INT + void(*callback_val)(uint8_t userdata, uint8_t value); +#else void(*callback_val)(uint8_t userdata, float value); +#endif uint8_t user_data; }; -/* - * this mapping is used for the edirol pcr-800 - * this should be changed when using another controller - */ -struct midiControllerMapping edirolMapping[] = +struct midiMapping_s { - { 0x8, 0x52, "back", NULL, Loop_ResetToStart, 0}, - { 0x8, 0x52, "back", NULL, Click_Reset, 0}, - { 0xD, 0x52, "stop", NULL, Loop_Stop, 0}, - { 0xe, 0x52, "start", NULL, Loop_PlayNormal, 0}, - { 0xe, 0x52, "start", NULL, Loop_StartAll, 0}, - { 0xa, 0x52, "rec", NULL, Loop_SetLength, 0}, + void (*rawMsg)(uint8_t *msg); +#ifdef MIDI_FMT_INT + void (*noteOn)(uint8_t ch, uint8_t note, uint8_t vel); + void (*noteOff)(uint8_t ch, uint8_t note); + void (*pitchBend)(uint8_t ch, uint8_t bend); + void (*modWheel)(uint8_t ch, uint8_t value); +#else + void (*noteOn)(uint8_t ch, uint8_t note, float vel); + void (*noteOff)(uint8_t ch, uint8_t note); + void (*pitchBend)(uint8_t ch, float bend); + void (*modWheel)(uint8_t ch, float value); +#endif + void (*rttMsg)(uint8_t msg); + void (*songPos)(uint16_t pos); - /* upper row of buttons */ - { 0x0, 0x50, "A1", NULL, Loop_SelectTrack, 0}, - { 0x1, 0x50, "A2", NULL, Loop_SelectTrack, 1}, - { 0x2, 0x50, "A3", NULL, Loop_SelectTrack, 2}, - { 0x3, 0x50, "A4", NULL, Loop_SelectTrack, 3}, - - { 0x4, 0x50, "A5", NULL, Click_ToggleOnOff, 0}, - { 0x5, 0x50, "A6", NULL, Loop_JumpPosQuarter, 1}, - { 0x6, 0x50, "A7", NULL, Loop_JumpPosQuarter, 2}, - { 0x7, 0x50, "A8", NULL, Loop_JumpPosQuarter, 3}, - { 0x5, 0x50, "A6", NULL, Click_JumpPosQuarter, 1}, - { 0x6, 0x50, "A7", NULL, Click_JumpPosQuarter, 2}, - { 0x7, 0x50, "A8", NULL, Click_JumpPosQuarter, 3}, - - { 0x0, 0x53, "A9", NULL, Click_OnOff, 0}, - - /* lower row of buttons */ - { 0x0, 0x51, "B1", NULL, Loop_StopChannel, 0}, - { 0x1, 0x51, "B2", NULL, Loop_StopChannel, 1}, - { 0x2, 0x51, "B3", NULL, Loop_StopChannel, 2}, - { 0x3, 0x51, "B4", NULL, Loop_StopChannel, 3}, - - { 0x4, 0x51, "B5", NULL, Loop_EraseTrack, 0}, - { 0x5, 0x51, "B6", NULL, Loop_EraseTrack, 1}, - { 0x6, 0x51, "B7", NULL, Loop_EraseTrack, 2}, - { 0x7, 0x51, "B8", NULL, Loop_EraseTrack, 3}, - - { 0x1, 0x53, "B9", NULL, MTLooper_ToggleSource, 0}, - - /* pedal */ - { 0x0, 0x0b, "VolumePedal", NULL, NULL, 0}, - - { 0x0, 0x11, "S1", NULL, Loop_SetChannelGainOut, 0}, - { 0x1, 0x11, "S2", NULL, Loop_SetChannelGainOut, 1}, - { 0x2, 0x11, "S3", NULL, Loop_SetChannelGainOut, 2}, - { 0x3, 0x11, "S4", NULL, Loop_SetChannelGainOut, 3}, - - { 0x4, 0x11, "S5", NULL, NULL, 0}, - { 0x5, 0x11, "S6", NULL, NULL, 0}, - { 0x6, 0x11, "S7", NULL, NULL, 0}, - { 0x7, 0x11, "S8", NULL, NULL, 0}, - - /* rotary */ - { 0x0, 0x10, "R1", NULL, Loop_SetChannelPan, 0}, - { 0x1, 0x10, "R2", NULL, Loop_SetChannelPan, 1}, - { 0x2, 0x10, "R3", NULL, Loop_SetChannelPan, 2}, - { 0x3, 0x10, "R4", NULL, Loop_SetChannelPan, 3}, - - { 0x4, 0x10, "R5", NULL, NULL, 0}, - { 0x5, 0x10, "R6", NULL, NULL, 0}, - { 0x6, 0x10, "R7", NULL, Click_SetTempo, 0}, - { 0x7, 0x10, "R8", NULL, Loop_SetSpeed, 0}, - - { 0x0, 0x12, "R9", NULL, Loop_SetChannelGainIn, 0}, + struct midiControllerMapping *controlMapping; + int mapSize; }; - - - - -/* use define to dump midi data */ -//#define DUMP_SERIAL2_TO_SERIAL +extern struct midiMapping_s midiMapping; /* definition in z_config.ino */ /* constant to normalize midi value to 0.0 - 1.0f */ -#define NORM127MUL 0.007874f +#define NORM127MUL 0.007874f -inline void Midi_NoteOn(uint8_t ch, uint8_t note) +inline void Midi_NoteOn(uint8_t ch, uint8_t note, uint8_t vel) { - Loop_NoteOn(ch, note); - // Synth_NoteOn(note); + if (vel > 127) + { + /* we will end up here in case of problems with the MIDI connection */ + vel = 127; + Serial.printf("to loud note detected!!!!!!!!!!!!!!!!!!!!!!!\n"); + } + + if (midiMapping.noteOn != NULL) + { +#ifdef MIDI_FMT_INT + midiMapping.noteOn(ch, note, vel); +#else + midiMapping.noteOn(ch, note, pow(2, ((vel * NORM127MUL) - 1.0f) * 6)); +#endif + } } inline void Midi_NoteOff(uint8_t ch, uint8_t note) { - // Synth_NoteOff(note); + if (midiMapping.noteOff != NULL) + { + midiMapping.noteOff(ch, note); + } } /* @@ -159,56 +181,59 @@ inline void Midi_NoteOff(uint8_t ch, uint8_t note) */ inline void Midi_ControlChange(uint8_t channel, uint8_t data1, uint8_t data2) { - for (int i = 0; i < (sizeof(edirolMapping) / sizeof(edirolMapping[0])); i++) + for (int i = 0; i < midiMapping.mapSize; i++) { - if ((edirolMapping[i].channel == channel) && (edirolMapping[i].data1 == data1)) + if ((midiMapping.controlMapping[i].channel == channel) && (midiMapping.controlMapping[i].data1 == data1)) { - if (edirolMapping[i].callback_mid != NULL) + if (midiMapping.controlMapping[i].callback_mid != NULL) { - edirolMapping[i].callback_mid(channel, data1, data2); + midiMapping.controlMapping[i].callback_mid(channel, data1, data2); } - if (edirolMapping[i].callback_val != NULL) + if (midiMapping.controlMapping[i].callback_val != NULL) { - edirolMapping[i].callback_val(edirolMapping[i].user_data, (float)data2 * NORM127MUL); - } - } - } - - if (data1 == 17) - { - if (channel < 10) - { - Synth_SetSlider(channel, data2 * NORM127MUL); - } - } -#if 0 - if (data1 == 17) - { - if (channel < 10) - { - Synth_SetSlider(channel, data2 * NORM127MUL); - } - } - if ((data1 == 18) && (channel == 1)) - { - Synth_SetSlider(8, data2 * NORM127MUL); - } +#ifdef MIDI_FMT_INT + midiMapping.controlMapping[i].callback_val(midiMapping.controlMapping[i].user_data, data2); +#else + midiMapping.controlMapping[i].callback_val(midiMapping.controlMapping[i].user_data, (float)data2 * NORM127MUL); #endif - if ((data1 == 16) && (channel < 9)) - { - Synth_SetRotary(channel, data2 * NORM127MUL); - + } + } } - if ((data1 == 18) && (channel == 0)) + + if (data1 == 1) { - Synth_SetRotary(8, data2 * NORM127MUL); + if (midiMapping.modWheel != NULL) + { +#ifdef MIDI_FMT_INT + midiMapping.modWheel(channel, data2); +#else + midiMapping.modWheel(channel, (float)data2 * NORM127MUL); +#endif + } + } +} + +inline void Midi_PitchBend(uint8_t ch, uint16_t bend) +{ + float value = ((float)bend - 8192.0f) * (1.0f / 8192.0f) - 1.0f; + if (midiMapping.pitchBend != NULL) + { + midiMapping.pitchBend(ch, value); + } +} + +inline void Midi_SongPositionPointer(uint16_t pos) +{ + if (midiMapping.songPos != NULL) + { + midiMapping.songPos(pos); } } /* * function will be called when a short message has been received over midi */ -inline void HandleShortMsg(uint8_t *data) +inline void Midi_HandleShortMsg(uint8_t *data, uint8_t cable __attribute__((unused))) { uint8_t ch = data[0] & 0x0F; @@ -218,7 +243,7 @@ inline void HandleShortMsg(uint8_t *data) case 0x90: if (data[2] > 0) { - Midi_NoteOn(ch, data[1]); + Midi_NoteOn(ch, data[1], data[2]); } else { @@ -232,77 +257,212 @@ inline void HandleShortMsg(uint8_t *data) case 0xb0: Midi_ControlChange(ch, data[1], data[2]); break; + /* pitchbend */ + case 0xe0: + Midi_PitchBend(ch, ((((uint16_t)data[1])) + ((uint16_t)data[2] << 8))); + break; + /* song position pointer */ + case 0xf2: + Midi_SongPositionPointer(((((uint16_t)data[1])) + ((uint16_t)data[2] << 8))); + break; } } +inline void Midi_RealTimeMessage(uint8_t msg) +{ + if (midiMapping.rttMsg != NULL) + { + midiMapping.rttMsg(msg); + } +} + +static void Midi_PortSetup(struct midi_port_s *port) +{ + /* reset the watchdog variables */ + port->inMsgWd = 0; + memset(port->inMsg, 0, sizeof(port->inMsg)); + port->inMsgIndex = 0; +} + void Midi_Setup() { - Serial2.begin(31250, SERIAL_8N1, RXD2, TXD2); - pinMode(RXD2, INPUT_PULLUP); /* 25: GPIO 16, u2_RXD */ +#ifdef MIDI_RECV_FROM_SERIAL + MidiPort.serial = &Serial; +#endif /* MIDI_RECV_FROM_SERIAL */ + +#ifdef MIDI_PORT_ACTIVE +#ifdef SWAP_SERIAL + Serial.printf("Switch Serial to Midi!\n"); + delay(20); + Serial.end(); + Serial.begin(MIDI_BAUDRATE); + Serial.swap(); /* using alternative rx and tx pin for Midi communication */ + delay(20); +#endif + MidiPort.serial = &Serial; + Midi_PortSetup(&MidiPort); +#endif + +#ifdef MIDI_PORT1_ACTIVE + +#ifdef MIDI_RX1_PIN +#ifdef MIDI_TX1_PIN + Serial.printf("Setup Serial1 with %d baud with rx: %d and tx %d\n", MIDI_SERIAL1_BAUDRATE, MIDI_RX1_PIN, MIDI_TX1_PIN); + Serial1.begin(MIDI_SERIAL1_BAUDRATE, SERIAL_8N1, MIDI_RX1_PIN, MIDI_TX1_PIN); +#else + Serial.printf("Setup Serial1 with %d baud with rx: %d only\n", MIDI_SERIAL1_BAUDRATE, MIDI_RX1_PIN); + Serial1.begin(MIDI_SERIAL1_BAUDRATE, SERIAL_8N1, MIDI_RX1_PIN); +#endif + pinMode(MIDI_RX1_PIN, INPUT_PULLUP); /* can be connected to open collector output */ +#else + Serial.printf("Setup Serial1 with %d baud with rx: RX1 pin\n", MIDI_SERIAL1_BAUDRATE); + Serial1.begin(MIDI_SERIAL1_BAUDRATE); +#endif + +#ifdef ARDUINO_SEEED_XIAO_M0 + pinMode(PIN_SERIAL1_RX, INPUT_PULLUP); +#endif + + MidiPort1.serial = &Serial1; + Midi_PortSetup(&MidiPort1); +#endif /* MIDI_PORT1_ACTIVE */ + + +#ifdef MIDI_PORT2_ACTIVE + +#ifdef MIDI_RX2_PIN +#ifdef MIDI_TX2_PIN + Serial.printf("Setup Serial2 with %d baud with rx: %d and tx %d\n", MIDI_SERIAL2_BAUDRATE, MIDI_RX2_PIN, MIDI_TX2_PIN); + Serial2.begin(MIDI_SERIAL2_BAUDRATE, SERIAL_8N1, MIDI_RX2_PIN, MIDI_TX2_PIN); +#else + Serial.printf("Setup Serial2 with %d baud with rx: %d only\n", MIDI_SERIAL2_BAUDRATE, MIDI_RX2_PIN); + Serial2.begin(MIDI_SERIAL2_BAUDRATE, SERIAL_8N1, MIDI_RX2_PIN); +#endif + pinMode(MIDI_RX2_PIN, INPUT_PULLUP); /* can be connected to open collector output */ +#else + Serial.printf("Setup Serial2 with %d baud with rx: RX2 pin\n", MIDI_SERIAL2_BAUDRATE); + Serial2.begin(MIDI_SERIAL2_BAUDRATE); +#endif + + MidiPort2.serial = &Serial2; + Midi_PortSetup(&MidiPort2); + Serial.printf("Setup MidiPort2 using Serial2\n"); +#endif /* MIDI_PORT2_ACTIVE */ } -/* - * this function should be called continuously to ensure that incoming messages can be processed - */ -void Midi_Process() +void Midi_CheckMidiPort(struct midi_port_s *port) { - /* - * watchdog to avoid getting stuck by receiving incomplete or wrong data - */ - static uint32_t inMsgWd = 0; - static uint8_t inMsg[3]; - static uint8_t inMsgIndex = 0; - //Choose Serial1 or Serial2 as required - if (Serial2.available()) + if (port->serial->available()) { - uint8_t incomingByte = Serial2.read(); + uint8_t incomingByte = port->serial->read(); -#ifdef DUMP_SERIAL2_TO_SERIAL +#ifdef MIDI_DUMP_SERIAL2_TO_SERIAL Serial.printf("%02x", incomingByte); #endif /* ignore live messages */ if ((incomingByte & 0xF0) == 0xF0) { + Midi_RealTimeMessage(incomingByte); return; } - if (inMsgIndex == 0) + if (port->inMsgIndex == 0) { if ((incomingByte & 0x80) != 0x80) { - inMsgIndex = 1; + port->inMsgIndex = 1; } } - inMsg[inMsgIndex] = incomingByte; - inMsgIndex += 1; + port->inMsg[port->inMsgIndex] = incomingByte; + port->inMsgIndex += 1; - if (inMsgIndex >= 3) + if ((port->inMsgIndex >= 3) || + ( + (((port->inMsg[0] & 0xF0) == 0xD0) + || ((port->inMsg[0] & 0xF0) == 0xC0)) + && (port->inMsgIndex >= 2)) + ) { -#ifdef DUMP_SERIAL2_TO_SERIAL - Serial.printf(">%02x %02x %02x\n", inMsg[0], inMsg[1], inMsg[2]); +#ifdef MIDI_DUMP_SERIAL2_TO_SERIAL + if (port->inMsgIndex >= 3) + { + Serial.printf("\n>%02x %02x %02x<\n", port->inMsg[0], port->inMsg[1], port->inMsg[2]); + } + else + { + Serial.printf("\n>%02x %02x<\n", port->inMsg[0], port->inMsg[1]); + } #endif - HandleShortMsg(inMsg); - inMsgIndex = 0; + Midi_HandleShortMsg(port->inMsg, 0); + port->inMsgIndex = 0; } /* * reset watchdog to allow new bytes to be received */ - inMsgWd = 0; + port->inMsgWd = 0; } else { - if (inMsgIndex > 0) + if (port->inMsgIndex > 0) { - inMsgWd++; - if (inMsgWd == 0xFFF) + port->inMsgWd++; + if (port->inMsgWd == 0xFFF) { - inMsgIndex = 0; + port->inMsgIndex = 0; } } } } +/* + * this function should be called continuously to ensure that incoming messages can be processed + */ +inline +void Midi_Process() +{ +#ifdef MIDI_PORT_ACTIVE + Midi_CheckMidiPort(&MidiPort); +#endif +#ifdef MIDI_PORT1_ACTIVE + Midi_CheckMidiPort(&MidiPort1); +#endif +#ifdef MIDI_PORT2_ACTIVE + Midi_CheckMidiPort(&MidiPort2); +#endif +} + +#ifndef ARDUINO_SEEED_XIAO_M0 +#ifndef SWAP_SERIAL +void Midi_SendShortMessage(uint8_t *msg) +{ +#ifdef MIDI_TX2_PIN + MidiPort2.serial->write(msg, 3); +#endif +} + +void Midi_SendRaw(uint8_t *msg) +{ +#ifdef MIDI_TX2_PIN + /* sysex */ + if (msg[0] == 0xF0) + { + int i = 2; + while (msg[i] != 0xF7) + { + i++; + } + MidiPort2.serial->write(msg, i + 1); + } + else + { + MidiPort2.serial->write(msg, 3); + } +#endif +} +#endif +#endif + diff --git a/status_module.ino b/status_module.ino index e515f42..93d8e88 100644 --- a/status_module.ino +++ b/status_module.ino @@ -40,7 +40,7 @@ */ -char statusMsg[128] = "\0"; /*!< buffer for top line message */ +char statusMsg[128] = ""; /*!< buffer for top line message */ uint32_t status_cnt = 0; /*!< counter for timeout to reset top line */ uint32_t loopMeterCount = 0; /*!< prescaler to reduce the serial output */ @@ -74,9 +74,9 @@ void Status_Setup(void) */ void Status_PrintVu(float *value) { - /* - * first pick a color - */ + /* + * first pick a color + */ if (*value >= 0.7071f) /* -3dB */ { Serial.printf("\033[0;31m"); /* red */ @@ -226,7 +226,7 @@ void Status_Process(void) void Status_ValueChangedFloat(const char *descr, float value) { status_cnt = 0; - sprintf(statusMsg, "%s: %0.2f\0", descr, value); + sprintf(statusMsg, "%s: %0.2f", descr, value); } /* @@ -235,7 +235,7 @@ void Status_ValueChangedFloat(const char *descr, float value) void Status_ValueChangedInt(const char *descr, int value) { status_cnt = 0; - sprintf(statusMsg, "%s: %d\0", descr, value); + sprintf(statusMsg, "%s: %d", descr, value); } /* @@ -244,5 +244,5 @@ void Status_ValueChangedInt(const char *descr, int value) void Status_TestMsg(const char *text) { status_cnt = 0; - sprintf(statusMsg, "%s\0", text); + sprintf(statusMsg, "%s", text); } diff --git a/z_config.ino b/z_config.ino new file mode 100644 index 0000000..27d639d --- /dev/null +++ b/z_config.ino @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021 Marcel Licence + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Dieses Programm ist Freie Software: Sie k�nnen es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * ver�ffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es n�tzlich sein wird, jedoch + * OHNE JEDE GEW�HR,; sogar ohne die implizite + * Gew�hr der MARKTF�HIGKEIT oder EIGNUNG F�R EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License f�r weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/* + * z_config.ino + * + * Put all your project configuration here (no defines etc) + * This file will be included at the and can access all + * declarations and type definitions + * + * Created on: 02.01.2022 + * Author: Marcel Licence + */ + +/* + * this mapping is used for the edirol pcr-800 + * this should be changed when using another controller + */ +struct midiControllerMapping edirolMapping[] = +{ + /* transport buttons */ + { 0x8, 0x52, "back", NULL, Loop_ResetToStart, 0}, + { 0x8, 0x52, "back", NULL, Click_Reset, 0}, + { 0xD, 0x52, "stop", NULL, Loop_Stop, 0}, + { 0xe, 0x52, "start", NULL, Loop_PlayNormal, 0}, + { 0xe, 0x52, "start", NULL, Loop_StartAll, 0}, + { 0xa, 0x52, "rec", NULL, Loop_SetLength, 0}, + + /* upper row of buttons */ + { 0x0, 0x50, "A1", NULL, Loop_SelectTrack, 0}, + { 0x1, 0x50, "A2", NULL, Loop_SelectTrack, 1}, + { 0x2, 0x50, "A3", NULL, Loop_SelectTrack, 2}, + { 0x3, 0x50, "A4", NULL, Loop_SelectTrack, 3}, + { 0x4, 0x50, "A5", NULL, Click_ToggleOnOff, 0}, + + { 0x5, 0x50, "A6", NULL, Loop_JumpPosQuarter, 1}, + { 0x6, 0x50, "A7", NULL, Loop_JumpPosQuarter, 2}, + { 0x7, 0x50, "A8", NULL, Loop_JumpPosQuarter, 3}, + { 0x5, 0x50, "A6", NULL, Click_JumpPosQuarter, 1}, + { 0x6, 0x50, "A7", NULL, Click_JumpPosQuarter, 2}, + { 0x7, 0x50, "A8", NULL, Click_JumpPosQuarter, 3}, + + { 0x0, 0x53, "A9", NULL, Click_OnOff, 0}, + + /* lower row of buttons */ + { 0x0, 0x51, "B1", NULL, Loop_StopChannel, 0}, + { 0x1, 0x51, "B2", NULL, Loop_StopChannel, 1}, + { 0x2, 0x51, "B3", NULL, Loop_StopChannel, 2}, + { 0x3, 0x51, "B4", NULL, Loop_StopChannel, 3}, + + { 0x4, 0x51, "B5", NULL, Loop_EraseTrack, 0}, + { 0x5, 0x51, "B6", NULL, Loop_EraseTrack, 1}, + { 0x6, 0x51, "B7", NULL, Loop_EraseTrack, 2}, + { 0x7, 0x51, "B8", NULL, Loop_EraseTrack, 3}, + + { 0x1, 0x53, "B9", NULL, MTLooper_ToggleSource, 0}, + + /* pedal */ + { 0x0, 0x0b, "VolumePedal", NULL, NULL, 0}, + + { 0x0, 0x11, "S1", NULL, Loop_SetChannelGainOut, 0}, + { 0x1, 0x11, "S2", NULL, Loop_SetChannelGainOut, 1}, + { 0x2, 0x11, "S3", NULL, Loop_SetChannelGainOut, 2}, + { 0x3, 0x11, "S4", NULL, Loop_SetChannelGainOut, 3}, + + { 0x4, 0x11, "S5", NULL, NULL, 0}, + { 0x5, 0x11, "S6", NULL, NULL, 0}, + { 0x6, 0x11, "S7", NULL, NULL, 0}, + { 0x7, 0x11, "S8", NULL, NULL, 0}, + + /* rotary */ + { 0x0, 0x10, "R1", NULL, Loop_SetChannelPan, 0}, + { 0x1, 0x10, "R2", NULL, Loop_SetChannelPan, 1}, + { 0x2, 0x10, "R3", NULL, Loop_SetChannelPan, 2}, + { 0x3, 0x10, "R4", NULL, Loop_SetChannelPan, 3}, + + { 0x4, 0x10, "R5", NULL, NULL, 0}, + { 0x5, 0x10, "R6", NULL, NULL, 0}, + { 0x6, 0x10, "R7", NULL, Click_SetTempo, 0}, + { 0x7, 0x10, "R8", NULL, Loop_SetSpeed, 0}, + + { 0x0, 0x12, "R9", NULL, Loop_SetChannelGainIn, 0}, + + /* Central slider */ +#ifndef AS5600_ENABLED + // { 0x0, 0x13, "H1", NULL, App_SetBrightness, 0}, +#else + { 0x0, 0x13, "H1", NULL, Sampler_ScratchFader, 0}, +#endif +}; + + +struct midiMapping_s midiMapping = +{ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + edirolMapping, + sizeof(edirolMapping) / sizeof(edirolMapping[0]), +}; +