Quick Sign In:  

Forum: VirtualDJ Technical Support

Topic: Custom MIDI Controller Not Working - Page: 1
Hello,

I have been working on a custom midi controller for working with STEMS. It is 20 pots, each with a built in button. It uses 4 multiplexors (2 analog for the pots and 2 digital for the buttons) which feed an Arduino UNO flashed to USB MIDI.

Using MidiView (the link in your documentation for MidiTrace is dead), I am getting correct values for each pot and button when I activate them. But when I try to program the controller in VDJ, it does not recognize any knob or button actions.

I have created a definition file and mapping file, and VDJ does recognize the controller is plugged in, but again, nothing happens in VDJ when twisting knobs and pushing buttons.

I have tried using different channels and CC messages in my Arduino code. If I use CH0, even MidiView doesn't recognize my input, but it works fine witty MidiView on CH1 and CH2.

Any advice or ideas?

Below is a cut an paste of the MIDI definition and mapping file, and below that is my Arduino code with main, .h, and .cpp files. Sorry for the long post, wasn't sure how/if I could attach a file.

Thanks!

Definition File (1 button 1 pot test)
<device name="MJC STEMS" author="Montalbon Junior" description="VDJ STEMS MIDI" version="800" type="MIDI" vid="0x03EB" pid="0x2048" decks="0" padColumns="0" padRows="0" padSides="0">

<!-- Buttons-->*
<button note="0x20" name="B0101" deck="1" channel="0" />
<button cc="0x68" value="0x7F" off="0x00" name="X1" deck="1" channel="0" />
<button cc="0x68" value="0x7F" off="0x00" name="X2" deck="1" channel="0" />
<!-- Sliders-->*
<slider ccmsb="0x50" name="P0101" deck="1" channel="0"/>
</device>


Mapping File
<?xml version="1.0" encoding="UTF-8"?>
<mapper device="MJC STEMS MIDI" version="850" date="2023-11-21" />


Arduino Code (main, .h, ,cpp below)
Main
#include <MIDI.h>
#include "Controller.h"

MIDI_CREATE_DEFAULT_INSTANCE();

//************************************************************
//***SET THE NUMBER OF CONTROLS USED**************************
//************************************************************
byte NUMBER_MUX_BUTTONS = 20; //---How many buttons are connected to a multiplexer?---------
byte NUMBER_MUX_POTS = 20; //---How many potentiometers are connected to a multiplexer?--
//************************************************************

//***ANY MULTIPLEXERS? (74HC4067)************************************
//MUX address pins must be connected to Arduino UNO pins 2,3,4,5
//A0 = PIN2, A1 = PIN3, A2 = PIN4, A3 = PIN5
//*******************************************************************
//Mux NAME (OUTPUT PIN, , How Many Mux Pins?(8 or 16) , Is It Analog?);
Mux M1D_CH1CH3(7, 16, false); //Digital multiplexer on Arduino pin 7
Mux M2A_Ch1CH3(A0,16, true); //Analog multiplexer on Arduino analog pin A0
Mux M3D_CH2CH4(8, 16, false); //Digital multiplexer on Arduino pin 8
Mux M4A_Ch2CH4(A1,16, true); //Analog multiplexer on Arduino analog pin A1
//*******************************************************************

//***DEFINE BUTTONS CONNECTED TO MULTIPLEXER*************************
//Button::Button(Mux mux, byte muxpin, byte command, byte value, byte channel, byte debounce)
//** Command parameter 0=NOTE 1=CC 2=Toggle CC **
//CH1-3 MUX Buttons (M1D)
Button MBU1(M1D_CH1CH3, 0, 2, 20, 1, 5); //0101 Vocals
Button MBU2(M1D_CH1CH3, 2, 2, 21, 1, 5); //0102 Instruments
Button MBU3(M1D_CH1CH3, 4, 2, 22, 1, 5); //0103 Bass
Button MBU4(M1D_CH1CH3, 6, 2, 23, 1, 5); //0104 Hi-hat
Button MBU5(M1D_CH1CH3, 8, 2, 24, 1, 5); //0105 Kick Drum
Button MBU6(M1D_CH1CH3, 1, 2, 25, 1, 5); //0301 Vocals
Button MBU7(M1D_CH1CH3, 3, 2, 26, 1, 5); //0302 Instruments
Button MBU8(M1D_CH1CH3, 5, 2, 27, 1, 5); //0303 Bass
Button MBU9(M1D_CH1CH3, 7, 2, 28, 1, 5); //0304 Hi-hat
Button MBU10(M1D_CH1CH3, 9, 2, 29, 1, 5); //0305 Kick Drum
//CH2-4 MUX Buttons (M3D)
Button MBU11(M3D_CH2CH4, 0, 2, 20, 1, 5); //0201 Vocals
Button MBU12(M3D_CH2CH4, 2, 2, 21, 1, 5); //0202 Instruments
Button MBU13(M3D_CH2CH4, 4, 2, 22, 1, 5); //0203 Bass
Button MBU14(M3D_CH2CH4, 6, 2, 23, 1, 5); //0204 Hi-hat
Button MBU15(M3D_CH2CH4, 8, 2, 24, 1, 5); //0205 Kick Drum
Button MBU16(M3D_CH2CH4, 1, 2, 25, 1, 5); //0401 Vocals
Button MBU17(M3D_CH2CH4, 3, 2, 26, 1, 5); //0402 Instruments
Button MBU18(M3D_CH2CH4, 5, 2, 27, 1, 5); //0403 Bass
Button MBU19(M3D_CH2CH4, 7, 2, 28, 1, 5); //0404 Hi-hat
Button MBU20(M3D_CH2CH4, 9, 2, 29, 1, 5); //0405 Kick Drum
//*****************************************************************
////Add multiplexed buttons used to array below like this-> Button *MUXBUTTONS[] {&MBU1, &MBU2, &MBU3, &MBU4, &MBU5, &MBU6.....};
Button *MUXBUTTONS[] {&MBU1, &MBU2, &MBU3, &MBU4, &MBU5, &MBU6, &MBU7, &MBU8, &MBU9, &MBU10, &MBU11, &MBU12, &MBU13, &MBU14, &MBU15, &MBU16, &MBU17, &MBU18, &MBU19, &MBU20};
//*******************************************************************

//***DEFINE POTENTIOMETERS CONNECTED TO MULTIPLEXER*******************
//Pot::Pot(Mux mux, byte muxpin, byte command, byte control, byte channel)
//**Command parameter is for future use**
//CH1-3 MUX POTS (M2A)
Pot MPO1(M2A_Ch1CH3, 0, 0, 50, 1); //0101 Vocals
Pot MPO2(M2A_Ch1CH3, 2, 0, 55, 1); //0102 Instruments
Pot MPO3(M2A_Ch1CH3, 4, 0, 50, 1); //0103 Bass
Pot MPO4(M2A_Ch1CH3, 6, 0, 55, 1); //0104 Hi-hat
Pot MPO5(M2A_Ch1CH3, 8, 0, 50, 1); //0105 Kick Drum
Pot MPO6(M2A_Ch1CH3, 1, 0, 55, 1); //0301 Vocals
Pot MPO7(M2A_Ch1CH3, 3, 0, 50, 1); //0302 Instruments
Pot MPO8(M2A_Ch1CH3, 5, 0, 55, 1); //0303 Bass
Pot MPO9(M2A_Ch1CH3, 7, 0, 50, 1); //0304 Hi-hat
Pot MPO10(M2A_Ch1CH3, 9, 0, 55, 1); //0305 Kick Drum
//CH2-4 MUX POTS (M4A)
Pot MPO11(M4A_Ch2CH4, 0, 0, 50, 1); //0201 Vocals
Pot MPO12(M4A_Ch2CH4, 2, 0, 55, 1); //0202 Instruments
Pot MPO13(M4A_Ch2CH4, 4, 0, 50, 1); //0203 Bass
Pot MPO14(M4A_Ch2CH4, 6, 0, 55, 1); //0204 Hi-hat
Pot MPO15(M4A_Ch2CH4, 8, 0, 50, 1); //0205 Kick Drum
Pot MPO16(M4A_Ch2CH4, 1, 0, 55, 1); //0401 Vocals
Pot MPO17(M4A_Ch2CH4, 3, 0, 50, 1); //0402 Instruments
Pot MPO18(M4A_Ch2CH4, 5, 0, 55, 1); //0403 Bass
Pot MPO19(M4A_Ch2CH4, 7, 0, 50, 1); //0404 Hi-hat
Pot MPO20(M4A_Ch2CH4, 9, 0, 55, 1); //0405 Kick Drum
//*******************************************************************
//Add multiplexed pots used to array below like this-> Pot *MUXPOTS[] {&MPO1, &MPO2, &MPO3, &MPO4, &MPO5, &MPO6.....};
Pot *MUXPOTS[] {&MPO1, &MPO2, &MPO3, &MPO4, &MPO5, &MPO6, &MPO7, &MPO8, &MPO9, &MPO10, &MPO11, &MPO12, &MPO13, &MPO14, &MPO15, &MPO16, &MPO17, &MPO18, &MPO19, &MPO20};
//*******************************************************************


//*******************************************************************
// MAIN
//*******************************************************************
void setup() {
MIDI.begin(MIDI_CHANNEL_OFF);
}

void loop() {
if (NUMBER_MUX_BUTTONS != 0) updateMuxButtons();
if (NUMBER_MUX_POTS != 0) updateMuxPots();
}

//*******************************************************************
// FUNCTIONS
//*******************************************************************
void updateMuxButtons() {

// Cycle through Mux Button array
for (int i = 0; i < NUMBER_MUX_BUTTONS; i = i + 1) {

MUXBUTTONS->muxUpdate();
byte message = MUXBUTTONS->getValue();

// Button is pressed
if (message == 0) {
switch (MUXBUTTONS->Bcommand) {
case 0: //Note
MIDI.sendNoteOn(MUXBUTTONS->Bvalue, 127, MUXBUTTONS->Bchannel);
break;
case 1: //CC
MIDI.sendControlChange(MUXBUTTONS->Bvalue, 127, MUXBUTTONS->Bchannel);
break;
case 2: //Toggle
if (MUXBUTTONS->Btoggle == 0) {
MIDI.sendControlChange(MUXBUTTONS->Bvalue, 127, MUXBUTTONS->Bchannel);
MUXBUTTONS->Btoggle = 1;
}
else if (MUXBUTTONS->Btoggle == 1) {
MIDI.sendControlChange(MUXBUTTONS->Bvalue, 0, MUXBUTTONS->Bchannel);
MUXBUTTONS->Btoggle = 0;
}
break;
}
}
// Button is not pressed
if (message == 1) {
switch (MUXBUTTONS->Bcommand) {
case 0:
MIDI.sendNoteOff(MUXBUTTONS->Bvalue, 0, MUXBUTTONS->Bchannel);
break;
case 1:
MIDI.sendControlChange(MUXBUTTONS->Bvalue, 0, MUXBUTTONS->Bchannel);
break;
}
}
}
}
//***********************************************************************

