Skip to content

Instantly share code, notes, and snippets.

@tancredi
Last active December 25, 2021 03:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tancredi/15872e8f2fadf13e8be3b04c1a42bcc5 to your computer and use it in GitHub Desktop.
Save tancredi/15872e8f2fadf13e8be3b04c1a42bcc5 to your computer and use it in GitHub Desktop.
MIDI-Ino - An Arduino custom MIDI controller with Mixxx mappings
#include <USB-MIDI.h>
USBMIDI_CREATE_DEFAULT_INSTANCE();
const float K_MAX = 690;
const float K_THRESHOLD = 1;
const byte K_PINS[5]= { A0, A1, A2, A3, A4 };
const byte L_PINS[5]= { 2, 3, 4, 5, 6 };
const byte B_PINS[5]= { 9, 10, 11, 12, 13 };
const float MIDI_MAX_VAL = 127;
const int CHANNEL = 1;
const int MIDI_START_K = 0x20;
const int MIDI_START_L = 0x30;
const int MIDI_START_B = 0x40;
int k_values[sizeof(K_PINS)];
bool b_values[sizeof(K_PINS)];
bool l_values[sizeof(L_PINS)];
void setup() {
MIDI.begin(1);
// Serial.begin(9600);
for (int i = 0; i < sizeof(K_PINS); i++) {
pinMode(K_PINS[i], INPUT);
}
for (int i = 0; i < sizeof(B_PINS); i++) {
pinMode(B_PINS[i], INPUT);
}
for (int i = 0; i < sizeof(L_PINS); i++) {
pinMode(L_PINS[i], OUTPUT);
}
MIDI.setHandleControlChange(handleControl);
}
int knobToVelocity(float value) {
float out = (value * MIDI_MAX_VAL) / K_MAX;
return floor(max(0, min(MIDI_MAX_VAL, out)));
}
void updateKnob(int i) {
int prev = k_values[i];
int val = knobToVelocity(analogRead(K_PINS[i]));
if (abs(val - prev) > K_THRESHOLD) {
MIDI.sendControlChange(MIDI_START_K + i, val, CHANNEL);
k_values[i] = val;
}
}
void updateLed(int i) {
int prev = k_values[i];
int val = knobToVelocity(analogRead(K_PINS[i]));
digitalWrite(L_PINS[i], l_values[i] ? HIGH : LOW);
}
void handleControl(byte channel, byte control, byte value) {
if (control >= MIDI_START_L && control < MIDI_START_L + sizeof(L_PINS)) {
int i = control - MIDI_START_L;
l_values[i] = value > 0;
}
}
void updateButton(int i) {
bool val = digitalRead(B_PINS[i]);
bool prev = b_values[i];
if (val != prev) {
MIDI.sendControlChange(MIDI_START_B + i, val, CHANNEL);
b_values[i] = val;
}
}
void loop() {
MIDI.read();
for (int i = 0; i < sizeof(K_PINS); i++) {
updateKnob(i);
}
for (int i = 0; i < sizeof(B_PINS); i++) {
updateButton(i);
}
for (int i = 0; i < sizeof(L_PINS); i++) {
updateLed(i);
}
delay(1);
}
const MI = {
previewPlaying: false,
previewToMiddleOnLoad: false,
};
const GROUPS = { PREVIEW: "[PreviewDeck1]" };
const MAX_POSITION = 1.14;
const LED_MIDI_START = 0x30;
const MIDI_ON = 0x7f;
const MIDI_OFF = 0x00;
MI.init = function () {
engine.makeConnection(GROUPS.PREVIEW, "track_loaded", handlePreviewLoaded);
};
MI.shutdown = function () {};
function handlePreviewLoaded(channel, group) {
if (MI.previewToMiddleOnLoad) {
MI.previewToMiddleOnLoad = false;
engine.setValue(GROUPS.PREVIEW, "playposition", MAX_POSITION / 2);
}
}
function setLed(index, status) {
midi.sendShortMsg(0xb0, LED_MIDI_START + index, status ? MIDI_ON : MIDI_OFF);
}
MI.togglePreviewFromTheMiddle = function (channel, group, value) {
const pressed = value;
const playing = engine.getValue(GROUPS.PREVIEW, "play");
if (!pressed) {
setLed(2, false);
return engine.setValue(GROUPS.PREVIEW, "play", false);
}
MI.previewToMiddleOnLoad = true;
setLed(2, true);
script.triggerControl(GROUPS.PREVIEW, "LoadSelectedTrackAndPlay", true);
};
<?xml version='1.0' encoding='utf-8'?>
<MixxxControllerPreset schemaVersion="1" mixxxVersion="2.0.0+">
<info>
<name>MIDI-Ino (Custom)</name>
<author>Tanc</author>
</info>
<controller id="MIDI-ino">
<scriptfiles>
<file filename="midi-ino.js" functionprefix="MI"/>
</scriptfiles>
<controls>
<control>
<group>[EffectRack1_EffectUnit1_Effect1]</group>
<key>meta</key>
<status>0xB0</status>
<midino>0x20</midino>
<options>
<Invert />
</options>
</control>
<control>
<group>[EffectRack1_EffectUnit1_Effect1]</group>
<key>enabled</key>
<status>0xB0</status>
<midino>0x40</midino>
<options>
<Normal />
</options>
</control>
<control>
<group>[EffectRack1_EffectUnit1_Effect2]</group>
<key>meta</key>
<status>0xB0</status>
<midino>0x21</midino>
<options>
<Invert />
</options>
</control>
<control>
<group>[EffectRack1_EffectUnit1_Effect2]</group>
<key>enabled</key>
<status>0xB0</status>
<midino>0x41</midino>
<options>
<Normal />
</options>
</control>
<control>
<description>Knob 3 (middle)</description>
<group>[Master]</group>
<key>headMix</key>
<status>0xB0</status>
<midino>0x22</midino>
<options>
<Normal />
</options>
</control>
<control>
<description>Button 3 (middle)</description>
<group>[PreviewDeck1]</group>
<key>MI.togglePreviewFromTheMiddle</key>
<status>0xB0</status>
<midino>0x42</midino>
<options>
<script-binding/>
</options>
</control>
<control>
<group>[EffectRack1_EffectUnit2_Effect1]</group>
<key>meta</key>
<status>0xB0</status>
<midino>0x23</midino>
<options>
<Normal />
</options>
</control>
<control>
<group>[EffectRack1_EffectUnit2_Effect1]</group>
<key>enabled</key>
<status>0xB0</status>
<midino>0x43</midino>
<options>
<Normal />
</options>
</control>
<control>
<group>[EffectRack1_EffectUnit2_Effect2]</group>
<key>meta</key>
<status>0xB0</status>
<midino>0x24</midino>
<options>
<Invert />
</options>
</control>
<control>
<group>[EffectRack1_EffectUnit2_Effect2]</group>
<key>enabled</key>
<status>0xB0</status>
<midino>0x44</midino>
<options>
<Normal />
</options>
</control>
</controls>
<outputs>
<output>
<group>[EffectRack1_EffectUnit1_Effect1]</group>
<key>enabled</key>
<status>0xB0</status>
<midino>0x30</midino>
<on>0x7F</on>
<off>0x00</off>
<minimum>0.5</minimum>
</output>
<output>
<group>[EffectRack1_EffectUnit1_Effect2]</group>
<key>enabled</key>
<status>0xB0</status>
<midino>0x31</midino>
<on>0x7F</on>
<off>0x00</off>
<minimum>0.5</minimum>
</output>
<output>
<group>[EffectRack1_EffectUnit1_Effect3]</group>
<key>enabled</key>
<status>0xB0</status>
<midino>0x32</midino>
<on>0x7F</on>
<off>0x00</off>
<minimum>0.5</minimum>
</output>
<output>
<group>[EffectRack1_EffectUnit2_Effect1]</group>
<key>enabled</key>
<status>0xB0</status>
<midino>0x33</midino>
<on>0x7F</on>
<off>0x00</off>
<minimum>0.5</minimum>
</output>
<output>
<group>[EffectRack1_EffectUnit2_Effect2]</group>
<key>enabled</key>
<status>0xB0</status>
<midino>0x34</midino>
<on>0x7F</on>
<off>0x00</off>
<minimum>0.5</minimum>
</output>
</outputs>
</controller>
</MixxxControllerPreset>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment