Go Down

Topic: UNTZtrument, how to prog buttons w. midi note number, velocity, ch, latch mode  (Read 5192 times) previous topic - next topic

Flintholm

Hi, I hope to find some help here to modify my UNTZtrument 64 and im not very good with programming so please excuse me.

My goal is to control a multi-midi-channel patch on my Waldorf Blofeld, with sustaining notes to make drones with individual notes (latch mode) on different midi channels

I would like to modify the basic patch Hello World or make a patch which does the job. As it is, it functions like a keyboard, sending out midi notes on the same midi channel.

I need to be able to specify every button with a midi note number, velocity, midi-channel, and in latch mode (first press note on, second press note on)

An example could be:

Row 1: 8 notes, C major scale, Midi Ch 1
Row 2: 8 notes, C major scale, Midi Ch 2
.
.
.
Row 8: 8 notes, C major scale, Midi Ch 8



Is this easy to do ?

Im greatfull for any help

best regards Henning Flintholm

Grumpy_Mike

Please read the how to use this forum sticky post, it will tell you how to ask a question.

First off what is a " UNTZtrument 64 "
When we know that we might stand a chance of being able to say if it is easy or not.

Flintholm

Hi - i will try to explain what i know about this instrument.

For what i know this is a open source adafruit 64 button midi controller built with a Arduino Leonardo Board

https://learn.adafruit.com/untztrument-trellis-midi-instrument

On this side these are 2 example libs, one sequencer and a simple Hello World example. The last one is putting out midi notes, but i cant find where to edit the notenumber, midi channel or latch function.

I hope this info will help


Grumpy_Mike

Thanks, it looks like he note is set at this line
Code: [Select]
note = LOWNOTE + y * WIDTH + x;
So at this point you just have to put in the MIDI note number you want. It seems to be calculated from the X-Y coordinates of the key you pressed.

So for a test try putting just a simple number in here for the value of the note. Then if I am right all keys should produce the same note.

Then change it so you just
Code: [Select]
note = 30 + y ;
And all columns should be he same set of notes.

To make any note map to any key simply have a two dimensional matrix addressed by the X and Y values and fill the array with the note mapping you want.

Or try replacing the constants LOWNOTE and WIDTH with simple numbers and have a play.

What changes do you want to make to the notes?

Flintholm

Hi Grumpy Mike

Thank you very much for your answer.

I have tried your suggestion and it works very good and simple.

If i change LOWNOTE with a number i will get the lowest note on the top-left button and all other buttons from here will be the next notenumber

If i change the WIDTH with a number every row will start with this offset - so far so good.

My next problem here is that every button in a row will be playing succesive notenumber which will give me a cromatic scale, but it would be nice to be able to have non-succesive numbers to make e scale or intervalvs. ex. row 1:

C1, F1, G1, G#1,C2, D#2, F2, G2

Next problem is that it would be important to have diffent midi channels for each row. I can change the midi channel on this line:

#define CHANNEL 3  // MIDI channel number

but it changes midi ch for all buttons

And it would also be important to have Latch mode for the buttons and not Momentary mode

Do you think it´s possible with this sketch at all or do i need an other sketch ?

---

I have found some more info on the UNZTrument:



https://learn.adafruit.com/untztrument-trellis-midi-instrument

https://learn.adafruit.com/untztrument-trellis-midi-instrument/hacking

https://docs-emea.rs-online.com/webdocs/1534/0900766b815343b5.pdf

https://wikifactory.com/@botler/diy-midi-controller

https://portal.cca.edu/learning/shops/hybrid-lab/tutorials/untztrument-trellis-midi-instrument/

--

I also have been able to use this sketch from "Notes and Volts" making a simple Midi Bass Pedal with Arduino Uno:

https://www.notesandvolts.com/2016/04/arduino-midi-controller-buttons.html

I feel comfortable using this sketch and im looking for something similar, maybe a rewrite of this sketch, but im not fit in programming.

Flintholm

#5
Nov 26, 2019, 06:32 pm Last Edit: Nov 27, 2019, 11:00 am by Flintholm Reason: i did not read the rules about writing code - sorry
On this site i found some interesting info:

https://portal.cca.edu/learning/shops/hybrid-lab/tutorials/untztrument-trellis-midi-instrument/

Here is the sketch:

Code: [Select]
Use the code below for testing your UNTZtrument matrix rather than the code in the Trellis guide; it's already set up for the tiled matrix.

For the HELLA UNTZtrument, change NUMTRELLIS to 8.

