aboutsummaryrefslogtreecommitdiffhomepage
path: root/backends
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2021-01-10 17:30:12 +0100
committercbdev <cb@cbcdn.com>2021-01-10 17:30:12 +0100
commita3a893f6b8b6c10ff281fcdfe0b4a4ddafe89023 (patch)
tree9492b5c9960ec746afb0fa6acfdaa178cac121ca /backends
parent7902842bd10b17d8d5b6bfc586f440299646d48c (diff)
downloadmidimonster-a3a893f6b8b6c10ff281fcdfe0b4a4ddafe89023.tar.gz
midimonster-a3a893f6b8b6c10ff281fcdfe0b4a4ddafe89023.tar.bz2
midimonster-a3a893f6b8b6c10ff281fcdfe0b4a4ddafe89023.zip
Implement program change control for the midi, winmidi and jack backends (Fixes #79)
Diffstat (limited to 'backends')
-rw-r--r--backends/jack.c9
-rw-r--r--backends/jack.h7
-rw-r--r--backends/jack.md7
-rw-r--r--backends/midi.c18
-rw-r--r--backends/midi.md4
-rw-r--r--backends/winmidi.c12
-rw-r--r--backends/winmidi.h7
-rw-r--r--backends/winmidi.md4
8 files changed, 52 insertions, 16 deletions
diff --git a/backends/jack.c b/backends/jack.c
index 176144f..fe74a80 100644
--- a/backends/jack.c
+++ b/backends/jack.c
@@ -79,7 +79,7 @@ static int mmjack_midiqueue_append(mmjack_port* port, mmjack_channel_ident ident
}
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);
+ jack_midi_data_t* event_data = jack_midi_event_reserve(buffer, sample_offset, (type == midi_aftertouch || type == midi_program) ? 2 : 3);
if(!event_data){
LOG("Failed to reserve MIDI stream data");
@@ -95,7 +95,7 @@ static void mmjack_process_midiout(void* buffer, size_t sample_offset, uint8_t t
event_data[1] = value & 0x7F;
event_data[2] = (value >> 7) & 0x7F;
}
- else if(type == midi_aftertouch){
+ else if(type == midi_aftertouch || type == midi_program){
event_data[1] = value & 0x7F;
event_data[2] = 0;
}
@@ -192,7 +192,7 @@ static int mmjack_process_midi(instance* inst, mmjack_port* port, size_t nframes
ident.fields.sub_control = 0;
value = event.buffer[1] | (event.buffer[2] << 7);
}
- else if(ident.fields.sub_type == midi_aftertouch){
+ else if(ident.fields.sub_type == midi_aftertouch || ident.fields.sub_type == midi_program){
ident.fields.sub_control = 0;
value = event.buffer[1];
}
@@ -501,6 +501,9 @@ static int mmjack_parse_midispec(mmjack_channel_ident* ident, char* spec){
else if(!strncmp(next_token, "aftertouch", 10)){
ident->fields.sub_type = midi_aftertouch;
}
+ else if(!strncmp(next_token, "program", 7)){
+ ident->fields.sub_type = midi_program;
+ }
else{
LOGPF("Unknown MIDI control type in spec %s", spec);
return 1;
diff --git a/backends/jack.h b/backends/jack.h
index 762282b..42905f1 100644
--- a/backends/jack.h
+++ b/backends/jack.h
@@ -24,12 +24,13 @@ static int mmjack_shutdown(size_t n, instance** inst);
enum /*mmjack_midi_channel_type*/ {
midi_none = 0,
midi_note = 0x90,
- midi_cc = 0xB0,
midi_pressure = 0xA0,
+ midi_cc = 0xB0,
+ midi_program = 0xC0,
midi_aftertouch = 0xD0,
midi_pitchbend = 0xE0,
- midi_rpn = 0xF0,
- midi_nrpn = 0xF1
+ midi_rpn = 0xF1,
+ midi_nrpn = 0xF2
};
typedef union {
diff --git a/backends/jack.md b/backends/jack.md
index 4ff77f6..c67f060 100644
--- a/backends/jack.md
+++ b/backends/jack.md
@@ -56,6 +56,9 @@ MIDI ports provide subchannels for the various MIDI controls available. Each MID
corresponding pressure controls for each note, 128 control change (CC) controls (numbered likewise),
one channel wide "aftertouch" control and one channel-wide pitchbend control.
+Every MIDI channel also provides `rpn` and `nrpn` controls, which are implemented on top of the MIDI protocol, using
+the CC controls 101/100/99/98/38/6. Both control types have 14-bit IDs and 14-bit values.
+
A MIDI port subchannel is specified using the syntax `channel<channel>.<type><index>`. The shorthand `ch` may be
used instead of the word `channel` (Note that `channel` here refers to the MIDI channel number).
@@ -66,16 +69,18 @@ The following values are recognized for `type`:
* `pressure` - Note pressure/aftertouch messages
* `aftertouch` - Channel-wide aftertouch messages
* `pitch` - Channel pitchbend messages
+* `program` - Channel program change 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>`.
+The `pitch`, `aftertouch` and `program` messages/events are channel-wide, thus they can be specified as `channel<channel>.<type>`.
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
+jack1.midi_in.ch15.note1 > jack1.midi_out.ch4.program
```
The MIDI subchannel syntax is intentionally kept compatible to the different MIDI backends also supported
diff --git a/backends/midi.c b/backends/midi.c
index e32c975..10c8c4a 100644
--- a/backends/midi.c
+++ b/backends/midi.c
@@ -14,6 +14,7 @@ enum /*_midi_channel_type*/ {
pressure,
aftertouch,
pitchbend,
+ program,
rpn,
nrpn
};
@@ -167,6 +168,9 @@ static channel* midi_channel(instance* inst, char* spec, uint8_t flags){
else if(!strncmp(channel, "pitch", 5)){
ident.fields.type = pitchbend;
}
+ else if(!strncmp(channel, "program", 7)){
+ ident.fields.type = program;
+ }
else if(!strncmp(channel, "aftertouch", 10)){
ident.fields.type = aftertouch;
}
@@ -208,6 +212,9 @@ static void midi_tx(int port, uint8_t type, uint8_t channel, uint8_t control, ui
case aftertouch:
snd_seq_ev_set_chanpress(&ev, channel, value);
break;
+ case program:
+ snd_seq_ev_set_pgmchange(&ev, channel, value);
+ break;
}
snd_seq_event_output(sequencer, &ev);
@@ -269,6 +276,8 @@ static char* midi_type_name(uint8_t type){
return "aftertouch";
case pitchbend:
return "pitch";
+ case program:
+ return "program";
}
return "unknown";
}
@@ -399,6 +408,7 @@ static int midi_handle(size_t num, managed_fd* fds){
case SND_SEQ_EVENT_CHANPRESS:
ident.fields.type = aftertouch;
ident.fields.channel = ev->data.control.channel;
+ ident.fields.control = 0;
val.normalised = (double) ev->data.control.value / 127.0;
break;
case SND_SEQ_EVENT_PITCHBEND:
@@ -407,6 +417,12 @@ static int midi_handle(size_t num, managed_fd* fds){
ident.fields.channel = ev->data.control.channel;
val.normalised = ((double) ev->data.control.value + 8192) / 16383.0;
break;
+ case SND_SEQ_EVENT_PGMCHANGE:
+ ident.fields.type = program;
+ ident.fields.control = 0;
+ ident.fields.channel = ev->data.control.channel;
+ val.normalised = (double) ev->data.control.value / 127.0;
+ break;
case SND_SEQ_EVENT_CONTROLLER:
ident.fields.type = cc;
ident.fields.channel = ev->data.control.channel;
@@ -437,7 +453,7 @@ static int midi_handle(size_t num, managed_fd* fds){
}
if(midi_config.detect && event_type){
- if(ident.fields.type == pitchbend || ident.fields.type == aftertouch){
+ if(ident.fields.type == pitchbend || ident.fields.type == aftertouch || ident.fields.type == program){
LOGPF("Incoming data on channel %s.ch%d.%s", inst->name, ident.fields.channel, event_type);
}
else{
diff --git a/backends/midi.md b/backends/midi.md
index 60a4d06..6280205 100644
--- a/backends/midi.md
+++ b/backends/midi.md
@@ -31,13 +31,14 @@ The MIDI backend supports mapping different MIDI events to MIDIMonster channels.
* `pressure` - Note pressure/aftertouch messages
* `aftertouch` - Channel-wide aftertouch messages
* `pitch` - Channel pitchbend messages
+* `program` - Channel program change messages
* `rpn` - Registered parameter numbers (14-bit extension)
* `nrpn` - Non-registered parameter numbers (14-bit extension)
A MIDIMonster channel is specified using the syntax `channel<channel>.<type><index>`. The shorthand `ch` may be
used instead of the word `channel` (Note that `channel` here refers to the MIDI channel number).
-The `pitch` and `aftertouch` events are channel-wide, thus they can be specified as `channel<channel>.<type>`.
+The `pitch`, `aftertouch` and `program` messages/events are channel-wide, thus they can be specified as `channel<channel>.<type>`.
MIDI channels range from `0` to `15`. Each MIDI channel consists of 128 notes (numbered `0` through `127`), which
additionally each have a pressure control, 128 CC's (numbered likewise), a channel pressure control (also called
@@ -53,6 +54,7 @@ midi1.channel15.pressure1 > midi1.channel0.note0
midi1.ch1.aftertouch > midi2.ch2.cc0
midi1.ch0.pitch > midi2.ch1.pitch
midi1.ch0.nrpn900 > midi2.ch0.rpn1
+midi2.ch15.note1 > midi1.ch2.program
```
#### Known bugs / problems
diff --git a/backends/winmidi.c b/backends/winmidi.c
index ec0fb44..a1fa686 100644
--- a/backends/winmidi.c
+++ b/backends/winmidi.c
@@ -169,6 +169,9 @@ static channel* winmidi_channel(instance* inst, char* spec, uint8_t flags){
else if(!strncmp(next_token, "aftertouch", 10)){
ident.fields.type = aftertouch;
}
+ else if(!strncmp(next_token, "program", 7)){
+ ident.fields.type = program;
+ }
else{
LOGPF("Unknown control type in %s", spec);
return NULL;
@@ -203,7 +206,7 @@ static void winmidi_tx(HMIDIOUT port, uint8_t type, uint8_t channel, uint8_t con
output.components.data1 = value & 0x7F;
output.components.data2 = (value >> 7) & 0x7F;
}
- else if(type == aftertouch){
+ else if(type == aftertouch || type == program){
output.components.data1 = value;
output.components.data2 = 0;
}
@@ -270,6 +273,8 @@ static char* winmidi_type_name(uint8_t typecode){
return "aftertouch";
case pitchbend:
return "pitch";
+ case program:
+ return "program";
}
return "unknown";
}
@@ -294,7 +299,8 @@ static int winmidi_handle(size_t num, managed_fd* fds){
if(backend_config.detect){
//pretty-print channel-wide events
if(backend_config.event[u].channel.fields.type == pitchbend
- || backend_config.event[u].channel.fields.type == aftertouch){
+ || backend_config.event[u].channel.fields.type == aftertouch
+ || backend_config.event[u].channel.fields.type == program){
LOGPF("Incoming data on channel %s.ch%d.%s, value %f",
backend_config.event[u].inst->name,
backend_config.event[u].channel.fields.channel,
@@ -450,7 +456,7 @@ static void CALLBACK winmidi_input_callback(HMIDIIN device, unsigned message, DW
val.normalised = (double) ((input.components.data2 << 7) | input.components.data1) / 16383.0;
val.raw.u64 = input.components.data2 << 7 | input.components.data1;
}
- else if(ident.fields.type == aftertouch){
+ else if(ident.fields.type == aftertouch || ident.fields.type == program){
ident.fields.control = 0;
val.normalised = (double) input.components.data1 / 127.0;
val.raw.u64 = input.components.data1;
diff --git a/backends/winmidi.h b/backends/winmidi.h
index 4d3e2dd..40b3554 100644
--- a/backends/winmidi.h
+++ b/backends/winmidi.h
@@ -31,12 +31,13 @@ typedef struct /*_winmidi_instance_data*/ {
enum /*_winmidi_channel_type*/ {
none = 0,
note = 0x90,
- cc = 0xB0,
pressure = 0xA0,
+ cc = 0xB0,
+ program = 0xC0,
aftertouch = 0xD0,
pitchbend = 0xE0,
- rpn = 0xF0,
- nrpn = 0xF1
+ rpn = 0xF1,
+ nrpn = 0xF2
};
typedef union {
diff --git a/backends/winmidi.md b/backends/winmidi.md
index 4de792a..9e7d9cc 100644
--- a/backends/winmidi.md
+++ b/backends/winmidi.md
@@ -33,13 +33,14 @@ The `winmidi` backend supports mapping different MIDI events as MIDIMonster chan
* `pressure` - Note pressure/aftertouch messages
* `aftertouch` - Channel-wide aftertouch messages
* `pitch` - Channel pitchbend messages
+* `program` - Channel program change messages
* `rpn` - Registered parameter numbers (14-bit extension)
* `nrpn` - Non-registered parameter numbers (14-bit extension)
A MIDIMonster channel is specified using the syntax `channel<channel>.<type><index>`. The shorthand `ch` may be
used instead of the word `channel` (Note that `channel` here refers to the MIDI channel number).
-The `pitch` and `aftertouch` events are channel-wide, thus they can be specified as `channel<channel>.<type>`.
+The `pitch`, `aftertouch` and `program` messages/events are channel-wide, thus they can be specified as `channel<channel>.<type>`.
MIDI channels range from `0` to `15`. Each MIDI channel consists of 128 notes (numbered `0` through `127`), which
additionally each have a pressure control, 128 CC's (numbered likewise), a channel pressure control (also called
@@ -55,6 +56,7 @@ midi1.channel15.pressure1 > midi1.channel0.note0
midi1.ch1.aftertouch > midi2.ch2.cc0
midi1.ch0.pitch > midi2.ch1.pitch
midi2.ch0.nrpn900 > midi1.ch1.rpn1
+midi2.ch15.note1 > midi1.ch2.program
```
#### Known bugs / problems