JACK Transport Control as MIDI Note event

What other apps and distros do you use to round out your studio?

Moderators: MattKingUSA, khz

Post Reply
User avatar
GraysonPeddie
Established Member
Posts: 659
Joined: Sun Feb 12, 2012 11:12 pm
Location: Altha, FL
Been thanked: 6 times
Contact:

JACK Transport Control as MIDI Note event

Post by GraysonPeddie »

Scenario

I am using VCV Rack as a MIDI controller. Because the "Strt" (Start), "Stop," and "Cont" (Continue) does not respond to JACK's transport controls, I need to trigger a MIDI event such as "note on" and "note off" which gets connected as "Gate" to "Run."

What I Intend To Do
  • If I hit "play" in QJackCtl's transport control, the "gate" connector in "MIDI-CV" will trigger a "Run" connector, which causes the "run" button to light up and start the clock.
  • If I hit "stop" in QJackCtl's transport control, the "gate" connector in "MIDI-CV" should trigger a "Run" connector again, which stops the clock.
In this case:
  • Pressing "start" in JACK transport should trigger a MIDI ON and MIDI OFF instruction, which causes "Gate" in MIDI-CV to light up and turn off once a note off command is sent.
  • Pressing "stop" in JACK transport should trigger the same thing as when "play" is initiated.
How about this? Press a key in a MIDI keyboard to start a clock.Press a key in a MIDI keyboard again to stop a clock.

Limitations

Note that VCV Rack does not adopt to tempo change when I hook up "Clock" in MIDI-CV to "BPM." I'm fine with that so long as the tempo stays the same throughout my experimentation.

Screenshot

https://open.lbry.com/@GraysonPeddie_Im ... U2hTCGRWup

(Image hosted in LBRY)

Question

How do I make JACK Transport play a simple MIDI note when playing and stopping? In other words, how do I get "play" to send a MIDI note on message and "stop" to send a MIDI note message?

Note

Mine does seem repetitive, but I'm trying to get my point across in my thread here. I'm trying to get everyone a clear picture of what I'm trying to do. Let me know if anyone needs my point/question rephrased.
--Grayson Peddie

Music Interest: New Age w/ a mix of modern smooth jazz, light techno/trance & downtempo -- something Epcot Future World/Tomorrowland-flavored.
tramp
Established Member
Posts: 2348
Joined: Mon Jul 01, 2013 8:13 am
Has thanked: 9 times
Been thanked: 468 times

Re: JACK Transport Control as MIDI Note event

Post by tramp »

GraysonPeddie wrote: Wed May 27, 2020 11:07 pm How do I make JACK Transport play a simple MIDI note when playing and stopping?
You need to write a wrapper for this.
I do that in guitarix by monitor the transport state and send Midi CC's to the engine when the state change.
On the road again.
tramp
Established Member
Posts: 2348
Joined: Mon Jul 01, 2013 8:13 am
Has thanked: 9 times
Been thanked: 468 times

Re: JACK Transport Control as MIDI Note event

Post by tramp »

here you go:

Code: Select all

/*   gcc -Wall jack_trans2midi.c -lm `pkg-config --cflags --libs jack` -o trans2midi */

#include <jack/jack.h>
#include <jack/midiport.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

#define min(x, y) (((x) < (y)) ? (x) : (y))

jack_client_t *client;
jack_port_t *output_port;

unsigned int ts;

static void signal_handler(int sig)
{
    jack_client_close(client);
    fprintf(stderr, "signal received, exiting ...\n");
    exit(0);
}

static int process(jack_nframes_t nframes, void *arg)
{
    jack_transport_state_t transport_state = jack_transport_query(client, NULL);
    int state_changed = 0;

    if (ts != transport_state) {
        ts = min(1,transport_state);
        state_changed = 1;
    }

    void* port_buf = jack_port_get_buffer(output_port, nframes);
    unsigned char* buffer;
    jack_midi_clear_buffer(port_buf);

    if (state_changed && ts ) {
        buffer = jack_midi_event_reserve(port_buf, 0, 3);
        buffer[2] = 0;        /* velocity */
        buffer[1] = 60;        /* note C4*/
        buffer[0] = 0x90;      /* note on */ 

    } else if (state_changed && !ts) {
        buffer = jack_midi_event_reserve(port_buf, 0, 3);
        buffer[2] = 0;        /* velocity */
        buffer[1] = 60;        /* note C4*/
        buffer[0] = 0x80;      /* note off */

    }
    return 0;
}