void updateMuxPots() {
for (int i = 0; i < NUMBER_MUX_POTS; i = i + 1) {
MUXPOTS->muxUpdate();
byte potmessage = MUXPOTS->getValue();
if (potmessage != 255) MIDI.sendControlChange(MUXPOTS->Pcontrol, potmessage, MUXPOTS->Pchannel);
}
}



Controller.h
#ifndef Controller_h
#define Controller_h

#include <Arduino.h>

//***********************************************************************
class Mux
{
public:
Mux(byte outpin_, byte numPins_, bool analog_);
byte outpin;
byte numPins;
bool analog;
};
//************************************************************************
//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
class Button
{
public:
Button(byte pin, byte command, byte value, byte channel, byte debounce);
Button(Mux mux, byte muxpin, byte command, byte value, byte channel, byte debounce);
byte getValue();
void muxUpdate();
void newValue(byte command, byte value, byte channel);
byte Bcommand;
byte Bvalue;
byte Bchannel;
byte Btoggle;

private:
byte _previous;
byte _current;
unsigned long _time;
int _debounce;
byte _pin;
byte _muxpin;
byte _numMuxPins;
byte _value;
byte _command;
bool _busy;
byte _status;
byte _last;
byte _enablepin;
};
//*************************************************************************
class Pot
{
public:
Pot(byte pin, byte command, byte control, byte channel);
Pot(Mux mux, byte muxpin ,byte command, byte control, byte channel);
void muxUpdate();
void newValue(byte command, byte value, byte channel);
byte getValue();
byte Pcommand;
byte Pcontrol;
byte Pchannel;

private:
byte _pin;
byte _muxpin;
byte _numMuxPins;
byte _control;
int _value;
int _oldValue;
bool _changed;
byte _enablepin;
};
//*************************************************************************
#endif


Controller.CPP
#include "Controller.h"

//****************************************************************************************
Mux::Mux(byte outpin_, byte numPins_, bool analog_)
{
outpin = outpin_;
//enablepin = enablepin_;
numPins = numPins_;
analog = analog_;
if (analog == false) pinMode(outpin, INPUT_PULLUP);
//pinMode(enablepin, OUTPUT);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
if (numPins > 8) pinMode(5, OUTPUT);
}
//****************************************************************************************
//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
Button::Button(byte pin, byte command, byte value, byte channel, byte debounce)
{
_pin = pin;
pinMode(_pin, INPUT_PULLUP);
_value = value;
_command = command;
_debounce = debounce;
_time = 0;
_busy = false;
_status = 0b00000010;
_last = 1;
Bcommand = command;
Bvalue = value;
Bchannel = channel;
Btoggle = 0;
}

Button::Button(Mux mux, byte muxpin, byte command, byte value, byte channel, byte debounce)
{
_pin = mux.outpin;
_numMuxPins = mux.numPins;
_muxpin = muxpin;
_value = value;
_command = command;
_debounce = debounce;
_time = 0;
_busy = false;
_status = 0b00000010;
_last = 1;
Bcommand = command;
Bvalue = value;
Bchannel = channel;
Btoggle = 0;
}

void Button::muxUpdate()
{
byte temp = _muxpin;
temp = temp << 2;
if (_numMuxPins > 8) PORTD = PORTD & B11000011;
else PORTD = PORTD & B11100011;
PORTD = PORTD | temp;
}

byte Button::getValue()
{
// If BUSY bit not set - read button
if (bitRead(_status, 0) == false) { // If busy false
if (digitalRead(_pin) == _last) return 2; // If same as last state - exit
}

// If NEW Bit set - Key just pressed, record time
if (bitRead(_status, 1) == true) { // If new is true
bitSet(_status, 0); // Set busy TRUE
bitClear(_status, 1); // Set New FALSE
_time = millis();
return 255;
}

// Check if debounce time has passed - If no, exit
if (millis() - _time < _debounce) return 255;

// Debounce time has passed. Read pin to see if still set the same
// If it has changed back - assume false alarm
if (digitalRead(_pin) == _last) {
bitClear(_status, 0); // Set busy false
bitSet(_status, 1); // Set new true
return 255;
}

// If this point is reached, event is valid. return event type
else {
bitClear(_status, 0); // Set busy false
bitSet(_status, 1); // Set new true
_last = ((~_last) & 0b00000001); // invert _last
return _last;
}
}

void Button::newValue(byte command, byte value, byte channel)
{
Bvalue = value;
Bcommand = command;
Bchannel = channel;
}

//********************************************************************
Pot::Pot(byte pin, byte command, byte control, byte channel)
{
_pin = pin;
_control = control;
_value = analogRead(_pin);
_value = _value >> 3;
_oldValue = _value << 3;
_value = _value << 3;
Pcommand = command;
Pcontrol = control;
Pchannel = channel;
}

void Pot::muxUpdate()
{
byte temp = _muxpin;
temp = temp << 2;
if (_numMuxPins > 8) PORTD = PORTD & B11000011;
else PORTD = PORTD & B11100011;
//PORTD = PORTD & B11000011;
PORTD = PORTD | temp;
}

