MIDIcontroller icon indicating copy to clipboard operation
MIDIcontroller copied to clipboard

This lib doesn't do Program Change messages

Open joshnishikawa opened this issue 1 year ago • 10 comments

Program Change is on the TODO list. Meanwhile, it's fairly easy to implement using read() functions. Update to version 2.6.6 and give some of the ideas in this sketch a shot.

#include "MIDIcontroller.h"

byte MIDIchannel = 0;
const byte switchPin = 19;
const byte encoderPinA = 18;
const byte encoderPinB = 17; 
const byte padPin = 14;
int programNumber = 0;

MIDIdrum myPad(padPin, 37);
MIDIenc myEnc(encoderPinA, encoderPinB, 36, PER_DETENT);
MIDIswitch mySwitch(switchPin, 35, TRIGGER);

void setup() {
  pinMode(switchPin, INPUT_PULLUP);
  myPad.inputRange(12, 720);
  myPad.sensitivity(99); // This is still experimental!
}

void loop() {
  int encValue = myEnc.read();
  int switchValue = mySwitch.read();
  int padValue = myPad.read();
  
  if ( encValue >= 0 ){
    programNumber = encValue;
    usbMIDI.sendProgramChange(programNumber, MIDIchannel);
  }

  if ( switchValue > 0 ){
    programNumber = min(127, programNumber + 1);
    myEnc.write(programNumber); // keep the encoder up-to-date
    usbMIDI.sendProgramChange(programNumber, MIDIchannel);
  }

  // whether myPad increments or decrements
  // depents on the state of the footswitch
  if ( padValue > 0 ){ // only inc/dec on rising edge
    if ( digitalRead(switchPin) == LOW ){
      programNumber = max(0, programNumber - 1);
    } else {
      programNumber = min(127, programNumber + 1);
    }

    myEnc.write(programNumber); // keep the encoder up-to-date
    usbMIDI.sendProgramChange(programNumber, MIDIchannel);
  }
}

Originally posted by @joshnishikawa in https://github.com/joshnishikawa/MIDIcontroller/issues/27#issuecomment-1459600733

joshnishikawa avatar Mar 13 '23 09:03 joshnishikawa

As of 2.8.8 there is an updated MIDIenc example that shows how to set up an encoder to send PROGRAM_CHANGE instead of CC#. There is also an updated MIDIswitch example that shows how to set up a switch to send Real Time messages (START, STOP, CONTINUE, CLOCK and SYSTEM_RESET). These are still largely untested. @digitalelements

joshnishikawa avatar Mar 19 '23 15:03 joshnishikawa

Fantastic !!! ... I'll give them a go today and get back with some feedback .. thank you !

digitalelements avatar Mar 19 '23 15:03 digitalelements

I did give this a quick looks see. I did encounter a compilation error. I tried to sift through and see if there was anything that made sense to me to tweak before reaching out. unfortunately there was not. I do see warnings of multiple libraries in the text but not sure if that is the cause ? I'm trying to find out if it's safe to just delete this folder Arduino15

the text ...