/***********************************************************

 This is a test example for the Adafruit Trellis w/HT16K33.

 Reads buttons and sets/clears LEDs in a loop.

 "momentary" mode lights only when a button is pressed.

 "latching" mode toggles LED on/off when pressed.

 4 or 8 matrices can be used.  #define NUMTRELLIS to the

 number in use.



 Designed specifically to work with the Adafruit Trellis

 ----> https://www.adafruit.com/products/1616

 ----> https://www.adafruit.com/products/1611



 Adafruit invests time and resources providing this

 open source code, please support Adafruit and open-source

 hardware by purchasing products from Adafruit!



 Written by Limor Fried/Ladyada for Adafruit Industries.  

 MIT license, all text above must be included in any redistribution

***********************************************************/



#include <Wire.h>

#include "Adafruit_Trellis.h"



#define NUMTRELLIS 4        // **** SET # OF TRELLISES HERE



#define MOMENTARY 0

#define LATCHING  1



#define MODE      LATCHING  // **** SET MODE HERE



Adafruit_Trellis matrix[NUMTRELLIS] = {

 Adafruit_Trellis(), Adafruit_Trellis(),

 Adafruit_Trellis(), Adafruit_Trellis()

#if NUMTRELLIS > 4

,Adafruit_Trellis(), Adafruit_Trellis(),

 Adafruit_Trellis(), Adafruit_Trellis()

#endif

};



Adafruit_TrellisSet trellis = Adafruit_TrellisSet(

 &matrix[0], &matrix[1], &matrix[2], &matrix[3]

#if NUMTRELLIS > 4

,&matrix[4], &matrix[5], &matrix[6], &matrix[7]

#endif

);



#define numKeys (NUMTRELLIS * 16)



// Connect Trellis Vin to 5V and Ground to ground.

// Connect I2C SDA pin to your Arduino SDA line.

// Connect I2C SCL pin to your Arduino SCL line.

// All Trellises share the SDA, SCL and INT pin!

// Even 8 tiles use only 3 wires max.



void setup() {

 Serial.begin(9600);

 Serial.println("Trellis Demo");



 // begin() with the addresses of each panel.

 // I find it easiest if the addresses are in order.

 trellis.begin(

   0x70, 0x71, 0x72, 0x73

#if NUMTRELLIS > 4

  ,0x74, 0x75, 0x76, 0x77

#endif

 );



 // light up all the LEDs in order

 for (uint8_t i=0; i<numKeys; i++) {

   trellis.setLED(i);

   trellis.writeDisplay();    

   delay(50);

 }

 // then turn them off

 for (uint8_t i=0; i<numKeys; i++) {

   trellis.clrLED(i);

   trellis.writeDisplay();    

   delay(50);

 }

}



void loop() {

 delay(30); // 30ms delay is required, dont remove me!

 

 if (MODE == MOMENTARY) {

   // If a button was just pressed or released...

   if (trellis.readSwitches()) {

     // go through every button

     for (uint8_t i=0; i<numKeys; i++) {

// if it was pressed, turn it on

if (trellis.justPressed(i)) {

Serial.print("v"); Serial.println(i);

trellis.setLED(i);

}

// if it was released, turn it off

if (trellis.justReleased(i)) {

Serial.print("^"); Serial.println(i);

trellis.clrLED(i);

}

     }

     // tell the trellis to set the LEDs we requested

     trellis.writeDisplay();

   }

 }



 if (MODE == LATCHING) {

   // If a button was just pressed or released...

   if (trellis.readSwitches()) {

     // go through every button

     for (uint8_t i=0; i<numKeys; i++) {

       // if it was pressed...

if (trellis.justPressed(i)) {

Serial.print("v"); Serial.println(i);

// Alternate the LED

if (trellis.isLED(i))

  trellis.clrLED(i);

else

  trellis.setLED(i);

       }

     }

     // tell the trellis to set the LEDs we requested

     trellis.writeDisplay();

   }

 }

}


-----------------

This sketch seem to work in latch mode. Buttons are turned on/off with each succesive press. But i doesnt send out midi. I noticed that it was´nt recogniced on my computer and saw that the line from the other sketch :

#include "MIDIUSB.h"

was missing. after adding this line the UNTXintrument was now recogniced, but it doesnt send out midi

Grumpy_Mike

OK time to read the rules about posting code here. Please read this:-
How to use this forum
Go back and modify that last post so you use code tabs.

Quote
I noticed that it was´nt recogniced on my computer and saw that the line from the other sketch :

#include "MIDIUSB.h"

