diff options
Diffstat (limited to 'backends')
| -rw-r--r-- | backends/jack.c | 99 | ||||
| -rw-r--r-- | backends/jack.h | 9 | ||||
| -rw-r--r-- | backends/jack.md | 7 | 
3 files changed, 86 insertions, 29 deletions
| diff --git a/backends/jack.c b/backends/jack.c index c84ed0f..a3caf73 100644 --- a/backends/jack.c +++ b/backends/jack.c @@ -18,8 +18,6 @@  	#endif  #endif -//FIXME pitchbend range is somewhat oob -  static struct /*_mmjack_backend_cfg*/ {  	unsigned verbosity;  	volatile sig_atomic_t jack_shutdown; @@ -80,18 +78,42 @@ static int mmjack_midiqueue_append(mmjack_port* port, mmjack_channel_ident ident  	return 0;  } +static void mmjack_process_midiout(void* buffer, size_t sample_offset, uint8_t type, uint8_t channel, uint8_t control, uint16_t value){ +	jack_midi_data_t* event_data = jack_midi_event_reserve(buffer, sample_offset, (type == midi_aftertouch) ? 2 : 3); + +	if(!event_data){ +		LOG("Failed to reserve MIDI stream data"); +		return; +	} + +	//build midi event +	event_data[0] = channel | type; +	event_data[1] = control & 0x7F; +	event_data[2] = value & 0x7F; + +	if(type == midi_pitchbend){ +		event_data[1] = value & 0x7F; +		event_data[2] = (value >> 7) & 0x7F; +	} +	else if(type == midi_aftertouch){ +		event_data[1] = value & 0x7F; +		event_data[2] = 0; +	} +} +  static int mmjack_process_midi(instance* inst, mmjack_port* port, size_t nframes, size_t* mark){ +	mmjack_instance_data* data = (mmjack_instance_data*) inst->impl;  	void* buffer = jack_port_get_buffer(port->port, nframes);  	jack_nframes_t event_count = jack_midi_get_event_count(buffer);  	jack_midi_event_t event; -	jack_midi_data_t* event_data;  	mmjack_channel_ident ident; -	size_t u; +	size_t u, frame;  	uint16_t value;  	if(port->input){  		if(event_count){  			DBGPF("Reading %u MIDI events from port %s", event_count, port->name); +			//TODO (n)rpn RX  			for(u = 0; u < event_count; u++){  				ident.label = 0;  				//read midi data from stream @@ -124,30 +146,33 @@ static int mmjack_process_midi(instance* inst, mmjack_port* port, size_t nframes  		//clear buffer  		jack_midi_clear_buffer(buffer); +		frame = 0;  		for(u = 0; u < port->queue_len; u++){ -			//build midi event  			ident.label = port->queue[u].ident.label; -			event_data = jack_midi_event_reserve(buffer, u, (ident.fields.sub_type == midi_aftertouch) ? 2 : 3); -			if(!event_data){ -				LOG("Failed to reserve MIDI stream data"); -				return 1; -			} -			event_data[0] = ident.fields.sub_channel | ident.fields.sub_type; -			if(ident.fields.sub_type == midi_pitchbend){ -				event_data[1] = port->queue[u].raw & 0x7F; -				event_data[2] = (port->queue[u].raw >> 7) & 0x7F; -			} -			else if(ident.fields.sub_type == midi_aftertouch){ -				event_data[1] = port->queue[u].raw & 0x7F; + +			if(ident.fields.sub_type == midi_rpn +					|| ident.fields.sub_type == midi_nrpn){ +				//transmit parameter number +				mmjack_process_midiout(buffer, frame++, midi_cc, ident.fields.sub_channel, (ident.fields.sub_type == midi_rpn) ? 101 : 99, (ident.fields.sub_control >> 7) & 0x7F); +				mmjack_process_midiout(buffer, frame++, midi_cc, ident.fields.sub_channel, (ident.fields.sub_type == midi_rpn) ? 100 : 98, ident.fields.sub_control & 0x7F); + +				//transmit parameter value +				mmjack_process_midiout(buffer, frame++, midi_cc, ident.fields.sub_channel, 6, (port->queue[u].raw >> 7) & 0x7F); +				mmjack_process_midiout(buffer, frame++, midi_cc, ident.fields.sub_channel, 38, port->queue[u].raw & 0x7F); + +				if(!data->midi_epn_tx_short){ +					//clear active parameter +					mmjack_process_midiout(buffer, frame++, midi_cc, ident.fields.sub_channel, 101, 127); +					mmjack_process_midiout(buffer, frame++, midi_cc, ident.fields.sub_channel, 100, 127); +				}  			}  			else{ -				event_data[1] = ident.fields.sub_control; -				event_data[2] = port->queue[u].raw & 0x7F; +				mmjack_process_midiout(buffer, frame++, ident.fields.sub_type, ident.fields.sub_channel, ident.fields.sub_control, port->queue[u].raw);  			}  		} -		if(port->queue_len){ -			DBGPF("Wrote %" PRIsize_t " MIDI events to port %s", port->queue_len, port->name); +		if(frame){ +			DBGPF("Wrote %" PRIsize_t " MIDI events to port %s", frame, port->name);  		}  		port->queue_len = 0;  	} @@ -305,6 +330,13 @@ static int mmjack_configure_instance(instance* inst, char* option, char* value){  		data->server_name = strdup(value);  		return 0;  	} +	else if(!strcmp(option, "epn-tx")){ +		data->midi_epn_tx_short = 0; +		if(!strcmp(value, "short")){ +			data->midi_epn_tx_short = 1; +		} +		return 0; +	}  	//register new port, first check for unique name  	for(p = 0; p < data->ports; p++){ @@ -385,6 +417,14 @@ static int mmjack_parse_midispec(mmjack_channel_ident* ident, char* spec){  		ident->fields.sub_type = midi_pressure;  		next_token += 8;  	} +	else if(!strncmp(next_token, "rpn", 3)){ +		ident->fields.sub_type = midi_rpn; +		next_token += 3; +	} +	else if(!strncmp(next_token, "nrpn", 4)){ +		ident->fields.sub_type = midi_nrpn; +		next_token += 4; +	}  	else if(!strncmp(next_token, "pitch", 5)){  		ident->fields.sub_type = midi_pitchbend;  	} @@ -399,7 +439,9 @@ static int mmjack_parse_midispec(mmjack_channel_ident* ident, char* spec){  	ident->fields.sub_control = strtoul(next_token, NULL, 10);  	if(ident->fields.sub_type == midi_none -			|| ident->fields.sub_control > 127){ +			|| (ident->fields.sub_type != midi_nrpn +				&& ident->fields.sub_type != midi_rpn +				&& ident->fields.sub_control > 127)){  		LOGPF("Invalid MIDI spec %s", spec);  		return 1;  	} @@ -467,9 +509,12 @@ static int mmjack_set(instance* inst, size_t num, channel** c, channel_value* v)  				break;  			case port_midi:  				value = v[u].normalised * 127.0; -				if(ident.fields.sub_type == midi_pitchbend){ -					value = ((uint16_t)(v[u].normalised * 16384.0)); +				if(ident.fields.sub_type == midi_pitchbend +						|| ident.fields.sub_type == midi_nrpn +						|| ident.fields.sub_type == midi_rpn){ +					value = ((uint16_t)(v[u].normalised * 16383.0));  				} +  				if(mmjack_midiqueue_append(data->port + ident.fields.port, ident, value)){  					pthread_mutex_unlock(&data->port[ident.fields.port].lock);  					return 1; @@ -494,8 +539,10 @@ static void mmjack_handle_midi(instance* inst, size_t index, mmjack_port* port){  		port->queue[u].ident.fields.port = index;  		chan = mm_channel(inst, port->queue[u].ident.label, 0);  		if(chan){ -			if(port->queue[u].ident.fields.sub_type == midi_pitchbend){ -				val.normalised = ((double)port->queue[u].raw) / 16384.0; +			if(port->queue[u].ident.fields.sub_type == midi_pitchbend +					|| port->queue[u].ident.fields.sub_type == midi_rpn +					|| port->queue[u].ident.fields.sub_type == midi_nrpn){ +				val.normalised = ((double)port->queue[u].raw) / 16383.0;  			}  			else{  				val.normalised = ((double)port->queue[u].raw) / 127.0; diff --git a/backends/jack.h b/backends/jack.h index 03ce052..ca62ea5 100644 --- a/backends/jack.h +++ b/backends/jack.h @@ -22,16 +22,17 @@ enum /*mmjack_midi_channel_type*/ {  	midi_cc = 0xB0,  	midi_pressure = 0xA0,  	midi_aftertouch = 0xD0, -	midi_pitchbend = 0xE0 +	midi_pitchbend = 0xE0, +	midi_rpn = 0xF0, +	midi_nrpn = 0xF1  };  typedef union {  	struct {  		uint32_t port; -		uint8_t pad;  		uint8_t sub_type;  		uint8_t sub_channel; -		uint8_t sub_control; +		uint16_t sub_control;  	} fields;  	uint64_t label;  } mmjack_channel_ident; @@ -70,6 +71,8 @@ typedef struct /*_jack_instance_data*/ {  	char* client_name;  	int fd; +	uint8_t midi_epn_tx_short; +  	jack_client_t* client;  	size_t ports;  	mmjack_port* port; diff --git a/backends/jack.md b/backends/jack.md index b6ff5a9..3d426f3 100644 --- a/backends/jack.md +++ b/backends/jack.md @@ -16,6 +16,7 @@ transport of control data via either JACK midi ports or control voltage (CV) inp  |---------------|-----------------------|-----------------------|-----------------------|  | `name`	| `Controller`		| `MIDIMonster`		| Client name for the JACK connection |  | `server`	| `jackserver`		| `default`		| JACK server identifier to connect to | +| `epn-tx`	| `short`		| `full`		| Configure whether to clear the active parameter number after transmitting a MIDI `nrpn` or `rpn` parameter. |  Channels (corresponding to JACK ports) need to be configured with their type and, if applicable, value limits.  To configure a port, specify it in the instance configuration using the following syntax: @@ -65,6 +66,8 @@ The following values are recognized for `type`:  * `pressure` - Note pressure/aftertouch messages  * `aftertouch` - Channel-wide aftertouch messages  * `pitch` - Channel pitchbend messages +* `rpn` - Registered parameter numbers (14-bit extension) +* `nrpn` - Non-registered parameter numbers (14-bit extension)  The `pitch` and `aftertouch` events are channel-wide, thus they can be specified as `channel<channel>.<type>`. @@ -72,6 +75,7 @@ Example mappings:  ```  jack1.cv_in > jack1.midi_out.ch0.note3  jack1.midi_in.ch0.pitch > jack1.cv_out +jack2.midi_in.ch0.nrpn900 > jack1.midi_out.ch1.rpn1  ```  The MIDI subchannel syntax is intentionally kept compatible to the different MIDI backends also supported @@ -79,6 +83,9 @@ by the MIDIMonster  #### Known bugs / problems +Extended parameter numbers (`rpn` and `nrpn` control types) can currently only be transmitted, not properly +received as such. Support for this functionality is planned. +  While JACK has rudimentary capabilities for transporting OSC messages, configuring and parsing such channels  with this backend would take a great amount of dedicated syntax & code. CV ports can provide fine-grained single  control channels as an alternative to MIDI. This feature may be implemented at some point in the future. | 
