Control_Surface.h usage on Teensy 4.1

Hi all, hi @PieterP,

So, after a hard learning work on nano, then migrated to STM32, i finally migrated on Teensy 4.1.
Here is a summary of my project:

I'm building a limited midi over usb control surface, including 6 rotary encoders, 2 instant buttons and a toggle switch, plus a 160x128 SPI TFT display.

At the moment, as i initially developped around USBComposite (on STM32), i'm able to send midi CC messages for each control: buttons, encoders. each control is intended to pilot plugins like compressors or eq inside a DAW (digital audio workstation).
I use studio One and protools but i'm focusing on Studio One because of the midi learning function of this DAW, which allows an easy association between any virtual button on the plugin with any physical button on the control surface (in fact, with any CC message).

If i declare my controller to be a generic controller in the DAW, i'm able to send my CC messages which are correctly understood and used my the DAW after the midi learning process.
But, as we previously discussed, the DAW won't send any message to the controller, which prevent me from getting current channel info, FX name, vu level, etc.

If i declare the controller to be a mackie control,i receive messages (which i will have to process later thanks to your lib) but even if i can see my CC messages in the DAW (debug tool), they are not interpreted.

If i declare on generic surface from which the DAW receive teensy controls and another mackie surface to which the DAW sends midi info, it can be a hybrid system, but it is not clean, nor perfect, and i know using MIDIUsb lib concurently with control surface lib will cause conflicts.

So, i will need some help about choices:

  • is it preferable to keep an hybrid system or to use a fully send/receive mackie control system?
  • in the case i use mackie control i/o, do i have to use specific CC values to make them usable? also, do i have to send them with the surface control lib instead of the MIDIUSB one?
  • is this possible to instanciate the VU class to be able to display on TFT panel instead of LEDs arrays and to display depending on the channel i'm editing an effect on?
  • is this possible to get the channel name/number over midi to display it on the TFT panel of my controller?
  • the same for the effects currently edited?
    ...

A lot of work :slight_smile:

Ronerone:

  • is it preferable to keep an hybrid system or to use a fully send/receive mackie control system?

I'm not familiar with the DAW you're using, so I can't answer that question. For your Arduino code it shouldn't matter, you can program it to send and receive any MIDI data you please. The limiting factor here is what the DAW sends and can map or interpret.

Ronerone:
If i declare the controller to be a mackie control,i receive messages (which i will have to process later thanks to your lib) but even if i can see my CC messages in the DAW (debug tool), they are not interpreted.
[...]

  • in the case i use mackie control i/o, do i have to use specific CC values to make them usable?

The MCU protocol uses MIDI Note On/Off messages for buttons, not MIDI CC.
You can find the predefined note numbers for MCU in the documentation. You can use them in your code like this:

NoteButton button = {
  5,              // Push button pin
  MCU::SOLO_1,    // Note number
};

You can also use any number in [0, 127], of course:

NoteButton button = {
  5,    // Push button pin
  8,    // Note number
};

See the NoteButton example for more information.

Ronerone:
also, do i have to send them with the surface control lib instead of the MIDIUSB one?

If you're using Control Surface, you can often just use the MIDI Output Elements like NoteButton, CCButton etc.
If you need more flexibility, you can either define your own MIDI Output Element as shown in this example, or just send MIDI in your main loop, see the "Can I use Control Surface as a general purpose MIDI library?" section on this page.
As shown on that page and in the examples that the page links to, Control Surface can do everything MIDIUSB does (and much more). See the MIDI Output for a simple example and the MIDI Sender documentation for a full overview of all MIDI sending functions.

Ronerone:

  • is this possible to instanciate the VU class to be able to display on TFT panel instead of LEDs arrays and to display depending on the channel i'm editing an effect on?

Yes. The VU class just receives the MCU VU value for a given channel, you can then use that value to display a level meter on your display, for example.
Until I find the time to finish the 2.0 release, I'd recommend using the new-input branch, as it improves the way MIDI input gets processed, and it allows you to detect changes more easily. For example:

#include <Control_Surface.h>
 
USBMIDI_Interface midi;
 
constexpr unsigned int decay = MCU::VUDecay::Hold;
// Try this option if your DAW doesn't decay the VU meters automatically:
//   constexpr unsigned int decay = 60; // milliseconds to decay one block
 
// VU meters
MCU::VU vumeter { 1, decay }; // track number [1, 8], decay rate
 
void setup() {
  Control_Surface.begin();
}
 
void loop() {
  Control_Surface.loop();
  // If the VU meter value changed
  if (vumeter.getDirty()) {
    // Do something with vumeter.getValue() and vumeter.getOverload()

    // Clear the dirty flag to acknowledge the change
    vumeter.clearDirty();
  }
}