was missing. after adding this line the UNTXintrument was now recogniced, but it doesnt send out midi
I think you are best sticking with your original modifications of the "hello world" sketch. The MIDIUSB library sends out it's messages in a different way to the direct connection's MIDI library. That is it uses a different call and its parameters are in a different order.

However your problems are the same, how to generate note numbers from key presses. Basically you have two ways to do this.
1) Work out a series of calculations that will calculate the note number from the X-Y readings of the button press.
2) Define a two dimensional byte array with the note numbers in, so you just select a note by using:-
Code: [Select]
note = noteArray[x][y];
 
Where noteArray is the array you defined.

Quote
Next problem is that it would be important to have diffent midi channels for each row. I can change the midi channel on this line:
#define CHANNEL 3  // MIDI channel number
but it changes midi ch for all buttons
Yes that does. What the #define does is simply replace a text string with the number. So every time the compiler sees "CHANNEL" it replaces it with the number 3.
Quote
And it would also be important to have Latch mode for the buttons and not Momentary mode
Do you think it´s possible with this sketch at all or do i need an other sketch ?
Yes you can do all that with this sketch.

For the channel number replace you #define macro with a variable
Code: [Select]
byte channel = 3;
By convention constants use all upper case, but now you have a variable.

Then like I said above work out a series of calculations that will calculate the channel number. In this case it is simple. If you want a different channel for each row, simply use the y coordinate to change the channel number.
Code: [Select]
channel = y +1;
Depending on what library you use you might not have to add one to this if it uses channel 0.

The latching mode is simply a matter of when you send the note off command. If, like at the moment, you send it when the button is released, you have a momentary note. To make it latched simply send the note off when the button is pressed a second time.

An example of this for a single button is shown in the IDE example "state change detection". You have to extend the idea to your situation where you have many numbers. This is not as daunting as it seems if again you use an array to hold the last value of each switch and compare with the incoming value. So you detect when a key is pressed not if it is currently being held down. You use this to "count" the number of key presses from 0 to 2. When you see a 1 in the updated count, turn the note on and when you see a 2, turn the note off and reset the count to zero.

You are pushing you coding skills here but it is not that much of a big step.


Flintholm

Dear Grumpy Mike

Thank you very much for you feedback and taking your time helping me :-) I think i will be able to make the changes, but i will have to spend some more time understanding your directions. I will get back when i have tried it out. best regards Flintholm

Flintholm

Hi again Grumpy Mike

I have now had some more time to try out some of your suggestions and i am happy that i´ve managed some of the stuff, but as you said, this will defenitly push my coding skills and i have a lot to learn.

I have managed the following:

# changing the midi channels on each row
# changing the startnote on each row (still chromatic)
# changing the velocity (globally for all notes)

i still have to:

# find out how to get the latching function - i will look into the IDE example "state change detection" later
# find a way to specify the notes so the will not be chromatic - i will have to find out how to do this NoteArray

In order to make it understandable for me, i need to clarify things - Is it following correct:

# The buttons in the Array are starting in the topleft corner and ascending in a normal reading direction (left to right) ?

# Numbers in programming always start with a 0 (zero) for number 1 ? like midi-channel 4, the number should be 3 ?

# Is the buttons X and the rows Y ?

# Is the first button and first row 0 (zero) ?

# Is the second button and second row 1 ?

In the Hello World example the notes are defined like this (for the chromatic ascending notenumbers):

Code: [Select]
note = LOWNOTE + y * WIDTH + x;

# Should i replace this line with the NoteArray which is defined like this:

Code: [Select]
note = noteArray[x][y];

as an example i would like to have these notes on the rows (in order to just get the understanding, i just take the first 3 notes and 3 rows, and i set a global velocity and i change the midi channel as earlier suggested)

Row 1, Button 1: C2/Note-number 48 (Midi Ch 9)
Row 1, Button 2: D2/Note-number 50 (Midi Ch 9)
Row 1, Button 3: E2/Note-number 52 (Midi Ch 9)

Row 2, Button 1: C3/Note-number 60 (Midi Ch 10)
Row 2, Button 2: D3/Note-number 62 (Midi Ch 10)
Row 2, Button 3: E3/Note-number 64 (Midi Ch 10)

Row 3, Button 1: C4/Note-number 72 (Midi Ch 11)
Row 3, Button 2: D4/Note-number 74 (Midi Ch 11)
Row 3, Button 3: E4/Note-number 76 (Midi Ch 11)

3 rows with 3 notes each in an Array:

{48, 50, 52}
{60, 62, 64}
{72, 74, 76}

Is this correct:

Code: [Select]
{ {48, 50, 52}, {60, 62, 64}, {72, 74, 76} };



