Go Down

Topic: Virtual, variable resistors, with saved presets recallable by MIDI (Read 16156 times) previous topic - next topic

Stoopalini

Gotcha.  It has to be able to function without being connected to a PC though.

I think I'll just use this instructable instead to send the MIDI messages in through the RX pin.  I can place a 1/8" TR or TRS jack in the effect pedal, and make a conversion cable to adapt the TR/TRS side to a female 5 pin DIN jack on the other end of the cable.  Then just connect the nano to the existing MIDI network. 

http://www.instructables.com/id/MIDI-TO-ARDUINO/

So the pedal could be used:

1) Normally without any external devices connected.
2) With the adapter cable to plug into a MIDI network for automated preset recalls.
3) With the break-out box connected for creating presets as well as manually recalling them.


Thanks for your help .... off to order a TRS jack, cable end, and female 5 pin DIN end.

Grumpy_Mike

Quote
It has to be able to function without being connected to a PC though.
OK that means there are no USB cables involved, that is fine.

Quote
I think I'll just use this instructable instead to send the MIDI messages in through the RX pin.
Well an instructables with a schematic, there's a rarity. It only takes three lines of code to send a MIDI note on message.

Stoopalini

Do you happen to know the lines of code to use to read and store program change messages incoming to the Arduino?

I think I'll add ability to the rotary encoder to set the MIDI channel #, then need the Arduino to read PC messages being sent to that channel and store the PC command in a variable. 

Grumpy_Mike

Quote
store program change messages incoming to the Arduino?
Store them where? Do you want them in a variable? How many do you want to store? Are they to be stored in the EEPROM?

As to read a program change MIDI message ( i assume you mean a control change ) is a three byte message, so if you are using a libiary you just set up the function to handle CC messages. If you are doing it "by hand" then you pars it like the normal note on message.

Stoopalini

Store a single PC message in a variable.  It's for recalling the presets of the POT values. It does not need to persist or be in EEPROM for storing on power off or reset. 

I mean Program Change messages (PC) not Control Change (CC) messages. 

In most guitar pedal implementations, PC messages are used for presets, while CC messages are used for individual parameters.  I'm really only concerned with PC messages at this time.

I'm finding info on how to read incoming CC messages, mostly because libraries have this down, but not seeming to find library info on how to read incoming PC messages. 

To be fair, I haven't had a ton of time to look into it yet though, and I will do so ... just thought I'd ask.  If I did it by hand, I'm assuming it's probably interrupt based (like the code I used for the rotary encoder), but really don't know yet. 

Stoopalini

I found this bit of info, so it helps me to understand the decoding needed to identify MIDI PC messages meant for the effect pedal. 

Quote from: https://www.cs.cmu.edu/~music/cmsip/readings/MIDI%20tutorial%20for%20programmers.html
The MIDI message used to specify the instrument is called a "program change" message. It has one STATUS byte and one DATA byte :

Status byte : 1100 CCCC
Data byte 1 : 0XXX XXXX

where CCCC is the MIDI channel (0 to 15) and XXXXXXX is the instrument number from 0 to 127. Similarly to MIDI channels, you will often see that the instrument numbers in synthesizers and in GM lists, are numbered from 1 to 128 so you also need to add or subtract 1 for the conversion.
So I can use the rotary encoder to allow selection of the desired midi channel, then save the selected channel (1-16) in EEPROM and use it to compare against the CCCC from the Status Byte. If the CCCC equals the channel number selected and stored, then take the data byte, convert it to decimal, and recall the POT values stored in the array position which equals the decimal. 

So now I understand the concept, I just need to figure out how to write code to do it. 

Any idea if there's a library with a function that makes reading the midi messages, filtering by PC (or CCCC), and then reading the data byte that proceeds for those that are matched/filtered?

Grumpy_Mike

OK the program control is just a two byte message
Quote
1100nnnn    0ppppppp    Program Change. This message sent when the patch number changes. (ppppppp) is the new program number.
So all you have to do is to look for 0xC as the most significant nibble of the first byte of the message, with the channel number being the least significant nibble, the next byte in the message is the number you need to store.

In this MIDI libiary https://github.com/FortySevenEffects/arduino_midi_library/

The callback function you want is:-

void handleProgramChange(byte channel, byte number);
from page Call back functions

Their is an example of using call back functions in the example folder.

Stoopalini

Perfect, thanks so much!

So I put this at the beginning:

Code: [Select]
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
int MIDIch = 1;  // Variable for storing MIDI channel for incomming MIDI Program Messages to be read


This in void setup; :
Code: [Select]

  MIDI.begin(MIDIch);  // Start MIDI with MIDIch as the Channel to listen on


This in void loop; :
Code: [Select]

  MIDI.setHandleProgramChange(MIDIProgramChange);  // Call MIDIProgramChange function when a PC message is received over MIDI


and defined this as a function:

Code: [Select]

void MIDIProgramChange(byte channel, byte number)  //  Run this function whenever a Program Change MIDI message is received
{
   // Do whatever I want when a program change message is received
}



It's passing "verify" with no errors, but I don't have the cable built yet to plug MIDI into the arduino.  I should have it done for tomorrow though.  Also need to integrate my opticoupler into the proto ...

Quick question though:  Since MIDI.begin(x) uses "x" as the argument for MIDI channel, does that mean I don't need to check the MIDI channel in my "MIDIProgramChange" function, as it will only be called when a MIDI program change is received in the serial buffer with a match to the channel defined by "x" in MIDI.begin?

I suppose it's probably good practice to check it anyway, but this helps me further understand how it's all working.



On another note:  Last night I spent about an hour getting the Digi-POTs working, and all appears to be good there.

Seems like the only 2 pieces left are the MIDI interface and a visible display.

Really appreciate your assistance Mike ...

Stoopalini

Apologies for all the questions, but I have another one related to Serial. 

I understand the nano only has one serial uart, and it's connected to both the USB port and the RX/TX pins.  And I can't do both MIDI and USB (i.e.: Serial.print for debugging) at the same time. 

Based on this, I'm thinking I should use "software serial" for MiDI, and use one of the digital pins instead of RX. 

I searched for "MIDI" software serial examples, but didn't find anything. 

Is there any particular reason this wouldn't work, or really slow down the code?

Grumpy_Mike

Quote
Quick question though:  Since MIDI.begin(x) uses "x" as the argument for MIDI channel, does that mean I don't need to check the MIDI channel in my "MIDIProgramChange" function, as it will only be called when a MIDI program change is received in the serial buffer with a match to the channel defined by "x" in MIDI.begin?
Yes.

Using software serial for debugging is the best way to do it. MIDI is too fast and software serial blocks when looking for an input. Not to sure it will go that fast either.

Stoopalini

Ok, thanks. 

I was able to get MIDI into the board today, and when I send program changes to it, I see the RX light flash, but it's not doing what I would expect. 

One thing is happening which I don't expect:  When MIDI data is received, it intermittently triggers functions which are setup to trigger based on readings from the analog potentiometers.  Maybe I need some component in the analog POT circuit to isolate them?

What's not happening is a recall of the values based on the PC# received. 

Here is the code I'm using.  I'm trying to get the PC number (1-128) stored into the "preset" variable. 

Code: [Select]


#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();

void setup() {
MIDI.begin(MIDI_CHANNEL_OMNI);
}

void loop() {
  MIDI.setHandleProgramChange(MIDIProgramChange);
}


void MIDIProgramChange(byte channel, byte number)  {
   preset=(number);
   RecallPreset();
}


With the above, the "preset" variable isn't updating with the PC #.  Can i even use "number" to refer to the PC message?  I wasn't sure it would work, but thought I'd give it a try.

The "RecallPreset" function recalls values for the Digi-POTs, using the "preset" variable as the array location to recall values from .... this works when recalling presets with the digital encoder but not when receiving a MIDI PC message.

Will the "preset=(number);" work to store the PC # into the variable, or is it coming in in binary, or HEX, and I need to convert it to DEC before storing in a variable?

I even tried placing a "MIDI.read();" line in the main loop, but it didn't seem to make a difference.


EDIT:  oh, and here's a current picture of the proto board:


Grumpy_Mike

The "set program handler" call has to be in the setup function not the loop.

You need to call  MIDI.read(); in the loop function otherwise the serial buffer with the messages in is never looked at.

Look at the examples that come with the libiary.

in the program handler why is number in brackets?

Quote
The "RecallPreset" function recalls....
Why is this function not in the code you posted, it can't be checked if you don't put in in. You need to post all your code.

Quote
Will the "preset=(number);" work to store the PC # into the variable, or is it coming in in binary, or HEX, and I need to convert it to DEC before storing in a variable?
All numbers in a computer are in binary, decimal and hex are only a convenient way for humans to look at them. There is no need to convert anything to anything.


Stoopalini

The "set program handler" call has to be in the setup function not the loop.

You need to call  MIDI.read(); in the loop function otherwise the serial buffer with the messages in is never looked at.

Look at the examples that come with the libiary.
Hmm, I'm certain I was looking at an example and the handler was in the loop, but as you already know, the examples with the library show it in setup.  I'll move it now and test later tonight after the kiddos go to bed.

in the program handler why is number in brackets?
I think I had started with an expression after the equal, and then limited it down to just the variable.  I'll remove the brackets.  Out of curiosity ... do they cause an issue?

Why is this function not in the code you posted, it can't be checked if you don't put in in. You need to post all your code.
The code is too big to fit into a post due to the 5000 char limitation of this forum.  I've posted the code as a file, in it's entirety, a few times ... but they shows 0 downloads.  So I figured it might be better to just post the snippets I am having issue with.  This function works consistently, so I wasn't concerned about the function itself, but thought I would explan what I am doing with the "preset" variable after trying to set it to the PC number. 

I attached the full code to this post, but here's the function as well:

Code: [Select]

    // Recall Preset Mode ---------------------------------------------------------------

void RecallPreset() {

  Serial.println ("Start RecallPreset Subroutine");

  actualvalue = myPresets[preset];  //  set Level POT value to the recalled value
  actual2value = myPresets2[preset];  //  set Drive POT value to the recalled value
  setbuttonstate = 0;  // reset buttonstate to 0

DriveDigiPOTValue = actual2value/4;
DriveDigiPOTWrite(DriveDigiPOTValue);
LevelDigiPOTValue = actualvalue/4;
LevelDigiPOTWrite(LevelDigiPOTValue);

    Serial.println ("Preset Recalled");
    Serial.println ("");
    ScreenWrite();
}

    // End Recall Preset Mode ---------------------------------------------------------------




All numbers in a computer are in binary, decimal and hex are only a convenient way for humans to look at them. There is no need to convert anything to anything.
Understood ... In previous programming languages, I've had to convert binary to HEX to DEC for particular usage.  I wasn't sure if I would have to do something similar here.

Thanks again ... I'm amazed you stay so active in these forums!  In all my searches for information, you tend to show up in just about every post with great knowledge! 

Stoopalini

Ok, I got it working.  I made the changes you pointed out, and then also had to disable the "Serial.begin();" line I had in there to enable serial monitoring for debugging purposes. 

After that, I'm now able to send MIDI PC messages to the Arduino, and it recalls the values from the arrays and applies those values to the Digi-POTs. 

I think I'm ready to test integrating the Digi-POT H, W, L pins to the pedal's circuitry now.  Although previously in this thread, there were some comments about isolating the power supply of the Arduino with the power supply of the pedal.

For this initial testing, I'll just run the pedal off a 9v battery, but will need to dig into the power integration soon so I can use it with my pedal boards power supply. 

Grumpy_Mike

Well done so far  :)

Quote
I'll remove the brackets.  Out of curiosity ... do they cause an issue?
No but it is an indication that you didn't understand something.

Go Up