There's a MCU::VUDisplay class that displays a nice VU meter on a display (as shown in this demo), but if you want more control over your display, like different views depending on the context, it's probably best to draw to the display yourself. You could use the existing VU meter drawing code for inspiration.

Ronerone:

  • is this possible to get the channel name/number over midi to display it on the TFT panel of my controller?

If your DAW can send it, then Control Surface can receive it. When using the MCU protocol, you can use the MCU::LCD class to get the content of the MCU's LCD display, including the track names (see this demo).

Ronerone:

  • the same for the effects currently edited?

Same answer: depends on whether your DAW sends the necessary information. If you know what MIDI messages you need to listen for, I can show you how to receive them using Control Surface, but you'll have to dive into the documentation for your DAW, do some reverse engineering or write a MIDI controller script or plugin for your DAW if it doesn't send the necessary data.

Pieter

Oh, before i analysis your comment (thanks a lot!), i add one point: my current setup displays few animations and indicators regarding encoders acceleration (locally managed) and rotation (simple color disc loop).
My understanding was that i wouldn't be able to do so if i used control_surface, except by modifying it directly in your code, which i'm not familiar with. Am i right?
(lol, i was writing about instanciating the VU class, which i could localize, but i'm absolutly not able to do so at the moment... poor skills, i'm gonna keep and learn).

Ronerone:
my current setup displays few animations and indicators regarding encoders acceleration (locally managed) and rotation (simple color disc loop).
My understanding was that i wouldn't be able to do so if i used control_surface, except by modifying it directly in your code, which i'm not familiar with. Am i right?

Using Control Surface doesn't mean that you have to throw out your existing code. Control Surface has easy to use rotary encoder classes that send out MIDI, but you don't have to use them, you can just as well write your own encoder logic. You don't have to edit any library code, you can just do it in your sketch.

You might want to replace some MIDIUSB.sendMIDI(...) by Control_Surface.send(...) so the two libraries don't interfere, but you're free to do whatever you want in your sketch.

Ronerone:
(lol, i was writing about instanciating the VU class, which i could localize, but i'm absolutly not able to do so at the moment... poor skills, i'm gonna keep and learn).

I'm not sure what you mean here?

PieterP:
You might want to replace some MIDIUSB.sendMIDI(...) by Control_Surface.send(...) so the two libraries don't interfere, but you're free to do whatever you want in your sketch.I'm not sure what you mean here?

Probably because i'm myself not really knowing what i'm talking about :grin: :grin: :grin:
You wrote a whole lot of golden info above! this is very useful!
at the moment, il use 1.2.04 library, directly accessible in platformIO. I really have to check how i can use the new-input branch.

Ronerone:
at the moment, il use 1.2.04 library, directly accessible in platformIO. I really have to check how i can use the new-input branch.

In your platformio.ini file, under lib_deps, use https://github.com/tttapa/Control-Surface.git#new-input instead of tttapa/Control Surface @ ^1.2.0-4, see pio lib install — PlatformIO latest documentation.

i had already tried the direct url which won't work (commented out).
it seems to be the same with GitHub - tttapa/Control-Surface: Arduino library for creating MIDI controllers and other MIDI devices..

lib_deps = 
	;https://github.com/tttapa/Control-Surface/tree/new-input
	 https://github.com/tttapa/Control-Surface.git#new-input
	;tttapa/Control Surface@^1.2.0-4
	olikraus/Ucglib@^1.5.2
	soligen2010/ClickEncoder@0.0.0-alpha+sha.9337a0c46c

i suppose i have another issue...

PACKAGES:
 - framework-arduinoteensy 1.153.0 (1.53)
 - toolchain-gccarmnoneeabi 1.50401.190816 (5.4.1)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Library Manager: Installing git+https://github.com/tttapa/Control-Surface.git#new-input