Pot::Pot(Mux mux, byte muxpin, byte command, byte control, byte channel)
{
_pin = mux.outpin;
_numMuxPins = mux.numPins;
_muxpin = muxpin;
_control = control;
muxUpdate();
_value = analogRead(_pin);
_value = _value >> 3;
_oldValue = _value << 3;
_value = _value << 3;
Pcommand = command;
Pcontrol = control;
Pchannel = channel;
}

byte Pot::getValue()
{
_value = analogRead(_pin);
int tmp = (_oldValue - _value);
if (tmp >= 8 || tmp <= -8) {
_oldValue = _value >> 3;
_oldValue = _oldValue << 3;
return _value >> 3;
}
return 255;
}

void Pot::newValue(byte command, byte value, byte channel) {
Pcommand = command;
Pcontrol = value;
Pchannel = channel;
}


 

geposted Thu 14 Dec 23 @ 1:56 am
locoDogPRO InfinityModeratorMember since 2013
your mapping is blank?
 

geposted Thu 14 Dec 23 @ 2:02 am
I created that mapping file manually. I thought I would be able to choose it from the Custom Mapping page in VDJ and update it from there, but it doesn't show up. Do I need to manually code the mapping file first?

Thanks for the quick response! I feel like I'm right there, but missing some of the fine details.
 

geposted Thu 14 Dec 23 @ 2:17 am
Hmmm.... Not sure. I'll dig into that and post back tomorrow.

Thanks for the help!
 

geposted Thu 14 Dec 23 @ 2:52 am
locoDogPRO InfinityModeratorMember since 2013
Error deleted
 

geposted Thu 14 Dec 23 @ 3:34 am
Actually <device name="MJC STEMS" and <mapper device="MJC STEMS MIDI" should match.

Description doesn't matter / doesn't have to match.
 

geposted Thu 14 Dec 23 @ 8:10 am
locoDogPRO InfinityModeratorMember since 2013
I must have blinked and missed that bit, I guess I just saw the description bit and thought about it as a descriptor.
 

geposted Thu 14 Dec 23 @ 8:49 am
Thanks for the tips guys, I've now got my mapper file showing up in the mapper editor and was able to make a change in VDJ and it showed up in my xml mapper file.

My problem now is that when I activate a button or pot, nothing shows up in the Activity window.

I will look more into VID&PID in the meantime.

 

geposted Thu 14 Dec 23 @ 2:46 pm
locoDogPRO InfinityModeratorMember since 2013
VID PID is probably good as it's detected,
question is the uno midi channel zero indexed or one?
 

geposted Thu 14 Dec 23 @ 3:08 pm
I'm not 100% sure how to determine that, but I'm leaning towards 1 since MidiView doesn't see messages when I set the channel to 0 in my Arduino code, but works fine on 1. Does that answer your question?
 

geposted Thu 14 Dec 23 @ 3:49 pm
locoDogPRO InfinityModeratorMember since 2013
sounds like you're right.
 

geposted Thu 14 Dec 23 @ 4:21 pm
Do you or anyone else know how to work around this. Is it something on the Arduino midi library side, or is there something in VDJ that can be updated to look on CH1?

Thanks again for all the help!
 

geposted Thu 14 Dec 23 @ 4:24 pm
djdadPRO InfinityDevelopment ManagerMember since 2005
Miditrace link fixed

Can you post a screenshot of Midiview (or other), showing what you get when a button is pressed or a knob is moved ?

I have the feeling that the keys in definition dont match with your Arduino code values. One thing i quickly see is that in Arduino code, you have decimal values, while in VDJ definition you put them as hexadecimal .
<slider ccmsb="0x50" name="P0101" deck="1" channel="0"/>
Pot MPO1(M2A_Ch1CH3, 0, 0, 50, 1); //0101 Vocals
So probably you need ..
<slider cc="0x32" name="P0101" deck="1" channel="0"/>

Also it seems that your cc buttons are coded to work as "toggle", so in this case, maybe the <button>s need to be defined as <toggle> too.

I dont see any Note type button in your code?? In definition you have <button note="0x20" name="B0101" deck="1" channel="0" />

Best would be to choose "Command parameter 0=NOTE or 1=CC but not 2=Toggle CC because you will lose the ability to map an action differently when pressed or released.
 

geposted Thu 14 Dec 23 @ 4:28 pm
 

geposted Thu 14 Dec 23 @ 4:44 pm
djdadPRO InfinityDevelopment ManagerMember since 2005
Yes, use the values you see on the far right column.
B0 32 xx is a <slider cc="0x32" channel="0"

and the button is cc="0x14" channel="0"
 

geposted Thu 14 Dec 23 @ 4:48 pm
djdadPRO InfinityDevelopment ManagerMember since 2005
One other thing i noticed (with my basic Arduino knowledge) ..is that you have multiple conflicts in your code.
E.g.
Button MBU1(M1D_CH1CH3, 0, 2, 20, 1, 5); //0101 Vocals

Button MBU11(M3D_CH2CH4, 0, 2, 20, 1, 5); //0201 Vocals

Both are programmed to send the same Midi message 20 on Ch 1 ?!?
If you select to use the same Midi channel 1 (0 for VDJ), then all of your CC buttons/pots, should have different CC values.
 

geposted Thu 14 Dec 23 @ 4:57 pm
Ok, great info!

I'm slowly figuring it out now. I'll see how far I can get with this and post back.

Thanks all!
 

geposted Thu 14 Dec 23 @ 5:20 pm
Thanks again for all the help, guys! I've got it working now!

djdad, thanks for your last post. It was the "fine detail" that I was looking for. I've got all the buttons and pots being recognized by VDJ individually, with all the conflicts resolved now.