Im sorry for the newbee questions, but im not so much into programming.

I think this is all i can cope with for now - I would like to make small steps and get the notes right before i jump into the Latching problem and the IDE example "state change detection"

best regards Flintholm

Grumpy_Mike

You are close with the array but you have not actually declared an array with those examples.
Code: [Select]


byte noteArray[3][3] = { {48, 50, 52}, {60, 62, 64}, {72, 74, 76} };
// or to understand it better lay it out like this:-
byte noteArray[3][3] = { {48, 50, 52},
                                     {60, 62, 64},
                                     {72, 74, 76}
                                    };


Then you can use:-
Code: [Select]
note = noteArray[y][x];
when you declare the array the first number is the number of groups and the second number how far into that group you have, so I would swap the X and Y if you define the array like you said.

As to your questions:-
Quote
# The buttons in the Array are starting in the topleft corner and ascending in a normal reading direction (left to right) ?
I don't know, I don't have that hardware but I would guess the buttons are ordered left to right.

Quote
# Numbers in programming always start with a 0 (zero) for number 1 ? like midi-channel 4, the number should be 3 ?
It depends on the library you are using, some require the "true" value 0 to 15, while others require the "musicians" value of 1 to 16. I call it the true value because the channel number is in the lower four bits of the command byte of a MIDI message. Four bits can have a value between 0 and 15, but musicians can't cope with that so we say we have channels 1 to 16. It is easy enough to correct for and so some libraries correct for it and others don't. It is not always easy to find in the documentation, so send stuff on channel 3 and monitor the MIDI and see what channel it shows up as.

Quote
# Is the buttons X and the rows Y ?
That is my understanding.

Quote
# Is the first button and first row 0 (zero) ?

# Is the second button and second row 1 ?
It would be astonishing if they were not.

Quote
In order to make it understandable for me, i need to clarify things - Is it following correct:
If you need to clarify something then it is best doing a test. Write a short code that is like the hello world example but don't do any of the MIDI stuff just print out variables to the serial monitor using a sensible baud rate and see for yourself how things are are arranged.

Flintholm

Hi Mike

Thanx - i will be back soon and check out your helpfull answers :-)

Flintholm

 

Flintholm

Dear Grumpy Mike

I was away from this stuff because i had to do some stupid tax-return paperwork (i hate it) i will have to start over again and try to work with your suggestions.

I think i understood and can work out the Array

Code: [Select]
note = noteArray[y][x];

If i want 8 rows with 8 notes each in an Array:

{2, 4, 6}
{8, 10, 12}
{14, 16, 18}
{20, 22, 24}
{26, 28, 30}
{32, 34, 36}
{38, 40, 42}
{44, 46, 48}

This is just an example with simple wholenote-steps, making it easy to type - i will adjust the final notes for my needs when i have the understanding of it.


should the code look like this:

Code: [Select]
byte noteArray[8][8] = { {2, 4, 6}, {8, 10, 12}, {14, 16, 18}, {20, 22, 24}, {26, 28, 30}, {32, 34, 36}, {38, 40, 42}, {44, 46, 48} };

if this is right - is it also right that i should use the line:

Code: [Select]
note = noteArray[y][x];

instead of the line:

Code: [Select]
note = LOWNOTE + y * WIDTH + x;

in the Hello world example ?

Is this right:

Code: [Select]
void loop() {
  unsigned long t = millis();
  if((t - prevReadTime) >= 20L) { // 20ms = min Trellis poll time
    if(untztrument.readSwitches()) { // Button state change?
      for(uint8_t i=0; i<N_BUTTONS; i++) { // For each button...
        // Get column/row for button, convert to MIDI note number
        uint8_t x, y, note;
        untztrument.i2xy(i, &x, &y);
        note = noteArray[y][x];
        if(untztrument.justPressed(i)) {
          noteOn(CHANNEL, note, 127);
          untztrument.setLED(i);
        } else if(untztrument.justReleased(i)) {
          noteOff(CHANNEL, note, 0);
          untztrument.clrLED(i);
        }
      }
      untztrument.writeDisplay();
    }
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
  }



If this is right, where should i put the part with the Array:

Code: [Select]
byte noteArray[8][8] = { {2, 4, 6}, {8, 10, 12}, {14, 16, 18}, {20, 22, 24}, {26, 28, 30}, {32, 34, 36}, {38, 40, 42}, {44, 46, 48} };


I hope to get this solved sometime - Now x-mas is comming with a lot of other timecomsuming stuff, but maybe ill get it together with you help. Im very greatfull - Thanks :-)

Grumpy_Mike

Quote
should the code look like this:

Code: [Select]

byte noteArray[8][8] = { {2, 4, 6}, {8, 10, 12}, {14, 16, 18}, {20, 22, 24}, {26, 28, 30}, {32, 34, 36}, {38, 40, 42}, {44, 46, 48} };



Yes apart from the obvious that there should be 8 numbers in each brace {} and 8 brace pairs. I would put each 8 number brace on a new line to make it more readable.

Quote
if this is right - is it also right that i should use the line:

Code: [Select]
note = noteArray[y][x];

Yes.

Quote
If this is right, where should i put the part with the Array:
Just before the setup function is defined.

Flintholm

Thank You Mike :-)

It´was ofcourse a fault that i only had 3 notenumbers in each brace. Also the listing you suggested is much better. I was not quite sure what the setup function was, but i supposed this was the right place:

Code: [Select]

byte noteArray[8][8] = {
{2, 4, 6, 8, 10, 12, 14, 16},
{8, 10, 12, 14,16, 18, 20, 22},
{14, 16, 18, 20, 22, 24, 26, 28},
{20, 22, 24, 26, 28, 30, 32, 34},
{26, 28, 30, 32, 34, 36, 38, 40},
{32, 34, 36, 38, 40, 42, 44, 46},
{38, 40, 42, 44, 46, 48, 50, 52},
{44, 46, 48, 48, 50, 52, 54, 56} };

void setup() {
  pinMode(LED, OUTPUT);
#ifndef HELLA
  untztrument.begin(addr[0], addr[1], addr[2], addr[3]);
#else
  untztrument.begin(addr[0], addr[1], addr[2], addr[3],
                    addr[4], addr[5], addr[6], addr[7]);
#endif // HELLA
  // Default Arduino I2C speed is 100 KHz, but the HT16K33 supports
  // 400 KHz.  We can force this for faster read & refresh, but may
  // break compatibility with other I2C devices...so be prepared to
  // comment this out, or save & restore value as needed.
#ifdef ARDUINO_ARCH_SAMD
  Wire.setClock(400000L);
#endif
#ifdef __AVR__
  TWBR = 12; // 400 KHz I2C on 16 MHz AVR
#endif
  untztrument.clear();
  untztrument.writeDisplay();
}

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
  MidiUSB.flush();
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
  MidiUSB.flush();
}

void loop() {
  unsigned long t = millis();
  if((t - prevReadTime) >= 20L) { // 20ms = min Trellis poll time
    if(untztrument.readSwitches()) { // Button state change?
      for(uint8_t i=0; i<N_BUTTONS; i++) { // For each button...
        // Get column/row for button, convert to MIDI note number
        uint8_t x, y, note;
        untztrument.i2xy(i, &x, &y);
        note = noteArray[y][x];
        if(untztrument.justPressed(i)) {
          noteOn(CHANNEL, note, 100);
          untztrument.setLED(i);
        } else if(untztrument.justReleased(i)) {
          noteOff(CHANNEL, note, 0);
          untztrument.clrLED(i);
        }
      }
      untztrument.writeDisplay();
    }
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
  }
}


Anyway i can confirm that it works :-)

Thats very nice - thanks Mike.

Now the last step is to make the pads latch instead of momentary.

You wrote earlier this:

The latching mode is simply a matter of when you send the note off command. If, like at the moment, you send it when the button is released, you have a momentary note. To make it latched simply send the note off when the button is pressed a second time.

An example of this for a single button is shown in the IDE example "state change detection". You have to extend the idea to your situation where you have many numbers. This is not as daunting as it seems if again you use an array to hold the last value of each switch and compare with the incoming value. So you detect when a key is pressed not if it is currently being held down. You use this to "count" the number of key presses from 0 to 2. When you see a 1 in the updated count, turn the note on and when you see a 2, turn the note off and reset the count to zero.


I will have to check up on this, but i think this will be the last hurdle for this project.

I proberly will come back for help on this after i have tried, but thanks very much for this so far :-)

best regards

Henning


Grumpy_Mike

Well done.

Quote
I was not quite sure what the setup function was,
there is at least a setup function and a loop function defined in every Arduino sketch. A function definition starts with the variable type it will return. These two functions never return a value so the function definition starts with the key word void, which means no type of variable. Beginners often say this is the void loop function but it is not, it is simply the loop function.

You can define other functions if you want. This is very useful for splitting the code into logical parts or where you want the same bit of code to run in various points in the code.

Read about functions here:- https://www.arduino.cc/en/Reference/FunctionDeclaration

Go Up