MIDI and MUX

Status
Not open for further replies.

j_dunavin

Well-known member
I have already created a nice midi controller for another project, but want to create a much larger controller.
Having read this:
https://www.pjrc.com/teensy/td_midi.html
I want to use the 74HC4051 to multiplex a larger amount of switches and potentiometers, but am having a hard time understanding the code.

In my previous example:
Code:
/*
This is an example of the "Analog" class of the MIDI_controller library.
Connect 4 faders or potentiometers to A0-A3. These will be MIDI channel volumes of channels 1-4.
Map these in your DAW or DJ software.

If you are using a Teensy, make sure you have the USB type set to MIDI.
If you are using an Arduino Uno or Mega, use the HIDUINO firmware for the ATmega16U2.


Written by tttapa, 21/08/2015
https://github.com/tttapa/MIDI_controller
*/

#include <MIDI_controller.h> // include the library

const static byte Channel_Volume = 0x7; // controller number 7 is defined as Channel Volume in the MIDI implementation.
const static size_t analogAverage = 8; // Use the average of 8 samples to get smooth transitions and prevent noise
const static byte velocity = 127; // the maximum velocity, since MIDI uses a 7-bit number for velocity.
const static int  latchTime = 3000;  // the amount of time (in ms) the note is held on. Read documentation or see source code for more information.
const static byte C4 = 60; // note number 60 is defined as middle C in the MIDI implementation.
const static byte E0 = 16; // note number 60 is defined as middle C in the MIDI implementation, so 16 is E0
const byte Channel = 1; // MIDI channel 1
const byte Controller = 0x14; // MIDI controller number
const int speedMultiply = 1; // no change in speed of the encoder

//________________________________________________________________________________________________________________________________

//DigitalLatch switch1(26, E0,   1, velocity, latchTime); // Create a new member of the class 'Digital', called 'button1', on pin 2, that sends MIDI messages with note 'C4' (60) on channel 1, with velocity 127).
DigitalLatch switch2(14, E0+1, 1, velocity, latchTime);
DigitalLatch switch3(15, E0+2, 1, velocity, latchTime);
DigitalLatch switch4(16, E0+3, 1, velocity, latchTime);
Analog fader1(17, Channel_Volume, 1); // Create a new instance of the class 'Analog, called 'fader1', on pin A0, that sends MIDI messages with controller 7 (channel volume) on channel 1.
Analog fader2(18, Channel_Volume, 2);
Analog fader3(19, Channel_Volume, 3);
Analog fader4(20, Channel_Volume, 4);
Analog fader5(21, Channel_Volume, 5);
Analog fader6(22, Channel_Volume, 6);
Analog fader7(23, Channel_Volume, 7);
Digital button1(0, C4, 1, velocity); // Create a new instance of the class 'Digital', called 'button1', on pin 2, that sends MIDI messages with note 'C4' (60) on channel 1, with velocity 127).
Digital button2(1, C4+1, 1, velocity); // C4 + 1 = C#4
Digital button3(4, C4+2, 1, velocity); // C4 + 2 = D4
Digital button4(5, C4+3, 1, velocity);
Digital button5(6, C4+4, 1, velocity);
Digital button6(7, C4+5, 1, velocity);
Digital button7(8, C4+6, 1, velocity);
Digital button8(9, C4+7, 1, velocity);
Digital button9(10, C4+8, 1, velocity);
Digital button10(11, C4+9, 1, velocity);
Digital button11(12, C4+10, 1, velocity);
Digital button12(24, C4+11, 1, velocity);
Digital button13(25, C4+12, 1, velocity);
Digital button14(26, C4+13, 1, velocity);
RotaryEncoder enc(3, 2, Controller, Channel, speedMultiply, NORMAL_ENCODER, POS1_NEG127); // Create a new instance of the class 'RotaryEncoder', called 'enc', on pin 2 and 3, controller number 0x14, on channel1, no change in speed (speed is multiplied by 1), it's used as a Jog wheel, and the sign mode is set to two's complement.

//________________________________________________________________________________________________________________________________

void setup(){
  
  USBMidiController.blink(LED_BUILTIN);  // flash the built-in LED (pin 13 on most boards) on every message
  USBMidiController.setDelay(15);  // wait 15 ms after each message not to flood the connection
  USBMidiController.begin();  // Initialise the USB MIDI connection
  delay(1000); // Wait a second...
  fader1.average(analogAverage); // Use the average of 8 samples to get smooth transitions and prevent noise
  fader2.average(analogAverage);
  fader3.average(analogAverage);
  fader4.average(analogAverage);
  fader5.average(analogAverage);
  fader6.average(analogAverage);
  fader7.average(analogAverage);
  
  
}

//________________________________________________________________________________________________________________________________

void loop(){
  //switch1.refresh(); // refresh the switch (check whether the input has changed since last time, if so, send it over MIDI)
  switch2.refresh();
  switch3.refresh();
  switch4.refresh();
  fader1.refresh(); // refresh the fader (check whether the input has changed since last time, if so, send it over MIDI)
  fader2.refresh();
  fader3.refresh();
  fader4.refresh();
  fader5.refresh();
  fader6.refresh();
  fader7.refresh();
  button1.refresh(); // refresh the button (check whether the input has changed since last time, if so, send it over MIDI)
  button2.refresh();
  button3.refresh();
  button4.refresh();
  button5.refresh();
  button6.refresh();
  button7.refresh();
  button8.refresh();
  button9.refresh();
  button10.refresh();
  button11.refresh();
  button12.refresh();
  button13.refresh();
  button14.refresh();
  enc.refresh();
}

Each input was assigned a note.
How will the buttons (with multiplexing) get their assigned notes?
I'm not making the connection on how to call out each button.

Any examples wold be greatly appreciated.
 
Typical you would use arrays to store the note numbers and buttons (state), then you loop through the button array and if the button is pressed send the note stored in the note array.
There's an example of how this works under File > Examples > Teensy > USB_MIDI called Many_Button_Knobs.

I don't know the MIDI controller library you're using, it could be that with the latest additions to the Teensy MIDI implementation that this is no longer required. The example I mentioned does not use it.
 
That is a very good example, but I don't see, or understand, how to add multiplexing. This example still calls out the pins individually.
 
That is a very good example, but I don't see, or understand, how to add multiplexing. This example still calls out the pins individually.

How to use the multiplexer is explained here: Analog Multiplexer/Demultiplexer - 4051.

Personally, I like using shift registers for adding digital inputs and/or outputs, see SN74HC165 shift register revisited. They're inexpensive and fast.
What's best for your project depends on what you're trying to achieve, the number of pots and switches and most important, the required functionality.
 
Seems easy enough to wire up.
I have to apologise for my lack of knowledge.
I still don't see or understand how you would assign the buttons to a midi note.

Right now I am looking at 18 potentiometers, 22 buttons, 7 encoders, 2 cap touch, one out for neopixel LED strip, and a partridge in a pear tree.
My 3.1 has enough inputs to direct wire the encoders, cat touch, etc. I just need to get all these buttons and faders in.
 
The "many knobs and buttons" code has been generalized in a number of ways on the forum including a few MUX examples but each one a little differently based on what the OP was trying to achieve.

https://forum.pjrc.com/threads/4717...troller-with-16-analog-inputs-using-Teensy-LC!
https://forum.pjrc.com/threads/4657...x-Midi-problem?p=154634&viewfull=1#post154634
https://forum.pjrc.com/threads/4707...iplexer-wiring?p=157268&viewfull=1#post157268



The essential feature is to add a dimension to designate which MUX each control is accessed thru.

usbMIDI.sendControlChange(CCID[j], data[j], channel);

usbMIDI.sendControlChange(CCID[j], data[j], channel);


If arrays are new to you I'd recommend reading up as doing this without them will produce a sketch with hundreds of near identical lines of code that are impossible to maintain.

Once you get vectors (one-dimensional arrays) then matrix (two or more) are really not all that different or difficult to use.

The same PIN is read each time the MUX is incremented trough a loop.

I like to increment the MUX-PIN variable within the main loop so the MUX settings are only updated once at the end of the code. Then by handling any other tasks you give lots of time for this relatively slow process.

Micro-second delay can also be added if necessary but it's much preferable to give time by doing other work. Failing to let MUX output voltages stabilize is the most common problem with DIY MUX solutions I've seen here.

Please don't try to tackle all of your wish list at once.

I don't believe anyone has been successful in building such a complex controller without understanding how their code works. Cobbling bit of code from sundry projects without understanding how they work is a recipe for failure.

That said, there's lots of help here if you get stuck along the way.
 
I must confess that I actually don't know what is considered best practise in this regard, using multiplexers, shift registers or other approaches. You'll probably have to take care of the debouncing yourself as simple button debounce libraries cannot be used.

As for the mapping of note numbers to buttons, that is done by index (button order). Regardless of the technical implementation there will be a for loop to loop through all the buttons. If the first button is pressed the note stored in the first array position of the note number array is sent, if the second button is pressed the note stored in the second array position will be sent and so on.

I don't know of any examples containing all the functions you need but I can help with the shift register part should you decide to use that approach for the buttons. As I said earlier I don't what's considered best practise here so I'm hesitant to give advise on what approach you should choose.

I see that oddson already posted a much better response. I can only agree with what is said, especially the two last paragraphs.
 
I must confess that I actually don't know what is considered best practise in this regard, using multiplexers, shift registers or other approaches...
Me neither... but the learning curve for each is part of that. If you need a MUX for the pots you might as well use them for the rest since you'll already know how they work...

FYI - BOUNCE and ResponseAnalogRead can both be used with MUX. You just have to build a lot of BOUCE objects that all point at the same PIN (one for each PIN of each MUX).

I only recently learned how one does that using a loop in the setup. Apparently the keyword 'new' is essential: https://forum.pjrc.com/threads/52552-MIDI-DJ-Controller-using-Teensy-3-6-am-I-doing-this-right
 
FYI - BOUNCE and ResponseAnalogRead can both be used with MUX. You just have to build a lot of BOUCE objects that all point at the same PIN (one for each PIN of each MUX).

I only recently learned how one does that using a loop in the setup. Apparently the keyword 'new' is essential: https://forum.pjrc.com/threads/52552-MIDI-DJ-Controller-using-Teensy-3-6-am-I-doing-this-right

Interesting, shows you what I know:)
I would like to know though how much overhead this creates. This won't matter in the context of a normal midi controller but it might matter when timing is critical like with a sequencer. Using shift registers I can read the states of 32 buttons in about 15µs using just three pins on the Teensy.

For mixed setup with buttons and pots the approach in the link you provide seem like the way to go as you have to wait for the pots to stabilise anyway. Nevertheless, it would be interesting how much time is actually required for signal stabilisation after mux switching.
 
But if you are reading only one pot per mux per run thru the main loop you use all the time you leave between each pass calling other functions on tighter loops.

The time required is only a few microseconds but Teensy is so fast you need to make sure you don't waste all that power by triggering the mux and immediately trying to read the result and then resorting to a delay.

Otherwise you need to look into interrupts.
 
Status
Not open for further replies.
Back
Top