Next is to figure out how to write my scripts for controlling the STEMS on each deck, but that is for another thread. For now, I will post the working code below for anyone looking for the same info in the future. If you are reading this, use the code below, not from the previous post. Good luck!

Here's a couple of pics of my prototype. Eventually I'd like to integrate it flush into the computer stand of my rig so that I can tilt it up and lock it in place when I want to use it. 'Till then...







Arduino: Main
/*****
Virtual DJ Stems 2.0 MIDI CONTROLLER V0.1

by Montalbon Junior

remixed from Notes and Volts
www.notesandvolts.com
Version 1.2 **Arduino UNO ONLY!**
******/

#include <MIDI.h>
#include "Controller.h"

MIDI_CREATE_DEFAULT_INSTANCE();

//**
//***SET THE NUMBER OF CONTROLS USED**
//******
byte NUMBER_MUX_BUTTONS = 20; //---How many buttons are connected to a multiplexer?---------
byte NUMBER_MUX_POTS = 20; //---How many potentiometers are connected to a multiplexer?--
//*******
//***ANY MULTIPLEXERS? (74HC4067)*
//MUX address pins must be connected to Arduino UNO pins 2,3,4,5
//A0 = PIN2, A1 = PIN3, A2 = PIN4, A3 = PIN5
//****
//Mux NAME (OUTPUT PIN, , How Many Mux Pins?(8 or 16) , Is It Analog?);
Mux M1D_CH1CH3(7, 16, false); //Digital multiplexer on Arduino pin 7
Mux M2A_Ch1CH3(A0,16, true); //Analog multiplexer on Arduino analog pin A0
Mux M3D_CH2CH4(8, 16, false); //Digital multiplexer on Arduino pin 8
Mux M4A_Ch2CH4(A1,16, true); //Analog multiplexer on Arduino analog pin A1
//*****

//***DEFINE BUTTONS CONNECTED TO MULTIPLEXER***
//Button::Button(Mux mux, byte muxpin, byte command, byte value, byte channel, byte debounce)
//** Command parameter 0=NOTE 1=CC 2=Toggle CC **
//CH1-3 MUX Buttons (M1D)
Button MBU1(M1D_CH1CH3, 0, 2, 20, 1, 5); //0101 Vocals
Button MBU2(M1D_CH1CH3, 2, 2, 21, 1, 5); //0102 Instruments
Button MBU3(M1D_CH1CH3, 4, 2, 22, 1, 5); //0103 Bass
Button MBU4(M1D_CH1CH3, 6, 2, 23, 1, 5); //0104 Hi-hat
Button MBU5(M1D_CH1CH3, 8, 2, 24, 1, 5); //0105 Kick Drum
Button MBU6(M1D_CH1CH3, 1, 2, 25, 1, 5); //0301 Vocals
Button MBU7(M1D_CH1CH3, 3, 2, 26, 1, 5); //0302 Instruments
Button MBU8(M1D_CH1CH3, 5, 2, 27, 1, 5); //0303 Bass
Button MBU9(M1D_CH1CH3, 7, 2, 28, 1, 5); //0304 Hi-hat
Button MBU10(M1D_CH1CH3, 9, 2, 29, 1, 5); //0305 Kick Drum
//CH2-4 MUX Buttons (M3D)
Button MBU11(M3D_CH2CH4, 0, 2, 30, 1, 5); //0201 Vocals
Button MBU12(M3D_CH2CH4, 2, 2, 31, 1, 5); //0202 Instruments
Button MBU13(M3D_CH2CH4, 4, 2, 32, 1, 5); //0203 Bass
Button MBU14(M3D_CH2CH4, 6, 2, 33, 1, 5); //0204 Hi-hat
Button MBU15(M3D_CH2CH4, 8, 2, 34, 1, 5); //0205 Kick Drum
Button MBU16(M3D_CH2CH4, 1, 2, 35, 1, 5); //0401 Vocals
Button MBU17(M3D_CH2CH4, 3, 2, 36, 1, 5); //0402 Instruments
Button MBU18(M3D_CH2CH4, 5, 2, 37, 1, 5); //0403 Bass
Button MBU19(M3D_CH2CH4, 7, 2, 38, 1, 5); //0404 Hi-hat
Button MBU20(M3D_CH2CH4, 9, 2, 39, 1, 5); //0405 Kick Drum
//******
////Add multiplexed buttons used to array below like this-> Button *MUXBUTTONS[] {&MBU1, &MBU2, &MBU3, &MBU4, &MBU5, &MBU6.....};
Button *MUXBUTTONS[] {&MBU1, &MBU2, &MBU3, &MBU4, &MBU5, &MBU6, &MBU7, &MBU8, &MBU9, &MBU10, &MBU11, &MBU12, &MBU13, &MBU14, &MBU15, &MBU16, &MBU17, &MBU18, &MBU19, &MBU20};
//*****