int main(int narg, char **args)
{
    ts = 0;

    if((client = jack_client_open ("trans2midi", JackNullOption, NULL)) == 0)
    {
        fprintf (stderr, "JACK server not running?\n");
        return 1;
    }
    jack_set_process_callback (client, process, 0);
    output_port = jack_port_register (client, "out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);

    if (jack_activate(client))
    {
        fprintf (stderr, "cannot activate client");
        return 1;
    }

    /* install a signal handler to properly quits jack client */
    signal(SIGTERM, signal_handler);
    signal(SIGINT, signal_handler);

    /* run until interrupted  ctrl-c*/
    sleep(-1);

    jack_client_close(client);
    exit (0);
}
On the road again.
studio32

Re: JACK Transport Control as MIDI Note event

Post by studio32 »

something
Last edited by studio32 on Mon Jul 27, 2020 4:24 am, edited 1 time in total.
User avatar
GraysonPeddie
Established Member
Posts: 659
Joined: Sun Feb 12, 2012 11:12 pm
Location: Altha, FL
Been thanked: 6 times
Contact:

Re: JACK Transport Control as MIDI Note event

Post by GraysonPeddie »

studio32 wrote: Thu May 28, 2020 9:12 am Without diving too deep into this use case.... Maybe Ableton Link can help you here?
https://cdm.link/2018/01/make-free-vcv- ... eton-link/
https://github.com/rncbc/jack_link
It does not look like the Link/Link v2 module has a "run" connector for VCV Rack that allows me to connect to the Impromptu Clock mule's "Run" connector. The "clock" light is always blinking. Also, for "Link v2," the "reset" light always flashes per measure, such as every 4 beats in a bar. That causes the maze modules to always start from the beginning every 4 beat, so that's not going to work. The "reset" connector is only useful if a stop command is initiated. Anyway, I will give it a try along with tramp's code. Thanks.

Update:

@tramp, I tried your code and even though it works, it does not look like your code is sending a "note off" message. My current behavior is as follows:
  1. Press play in MUSE Sequencer and both VCV Rack and QMidiArp plays. The "gate" connector in MIDI-CV lights up.
  2. Press stop (same sequencer) and even though QMidiArp stops, VCV Rack keeps playing.
  3. Press play again and VCV Rack stops playing but QMidiArp plays. The "gate" connector in MIDI-CV turns off.
  4. Press stop again and both VCV Rack and QMidiArp stops playing.
So, I came up with this:

Code: Select all

#include <jack/jack.h>
#include <jack/midiport.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

#define min(x, y) (((x) < (y)) ? (x) : (y))

jack_client_t *client;
jack_port_t *output_port;

unsigned int ts;
int send_note_off = 0;

static void signal_handler(int sig)
{
    jack_client_close(client);
    fprintf(stderr, "signal received, exiting ...\n");
    exit(0);
}

static int process(jack_nframes_t nframes, void *arg)
{
    jack_transport_state_t transport_state = jack_transport_query(client, NULL);
    int state_changed = 0;

    if (ts != transport_state) {
        ts = min(1,transport_state);
        state_changed = 1;
    }

    void* port_buf = jack_port_get_buffer(output_port, nframes);
    unsigned char* buffer;
    jack_midi_clear_buffer(port_buf);

    // Send a "note on" message once a state has changed.
    // The "note on" message will not be sent again unless the
    // "send_note_off" variable is set to 0.
    if (state_changed && send_note_off == 0 ) {
        fprintf (stderr, "Sending note on...\n");
        buffer = jack_midi_event_reserve(port_buf, 0, 3);
        buffer[2] = 1;        /* velocity */
        buffer[1] = 60;        /* note C4*/
        buffer[0] = 0x90;      /* note on */ 
        send_note_off = 1;
    }
    // After the note on process is complete, send the "note off" message.
    else if(send_note_off == 1) {
        fprintf (stderr, "Sending note off...\n");
        buffer = jack_midi_event_reserve(port_buf, 0, 3);
        buffer[2] = 1;        /* velocity */
        buffer[1] = 60;        /* note C4*/
        buffer[0] = 0x80;      /* note off */
        send_note_off = 0;
    }
    //else if ((state_changed && !ts) && note_off_process_complete == 0 )  {
    //    fprintf (stderr, "Sending note off...\n");
    //    buffer = jack_midi_event_reserve(port_buf, 0, 3);
    //    buffer[2] = 1;        /* velocity */
    //    buffer[1] = 60;        /* note C4*/
    //    buffer[0] = 0x80;      /* note off */
    //    note_on_process_complete = 0;
    //    note_off_process_complete = 1;
    //}
    return 0;
}

int main(int narg, char **args)
{
    ts = 0;

    if((client = jack_client_open ("trans2midi", JackNullOption, NULL)) == 0)
    {
        fprintf (stderr, "JACK server not running?\n");
        return 1;
    }
    jack_set_process_callback (client, process, 0);
    output_port = jack_port_register (client, "out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);

    if (jack_activate(client))
    {
        fprintf (stderr, "cannot activate client");
        return 1;
    }

    /* install a signal handler to properly quits jack client */
    signal(SIGTERM, signal_handler);
    signal(SIGINT, signal_handler);

    /* run until interrupted  ctrl-c*/
    sleep(-1);

    jack_client_close(client);
    exit (0);
}
The "play" and "stop" buttons sent a MIDI note on and MIDI note off message to the client which triggers the gate connector twice and it works.

Because I don't qualify as a C++/JACK developer, I don't understand all the stuff that's in the code, but at least I do understand how MIDI messages are sent using a buffer, such as velocity, note number, and note on/off messages. And of course, I do have JACK Link for BPM and it works fine.
--Grayson Peddie

Music Interest: New Age w/ a mix of modern smooth jazz, light techno/trance & downtempo -- something Epcot Future World/Tomorrowland-flavored.
tramp
Established Member
Posts: 2348
Joined: Mon Jul 01, 2013 8:13 am
Has thanked: 9 times
Been thanked: 468 times

Re: JACK Transport Control as MIDI Note event

Post by tramp »

GraysonPeddie wrote: Thu May 28, 2020 3:11 pm @tramp, I tried your code and even though it works, it does not look like your code is sending a "note off" message. My current behavior is as follows:
Oh, my mistake, I understand you wrong. I've assumed that you would have a NotOn event on transport start and a NoteOff event and transport stop.
I didn't used VCV rack so I don't know what it needs to trigger the gate.
However, I'm glad you find your solution.
On the road again.
User avatar
GraysonPeddie
Established Member
Posts: 659
Joined: Sun Feb 12, 2012 11:12 pm
Location: Altha, FL
Been thanked: 6 times
Contact:

Re: JACK Transport Control as MIDI Note event

Post by GraysonPeddie »

No problem. And thank you for the code.
--Grayson Peddie

Music Interest: New Age w/ a mix of modern smooth jazz, light techno/trance & downtempo -- something Epcot Future World/Tomorrowland-flavored.
Post Reply