Index: jack_host.c =================================================================== --- jack_host.c (revision 195) +++ jack_host.c (working copy) @@ -21,16 +22,28 @@ #include #include #include +#include +#include "lv2-miditype.h" +#define LV2MIDI_BUFFER_SIZE 8192 +#define PORT_TYPE_JACK_AUDIO_IN 0 +#define PORT_TYPE_JACK_AUDIO_OUT 1 +#define PORT_TYPE_JACK_MIDI_IN 2 +#define PORT_TYPE_JACK_MIDI_OUT 3 +#define PORT_TYPE_CONTROL_IN 4 +#define PORT_TYPE_CONTROL_OUT 5 + /** This program's data */ struct JackHost { jack_client_t* jack_client; /**< Jack client */ SLV2Plugin* plugin; /**< Plugin "class" (actually just a few strings) */ SLV2Instance* instance; /**< Plugin "instance" (loaded shared lib) */ uint32_t num_ports; /**< Size of the two following arrays: */ - jack_port_t** jack_ports; /**< For audio ports, otherwise NULL */ + int* port_types; /**< port types */ + jack_port_t** jack_ports; /**< For jack ports, otherwise NULL */ float* controls; /**< For control ports, otherwise 0.0f */ + LV2_MIDI* midi_ports; /**< For jack ports, otherwise NULL */ }; @@ -97,8 +110,10 @@ /* Create ports */ host.num_ports = slv2_plugin_get_num_ports(host.plugin); + host.port_types = calloc((size_t)host.num_ports, sizeof(int)); host.jack_ports = calloc((size_t)host.num_ports, sizeof(jack_port_t*)); host.controls = calloc((size_t)host.num_ports, sizeof(float*)); + host.midi_ports = calloc(host.num_ports, sizeof(LV2_MIDI)); for (uint32_t i=0; i < host.num_ports; ++i) create_port(&host, i); @@ -112,6 +127,8 @@ getc(stdin); printf("\n"); + jack_deactivate(host.jack_client); + /* Deactivate plugin and JACK */ slv2_instance_free(host.instance); slv2_list_free(plugins); @@ -149,10 +166,19 @@ create_port(struct JackHost* host, uint32_t port_index) { - /* Make sure this is a float port */ + int midiport; + + /* Get the 'class' of the port (control input, audio output, etc) */ + enum SLV2PortClass class = slv2_port_get_class(host->plugin, port_index); + char* type = slv2_port_get_data_type(host->plugin, port_index); - if (strcmp(type, SLV2_DATA_TYPE_FLOAT)) + if (strcmp(type, SLV2_DATA_TYPE_FLOAT) == 0) { + midiport = 0; + } else if (strcmp(type, SLV2_DATA_TYPE_MIDI) == 0) { + midiport = 1; + } else { die("Unrecognized data type, aborting."); + } free(type); /* Get the port symbol (label) for console printing */ @@ -162,24 +188,41 @@ host->jack_ports[port_index] = NULL; host->controls[port_index] = 0.0f; - /* Get the 'class' of the port (control input, audio output, etc) */ - enum SLV2PortClass class = slv2_port_get_class(host->plugin, port_index); - /* Connect the port based on it's 'class' */ switch (class) { case SLV2_CONTROL_RATE_INPUT: - host->controls[port_index] = slv2_port_get_default_value(host->plugin, port_index); - slv2_instance_connect_port(host->instance, port_index, &host->controls[port_index]); - printf("Set %s to %f\n", symbol, host->controls[port_index]); + if (midiport) { + host->port_types[port_index] = PORT_TYPE_JACK_MIDI_IN; + host->jack_ports[port_index] = jack_port_register(host->jack_client, + symbol, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); + host->midi_ports[port_index].capacity = LV2MIDI_BUFFER_SIZE; + host->midi_ports[port_index].data = malloc(LV2MIDI_BUFFER_SIZE); + } else { + host->port_types[port_index] = PORT_TYPE_CONTROL_IN; + host->controls[port_index] = slv2_port_get_default_value(host->plugin, port_index); + slv2_instance_connect_port(host->instance, port_index, &host->controls[port_index]); + printf("Set %s to %f\n", symbol, host->controls[port_index]); + } break; case SLV2_CONTROL_RATE_OUTPUT: - slv2_instance_connect_port(host->instance, port_index, &host->controls[port_index]); + if (midiport) { + host->port_types[port_index] = PORT_TYPE_JACK_MIDI_OUT; + host->jack_ports[port_index] = jack_port_register(host->jack_client, + symbol, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); + host->midi_ports[port_index].capacity = LV2MIDI_BUFFER_SIZE; + host->midi_ports[port_index].data = malloc(LV2MIDI_BUFFER_SIZE); + } else { + host->port_types[port_index] = PORT_TYPE_CONTROL_OUT; + slv2_instance_connect_port(host->instance, port_index, &host->controls[port_index]); + } break; case SLV2_AUDIO_RATE_INPUT: + host->port_types[port_index] = PORT_TYPE_JACK_AUDIO_IN; host->jack_ports[port_index] = jack_port_register(host->jack_client, symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); break; case SLV2_AUDIO_RATE_OUTPUT: + host->port_types[port_index] = PORT_TYPE_JACK_AUDIO_OUT; host->jack_ports[port_index] = jack_port_register(host->jack_client, symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); break; @@ -190,22 +233,106 @@ free(symbol); } +/** Translate from an LV2 MIDI buffer to a JACK MIDI buffer. */ +void lv2midi2jackmidi(LV2_MIDI* input_buf, jack_port_t* jack_port, + jack_nframes_t nframes) { + jack_nframes_t timestamp; + size_t data_size; + void* output_buf; + unsigned char* data; + output_buf = jack_port_get_buffer(jack_port, nframes); + + jack_midi_clear_buffer(output_buf, nframes); + + /* iterate over all MIDI events and write them to the JACK port */ + data = input_buf->data; + + for (size_t i = 0; i < input_buf->event_count; ++i) { + /* retrieve LV2 MIDI event */ + timestamp = *((double*)data); + data += sizeof(double); + data_size = *((size_t*)data); + data += sizeof(size_t); + + /* write JACK MIDI event */ + jack_midi_event_write(output_buf, timestamp, + (jack_midi_data_t*)data, + data_size, nframes); + data += data_size; + } +} + + +/** Translate from a JACK MIDI buffer to an LV2 MIDI buffer. */ +void jackmidi2lv2midi(jack_port_t* jack_port, LV2_MIDI* output_buf, + jack_nframes_t nframes) { + static unsigned bank = 0; + + void* input_buf = jack_port_get_buffer(jack_port, nframes); + jack_midi_event_t input_event; + jack_nframes_t input_event_index = 0; + jack_nframes_t input_event_count = + jack_midi_port_get_info(input_buf, nframes)->event_count; + jack_nframes_t timestamp; + output_buf->event_count = 0; + + /* iterate over all incoming JACK MIDI events */ + unsigned char* data = output_buf->data; + for (unsigned int i = 0; i < input_event_count; ++i) { + /* retrieve JACK MIDI event */ + jack_midi_event_get(&input_event, input_buf, i, nframes); + if ((data - output_buf->data) + sizeof(double) + + sizeof(size_t) + input_event.size >= output_buf->capacity) + break; + + // write LV2 MIDI event + *((double*)data) = input_event.time; + data += sizeof(double); + *((size_t*)data) = input_event.size; + data += sizeof(size_t); + memcpy(data, input_event.buffer, input_event.size); + + // normalise note events if needed + if ((input_event.size == 3) && ((data[0] & 0xF0) == 0x90) && + (data[2] == 0)) + data[0] = 0x80 | (data[0] & 0x0F); + + data += input_event.size; + ++output_buf->event_count; + } + + output_buf->size = data - output_buf->data; +} + /** Jack process callback. */ int jack_process_cb(jack_nframes_t nframes, void* data) { + uint32_t i; struct JackHost* host = (struct JackHost*)data; /* Connect plugin ports directly to JACK buffers */ - for (uint32_t i=0; i < host->num_ports; ++i) - if (host->jack_ports[i] != NULL) + for (i=0; i < host->num_ports; ++i) + if (host->port_types[i] == PORT_TYPE_JACK_MIDI_IN) { + jackmidi2lv2midi(host->jack_ports[i], host->midi_ports + i, nframes); + slv2_instance_connect_port(host->instance, i, host->midi_ports + i); + } else if (host->port_types[i] == PORT_TYPE_JACK_MIDI_OUT) { + slv2_instance_connect_port(host->instance, i, host->midi_ports + i); + } else if (host->port_types[i] == PORT_TYPE_JACK_AUDIO_IN || + host->port_types[i] == PORT_TYPE_JACK_AUDIO_OUT) { slv2_instance_connect_port(host->instance, i, jack_port_get_buffer(host->jack_ports[i], nframes)); + } /* Run plugin for this cycle */ slv2_instance_run(host->instance, nframes); + for (i=0; i < host->num_ports; ++i) + if (host->port_types[i] == PORT_TYPE_JACK_MIDI_OUT) { + lv2midi2jackmidi(host->midi_ports + i, host->jack_ports[i], nframes); + } + return 0; }