//***DEFINE POTENTIOMETERS CONNECTED TO MULTIPLEXER*******************
//Pot::Pot(Mux mux, byte muxpin, byte command, byte control, byte channel)
//**Command parameter is for future use**
//CH1-3 MUX POTS (M2A)
Pot MPO1(M2A_Ch1CH3, 0, 0, 40, 1); //0101 Vocals
Pot MPO2(M2A_Ch1CH3, 2, 0, 41, 1); //0102 Instruments
Pot MPO3(M2A_Ch1CH3, 4, 0, 42, 1); //0103 Bass
Pot MPO4(M2A_Ch1CH3, 6, 0, 43, 1); //0104 Hi-hat
Pot MPO5(M2A_Ch1CH3, 8, 0, 44, 1); //0105 Kick Drum
Pot MPO6(M2A_Ch1CH3, 1, 0, 45, 1); //0301 Vocals
Pot MPO7(M2A_Ch1CH3, 3, 0, 46, 1); //0302 Instruments
Pot MPO8(M2A_Ch1CH3, 5, 0, 47, 1); //0303 Bass
Pot MPO9(M2A_Ch1CH3, 7, 0, 48, 1); //0304 Hi-hat
Pot MPO10(M2A_Ch1CH3, 9, 0, 49, 1); //0305 Kick Drum
//CH2-4 MUX POTS (M4A)
Pot MPO11(M4A_Ch2CH4, 0, 0, 50, 1); //0201 Vocals
Pot MPO12(M4A_Ch2CH4, 2, 0, 51, 1); //0202 Instruments
Pot MPO13(M4A_Ch2CH4, 4, 0, 52, 1); //0203 Bass
Pot MPO14(M4A_Ch2CH4, 6, 0, 53, 1); //0204 Hi-hat
Pot MPO15(M4A_Ch2CH4, 8, 0, 54, 1); //0205 Kick Drum
Pot MPO16(M4A_Ch2CH4, 1, 0, 55, 1); //0401 Vocals
Pot MPO17(M4A_Ch2CH4, 3, 0, 56, 1); //0402 Instruments
Pot MPO18(M4A_Ch2CH4, 5, 0, 57, 1); //0403 Bass
Pot MPO19(M4A_Ch2CH4, 7, 0, 58, 1); //0404 Hi-hat
Pot MPO20(M4A_Ch2CH4, 9, 0, 59, 1); //0405 Kick Drum
//*****
//Add multiplexed pots used to array below like this-> Pot *MUXPOTS[] {&MPO1, &MPO2, &MPO3, &MPO4, &MPO5, &MPO6.....};
Pot *MUXPOTS[] {&MPO1, &MPO2, &MPO3, &MPO4, &MPO5, &MPO6, &MPO7, &MPO8, &MPO9, &MPO10, &MPO11, &MPO12, &MPO13, &MPO14, &MPO15, &MPO16, &MPO17, &MPO18, &MPO19, &MPO20};
//*****


//******
// MAIN
//******
void setup() {
MIDI.begin(MIDI_CHANNEL_OFF);
}

void loop() {
if (NUMBER_MUX_BUTTONS != 0) updateMuxButtons();
if (NUMBER_MUX_POTS != 0) updateMuxPots();
}

//******
// FUNCTIONS
//*****
void updateMuxButtons() {

// Cycle through Mux Button array
for (int i = 0; i < NUMBER_MUX_BUTTONS; i = i + 1) {

MUXBUTTONS->muxUpdate();
byte message = MUXBUTTONS->getValue();

// Button is pressed
if (message == 0) {
switch (MUXBUTTONS->Bcommand) {
case 0: //Note
MIDI.sendNoteOn(MUXBUTTONS->Bvalue, 127, MUXBUTTONS->Bchannel);
break;
case 1: //CC
MIDI.sendControlChange(MUXBUTTONS->Bvalue, 127, MUXBUTTONS->Bchannel);
break;
case 2: //Toggle
if (MUXBUTTONS->Btoggle == 0) {
MIDI.sendControlChange(MUXBUTTONS->Bvalue, 127, MUXBUTTONS->Bchannel);
MUXBUTTONS->Btoggle = 1;
}
else if (MUXBUTTONS->Btoggle == 1) {
MIDI.sendControlChange(MUXBUTTONS->Bvalue, 0, MUXBUTTONS->Bchannel);
MUXBUTTONS->Btoggle = 0;
}
break;
}
}
// Button is not pressed
if (message == 1) {
switch (MUXBUTTONS->Bcommand) {
case 0:
MIDI.sendNoteOff(MUXBUTTONS->Bvalue, 0, MUXBUTTONS->Bchannel);
break;
case 1:
MIDI.sendControlChange(MUXBUTTONS->Bvalue, 0, MUXBUTTONS->Bchannel);
break;
}
}
}
}
//*****

void updateMuxPots() {
for (int i = 0; i < NUMBER_MUX_POTS; i = i + 1) {
MUXPOTS->muxUpdate();
byte potmessage = MUXPOTS->getValue();
if (potmessage != 255) MIDI.sendControlChange(MUXPOTS->Pcontrol, potmessage, MUXPOTS->Pchannel);
}
}




Arduino: Control.cpp
#include "Controller.h"

//******
Mux::Mux(byte outpin_, byte numPins_, bool analog_)
{
outpin = outpin_;
//enablepin = enablepin_;
numPins = numPins_;
analog = analog_;
if (analog == false) pinMode(outpin, INPUT_PULLUP);
//pinMode(enablepin, OUTPUT);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
if (numPins > 8) pinMode(5, OUTPUT);
}
//******
//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
Button::Button(byte pin, byte command, byte value, byte channel, byte debounce)
{
_pin = pin;
pinMode(_pin, INPUT_PULLUP);
_value = value;
_command = command;
_debounce = debounce;
_time = 0;
_busy = false;
_status = 0b00000010;
_last = 1;
Bcommand = command;
Bvalue = value;
Bchannel = channel;
Btoggle = 0;
}

Button::Button(Mux mux, byte muxpin, byte command, byte value, byte channel, byte debounce)
{
_pin = mux.outpin;
_numMuxPins = mux.numPins;
_muxpin = muxpin;
_value = value;
_command = command;
_debounce = debounce;
_time = 0;
_busy = false;
_status = 0b00000010;
_last = 1;
Bcommand = command;
Bvalue = value;
Bchannel = channel;
Btoggle = 0;
}

void Button::muxUpdate()
{
byte temp = _muxpin;
temp = temp << 2;
if (_numMuxPins > 8) PORTD = PORTD & B11000011;
else PORTD = PORTD & B11100011;
PORTD = PORTD | temp;
}

byte Button::getValue()
{
// If BUSY bit not set - read button
if (bitRead(_status, 0) == false) { // If busy false
if (digitalRead(_pin) == _last) return 2; // If same as last state - exit
}

// If NEW Bit set - Key just pressed, record time
if (bitRead(_status, 1) == true) { // If new is true
bitSet(_status, 0); // Set busy TRUE
bitClear(_status, 1); // Set New FALSE
_time = millis();
return 255;
}

// Check if debounce time has passed - If no, exit
if (millis() - _time < _debounce) return 255;

// Debounce time has passed. Read pin to see if still set the same
// If it has changed back - assume false alarm
if (digitalRead(_pin) == _last) {
bitClear(_status, 0); // Set busy false
bitSet(_status, 1); // Set new true
return 255;
}

// If this point is reached, event is valid. return event type
else {
bitClear(_status, 0); // Set busy false
bitSet(_status, 1); // Set new true
_last = ((~_last) & 0b00000001); // invert _last
return _last;
}
}

void Button::newValue(byte command, byte value, byte channel)
{
Bvalue = value;
Bcommand = command;
Bchannel = channel;
}

//****
Pot::Pot(byte pin, byte command, byte control, byte channel)
{
_pin = pin;
_control = control;
_value = analogRead(_pin);
_value = _value >> 3;
_oldValue = _value << 3;
_value = _value << 3;
Pcommand = command;
Pcontrol = control;
Pchannel = channel;
}

void Pot::muxUpdate()
{
byte temp = _muxpin;
temp = temp << 2;
if (_numMuxPins > 8) PORTD = PORTD & B11000011;
else PORTD = PORTD & B11100011;
//PORTD = PORTD & B11000011;
PORTD = PORTD | temp;
}

Pot::Pot(Mux mux, byte muxpin, byte command, byte control, byte channel)
{
_pin = mux.outpin;
_numMuxPins = mux.numPins;
_muxpin = muxpin;
_control = control;
muxUpdate();
_value = analogRead(_pin);
_value = _value >> 3;
_oldValue = _value << 3;
_value = _value << 3;
Pcommand = command;
Pcontrol = control;
Pchannel = channel;
}

byte Pot::getValue()
{
_value = analogRead(_pin);
int tmp = (_oldValue - _value);
if (tmp >= 8 || tmp <= -8) {
_oldValue = _value >> 3;
_oldValue = _oldValue << 3;
return _value >> 3;
}
return 255;
}

void Pot::newValue(byte command, byte value, byte channel) {
Pcommand = command;
Pcontrol = value;
Pchannel = channel;
}


Arduino: Control.h
#ifndef Controller_h
#define Controller_h

#include <Arduino.h>

//*******
class Mux
{
public:
Mux(byte outpin_, byte numPins_, bool analog_);
byte outpin;
byte numPins;
bool analog;
};
//******
//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
class Button
{
public:
Button(byte pin, byte command, byte value, byte channel, byte debounce);
Button(Mux mux, byte muxpin, byte command, byte value, byte channel, byte debounce);
byte getValue();
void muxUpdate();
void newValue(byte command, byte value, byte channel);
byte Bcommand;
byte Bvalue;
byte Bchannel;
byte Btoggle;

private:
byte _previous;
byte _current;
unsigned long _time;
int _debounce;
byte _pin;
byte _muxpin;
byte _numMuxPins;
byte _value;
byte _command;
bool _busy;
byte _status;
byte _last;
byte _enablepin;
};
//*******
class Pot
{
public:
Pot(byte pin, byte command, byte control, byte channel);
Pot(Mux mux, byte muxpin ,byte command, byte control, byte channel);
void muxUpdate();
void newValue(byte command, byte value, byte channel);
byte getValue();
byte Pcommand;
byte Pcontrol;
byte Pchannel;

private:
byte _pin;
byte _muxpin;
byte _numMuxPins;
byte _control;
int _value;
int _oldValue;
bool _changed;
byte _enablepin;
};
//******


VDJ Definition File
<device name="MJC STEMS MIDI" author="Montalbon Junior" description="MJC STEMS MIDI" version="800" type="MIDI" vid="0x03EB" pid="0x2048" decks="4" padColumns="0" padRows="0" padSides="0">

