Major update to use latest modules developed in the meantime

This commit is contained in:
Marcel Licence
2022-01-02 17:27:55 +01:00
parent 358a7b6c17
commit a6db0017e4
10 changed files with 779 additions and 241 deletions
+4 -4
View File
@@ -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))
+70
View File
@@ -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 <http://www.gnu.org/licenses/>.
*
* 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 <https://www.gnu.org/licenses/>.
*/
/**
* @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 <cdt.h>
#endif
/*
* include the board configuration
* there you will find the most hardware depending pin settings
*/
#define BOARD_ESP32_AUDIO_KIT_AC101
#include <ml_boards.h> /* requires https://github.com/marcel-licence/ML_SynthTools */
/* our samplerate */
#define SAMPLE_RATE 44100
/* on board led */
#define LED_PIN 19
#endif /* CONFIG_H_ */
+10 -16
View File
@@ -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 <https://www.gnu.org/licenses/>.
*/
/*
* 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 <cdt.h>
#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
+166 -30
View File
@@ -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 <https://www.gnu.org/licenses/>.
*/
/*
* 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 <cdt.h>
#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
+71 -21
View File
@@ -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 <cdt.h>
#endif
/*
*
/$$$$$$$$ /$$ /$$ /$$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$ /$$ /$$ /$$ /$$
| $$_____/ | $$ | $$ | $$__ $$ /$$__ $$| $$__ $$ /$$__ $$| $$$ /$$$| $$| $$
| $$ /$$$$$$$ /$$$$$$ | $$$$$$$ | $$ /$$$$$$ | $$ \ $$| $$ \__/| $$ \ $$| $$ \ $$| $$$$ /$$$$| $$| $$
| $$$$$ | $$__ $$ |____ $$| $$__ $$| $$ /$$__ $$ | $$$$$$$/| $$$$$$ | $$$$$$$/| $$$$$$$$| $$ $$/$$ $$| $$| $$
| $$__/ | $$ \ $$ /$$$$$$$| $$ \ $$| $$| $$$$$$$$ | $$____/ \____ $$| $$__ $$| $$__ $$| $$ $$$| $$|__/|__/
| $$ | $$ | $$ /$$__ $$| $$ | $$| $$| $$_____/ | $$ /$$ \ $$| $$ \ $$| $$ | $$| $$\ $ | $$
| $$$$$$$$| $$ | $$| $$$$$$$| $$$$$$$/| $$| $$$$$$$ | $$ | $$$$$$/| $$ | $$| $$ | $$| $$ \/ | $$ /$$ /$$
|________/|__/ |__/ \_______/|_______/ |__/ \_______/ |__/ \______/ |__/ |__/|__/ |__/|__/ |__/|__/|__/
*/
#include "config.h"
#include <Arduino.h>
#include <WiFi.h>
/* 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;
-4
View File
@@ -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;
+5 -5
View File
@@ -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;
+314 -154
View File
@@ -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 <https://www.gnu.org/licenses/>.
*/
/*
* 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 <cdt.h>
#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
+7 -7
View File
@@ -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);
}
+132
View File
@@ -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 <http://www.gnu.org/licenses/>.
*
* Dieses Programm ist Freie Software: Sie knnen 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
* verffentlichten Version, weiter verteilen und/oder modifizieren.
*
* Dieses Programm wird in der Hoffnung bereitgestellt, dass es ntzlich sein wird, jedoch
* OHNE JEDE GEWHR,; sogar ohne die implizite
* Gewhr der MARKTFHIGKEIT oder EIGNUNG FR EINEN BESTIMMTEN ZWECK.
* Siehe die GNU General Public License fr weitere Einzelheiten.
*
* Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
* Programm erhalten haben. Wenn nicht, siehe <https://www.gnu.org/licenses/>.
*/
/*
* 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]),
};