FileNotFoundError: [WinError 2] Le fichier spécifié est introuvable:
  File "C:\Users\Ron\.platformio\penv\lib\site-packages\platformio\builder\main.py", line 170:
    env.SConscript("$BUILD_SCRIPT")
  File "C:\Users\Ron\.platformio\packages\tool-scons\scons-local-4.0.1\SCons\Script\SConscript.py", line 598:
    return _SConscript(self.fs, *files, **subst_kw)
  File "C:\Users\Ron\.platformio\packages\tool-scons\scons-local-4.0.1\SCons\Script\SConscript.py", line 287:
    exec(compile(scriptdata, scriptname, 'exec'), call_stack[-1].globals)
  File "C:\Users\Ron\.platformio\platforms\teensy\builder\main.py", line 144:
    target_elf = env.BuildProgram()
  File "C:\Users\Ron\.platformio\packages\tool-scons\scons-local-4.0.1\SCons\Environment.py", line 219:
    return self.method(*nargs, **kwargs)
  File "C:\Users\Ron\.platformio\penv\lib\site-packages\platformio\builder\tools\platformio.py", line 62:
    env.ProcessProjectDeps()
  File "C:\Users\Ron\.platformio\packages\tool-scons\scons-local-4.0.1\SCons\Environment.py", line 219:
    return self.method(*nargs, **kwargs)
  File "C:\Users\Ron\.platformio\penv\lib\site-packages\platformio\builder\tools\platformio.py", line 140:
    project_lib_builder = env.ConfigureProjectLibBuilder()
  File "C:\Users\Ron\.platformio\packages\tool-scons\scons-local-4.0.1\SCons\Environment.py", line 219:
    return self.method(*nargs, **kwargs)
  File "C:\Users\Ron\.platformio\penv\lib\site-packages\platformio\builder\tools\piolib.py", line 1062:
    project.install_dependencies()
  File "C:\Users\Ron\.platformio\penv\lib\site-packages\platformio\builder\tools\piolib.py", line 898:
    lm.install(spec)
  File "c:\users\ron\.platformio\penv\lib\site-packages\platformio\package\manager\_install.py", line 49:
    spec, silent=silent, skip_dependencies=skip_dependencies, force=force
  File "c:\users\ron\.platformio\penv\lib\site-packages\platformio\package\manager\library.py", line 86:
    force=force,
  File "c:\users\ron\.platformio\penv\lib\site-packages\platformio\package\manager\_install.py", line 97:
    pkg = self.install_from_url(spec.url, spec, silent=silent)
  File "c:\users\ron\.platformio\penv\lib\site-packages\platformio\package\manager\_install.py", line 141:
    vcs = VCSClientFactory.new(tmp_dir, url)
  File "c:\users\ron\.platformio\penv\lib\site-packages\platformio\package\vcsclient.py", line 55:
    src_dir, remote_url, tag, silent
  File "c:\users\ron\.platformio\penv\lib\site-packages\platformio\package\vcsclient.py", line 137:
    self.configure()
  File "c:\users\ron\.platformio\penv\lib\site-packages\platformio\package\vcsclient.py", line 146:
    result = proc.exec_command([cls.command, "--exec-path"])
  File "c:\users\ron\.platformio\penv\lib\site-packages\platformio\proc.py", line 113:
    p = subprocess.Popen(*args, **kwargs)
  File "C:\Users\Ron\.platformio\python3\lib\subprocess.py", line 800:
    restore_signals, start_new_session)
  File "C:\Users\Ron\.platformio\python3\lib\subprocess.py", line 1207:
    startupinfo)

Do you have git installed and is it in your %PATH%?

no, and this is what you already pointed out in the other thread. This is what i have to manage now.
I wanted to avoid it as i'm working on my "production" station, not a dev one, but if it is mandatory, i will proceed. sorry for that.

Ronerone:
no, and this is what you already pointed out in the other thread. This is what i have to manage now.
I wanted to avoid it as i'm working on my "production" station, not a dev one, but if it is mandatory, i will proceed. sorry for that.

I'm not sure if that's the error, the message isn't that clear, but I'm assuming PIO needs Git to be able to checkout libraries using the git protocol. I'm not familiar enough with PlatformIO or Windows to be of any help there.
If you do install git, don't forget to add it to your path during the installation.

i was proceeding with the install. several options including one allowing git usage from external softwares had to be checked or not, this last one probably modify PATH in that aim. gonna check after install has complete.

Looking at the stack trace you posted and at the PIO source code, it indeed looks like it failed trying to run git: platformio-core/vcsclient.py at 1ec2e55322d02ce331a2ad33c22c8b618db2a83d · platformio/platformio-core · GitHub

ok, it seems to try to compile so this was probably the issue!
Some of the sample code parts i had won't allow compilation to complete, maybe some syntax changes?
i will remove them.
there is also a warning about VU which i hadn't integrated yet.
Is there a way to validate i correctly loaded the new branch?

In file included from .pio\libdeps\teensy41\Control Surface@src-e1ce3605bead1f14d6817ed604e61845\src/Control_Surface.h:91:0,
                 from src\main.cpp:1:
.pio\libdeps\teensy41\Control Surface@src-e1ce3605bead1f14d6817ed604e61845\src/MIDI_Inputs/MCU/VU.hpp:188:7: warning: 'CS::MCU::VU' has a field 'CS::MCU::VU::decayTimer' whose type uses the anonymous namespace
 class VU : public MatchingMIDIInputElement<MIDIMessageType::CHANNEL_PRESSURE,
       ^
src\main.cpp: In function 'bool sysExMessageCallback(CS::SysExMessage)':
src\main.cpp:239:41: error: 'struct CS::SysExMessage' has no member named 'CN'
   Serial << dec << F("on cable ") << se.CN << endl;
                                         ^
src\main.cpp: In function 'bool realTimeMessageCallback(CS::RealTimeMessage)':
src\main.cpp:248:35: error: 'struct CS::RealTimeMessage' has no member named 'CN'
          << F(" on cable ") << rt.CN << endl;
                                   ^
src\main.cpp: In function 'void RotaryState(int)':
src\main.cpp:560:62: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
       if(encPos[numRotary]=EncoderList[numRotary]->getValue()) {
                                                              ^

Ronerone:

In file included from .pio\libdeps\teensy41\Control Surface@src-e1ce3605bead1f14d6817ed604e61845\src/Control_Surface.h:91:0,

from src\main.cpp:1:
.pio\libdeps\teensy41\Control Surface@src-e1ce3605bead1f14d6817ed604e61845\src/MIDI_Inputs/MCU/VU.hpp:188:7: warning: 'CS::MCU::VU' has a field 'CS::MCU::VU::decayTimer' whose type uses the anonymous namespace
class VU : public MatchingMIDIInputElement<MIDIMessageType::CHANNEL_PRESSURE,
      ^
src\main.cpp: In function 'bool sysExMessageCallback(CS::SysExMessage)':
src\main.cpp:239:41: error: 'struct CS::SysExMessage' has no member named 'CN'
  Serial << dec << F("on cable ") << se.CN << endl;
                                        ^
src\main.cpp: In function 'bool realTimeMessageCallback(CS::RealTimeMessage)':
src\main.cpp:248:35: error: 'struct CS::RealTimeMessage' has no member named 'CN'
         << F(" on cable ") << rt.CN << endl;
                                  ^
src\main.cpp: In function 'void RotaryState(int)':
src\main.cpp:560:62: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
      if(encPos[numRotary]=EncoderList[numRotary]->getValue()) {
                                                             ^

You can ignore the first warning. The errors about CN are solved by replacing “CN” by “cable”, it's a change from Control Surface 1.x to 2.x.
The final warning is probably a bug. Did you mean to assign or compare the values?

PieterP:
Did you mean to assign or compare the values?

are you refering to my question "Is there a way to validate i correctly loaded the new branch?" ?

I now have the answer: "The errors about CN are solved by replacing "CN" by "cable", it's a change from Control Surface 1.x to 2.x. "
-> that mean i have the new branch loaded :wink:

Ronerone:
are you refering to my question "Is there a way to validate i correctly loaded the new branch?" ?

I meant this error, it's an assignment expression, which is often a bug when used as the condition of an if statement:

src\main.cpp: In function 'void RotaryState(int)':
src\main.cpp:560:62: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
       if(encPos[numRotary]=EncoderList[numRotary]->getValue()) {
                                                              ^

Yes, it looks like you do have the correct branch installed now.

yes, thanks again! i now have to analyse all the info you posted above.
it will take some time :slight_smile:

Pieter, at the moment i'm using ucglib instead of adafruit.
i'm looking at the example:
Control Surface: MCU-OLED-SSD1306-x2.ino (tttapa.github.io)
will i be able to interact with ucglib or do i have to use adafruit?

PieterP:
The final warning is probably a bug. Did you mean to assign or compare the values?

Sorry, i hadn't answered. I meant to assign, the getValue is the condition. it seems to work well that way.

Ronerone:
Pieter, at the moment i'm using ucglib instead of adafruit.
i'm looking at the example:
Control Surface: MCU-OLED-SSD1306-x2.ino (tttapa.github.io)
will i be able to interact with ucglib or do i have to use adafruit?

Control Surface uses an abstract interface to handle displays, which allows you to easily write adapters for different kinds of display libraries. However, the API of this interface was based roughly on the Adafruit_GFX library, so using an Adafruit library will most likely be easier.
You can read more in the FAQ: https://tttapa.github.io/Control-Surface-doc/new-input/Doxygen/df/df5/md_pages_FAQ.html#autotoc_md305
I've never used ucglib myself, but it shouldn't be too hard to write an adapter.
All of this only applies when you use Control Surface's Display Elements of course, in your sketch you can use whatever display library you want.