Search code examples
c++infinite-loopmidijack

My MIDI JACK messages are sended from the port buffer AGAIN AND AGAIN


these are my specific Jack MIDI lines:

const char* client_name {"Commander"};
jack_client_t* client {NULL};
jack_port_t* output_port {NULL};
jack_nframes_t local_nframes = {0};

int process(jack_nframes_t nframes, [[maybe_unused]]void* arg) {/*{{{*/
    local_nframes = nframes;
    return 0;
}/*}}}*/

void Keyboard::connect() noexcept{/*{{{*/
    // Open a connection to the JACK server
    client = jack_client_open(client_name, JackNullOption, NULL);
    if (client == NULL) {
        std::cerr << "Failed to open " << client_name << "client at Keyboard::connect()\n";
        std::exit(EXIT_FAILURE);
    }

    // Create a MIDI output port
    output_port = jack_port_register(client, "output", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
    if (output_port == NULL) {
        // Failed to create output port
        jack_client_close(client);
        std::cerr << "Failed to open " << client_name << "port at Keyboard::connect()\n";
        std::exit(EXIT_FAILURE);
    }

    // Set the process callback function
    jack_set_process_callback(client, process, 0);

    // Activate the client
    if (jack_activate(client) != 0) {
        // Failed to activate client
        jack_port_unregister(client, output_port);
        jack_client_close(client);
        std::cerr << "Failed to activate " << client_name << "client at Keyboard::connect()\n";
        std::exit(EXIT_FAILURE);
    }

    MIDI = Switch::ON;
}/*}}}*/

void Keyboard::dump_performance(const Performance& _Performance) noexcept/*{{{*/
{
    set_performance_buffer(_Performance); // Pass to the buffer

    // Sleep
    static struct timespec keyboardTimer;
    keyboardTimer.tv_sec = 0;
    keyboardTimer.tv_nsec = 200000000;

    // Change page
    [[maybe_unused]]jack_midi_data_t combiSysEx[7] = {0xF0, 0x42, 0x30, 0x7A, 0x4E, 0x00, 0xF7};
    [[maybe_unused]]jack_midi_data_t timbreSysEx[7] = {0xF0, 0x42, 0x30, 0x7A, 0x4E, 0x01, 0xF7};

    // MIDI Ch = msb[0], lsb[0], pc[0] are the same, only the last digit changes
    [[maybe_unused]]jack_midi_data_t msb[3] = {0xB0, 0x00, 0x3F};
    [[maybe_unused]]jack_midi_data_t lsb[3] = {0xB0, 0x20, 0x00};
    [[maybe_unused]]jack_midi_data_t pc[2] = {0xC0, 0x00};

    // Adjustment
    lsb[2] = performance_buffer.patch.bnk - 65; /*LSB*/
    pc[1] = performance_buffer.patch.num; /*PC*/

    // Retrieve the output port buffer
    void* port_buffer = jack_port_get_buffer(output_port, local_nframes);

    // Clear the port buffer before writing MIDI events
    jack_midi_clear_buffer(port_buffer);

    // Send the MIDI messages to the output port
    if (output_port != nullptr) {
        jack_midi_event_write(port_buffer, 0, combiSysEx,   sizeof(combiSysEx));
        jack_midi_event_write(port_buffer, 0, timbreSysEx,  sizeof(timbreSysEx));
        jack_midi_event_write(port_buffer, 0, msb,          sizeof(msb));
        jack_midi_event_write(port_buffer, 0, lsb,          sizeof(lsb));
        jack_midi_event_write(port_buffer, 0, pc,           sizeof(pc));
    }

    // Sleep
    nanosleep(&keyboardTimer, NULL);

    set_scene(performance_buffer.default_scene);
    //dump_scene();
}/*}}}*/

void Keyboard::disconnect() noexcept {/*{{{*/
    // Deactivate, unregister, and close the client
    jack_deactivate(client);
    jack_port_unregister(client, output_port);
    jack_client_close(client);

    // Set MIDI switch to OFF or perform any necessary cleanup
    MIDI = Switch::OFF;
}/*}}}*/

When the first jack_midi_event_write() function from Keyboard::dump_performance() is called, it correctly send the message but infinite times. There is no way to stop. Even if i disconnect and reconnect to the other client. I have to disconnect my client.

There is no loop anywhere in my program. In fact the each message is repeated indefinedly and the program continues without problem. Is like the port_buffer continues sending all his received messages.

My client connected to REAPER

REAPER receiving again and again the same messages

How could I solve it?

  • Recorded the output of my program into Reaper and notice the brutal looping of the same messages.
  • Try to send just one message

Nothing happens


Solution

  • In fact, it was a misunderstood of the JACK mechanism and the behaviour of the Jack callback functions.

    The process() function is designed to containt all the data input and output messages. Also, it works asynchroniusly from the execution of the program (runs independently our program workflow), and at last but not least: the output port buffer works as an independent antena that will send again and again his received messages. So there are the key conepts.

    The solution? Put all the jack_midi_event_write() calls inside the acurate callback functions and ensure with flags that the messages will be called by jack just once. So the creation of boolean flags that set true when we want the action and false inside the callback function is a good approach. Also remember, let's declare this flag variables as volatile

    // GENERIC MESSAGES{{{
    jack_midi_data_t to_combi_SysEx[] {0xF0, 0x42, 0x30, 0x7A, 0x4E, 0x00, 0xF7};
    jack_midi_data_t to_timbre_SysEx[]{0xF0, 0x42, 0x30, 0x7A, 0x4E, 0x01, 0xF7};
    struct PatchChangeT {
        jack_midi_data_t msb[3] {0xB0, 0x00, 0x3F};
        jack_midi_data_t lsb[3] {0xB0, 0x20, 0x00};
        jack_midi_data_t pc[2]  {0xC0, 0x00};
    } PatchChange;/*}}}*/
    
    // Client {{{
    const char* client_name {"Commander"};
    jack_client_t* client {NULL};
    jack_port_t* output_port {NULL};
    jack_nframes_t local_nframes = {0};/*}}}*/
    
    // Control{{{
    volatile bool should_send_performance {FALSE};/*}}}*/
    
    int process([[maybe_unused]]jack_nframes_t nframes, [[maybe_unused]]void* arg)/*{{{*/
    {
        void* port_buffer = jack_port_get_buffer(output_port, nframes);
        jack_midi_clear_buffer(port_buffer);
    
        if (should_send_performance) {
            jack_midi_event_write(port_buffer, 0, to_combi_SysEx,  sizeof(to_combi_SysEx));
            jack_midi_event_write(port_buffer, 0, to_timbre_SysEx, sizeof(to_timbre_SysEx));
            jack_midi_event_write(port_buffer, 0, PatchChange.msb, sizeof(PatchChange.msb));
            jack_midi_event_write(port_buffer, 0, PatchChange.lsb, sizeof(PatchChange.lsb));
            jack_midi_event_write(port_buffer, 0, PatchChange.pc,  sizeof(PatchChange.pc));
    
            should_send_performance = false;
        }
        return 0;
    }/*}}}*/
    
    void Keyboard::connect() noexcept {/*{{{*/
        // Connect to the JACK server
        if ((client = jack_client_open(client_name, JackNullOption, NULL)) == NULL) {
            std::cerr << "Failed to open JACK client " << client_name << " at Keyboard::connect()\n";
            std::exit(EXIT_FAILURE); 
        }
    
        // Register the process callback
        jack_set_process_callback(client, process, 0);
    
        // Create the MIDI output port
        if ((output_port = 
                jack_port_register(client, "midi_out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0)) == NULL) {
            std::cerr << "Failed to register JACK port midi_out at Keyboard::connect()\n";
            std::exit(EXIT_FAILURE); 
        }
    
        // Activate the client
        if (jack_activate(client)) {
            std::cerr << "Failed to activate JACK client " << client_name << " at Keyboard::connect()\n";
            std::exit(EXIT_FAILURE); 
        }
    
        MIDI = Switch::ON;
    }
    /*}}}*/
    
    void Keyboard::dump_performance(const Performance& _Performance) noexcept {/*{{{*/
        // SET BUFFER
        set_performance_buffer(_Performance); // Pass to the buffer
    
        // SLEEP
        static struct timespec keyboardTimer;
        keyboardTimer.tv_sec = 0;
        keyboardTimer.tv_nsec = 200000000;
    
        // ADJUST MESSAGES
        PatchChange.lsb[2] = performance_buffer.patch.bnk - 65;
        PatchChange.pc[1] = performance_buffer.patch.num;
    
        should_send_performance = true;
    }/*}}}*/
    
    void Keyboard::disconnect() noexcept {/*{{{*/
        // Deactivate, unregister, and close the client
        jack_deactivate(client);
        jack_port_unregister(client, output_port);
        jack_client_close(client);
    
        // Set MIDI switch to OFF or perform any necessary cleanup
        MIDI = Switch::OFF;
    }/*}}}*/
    

    Thank you very much