aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--backends/maweb.c189
-rw-r--r--backends/maweb.h18
-rw-r--r--backends/maweb.md76
-rw-r--r--backends/winmidi.c3
4 files changed, 181 insertions, 105 deletions
diff --git a/backends/maweb.c b/backends/maweb.c
index c88ca8c..be356d0 100644
--- a/backends/maweb.c
+++ b/backends/maweb.c
@@ -19,72 +19,57 @@ static uint64_t update_interval = 50;
static uint64_t last_update = 0;
static uint64_t updates_inflight = 0;
-static char* cmdline_keys[] = {
- "SET",
- "PREV",
- "NEXT",
- "CLEAR",
- "FIXTURE_CHANNEL",
- "FIXTURE_GROUP_PRESET",
- "EXEC_CUE",
- "STORE_UPDATE",
- "OOPS",
- "ESC",
- "0",
- "1",
- "2",
- "3",
- "4",
- "5",
- "6",
- "7",
- "8",
- "9",
- "PUNKT",
- "PLUS",
- "MINUS",
- "THRU",
- "IF",
- "AT",
- "FULL",
- "HIGH",
- "ENTER",
- "OFF",
- "ON",
- "ASSIGN",
- "LABEL",
- "COPY",
- "TIME",
- "PAGE",
- "MACRO",
- "DELETE",
- "GOTO",
- "GO_PLUS",
- "GO_MINUS",
- "PAUSE",
- "SELECT",
- "FIXTURE",
- "SEQU",
- "CUE",
- "PRESET",
- "EDIT",
- "UPDATE",
- "EXEC",
- "STORE",
- "GROUP",
- "PROG_ONLY",
- "SPECIAL_DIALOGUE",
- "SOLO",
- "ODD",
- "EVEN",
- "WINGS",
- "RESET",
- "MA",
- "layerMode",
- "featureSort",
- "fixtureSort",
- "channelSort",
- "hideName"
+static maweb_command_key cmdline_keys[] = {
+ {"PREV", 109, 0, 1}, {"SET", 108, 1, 0, 1}, {"NEXT", 110, 0, 1},
+ {"TIME", 58, 1, 1}, {"EDIT", 55, 1, 1}, {"UPDATE", 57, 1, 1},
+ {"OOPS", 53, 1, 1}, {"ESC", 54, 1, 1}, {"CLEAR", 105, 1, 1},
+ {"0", 86, 1, 1}, {"1", 87, 1, 1}, {"2", 88, 1, 1},
+ {"3", 89, 1, 1}, {"4", 90, 1, 1}, {"5", 91, 1, 1},
+ {"6", 92, 1, 1}, {"7", 93, 1, 1}, {"8", 94, 1, 1},
+ {"9", 95, 1, 1}, {"PUNKT", 98, 1, 1}, {"ENTER", 106, 1, 1},
+ {"PLUS", 96, 1, 1}, {"MINUS", 97, 1, 1}, {"THRU", 102, 1, 1},
+ {"IF", 103, 1, 1}, {"AT", 104, 1, 1}, {"FULL", 99, 1, 1},
+ {"MA", 68, 0, 1}, {"HIGH", 100, 1, 1, 1}, {"SOLO", 101, 1, 1, 1},
+ {"SELECT", 42, 1, 1}, {"OFF", 43, 1, 1}, {"ON", 46, 1, 1},
+ {"ASSIGN", 63, 1, 1}, {"LABEL", 0, 1, 1},
+ {"COPY", 73, 1, 1}, {"DELETE", 69, 1, 1}, {"STORE", 59, 1, 1},
+ {"GOTO", 56, 1, 1}, {"PAGE", 70, 1, 1}, {"MACRO", 71, 1, 1},
+ {"PRESET", 72, 1, 1}, {"SEQU", 74, 1, 1}, {"CUE", 75, 1, 1},
+ {"EXEC", 76, 1, 1}, {"FIXTURE", 83, 1, 1}, {"GROUP", 84, 1, 1},
+ {"GO_MINUS", 10, 1, 1}, {"PAUSE", 9, 1, 1}, {"GO_PLUS", 11, 1, 1},
+
+ {"FIXTURE_CHANNEL", 0, 1, 1}, {"FIXTURE_GROUP_PRESET", 0, 1, 1},
+ {"EXEC_CUE", 0, 1, 1}, {"STORE_UPDATE", 0, 1, 1}, {"PROG_ONLY", 0, 1, 1, 1},
+ {"SPECIAL_DIALOGUE", 0, 1, 1},
+ {"ODD", 0, 1, 1}, {"EVEN", 0, 1, 1},
+ {"WINGS", 0, 1, 1}, {"RESET", 0, 1, 1},
+ //gma2 internal only
+ {"CHPGPLUS", 3}, {"CHPGMINUS", 4},
+ {"FDPGPLUS", 5}, {"FDPGMINUS", 6},
+ {"BTPGPLUS", 7}, {"BTPGMINUS", 8},
+ {"X1", 12}, {"X2", 13}, {"X3", 14},
+ {"X4", 15}, {"X5", 16}, {"X6", 17},
+ {"X7", 18}, {"X8", 19}, {"X9", 20},
+ {"X10", 21}, {"X11", 22}, {"X12", 23},
+ {"X13", 24}, {"X14", 25}, {"X15", 26},
+ {"X16", 27}, {"X17", 28}, {"X18", 29},
+ {"X19", 30}, {"X20", 31},
+ {"V1", 120}, {"V2", 121}, {"V3", 122},
+ {"V4", 123}, {"V5", 124}, {"V6", 125},
+ {"V7", 126}, {"V8", 127}, {"V9", 128},
+ {"V10", 129},
+ {"NIPPLE", 40},
+ {"TOOLS", 119}, {"SETUP", 117}, {"BACKUP", 117},
+ {"BLIND", 60}, {"FREEZE", 61}, {"PREVIEW", 62},
+ {"FIX", 41}, {"TEMP", 44}, {"TOP", 45},
+ {"VIEW", 66}, {"EFFECT", 67}, {"CHANNEL", 82},
+ {"MOVE", 85}, {"BLACKOUT", 65},
+ {"PLEASE", 106},
+ {"LIST", 32}, {"USER1", 33}, {"USER2", 34},
+ {"ALIGN", 64}, {"HELP", 116},
+ {"UP", 107}, {"DOWN", 111},
+ {"FASTREVERSE", 47}, {"LEARN", 48}, {"FASTFORWARD", 49},
+ {"GO_MINUS_SMALL", 50}, {"PAUSE_SMALL", 51}, {"GO_PLUS_SMALL", 52}
};
int init(){
@@ -199,6 +184,22 @@ static int maweb_configure_instance(instance* inst, char* option, char* value){
return 1;
#endif
}
+ else if(!strcmp(option, "cmdline")){
+ if(!strcmp(value, "console")){
+ data->cmdline = cmd_console;
+ }
+ else if(!strcmp(value, "remote")){
+ data->cmdline = cmd_remote;
+ }
+ else if(!strcmp(value, "downgrade")){
+ data->cmdline = cmd_downgrade;
+ }
+ else{
+ fprintf(stderr, "Unknown maweb commandline mode %s for instance %s\n", value, inst->name);
+ return 1;
+ }
+ return 0;
+ }
fprintf(stderr, "Unknown configuration parameter %s for maweb instance %s\n", option, inst->name);
return 1;
@@ -268,10 +269,15 @@ static channel* maweb_channel(instance* inst, char* spec){
chan.index = strtoul(next_token, NULL, 10);
}
else{
- for(n = 0; n < sizeof(cmdline_keys) / sizeof(char*); n++){
- //FIXME this is broken for layerMode
- if(!strcmp(spec, cmdline_keys[n]) || (*spec == 'l' && !strcmp(spec + 1, cmdline_keys[n]))){
- chan.type = (*spec == 'l') ? cmdline_local : cmdline;
+ for(n = 0; n < sizeof(cmdline_keys) / sizeof(maweb_command_key); n++){
+ if(!strcmp(spec, cmdline_keys[n].name)){
+ if((data->cmdline == cmd_remote && !cmdline_keys[n].press && !cmdline_keys[n].release)
+ || (data->cmdline == cmd_console && !cmdline_keys[n].lua)){
+ fprintf(stderr, "maweb cmdline key %s does not work with the current commandline mode for instance %s\n", spec, inst->name);
+ return NULL;
+ }
+
+ chan.type = cmdline;
chan.index = n + 1;
chan.page = 1;
break;
@@ -599,6 +605,8 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le
field = json_obj_str(payload, "appType", NULL);
if(!strncmp(field, "dot2", 4)){
data->peer_type = peer_dot2;
+ //the dot2 can't handle lua commands
+ data->cmdline = cmd_remote;
}
else if(!strncmp(field, "gma2", 4)){
data->peer_type = peer_ma2;
@@ -860,14 +868,41 @@ static int maweb_set(instance* inst, size_t num, channel** c, channel_value* v){
data->session);
break;
case cmdline:
- snprintf(xmit_buffer, sizeof(xmit_buffer),
- "{\"keyname\":\"%s\","
- //"\"autoSubmit\":false,"
- "\"value\":%d"
- "}", cmdline_keys[chan->index],
- (v[n].normalised > 0.9) ? 1 : 0);
+ if(cmdline_keys[chan->index].lua
+ && (data->cmdline == cmd_console || data->cmdline == cmd_downgrade)
+ && data->peer_type != peer_dot2){
+ //push canbus events
+ snprintf(xmit_buffer, sizeof(xmit_buffer),
+ "{\"command\":\"LUA 'gma.canbus.hardkey(%d, %s, false)'\","
+ "\"requestType\":\"command\","
+ "\"session\":%" PRIu64
+ "}", cmdline_keys[chan->index].lua,
+ (v[n].normalised > 0.9) ? "true" : "false",
+ data->session);
+ }
+ else if((cmdline_keys[chan->index].press || cmdline_keys[chan->index].release)
+ && (data->cmdline != cmd_console)){
+ //send press/release events if required
+ if((cmdline_keys[chan->index].press && v[n].normalised > 0.9)
+ || (cmdline_keys[chan->index].release && v[n].normalised < 0.9)){
+ snprintf(xmit_buffer, sizeof(xmit_buffer),
+ "{\"keyname\":\"%s\","
+ "\"autoSubmit\":%s,"
+ "\"value\":%d"
+ "}", cmdline_keys[chan->index].name,
+ cmdline_keys[chan->index].auto_submit ? "true" : "null",
+ (v[n].normalised > 0.9) ? 1 : 0);
+ }
+ else{
+ continue;
+ }
+ }
+ else{
+ fprintf(stderr, "maweb commandline key %s not executed on %s due to mode mismatch\n",
+ cmdline_keys[chan->index].name, inst->name);
+ continue;
+ }
break;
- //TODO cmdline_local
default:
fprintf(stderr, "maweb control not yet implemented\n");
return 1;
diff --git a/backends/maweb.h b/backends/maweb.h
index 3738367..b9de68f 100644
--- a/backends/maweb.h
+++ b/backends/maweb.h
@@ -25,8 +25,7 @@ typedef enum /*_maweb_channel_type*/ {
exec_button = 2, //gma: 0 dot: 0
exec_lower = 3, //gma: 1 dot: 1
exec_upper = 4, //gma: 2 dot: 0
- cmdline,
- cmdline_local
+ cmdline
} maweb_channel_type;
typedef enum /*_maweb_peer_type*/ {
@@ -43,6 +42,12 @@ typedef enum /*_ws_conn_state*/ {
ws_closed
} maweb_state;
+typedef enum /*_maweb_cmdline_mode*/ {
+ cmd_remote = 0,
+ cmd_console,
+ cmd_downgrade
+} maweb_cmdline_mode;
+
typedef enum /*_ws_frame_op*/ {
ws_text = 1,
ws_binary = 2,
@@ -50,6 +55,14 @@ typedef enum /*_ws_frame_op*/ {
ws_pong = 10
} maweb_operation;
+typedef struct {
+ char* name;
+ unsigned lua;
+ uint8_t press;
+ uint8_t release;
+ uint8_t auto_submit;
+} maweb_command_key;
+
typedef struct /*_maweb_channel*/ {
maweb_channel_type type;
uint16_t page;
@@ -73,6 +86,7 @@ typedef struct /*_maweb_instance_data*/ {
size_t channels;
maweb_channel_data* channel;
+ maweb_cmdline_mode cmdline;
int fd;
maweb_state state;
diff --git a/backends/maweb.md b/backends/maweb.md
index 93cd776..a18cde4 100644
--- a/backends/maweb.md
+++ b/backends/maweb.md
@@ -26,6 +26,14 @@ Web Remote. Set a web remote password using the option below the activation sett
| `host` | `10.23.42.21 80` | none | Host address (and optional port) of the MA Web Remote |
| `user` | `midimonster` | none | User for the remote session (GrandMA2) |
| `password` | `midimonster` | `midimonster` | Password for the remote session |
+| `cmdline` | `console` | `remote` | Commandline key handling mode (see below) |
+
+The per-instance command line mode may be one of `remote`, `console` or `downgrade`. The first option handles
+command keys with a "virtual" commandline belonging to the Web Remote connection. Any commands entered are
+not visible on the main console. The `console` mode is only available with GrandMA2 remotes and injects the
+key events into the main console's command line. When connected to a dot2 console, the use of commandline keys
+will not be possible. With the `downgrade` mode, keys are handled on the console if possible, falling back to
+remote handling if not.
#### Channel specification
@@ -33,7 +41,7 @@ Currently, three types of MA controls can be assigned, with each having some sub
* Fader executor
* Button executor
-* Command line buttons
+* Command keys
##### Executors
@@ -70,33 +78,52 @@ mw1.page2.button103 > mw1.page3.fader101
mw1.page2.button803 > mw1.page3.button516
```
-##### Command line buttons
+##### Command keys
-Command line buttons will be pressed when the incoming event value is greater than `0.9` and released when it is less than that.
+Command keys will be pressed when the incoming event value is greater than `0.9` and released when it is less than that.
They can be mapped using the syntax
```
-mw1.<button-name>
+mw1.<key-name>
```
-The following button names are recognized by the backend:
-
-| Supported | Command | Line | Keys | | | |
-|---------------|---------------|---------------|---------------|-----------------------|-------------------------------|---------------|
-| `SET` | `PREV` | `NEXT` | `CLEAR` | `FIXTURE_CHANNEL` | `FIXTURE_GROUP_PRESET` | `EXEC_CUE` |
-| `STORE_UPDATE`| `OOPS` | `ESC` | `OFF` | `ON` | `MA` | `STORE` |
-| `0` | `1` | `2` | `3` | `4` | `5` | `6` |
-| `7` | `8` | `9` | `PUNKT` | `PLUS` | `MINUS` | `THRU` |
-| `IF` | `AT` | `FULL` | `HIGH` | `ENTER` | `ASSIGN` | `LABEL` |
-| `COPY` | `TIME` | `PAGE` | `MACRO` | `DELETE` | `GOTO` | `GO_PLUS` |
-| `GO_MINUS` | `PAUSE` | `SELECT` | `FIXTURE` | `SEQU` | `CUE` | `PRESET` |
-| `EDIT` | `UPDATE` | `EXEC` | `GROUP` | `PROG_ONLY` | `SPECIAL_DIALOGUE` | `SOLO` |
-| `ODD` | `EVEN` | `WINGS` | `RESET` | `layerMode` | `featureSort` | `fixtureSort` |
-| `channelSort` | `hideName` | | | | | |
-
-Note that each Web Remote connection has it's own command line, as such commands entered using this backend will not affect
-the command line on the main console. To do that, you will need to use another backend to feed input to the MA, such as
-the ArtNet or MIDI backends.
+The following keys are mappable in all commandline modes and work on all consoles
+
+| Supported | Command | Line | Keys | | |
+|---------------|---------------|---------------|---------------|---------------|---------------|
+| `PREV` | `SET` | `NEXT` | `TIME` | `EDIT` | `UPDATE` |
+| `OOPS` | `ESC` | `CLEAR` | `0` | `1` | `2` |
+| `3` | `4` | `5` | `6` | `7` | `8` |
+| `9` | `PUNKT` | `ENTER` | `PLUS` | `MINUS` | `THRU` |
+| `IF` | `AT` | `FULL` | `MA` | `HIGH` | `SOLO` |
+| `SELECT` | `OFF` | `ON` | `ASSIGN` | `COPY` | `DELETE` |
+| `STORE` | `GOTO` | `PAGE` | `MACRO` | `PRESET` | `SEQU` |
+| `CUE` | `EXEC` | `FIXTURE` | `GROUP` | `GO_MINUS` | `PAUSE` |
+| `GO_PLUS` | | | | | |
+
+The following keys only work in the `remote` or `downgrade` commandline mode, but on all consoles
+
+| Web | Remote | specific | | |
+|---------------|-----------------------|-------------------------------|---------------|-----------------------|
+| `LABEL` |`FIXTURE_CHANNEL` | `FIXTURE_GROUP_PRESET` | `EXEC_CUE` | `STORE_UPDATE` |
+| `PROG_ONLY` | `SPECIAL_DIALOGUE` | `ODD` | `EVEN` | `WINGS` |
+| `RESET` | | | | |
+
+The following keys only work in the `console` or `downgrade` command line modes on a GrandMA2
+
+| GrandMA2 | console | only | | | |
+|---------------|---------------|---------------|---------------|---------------|---------------|
+| `CHPGPLUS` | `CHPGMINUS` | `FDPGPLUS` | `FDPGMINUS` | `BTPGPLUS` | `BTPGMINUS` |
+| `X1` | `X2` | `X3` | `X4` | `X5` | `X6` |
+| `X7` | `X8` | `X9` | `X10` | `X11` | `X12` |
+| `X13` | `X14` | `X15` | `X16` | `X17` | `X18` |
+| `X19` | `X20` | `V1` | `V2` | `V3` | `V4` |
+| `V5` | `V6` | `V7` | `V8` | `V9` | `V10` |
+| `NIPPLE` | `TOOLS` | `SETUP` | `BACKUP` | `BLIND` | `FREEZE` |
+| `PREVIEW` | `FIX` | `TEMP` | `TOP` | `VIEW` | `EFFECT` |
+| `CHANNEL` | `MOVE` | `BLACKOUT` | `PLEASE` | `LIST` | `USER1` |
+| `USER2` | `ALIGN` | `HELP` | `UP` | `DOWN` | `FASTREVERSE` |
+| `LEARN` | `FASTFORWARD` | `GO_MINUS_SMALL` | `PAUSE_SMALL` | `GO_PLUS_SMALL` | |
#### Known bugs / problems
@@ -109,6 +136,5 @@ Data input from the console is done by actively querying the state of all mapped
at low latency. A lower input interval value will produce data with lower latency, at the cost of network & CPU usage.
Higher values will make the input "step" more, but will not consume as many CPU cycles and network bandwidth.
-When requesting button executor events on the fader pages (execs 101 to 222) of a dot2 console, map at least one fader control from the 0 - 22 range or input will not work due to strange limitations in the MA Web API.
-
-Command line events are sent, but I'm not sure they're being handled yet.
+When requesting button executor events on the fader pages (execs 101 to 222) of a dot2 console, map at least one fader control from the 0 - 22 range
+or input will not work due to strange limitations in the MA Web API.
diff --git a/backends/winmidi.c b/backends/winmidi.c
index 67187ac..13c4b4a 100644
--- a/backends/winmidi.c
+++ b/backends/winmidi.c
@@ -21,6 +21,7 @@ static struct {
};
//TODO detect option
+//TODO receive feedback socket until EAGAIN
int init(){
backend winmidi = {
@@ -465,7 +466,7 @@ static int winmidi_start(){
}
//open the feedback sockets
- //for some reason this while construct fails to work on 'real' windows with ipv6
+ //for some reason the feedback connection fails to work on 'real' windows with ipv6
backend_config.socket_pair[0] = mmbackend_socket("127.0.0.1", "0", SOCK_DGRAM, 1, 0);
if(backend_config.socket_pair[0] < 0){
fprintf(stderr, "winmidi failed to open feedback socket\n");