``In file included from /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIcontroller.h:8:0, from /private/var/folders/w4/q9lkszhj4k16dnhj8l7ctbgc0000gn/T/.arduinoIDE-unsaved2023219-636-1eo0tgb.afs5/MIDIswitch/MIDIswitch.ino:11: /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h: In constructor 'MIDIcapSens::MIDIcapSens(int, byte, byte, byte)': /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:58:33: error: no matching function for call to 'MIDItouch::MIDItouch(int&, byte&, byte&, byte&)' : MIDItouch(p, num, min, max){} ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:25:5: note: candidate: MIDItouch::MIDItouch(int, byte, byte) MIDItouch(int p, byte num, byte k); ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:25:5: note: candidate expects 3 arguments, 4 provided /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:22:5: note: candidate: MIDItouch::MIDItouch(int, byte) MIDItouch(int p, byte num); ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:22:5: note: candidate expects 2 arguments, 4 provided /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:19:5: note: candidate: MIDItouch::MIDItouch() MIDItouch(); ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:19:5: note: candidate expects 0 arguments, 4 provided /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:12:7: note: candidate: constexpr MIDItouch::MIDItouch(const MIDItouch&) class MIDItouch: public TouchVariable{ ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:12:7: note: candidate expects 1 argument, 4 provided /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h: In constructor 'MIDIcapSens::MIDIcapSens(int, byte, byte, byte, byte)': /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:60:36: error: no matching function for call to 'MIDItouch::MIDItouch(int&, byte&, byte&, byte&, byte&)' : MIDItouch(p, num, min, max, m){} ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:25:5: note: candidate: MIDItouch::MIDItouch(int, byte, byte) MIDItouch(int p, byte num, byte k); ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:25:5: note: candidate expects 3 arguments, 5 provided /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:22:5: note: candidate: MIDItouch::MIDItouch(int, byte) MIDItouch(int p, byte num); ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:22:5: note: candidate expects 2 arguments, 5 provided /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:19:5: note: candidate: MIDItouch::MIDItouch() MIDItouch(); ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:19:5: note: candidate expects 0 arguments, 5 provided /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:12:7: note: candidate: constexpr MIDItouch::MIDItouch(const MIDItouch&) class MIDItouch: public TouchVariable{ ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDItouch.h:12:7: note: candidate expects 1 argument, 5 provided Multiple libraries were found for "Bounce2.h" Used: /Users/chrisryan/Documents/Arduino/libraries/Bounce2 Not used: /Users/chrisryan/Library/Arduino15/packages/teensy/hardware/avr/1.57.2/libraries/Bounce2 Multiple libraries were found for "Encoder.h" Used: /Users/chrisryan/Documents/Arduino/libraries/Encoder Not used: /Users/chrisryan/Library/Arduino15/packages/teensy/hardware/avr/1.57.2/libraries/Encoder exit status 1

Compilation error: exit status 1``

digitalelements avatar Mar 19 '23 17:03 digitalelements

Sorry, that's from something completely unrelated that I just (incompletely) removed. Now that it's completely gone, there's version 3.8.8 (see the README for breaking changes).

As for the warnings about multiple libraries, it's not a problem. You just already had the Bounce2 and Encoder libraries installed so you didn't need to include them when installing MIDIcontroller. Definitely don't delete the Arduino15 folder! If anything, delete: /Users/chrisryan/Documents/Arduino/libraries/Bounce2 and /Users/chrisryan/Documents/Arduino/libraries/Encoder

joshnishikawa avatar Mar 19 '23 22:03 joshnishikawa

No worries,

I'll give everything the once over and get back ... have a great day !!!

digitalelements avatar Mar 19 '23 22:03 digitalelements

I got things loaded up with 3.8.8. I did get a few errors while uploading with things did compile. I'll share the text at the end.

I tested the new examples (MIDIenc & MIDIswitch) are there others I should test ?

New MIDI messages are working very nicely .. (Start,Stop,Cont etc) Totally love the examples you've built into the sketches for experimenting !!.

I would like to propose an additional Mode to Momentary,Latch and Trigger. That would be a Toggle. So each switch press Toggles between 2 values. i.e. Start/Stop CC value 0/64 or even MIDI Note numbers. Some DAWs want 2 MIDI events (sometimes midi notes) to start stop the timeline. so a toggle would be valuable and not require 2 switches. unless i'm crazy and there is already a way to do this ?

Ultra Bonus ... I'd sure love to send the Start/Stop messages from my drum pad as well.

Regards,

digitalelements avatar Mar 20 '23 21:03 digitalelements

forgot the warnings text ... Ooops

/Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp: In member function 'int MIDIdrum::read()': /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp:59:33: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] if (timer < 2 && newValue >= upperThreshold){ ^ In file included from /Users/chrisryan/Library/Arduino15/packages/teensy/hardware/avr/1.57.2/cores/teensy4/WProgram.h:45:0, from /private/var/folders/w4/q9lkszhj4k16dnhj8l7ctbgc0000gn/T/arduino/sketches/1832C24C5436A66E5202FE83DAEEA078/pch/Arduino.h:6, from /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.h:4, from /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp:1: /Users/chrisryan/Library/Arduino15/packages/teensy/hardware/avr/1.57.2/cores/teensy4/wiring.h:172:9: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] (_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \ ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp:76:20: note: in expansion of macro 'constrain' newValue = constrain(peak, upperThreshold, inHi); ^ /Users/chrisryan/Library/Arduino15/packages/teensy/hardware/avr/1.57.2/cores/teensy4/wiring.h:172:33: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] (_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \ ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp:76:20: note: in expansion of macro 'constrain' newValue = constrain(peak, upperThreshold, inHi); ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp:77:29: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] newValue = newValue >= inHi ? outHi : map(peak,upperThreshold,inHi,outLo,outHi); ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp:90:20: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] if (newValue > threshold) { ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp:107:20: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] if (newValue >= threshold) { ^ In file included from /Users/chrisryan/Library/Arduino15/packages/teensy/hardware/avr/1.57.2/cores/teensy4/WProgram.h:45:0, from /private/var/folders/w4/q9lkszhj4k16dnhj8l7ctbgc0000gn/T/arduino/sketches/1832C24C5436A66E5202FE83DAEEA078/pch/Arduino.h:6, from /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.h:4, from /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp:1: /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp: In member function 'void MIDIdrum::setThreshold(unsigned int)': /Users/chrisryan/Library/Arduino15/packages/teensy/hardware/avr/1.57.2/cores/teensy4/wiring.h:172:9: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] (_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \ ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp:150:15: note: in expansion of macro 'constrain' threshold = constrain(thresh, 0, 1023); ^ /Users/chrisryan/Library/Arduino15/packages/teensy/hardware/avr/1.57.2/cores/teensy4/wiring.h:172:33: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] (_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \ ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIdrum.cpp:150:15: note: in expansion of macro 'constrain' threshold = constrain(thresh, 0, 1023); ^ /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIswitch.cpp: In member function 'int MIDIswitch::read()': /Users/chrisryan/Documents/Arduino/libraries/MIDIcontroller/src/MIDIswitch.cpp:87:1: warning: control reaches end of non-void function [-Wreturn-type] }; ^ Memory Usage on Teensy 4.0: FLASH: code:15728, data:5168, headers:8796 free for files:2001924 RAM1: variables:6400, code:14024, padding:18744 free for local variables:485120 RAM2: variables:17568 free for malloc/new:506720

digitalelements avatar Mar 21 '23 11:03 digitalelements

No prob. The warnings about comparing signed and unsigned integers are nothing to worry about. I use -1 to indicate that nothing is happening since a value of 0 would actually be a valid value for Note OFF or CC OFF.

I tested the new examples (MIDIenc & MIDIswitch) are there others I should test ?

Just test what you'll use. I'll go ahead and give everything a thorough test before I put out another release. By the way, I screwed up the version number. It should have been 3.0.0. Newest (as of this post) is 3.1.0.

Totally love the examples you've built into the sketches for experimenting !!.

Thanks! Yeah, I should have done them like that a long time ago. I removed redundant examples.

So each switch press Toggles between 2 values. i.e. Start/Stop

I tried to get this to work but it's actually pretty complicated to set this in the constructor because CC and Real Time are just completely different types of messages. Luckily, it's fairly easy to set up a sketch to do that. See the MIDIswitch_toggle example.

CC value 0/64

This functionality has always been there. Just call mySwitch.outputRange(0, 64); or whatever two values you like.

or even MIDI Note numbers. Some DAWs want 2 MIDI events (sometimes midi notes) to start stop the timeline.

Well, overloading switches to send notes is problematic too. I do need to write a MIDInote class to handle notes for non-analog inputs though... but I need to brush up on my knowledge of interrupts.

I'd sure love to send the Start/Stop messages from my drum pad as well.

If you need the pad to toggle between two different notes, the MIDIdrum_toggle example does that (I'm going to move that to a separate gist eventually though). If you need to toggle the Real Time START/STOP, I think we had some luck setting up an FSR as a MIDIswitch. Maybe you could just use the MIDIswitch_toggle example in that case.

joshnishikawa avatar Mar 22 '23 06:03 joshnishikawa

I had some time with the New examples (MIDIdrum_toggle & MIDIswitch_toggle) the MIDIdrum toggle was a bit strange in that it was outputting a ton of different MIDI notes other than what was designated in the sketch. So put that one away for more testing. I tried the MIDIswitch_toggle with a standard foot switch which worked great to do Start/Stop. I realized that what I was hoping to do is much like the Encoder sketch you built. but in this case the foot switch is the " Master" storing the state of the foot switch (as you do withe the encoder) and reading the drum pad and allowing it to share changing the state of the MIDIswitch. This will keep both a pad and drum in sync. 2 seperate MIDIswitch toggles would have you chasing start/stop. hope this makes sense.

digitalelements avatar Mar 22 '23 21:03 digitalelements

MIDIdrum toggle was a bit strange in that it was outputting a ton of different MIDI notes other than what was designated in the sketch.

Okay, there was a bug there giving me a few extra hits, not a ton. 3.1.1 fixes it for me but I'm not sure what your output looked like. Try it now.

foot switch is the " Master" storing the state of the foot switch (as you do withe the encoder) and reading the drum pad and allowing it to share changing the state of the MIDIswitch

Try this gist. I also put the one for Program Change in this gist

joshnishikawa avatar Mar 23 '23 22:03 joshnishikawa