<!-- Buttons-->*
<button cc="20" name="Vocal_B0101" deck="1" channel="0" />
<button cc="21" name="Instru_B0102" deck="1" channel="0" />
<button cc="22" name="Bass_B0103" deck="1" channel="0" />
<button cc="23" name="HiHat_B0104" deck="1" channel="0" />
<button cc="24" name="Kick_B0105" deck="1" channel="0" />
<button cc="25" name="Vocal_B0301" deck="3" channel="0" />
<button cc="26" name="Instru_B0302" deck="3" channel="0" />
<button cc="27" name="Bass_B0303" deck="3" channel="0" />
<button cc="28" name="HiHat_B0304" deck="3" channel="0" />
<button cc="29" name="Kick_B0305" deck="3" channel="0" />
<button cc="30" name="Vocal_B0201" deck="2" channel="0" />
<button cc="31" name="Instru_B0202" deck="2" channel="0" />
<button cc="32" name="Bass_B0203" deck="2" channel="0" />
<button cc="33" name="HiHat_B0204" deck="2" channel="0" />
<button cc="34" name="Kick_B0205" deck="2" channel="0" />
<button cc="35" name="Vocal_B0401" deck="4" channel="0" />
<button cc="36" name="Instru_B0402" deck="4" channel="0" />
<button cc="37" name="Bass_B0403" deck="4" channel="0" />
<button cc="38" name="HiHat_B0404" deck="4" channel="0" />
<button cc="39" name="Kick_B0405" deck="4" channel="0" />

<!-- Pots-->*
<slider cc="40" name="Vocal_P0101" deck="1" channel="0"/>
<slider cc="41" name="Instru_P0102" deck="1" channel="0"/>
<slider cc="42" name="Bass_P0103" deck="1" channel="0"/>
<slider cc="43" name="HiHat_P0104" deck="1" channel="0"/>
<slider cc="44" name="Kick_P0105" deck="1" channel="0"/>
<slider cc="45" name="Vocal_P0301" deck="3" channel="0"/>
<slider cc="46" name="Instru_P0302" deck="3" channel="0"/>
<slider cc="47" name="Bass_P0303" deck="3" channel="0"/>
<slider cc="48" name="HiHat_B0304" deck="3" channel="0"/>
<slider cc="49" name="Kick_P0305" deck="3" channel="0"/>
<slider cc="50" name="Vocal_P0201" deck="2" channel="0"/>
<slider cc="51" name="Instru_P0202" deck="2" channel="0"/>
<slider cc="52" name="Bass_P0203" deck="2" channel="0"/>
<slider cc="53" name="HiHat_P0204" deck="2" channel="0"/>
<slider cc="54" name="Kick_P0205" deck="2" channel="0"/>
<slider cc="55" name="Vocal_P0401" deck="4" channel="0"/>
<slider cc="56" name="Instru_P0402" deck="4" channel="0"/>
<slider cc="57" name="Bass_P0403" deck="4" channel="0"/>
<slider cc="58" name="HiHat_P0404" deck="4" channel="0"/>
<slider cc="59" name="Kick_P0405" deck="4" channel="0"/>
</device>



Arduino: Mapper File ///

<?xml version="1.0" encoding="UTF-8"?>
<mapper device="MJC STEMS MIDI" author="MJC" version="850" date="2023-12-14">

<!-- Buttons-->*
<map value="Vocal_B0101" action="play_pause" />
<map value="Instru_B0102" action="play_pause" />
<map value="Bass_B0103" action="play_pause" />
<map value="HiHat_B0104" action="play_pause" />
<map value="Kick_B0105" action="play_pause" />
<map value="Vocal_B0301" action="play_pause" />
<map value="Instru_B0302" action="play_pause" />
<map value="Bass_B0303" action="play_pause" />
<map value="HiHat_B0304" action="play_pause" />
<map value="Kick_B0305" action="play_pause" />
<map value="Vocal_B0201" action="play_pause" />
<map value="Instru_B0202" action="play_pause" />
<map value="Bass_B0203" action="play_pause" />
<map value="HiHat_B0204" action="play_pause" />
<map value="Kick_B0205" action="play_pause" />
<map value="Vocal_B0401" action="play_pause" />
<map value="Instru_B0402" action="play_pause" />
<map value="Bass_B0403" action="play_pause" />
<map value="HiHat_B0404" action="play_pause" />
<map value="Kick_B0405" action="play_pause" />

<!-- Pots-->*
<map value="Vocal_P0101" action="master_volume" />
<map value="Instru_P0102" action="master_volume" />
<map value="Bass_P0103" action="master_volume" />
<map value="HiHat_P0104" action="master_volume" />
<map value="Kick_P0105" action="master_volume" />
<map value="Vocal_P0301" action="master_volume" />
<map value="Instru_P0302" action="master_volume" />
<map value="Bass_P0303" action="master_volume" />
<map value="HiHat_P0304" action="master_volume" />
<map value="Kick_P0305" action="master_volume" />
<map value="Vocal_P0201" action="master_volume" />
<map value="Instru_P0202" action="master_volume" />
<map value="Bass_P0203" action="master_volume" />
<map value="HiHat_B0204" action="master_volume" />
<map value="Kick_P0205" action="master_volume" />
<map value="Vocal_P0401" action="master_volume" />
<map value="Instru_P0402" action="master_volume" />
<map value="Bass_P0403" action="master_volume" />
<map value="HiHat_P0404" action="master_volume" />
<map value="Kick_P0405" action="master_volume" />
</mapper>



 

geposted Sat 16 Dec 23 @ 1:08 am
locoDogPRO InfinityModeratorMember since 2013
Format your code if you post like this again.
 

geposted Sat 16 Dec 23 @ 3:15 am
How do I do that? I got lucky on my first post and was impressed with the forum's code translator. I tried to do the same with this last post but it didn't take. Are there instructions I've missed somewhere?

EDIT: I see that my post was edited to show code properly. I'm guessing that was done on my first post also. Thanks. Are there instructions for posting code that I missed. Thanks!



 

geposted Sat 16 Dec 23 @ 3:35 am
91%