probonopd / MiniDexed

Dexed FM synthesizer similar to 8x DX7 (TX816/TX802) running on a bare metal Raspberry Pi (without a Linux kernel or operating system)
https://github.com/probonopd/MiniDexed/wiki
1.09k stars 80 forks source link

Use buttons on MIDI controllers instead of RPi GPIO buttons #316

Closed probonopd closed 1 year ago

probonopd commented 2 years ago

It would be nice if we could use buttons on MIDI controllers instead of GPIO buttons on the Raspberry Pi. This way, one would not have to deal with connecting physical buttons to Raspberry Pi pins but could use the buttons built into many MIDI controllers.

E.g., these guys:

image

(Example shows the Nektar Impact LX61+.)

The buttons toggle between 0 and 127: B0 xx 7F (key down), B0 xx 00 (key up) with xx being different for each button.

Maybe we could have in minidexed.ini something like this:

MidiButtonPrev=0xB001
MidiButtonNext=0xB002
MidiButtonBack=0xB003
MidiButtonSelect=0xB004
MidiButtonHome=0xB005
MidiButtonShortcut=0xB006

Value 0x7F means "button down", value 0x00 means "button up".

@diyelectromusic and @stevelittlefish do you think this could be wired into the existing logic for interpreting clicks, double-clicks, and long-presses?

diyelectromusic commented 2 years ago

I'm sure something like this could be worked in, but I'm not quite sure how it would work with the long/short press timings.

One thing I've been missing when looking through the code (as was evident in the polling/interrupt question) is any picture of the design of the scheduling in MiniDexed. How did you put it all together at the top level? What is the "main loop" and what is already interrupt driven? And so on?

The trick for something like MIDI buttons will be ensuring the MIDI handling and UI code interact in a neat way and not get stuck waiting for each other, and things like that...

Ideally MIDI control would be some kind of internal triggers that map over to buttons, but the actual MIDI events that trigger them would be configurable. Some people might wish to use MIDI CC messages, but some might rather map them on to NoteOn/NoteOff events (for example) and it would be nice to be able to do something with continuous controllers for values too eventually that allows the "encoder" style menu handling too (although MIDI CC values are more like pots than encoders, but we could have a think).

stevelittlefish commented 2 years ago

I think the best way to achieve this would be to do a bit of refactoring.

At the moment uibuttons.cpp has code to handle polling the buttons, and also handling the timings. This then calls an event handler in userinterface.cpp.

If you want the buttons and the midi controls to share the same timing code, I think we would need to add another layer in the middle, so there would be a first layer that handles the polling and the midi messages, which then sends some simple messages (basically button pressed, button released) to an intermediate layer which handles the timings. This middle layer would then call the event handlers in the UI.

Alternatively, a simpler but maybe messier way to implement this would just be to have some new code to handle the midi messages with its own timing code. This would mean duplicating the timing logic in the midi handler, but would mean we wouldn't need to add that middle layer.

EDIT:

The other option is to not allow long press / double click on controller buttons. I don't know how many buttons midi controllers tend to have, but if there are normally at least 7 buttons available then you could just map everything to a separate button.

luisfcorreia commented 2 years ago

IMHO, midi handling superseeds anything else and must have their own timing logic

Button handling can be done using a very small fraction of available time slot. Maybe once per every eight midi message handling cycles.

probonopd commented 2 years ago

Maybe @rsta2 can give us some guidance/recommendations here?

diyelectromusic commented 2 years ago

As I say, I think we should try to document the current "loop" and scanning in the system to help with conversations like this. If you don't have it already to hand @probonopd then I'll take a wander through the code and see if I can work it out...

diyelectromusic commented 2 years ago

As I say, I think we should try to document the current "loop" and scanning in the system to help with conversations like this. If you don't have it already to hand @probonopd then I'll take a wander through the code and see if I can work it out...

Ok, I've had a first go at documenting how I think it all starts up and then runs. Maybe those who know more than me could take a look: https://github.com/probonopd/MiniDexed/wiki/Development

Aside: I have that graphic in an OpenOffice odp file at present - is there somewhere useful to store it?

stevelittlefish commented 2 years ago

@diyelectromusic That's very helpful, especially the bullets giving an overview of how it works. I think this also shows that on a multi-core raspberry pi the discussions we've had about polling vs interrupts and having an inconsistent running time of the polling algorithm aren't that applicable. We probably need to try to test everything on a single core pi to make sure there are no issues with stuttering!

diyelectromusic commented 2 years ago

I've started taking a look at some possibilities of having MIDI buttons. It's just experimenting at this stage, but you'll be able to track progress here: https://github.com/diyelectromusic/MiniDexed/tree/buttonmidi

diyelectromusic commented 2 years ago

I've started taking a look at some possibilities of having MIDI buttons. It's just experimenting at this stage, but you'll be able to track progress here: https://github.com/diyelectromusic/MiniDexed/tree/buttonmidi

Ok, I now have a "it seems to work for me" version on the above link... Do take a look! :)

There are some new minidexed.ini entries (see the new sample ini file) to define the MIDI CC numbers to use and the MIDI channel to listen on for the UI.

I've only assigned a single-click action to MIDI buttons. I'm not sure if it makes sense to do things like try to time a double click or long press over MIDI? But in principle I haven't ruled it out in the future, but tried to keep things relatively simple for now.

I've basically invented a pseudo-GPIO pin called a "midipin" whose status can be set from the MIDI subsystem, but then can be read just like a regular IO pin. That way most of the button handling is actually still the same (hence long press and double click could be added if we really want to try it!).

I had hoped to have my CMIDIPin inherit from the Circle CGPIOPin class, so actually almost all the button code was ignorant of if it was using a GPIO or MIDI pin, but my C++ wasn't up to the job! So it is an either/or at present, but there are only a couple of places where it matters anyway, so I'm not worrying about it! Fixing this in a nicer C++ way is left as an exercise for the more competent-than-I reader!

Note: I've only implemented buttons. I can't really see how we'd map MIDI CCs (with a 0-127 range) onto a rotary encoder, so I've not even gone there... but in theory now the MIDI devices can trigger events in the UI we could take a look if someone can suggest a good mapping...

probonopd commented 2 years ago

Ok, I've had a first go at documenting how I think it all starts up and then runs.

Thanks @diyelectromusic, this is really helpful. As for storing the OpenOffice odp, if you attach it zipped to this thread then we can link to the attachment from the Wiki page.

Ok, I now have a "it seems to work for me" version on the above link... Do take a look! :)

Cool, will do! :+1:

diyelectromusic commented 2 years ago

If anyone is interested in giving my branch that does this a go, I'd be very happy to hear what you think about it and if you think this is responsive/useful enough to move forward... :)

diyelectromusic commented 1 year ago

I think this issue is resolved now?

probonopd commented 1 year ago

Yes, thanks a lot @diyelectromusic :+1: