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.
REAPER receiving again and again the same messages
How could I solve it?
Nothing happens
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